@gui-chat-plugin/sequence 0.1.0 → 0.2.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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - **State per conversation**: the slideshow in progress, the steps and panels waiting for the
6
+ user, and the repeat guard are kept per `context.conversationId` (gui-chat-protocol 2.2), the 50
7
+ most recently used. A host that runs `execute()` on a server for several browser tabs mixed them:
8
+ one tab's slide was filed under the other tab's slideshow (found in the review of
9
+ receptron/MulmoChat#232). Needs gui-chat-protocol ^2.2.0.
10
+
3
11
  ## 0.1.0
4
12
 
5
13
  First release: `presentSlide`, `defineStoryboard` and `presentPanel`, from MulmoChat and
package/README.md CHANGED
@@ -31,6 +31,7 @@ The core entry (`@gui-chat-plugin/sequence`) has the same tools without Views, f
31
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
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
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
+ | `conversationId` | keeping each conversation's slideshows, holds and repeats apart | one conversation |
34
35
  | `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
 
36
37
  `generateImage` and `editImages` are gui-chat-protocol's `context.app` conventions: they save each
@@ -52,10 +53,12 @@ model to explain the step.
52
53
  ### State
53
54
 
54
55
  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.
56
+ runs, per conversation (`context.conversationId`, gui-chat-protocol 2.2): a host that runs
57
+ `execute()` for several browser tabs or sessions in one process sets it, so they don't mix. A host
58
+ that doesn't has one conversation. The 50 most recently used conversations are kept. Calls may
59
+ overlap (a host that runs `execute()` on a server gets them as concurrent requests): a call is
60
+ claimed before anything is awaited, so an identical one waits for it and a later step is held while
61
+ it is drawn.
59
62
 
60
63
  ## Records
61
64
 
@@ -25,3 +25,10 @@ export declare function usableReferences(context: SequenceContext, paths: readon
25
25
  /** A picture from the host: from `references` (saved paths) when there are
26
26
  * any, else from the prompt alone. A failure is a result saying why. */
27
27
  export declare function drawPicture(context: SequenceContext, prompt: string, references?: readonly string[]): Promise<ToolResult>;
28
+ /**
29
+ * State kept between calls, one per conversation (gui-chat-protocol's
30
+ * ToolContext.conversationId): a host that runs execute() on a server for
31
+ * several browser tabs or sessions would otherwise mix them (one tab's slide
32
+ * filed under the other's slideshow). A host that doesn't say has one.
33
+ */
34
+ export declare function perConversation<T>(create: () => T): (context: SequenceContext) => T;
@@ -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=50;function N(e){let t=new Map;return n=>{let r=n.conversationId??``,i=t.get(r);if(i===void 0?i=e():t.delete(r),t.set(r,i),t.size>M){let e=t.keys().next().value;e!==void 0&&t.delete(e)}return i}}var P=`slideshows`,F=`storyboards`,I={},L=new WeakMap,R=new WeakMap;function z(e){let t=L.get(e);return t||L.set(e,t=new Map),t}function ne(t,n){let r=R.get(t);r||R.set(t,r=new Map);let i=r.get(n);return i||r.set(n,i=(0,e.createSerialLock)()),i}var B=(e,t)=>`${e}/${t}.json`;async function V(e,t){let n=z(e??I),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 H(e,t,n){return s.test(n)?V(e,B(t,n)):null}async function U(e,t,n,r){let i=B(t,n),a=e??I;return ne(a,i)(async()=>{let t=r(await V(e,i));if(z(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 W=e=>`the ${e} could not be saved, so it lasts only until the app or server restarts`,re=6e4;function G(){let e=new Map;return{claim:t=>{let n=Date.now();for(let[t,r]of e)n-r.at>re&&e.delete(t);let r=e.get(t);if(r)return{earlier:r.shown};let i=()=>void 0,a=new Promise(e=>i=e);return e.set(t,{at:n,shown:a}),{settle:n=>{!n&&e.get(t)?.shown===a&&e.delete(t),i(n)}}}}}function K({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 q=(e,{slide:t,totalSlides:n,title:r,imagePrompt:i})=>JSON.stringify([e,t,n,r,i]),J=N(()=>({latest:{presentation:null,steps:null},lastShown:null,repeats:G()}));function Y(e,t){let n=e.latest[t.mode],r=n?.totalSlides===t.totalSlides&&(t.slide!==1||n.firstTitle===t.title);if(n&&r)return n;let i={id:O(),mode:t.mode,totalSlides:t.totalSlides,firstTitle:t.slide===1?t.title:``,slides:new Map,current:0,shownAt:-1/0};return e.latest[t.mode]=i,i}function X(e,t,n,r){t.current=n.slide,t.shownAt=Date.now(),e.lastShown=t;let i=n.mode===`steps`?`step`:`slide`,{uuid:a,...o}=r;return{...o,message:`${i} ${n.slide} of ${n.totalSlides} is on the screen again, as it was`,instructions:m(n),instructionsRequired:!0,sequence:K(n)}}function ie(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 ae(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=ie(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 U(e.files?.artifacts,P,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?``:W(`slideshow`)].filter(Boolean).join(`; `),instructions:m(n),instructionsRequired:!0,sequence:K(n)}}async function Z(e,t){let n=p(t);if(!n)return{message:f,sequence:null};let r=J(e),i=Y(r,n),a=n.mode===`steps`;if(a&&n.slide>i.current&&i.current>0&&!D(e,i.shownAt)){console.info(`[sequence] holding step ${n.slide} for the user`);let e=i.shownAt===1/0?`is still being drawn`:`is still on the screen`;return{message:`step ${n.slide} was not shown: the user hasn't said they're ready yet, and step ${i.current} ${e}. Wait for them before calling presentSlide for step ${n.slide}.`,cancelled:!0}}let o=a?i.slides.get(n.slide):void 0;if(o)return te(e.currentResult,o)?{message:`step ${n.slide} of ${n.totalSlides} is already on the screen`,cancelled:!0}:X(r,i,n,o);let s=r.repeats.claim(q(i.id,n));if(`earlier`in s){if(!await s.earlier)return Z(e,t);let a=i.slides.get(n.slide);return a&&r.lastShown!==i?X(r,i,n,a):{message:`slide ${n.slide} of ${n.totalSlides} is already on the screen`,cancelled:!0}}let{settle:c}=s,l={current:i.current,shownAt:i.shownAt};i.current=n.slide,i.shownAt=1/0;let u=!1;try{let t=await ae(e,i,n);return u=!!E(t).imageData,u&&(i.slides.set(n.slide,t),i.current=n.slide,i.shownAt=Date.now(),r.lastShown=i),t}finally{c(u),!u&&i.current===n.slide&&(i.current=l.current,i.shownAt=l.shownAt)}}var oe=({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}"`}),se=({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}}),Q=(e,t)=>e?`${e}. ${t}`:t,ce=(e,{name:t,description:n})=>Q(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 le(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,ce(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 U(e.files?.artifacts,F,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?``:W(`storyboard`)].filter(Boolean).join(`; `),instructions:v(o),instructionsRequired:!0,sequence:oe(o)}}function ue(e,t,n){let r=[Q(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 de(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=ue(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 U(e.files?.artifacts,F,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?``:W(`storyboard`)].filter(Boolean).join(`; `),instructions:x(t,n.panel,d),instructionsRequired:!0,sequence:se(t,n.panel,d)}}var fe=N(()=>({repeats:G(),awaitingChoice:new Map}));async function $(e,t){let n=b(t);if(!n)return{message:y,sequence:null};let{storyboardId:r,panel:i}=n,{repeats:a,awaitingChoice:o}=fe(e),s=o.get(r);if(s&&i>s.panel&&!D(e,s.shownAt))return console.info(`[sequence] holding panel ${i} for the user`),{message:s.shownAt===1/0?`panel ${i} was not shown: panel ${s.panel} is still being drawn. Wait for it before going on.`:`panel ${i} was not shown: the user hasn't picked a choice yet, and panel ${s.panel} is still on the screen. Wait for their answer.`,cancelled:!0};let c=a.claim(JSON.stringify(n));if(`earlier`in c)return await c.earlier?{message:`panel ${i} is already on the screen`,cancelled:!0}:$(e,t);let{settle:l}=c,u={panel:i,shownAt:1/0},d=o.get(r);o.set(r,u);let f=()=>o.get(r)===u,p=!1;try{let t=await H(e.files?.artifacts,F,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 de(e,t,n);p=!!E(a).imageData;let s=a.data;return p&&f()&&(s?.choices?.length?o.set(r,{panel:i,shownAt:Date.now()}):o.delete(r)),a}finally{l(p),!p&&f()&&(d?o.set(r,d):o.delete(r))}}var pe={toolDefinition:g,generatingMessage:`Making the slide...`,isEnabled:()=>!0,execute:(e,t)=>Z(e,t)},me={toolDefinition:C,generatingMessage:`Drawing the characters...`,isEnabled:()=>!0,execute:(e,t)=>le(e,t)},he={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 F}}),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 P}}),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 he}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,"p",{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return pe}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return me}}),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}});
@@ -221,22 +221,34 @@ async function j(e, t, n = []) {
221
221
  return { message: `image generation failed: ${e instanceof Error ? e.message : String(e)}` };
222
222
  }
223
223
  }
224
+ var M = 50;
225
+ function N(e) {
226
+ let t = /* @__PURE__ */ new Map();
227
+ return (n) => {
228
+ let r = n.conversationId ?? "", i = t.get(r);
229
+ if (i === void 0 ? i = e() : t.delete(r), t.set(r, i), t.size > M) {
230
+ let e = t.keys().next().value;
231
+ e !== void 0 && t.delete(e);
232
+ }
233
+ return i;
234
+ };
235
+ }
224
236
  //#endregion
225
237
  //#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;
238
+ var P = "slideshows", F = "storyboards", I = {}, L = /* @__PURE__ */ new WeakMap(), R = /* @__PURE__ */ new WeakMap();
239
+ function z(e) {
240
+ let t = L.get(e);
241
+ return t || L.set(e, t = /* @__PURE__ */ new Map()), t;
230
242
  }
231
- function R(t, n) {
232
- let r = I.get(t);
233
- r || I.set(t, r = /* @__PURE__ */ new Map());
243
+ function ne(t, n) {
244
+ let r = R.get(t);
245
+ r || R.set(t, r = /* @__PURE__ */ new Map());
234
246
  let i = r.get(n);
235
247
  return i || r.set(n, i = e()), i;
236
248
  }
237
- var z = (e, t) => `${e}/${t}.json`;
238
- async function B(e, t) {
239
- let n = L(e ?? P), r = n.get(t);
249
+ var B = (e, t) => `${e}/${t}.json`;
250
+ async function V(e, t) {
251
+ let n = z(e ?? I), r = n.get(t);
240
252
  if (r) return r;
241
253
  if (!e) return null;
242
254
  try {
@@ -246,14 +258,14 @@ async function B(e, t) {
246
258
  return null;
247
259
  }
248
260
  }
249
- async function V(e, t, n) {
250
- return s.test(n) ? B(e, z(t, n)) : null;
261
+ async function H(e, t, n) {
262
+ return s.test(n) ? V(e, B(t, n)) : null;
251
263
  }
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;
264
+ async function U(e, t, n, r) {
265
+ let i = B(t, n), a = e ?? I;
266
+ return ne(a, i)(async () => {
267
+ let t = r(await V(e, i));
268
+ if (z(a).set(i, t), !e) return !1;
257
269
  try {
258
270
  return await e.write(i, JSON.stringify(t, null, 2)), !0;
259
271
  } catch (e) {
@@ -261,12 +273,12 @@ async function H(e, t, n, r) {
261
273
  }
262
274
  });
263
275
  }
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() {
276
+ var W = (e) => `the ${e} could not be saved, so it lasts only until the app or server restarts`, re = 6e4;
277
+ function G() {
266
278
  let e = /* @__PURE__ */ new Map();
267
279
  return { claim: (t) => {
268
280
  let n = Date.now();
269
- for (let [t, r] of e) n - r.at > ne && e.delete(t);
281
+ for (let [t, r] of e) n - r.at > re && e.delete(t);
270
282
  let r = e.get(t);
271
283
  if (r) return { earlier: r.shown };
272
284
  let i = () => void 0, a = new Promise((e) => i = e);
@@ -280,7 +292,7 @@ function W() {
280
292
  }
281
293
  //#endregion
282
294
  //#region src/core/presentSlide.ts
283
- function G({ slide: e, totalSlides: t, mode: n }) {
295
+ function K({ slide: e, totalSlides: t, mode: n }) {
284
296
  return n === "steps" ? {
285
297
  step: e,
286
298
  total: t,
@@ -298,42 +310,46 @@ function G({ slide: e, totalSlides: t, mode: n }) {
298
310
  nextCall: `call presentSlide for slide ${e + 1} of ${t}`
299
311
  };
300
312
  }
301
- var re = W(), ie = (e, { slide: t, totalSlides: n, title: r, imagePrompt: i }) => JSON.stringify([
313
+ var q = (e, { slide: t, totalSlides: n, title: r, imagePrompt: i }) => JSON.stringify([
302
314
  e,
303
315
  t,
304
316
  n,
305
317
  r,
306
318
  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 = {
319
+ ]), J = N(() => ({
320
+ latest: {
321
+ presentation: null,
322
+ steps: null
323
+ },
324
+ lastShown: null,
325
+ repeats: G()
326
+ }));
327
+ function Y(e, t) {
328
+ let n = e.latest[t.mode], r = n?.totalSlides === t.totalSlides && (t.slide !== 1 || n.firstTitle === t.title);
329
+ if (n && r) return n;
330
+ let i = {
315
331
  id: O(),
316
- mode: e.mode,
317
- totalSlides: e.totalSlides,
318
- firstTitle: e.slide === 1 ? e.title : "",
332
+ mode: t.mode,
333
+ totalSlides: t.totalSlides,
334
+ firstTitle: t.slide === 1 ? t.title : "",
319
335
  slides: /* @__PURE__ */ new Map(),
320
336
  current: 0,
321
337
  shownAt: -Infinity
322
338
  };
323
- return K[e.mode] = r, r;
339
+ return e.latest[t.mode] = i, i;
324
340
  }
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;
341
+ function X(e, t, n, r) {
342
+ t.current = n.slide, t.shownAt = Date.now(), e.lastShown = t;
343
+ let i = n.mode === "steps" ? "step" : "slide", { uuid: a, ...o } = r;
328
344
  return {
329
- ...a,
330
- message: `${r} ${t.slide} of ${t.totalSlides} is on the screen again, as it was`,
331
- instructions: m(t),
345
+ ...o,
346
+ message: `${i} ${n.slide} of ${n.totalSlides} is on the screen again, as it was`,
347
+ instructions: m(n),
332
348
  instructionsRequired: !0,
333
- sequence: G(t)
349
+ sequence: K(n)
334
350
  };
335
351
  }
336
- function ae(e, t) {
352
+ function ie(e, t) {
337
353
  if (e.mode !== "steps") return e.title ? `A presentation slide titled "${e.title}". ${e.imagePrompt}` : e.imagePrompt;
338
354
  let n = e.title ? `, "${e.title}"` : "";
339
355
  return [
@@ -342,13 +358,13 @@ function ae(e, t) {
342
358
  "Show the action clearly, with little or no text."
343
359
  ].filter(Boolean).join(" ");
344
360
  }
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);
361
+ async function ae(e, t, n) {
362
+ let r = t.slides.get(n.slide - 1), i = n.mode === "steps" ? E(r).imagePath : void 0, a = i ? await A(e, [i]) : [], o = ie(n, a.length > 0), s = await j(e, o, a), { imageData: c, imagePath: l } = E(s);
347
363
  if (!c) return {
348
364
  ...s,
349
365
  sequence: null
350
366
  };
351
- let u = t.id, d = await H(e.files?.artifacts, M, u, (e) => {
367
+ let u = t.id, d = await U(e.files?.artifacts, P, u, (e) => {
352
368
  let t = e ?? {
353
369
  id: u,
354
370
  title: "",
@@ -377,65 +393,65 @@ async function oe(e, t, n) {
377
393
  message: [
378
394
  `${p} ${n.slide} of ${n.totalSlides} of slideshow "${u}" is on the screen`,
379
395
  l ? `saved to ${l}` : "",
380
- d ? "" : U("slideshow")
396
+ d ? "" : W("slideshow")
381
397
  ].filter(Boolean).join("; "),
382
398
  instructions: m(n),
383
399
  instructionsRequired: !0,
384
- sequence: G(n)
400
+ sequence: K(n)
385
401
  };
386
402
  }
387
- async function X(e, t) {
403
+ async function Z(e, t) {
388
404
  let n = p(t);
389
405
  if (!n) return {
390
406
  message: f,
391
407
  sequence: null
392
408
  };
393
- let r = J(n), i = n.mode === "steps";
394
- if (i && n.slide > r.current && r.current > 0 && !D(e, r.shownAt)) {
409
+ let r = J(e), i = Y(r, n), a = n.mode === "steps";
410
+ if (a && n.slide > i.current && i.current > 0 && !D(e, i.shownAt)) {
395
411
  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";
412
+ let e = i.shownAt === Infinity ? "is still being drawn" : "is still on the screen";
397
413
  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}.`,
414
+ message: `step ${n.slide} was not shown: the user hasn't said they're ready yet, and step ${i.current} ${e}. Wait for them before calling presentSlide for step ${n.slide}.`,
399
415
  cancelled: !0
400
416
  };
401
417
  }
402
- let a = i ? r.slides.get(n.slide) : void 0;
403
- if (a) return te(e.currentResult, a) ? {
418
+ let o = a ? i.slides.get(n.slide) : void 0;
419
+ if (o) return te(e.currentResult, o) ? {
404
420
  message: `step ${n.slide} of ${n.totalSlides} is already on the screen`,
405
421
  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) : {
422
+ } : X(r, i, n, o);
423
+ let s = r.repeats.claim(q(i.id, n));
424
+ if ("earlier" in s) {
425
+ if (!await s.earlier) return Z(e, t);
426
+ let a = i.slides.get(n.slide);
427
+ return a && r.lastShown !== i ? X(r, i, n, a) : {
412
428
  message: `slide ${n.slide} of ${n.totalSlides} is already on the screen`,
413
429
  cancelled: !0
414
430
  };
415
431
  }
416
- let { settle: s } = o, c = {
417
- current: r.current,
418
- shownAt: r.shownAt
432
+ let { settle: c } = s, l = {
433
+ current: i.current,
434
+ shownAt: i.shownAt
419
435
  };
420
- r.current = n.slide, r.shownAt = Infinity;
421
- let l = !1;
436
+ i.current = n.slide, i.shownAt = Infinity;
437
+ let u = !1;
422
438
  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;
439
+ let t = await ae(e, i, n);
440
+ return u = !!E(t).imageData, u && (i.slides.set(n.slide, t), i.current = n.slide, i.shownAt = Date.now(), r.lastShown = i), t;
425
441
  } finally {
426
- s(l), !l && r.current === n.slide && (r.current = c.current, r.shownAt = c.shownAt);
442
+ c(u), !u && i.current === n.slide && (i.current = l.current, i.shownAt = l.shownAt);
427
443
  }
428
444
  }
429
445
  //#endregion
430
446
  //#region src/core/storyboard.ts
431
- var se = ({ id: e, title: t, totalPanels: n }) => ({
447
+ var oe = ({ id: e, title: t, totalPanels: n }) => ({
432
448
  step: 0,
433
449
  total: n,
434
450
  kind: "story",
435
451
  label: `The cast of "${t}"`,
436
452
  onShown: "introduce the characters",
437
453
  nextCall: `call presentPanel for panel 1 of ${n} with storyboardId "${e}"`
438
- }), ce = ({ id: e, totalPanels: t }, n, r) => ({
454
+ }), se = ({ id: e, totalPanels: t }, n, r) => ({
439
455
  step: n,
440
456
  total: t,
441
457
  kind: "story",
@@ -443,14 +459,14 @@ var se = ({ id: e, title: t, totalPanels: n }) => ({
443
459
  onShown: "tell that part of the story",
444
460
  nextCall: `call presentPanel for panel ${n + 1} of ${t} with storyboardId "${e}"`,
445
461
  ...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) {
462
+ }), Q = (e, t) => e ? `${e}. ${t}` : t, ce = (e, { name: t, description: n }) => Q(e, `Character reference sheet of ${t}: ${n}. Full body, front, side and back views, on a plain white background. No text or labels.`);
463
+ async function le(e, t) {
448
464
  let n = _(t);
449
465
  if (typeof n == "string") return {
450
466
  message: n,
451
467
  sequence: null
452
468
  };
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);
469
+ let r = await Promise.all(n.characters.map((t) => j(e, ce(n.style, t)))), i = r.map(E), a = r.find((e, t) => !i[t]?.imageData);
454
470
  if (i.every(({ imageData: e }) => !e) && a) return {
455
471
  ...a,
456
472
  message: `the character sheets couldn't be made: ${a.message}`,
@@ -467,7 +483,7 @@ async function ue(e, t) {
467
483
  };
468
484
  }),
469
485
  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;
486
+ }, s = await U(e.files?.artifacts, F, 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
487
  return {
472
488
  data: {
473
489
  storyboardId: o.id,
@@ -488,18 +504,18 @@ async function ue(e, t) {
488
504
  `storyboard "${o.id}" is ready with ${o.totalPanels} panels; the cast is on the screen`,
489
505
  c.length ? `character sheets saved to ${c.join(", ")}` : "",
490
506
  l.length ? `no sheet for ${l.join("; ")}: they will be drawn from their description` : "",
491
- s ? "" : U("storyboard")
507
+ s ? "" : W("storyboard")
492
508
  ].filter(Boolean).join("; "),
493
509
  instructions: v(o),
494
510
  instructionsRequired: !0,
495
- sequence: se(o)
511
+ sequence: oe(o)
496
512
  };
497
513
  }
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}`);
514
+ function ue(e, t, n) {
515
+ let r = [Q(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
516
  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
517
  }
502
- async function fe(e, t, n) {
518
+ async function de(e, t, n) {
503
519
  let r = [], i = [];
504
520
  for (let a of n.characters) {
505
521
  let n = t.characters.find((e) => u(e.name, a));
@@ -512,7 +528,7 @@ async function fe(e, t, n) {
512
528
  });
513
529
  }
514
530
  }
515
- let a = i.map(({ sheet: e }) => e).filter((e) => !!e), o = de(t, n, i.map(({ character: e, sheet: t }) => ({
531
+ let a = i.map(({ sheet: e }) => e).filter((e) => !!e), o = ue(t, n, i.map(({ character: e, sheet: t }) => ({
516
532
  character: e,
517
533
  hasSheet: !!t
518
534
  }))), s = await j(e, o, a), { imageData: c, imagePath: l } = E(s);
@@ -520,7 +536,7 @@ async function fe(e, t, n) {
520
536
  ...s,
521
537
  sequence: null
522
538
  };
523
- let d = n.panel < t.totalPanels && n.choices.length >= 2 ? n.choices : [], f = await H(e.files?.artifacts, N, t.id, (e) => {
539
+ let d = n.panel < t.totalPanels && n.choices.length >= 2 ? n.choices : [], f = await U(e.files?.artifacts, F, t.id, (e) => {
524
540
  let r = e ?? t;
525
541
  return d.length && (r.interactive = !0), r.panels[String(n.panel)] = {
526
542
  caption: n.caption,
@@ -546,38 +562,41 @@ async function fe(e, t, n) {
546
562
  `panel ${n.panel} of ${t.totalPanels} is on the screen`,
547
563
  l ? `saved to ${l}` : "",
548
564
  r.length ? `not in the cast, so drawn from the prompt alone: ${r.join(", ")}` : "",
549
- f ? "" : U("storyboard")
565
+ f ? "" : W("storyboard")
550
566
  ].filter(Boolean).join("; "),
551
567
  instructions: x(t, n.panel, d),
552
568
  instructionsRequired: !0,
553
- sequence: ce(t, n.panel, d)
569
+ sequence: se(t, n.panel, d)
554
570
  };
555
571
  }
556
- var pe = W(), Q = /* @__PURE__ */ new Map();
572
+ var fe = N(() => ({
573
+ repeats: G(),
574
+ awaitingChoice: /* @__PURE__ */ new Map()
575
+ }));
557
576
  async function $(e, t) {
558
577
  let n = b(t);
559
578
  if (!n) return {
560
579
  message: y,
561
580
  sequence: null
562
581
  };
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.`,
582
+ let { storyboardId: r, panel: i } = n, { repeats: a, awaitingChoice: o } = fe(e), s = o.get(r);
583
+ if (s && i > s.panel && !D(e, s.shownAt)) return console.info(`[sequence] holding panel ${i} for the user`), {
584
+ message: s.shownAt === Infinity ? `panel ${i} was not shown: panel ${s.panel} is still being drawn. Wait for it before going on.` : `panel ${i} was not shown: the user hasn't picked a choice yet, and panel ${s.panel} is still on the screen. Wait for their answer.`,
566
585
  cancelled: !0
567
586
  };
568
- let o = pe.claim(JSON.stringify(n));
569
- if ("earlier" in o) return await o.earlier ? {
587
+ let c = a.claim(JSON.stringify(n));
588
+ if ("earlier" in c) return await c.earlier ? {
570
589
  message: `panel ${i} is already on the screen`,
571
590
  cancelled: !0
572
591
  } : $(e, t);
573
- let { settle: s } = o, c = {
592
+ let { settle: l } = c, u = {
574
593
  panel: i,
575
594
  shownAt: Infinity
576
- }, l = Q.get(r);
577
- Q.set(r, c);
578
- let u = () => Q.get(r) === c, d = !1;
595
+ }, d = o.get(r);
596
+ o.set(r, u);
597
+ let f = () => o.get(r) === u, p = !1;
579
598
  try {
580
- let t = await V(e.files?.artifacts, N, r);
599
+ let t = await H(e.files?.artifacts, F, r);
581
600
  if (!t) return {
582
601
  message: `there is no storyboard "${r}"; call defineStoryboard first`,
583
602
  sequence: null
@@ -586,34 +605,34 @@ async function $(e, t) {
586
605
  message: `storyboard "${t.id}" has ${t.totalPanels} panels`,
587
606
  sequence: null
588
607
  };
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, {
608
+ let a = await de(e, t, n);
609
+ p = !!E(a).imageData;
610
+ let s = a.data;
611
+ return p && f() && (s?.choices?.length ? o.set(r, {
593
612
  panel: i,
594
613
  shownAt: Date.now()
595
- }) : Q.delete(r)), a;
614
+ }) : o.delete(r)), a;
596
615
  } finally {
597
- s(d), !d && u() && (l ? Q.set(r, l) : Q.delete(r));
616
+ l(p), !p && f() && (d ? o.set(r, d) : o.delete(r));
598
617
  }
599
618
  }
600
619
  //#endregion
601
620
  //#region src/core/index.ts
602
- var me = {
621
+ var pe = {
603
622
  toolDefinition: g,
604
623
  generatingMessage: "Making the slide...",
605
624
  isEnabled: () => !0,
606
- execute: (e, t) => X(e, t)
607
- }, he = {
625
+ execute: (e, t) => Z(e, t)
626
+ }, me = {
608
627
  toolDefinition: C,
609
628
  generatingMessage: "Drawing the characters...",
610
629
  isEnabled: () => !0,
611
- execute: (e, t) => ue(e, t)
612
- }, ge = {
630
+ execute: (e, t) => le(e, t)
631
+ }, he = {
613
632
  toolDefinition: w,
614
633
  generatingMessage: "Drawing the panel...",
615
634
  isEnabled: () => !0,
616
635
  execute: (e, t) => $(e, t)
617
636
  };
618
637
  //#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 };
638
+ export { b as C, m as D, u as E, x as S, _ as T, h as _, F as a, S as b, C as c, a as d, y as f, g, t as h, P as i, i as l, w as m, he as n, H as o, r as p, pe as r, n as s, me as t, o as u, s as v, p as w, v as x, f as y };
package/dist/index.cjs CHANGED
@@ -1 +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;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core-AQFO5Vcb.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.js CHANGED
@@ -1,2 +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";
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-qe3zK8TI.js";
2
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/vue.cjs CHANGED
@@ -1 +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;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./core-AQFO5Vcb.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 CHANGED
@@ -1,4 +1,4 @@
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";
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-qe3zK8TI.js";
2
2
  import { defineComponent as D, h as O, markRaw as k } from "vue";
3
3
  import { ImagePreview as A } from "@mulmochat-plugin/ui-image";
4
4
  //#region src/vue/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gui-chat-plugin/sequence",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Slideshows, step-by-step guides and stories told in pictures (presentSlide, defineStoryboard, presentPanel) for GUI Chat Protocol hosts",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -33,12 +33,12 @@
33
33
  },
34
34
  "peerDependencies": {
35
35
  "@mulmochat-plugin/ui-image": "^0.4.1",
36
- "gui-chat-protocol": "^2.1.0",
36
+ "gui-chat-protocol": "^2.2.0",
37
37
  "vue": "^3.5.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@mulmochat-plugin/ui-image": "^0.4.1",
41
- "gui-chat-protocol": "^2.1.0",
41
+ "gui-chat-protocol": "^2.2.0",
42
42
  "vue": "^3.5.43"
43
43
  },
44
44
  "license": "MIT",
@@ -1 +0,0 @@
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}});