diff --git a/app/web/components/CartoonNextAction.tsx b/app/web/components/CartoonNextAction.tsx new file mode 100644 index 0000000..19ea48f --- /dev/null +++ b/app/web/components/CartoonNextAction.tsx @@ -0,0 +1,145 @@ +import { useEffect, useState } from "react"; +import type { StoryProgress } from "@app-lib/story-progress"; +import type { CoachUiAction } from "@app-lib/cartoon-coach"; +import { WorkflowCoachView } from "./WorkflowCoach"; + +export function storyInfoNextStep(progress: StoryProgress): string { + if (progress.cover !== "present") { + return progress.cover === "invalid" + ? "Replace the cover image - it must be a valid WebP or JPEG." + : "Add a cover image before publishing."; + } + const missing: string[] = []; + if (!progress.metadata.language) missing.push("language"); + if (!progress.metadata.genre) missing.push("genre"); + if (!progress.metadata.title) missing.push("title"); + return `Add the story ${missing.join(" and ") || "details"} before publishing.`; +} + +export function cartoonWorkflowActiveKey(progress: StoryProgress): string | null { + const coach = progress.coach ?? null; + const m = progress.metadata; + const hasStructure = progress.setup.hasStructure; + const hasGenesis = progress.setup.hasGenesis; + const coverDone = progress.cover === "present"; + const metadataIncomplete = !m.title || !m.language || !m.genre; + const activeEp = progress.episodes.find((e) => !e.published) ?? null; + const productionPending = !!activeEp && activeEp.state !== "ready"; + + if (!hasStructure) return "whitepaper"; + if (!hasGenesis) return "genesis.md"; + if (metadataIncomplete) return "story-info"; + if (productionPending && coach?.episodeFile) return coach.episodeFile; + if (!coverDone) return "story-info"; + return coach?.episodeFile ?? null; +} + +export function StoryInfoNextActionCard({ + progress, + onOpenStoryInfo, +}: { + progress: StoryProgress; + onOpenStoryInfo?: () => void; +}) { + return ( +
+ Next: + {storyInfoNextStep(progress)} +
+Production Progress
No next action available
+This workflow has no queued next step right now.
++ Next: + {coach.action} +
+ {copied &&Prompt copied.
} +>8));if(y
>8));return y>A?v:E}return v}let _=a(c,h,p),b=Yr(d,on.relativeLuminance(_>>8));if(b
>8));return b>y?_:v}return _}}e.ensureContrastRatio=i;function s(c,h,p){let d=c>>24&255,x=c>>16&255,_=c>>8&255,b=h>>24&255,v=h>>16&255,y=h>>8&255,E=Yr(on.relativeLuminance2(b,v,y),on.relativeLuminance2(d,x,_));for(;E
0||v>0||y>0);)b-=Math.max(0,Math.ceil(b*.1)),v-=Math.max(0,Math.ceil(v*.1)),y-=Math.max(0,Math.ceil(y*.1)),E=Yr(on.relativeLuminance2(b,v,y),on.relativeLuminance2(d,x,_));return(b<<24|v<<16|y<<8|255)>>>0}e.reduceLuminance=s;function a(c,h,p){let d=c>>24&255,x=c>>16&255,_=c>>8&255,b=h>>24&255,v=h>>16&255,y=h>>8&255,E=Yr(on.relativeLuminance2(b,v,y),on.relativeLuminance2(d,x,_));for(;E
>>0}e.increaseLuminance=a;function o(c){return[c>>24&255,c>>16&255,c>>8&255,c&255]}e.toChannels=o})(mu||(mu={}));function Qs(e){let t=e.toString(16);return t.length<2?"0"+t:t}function Yr(e,t){return e >8));if(y >8));return y>A?v:N}return v}let x=a(c,h,p),b=Yr(d,un.relativeLuminance(x>>8));if(b >8));return b>y?x:v}return x}}e.ensureContrastRatio=i;function s(c,h,p){let d=c>>24&255,_=c>>16&255,x=c>>8&255,b=h>>24&255,v=h>>16&255,y=h>>8&255,N=Yr(un.relativeLuminance2(b,v,y),un.relativeLuminance2(d,_,x));for(;N 0||v>0||y>0);)b-=Math.max(0,Math.ceil(b*.1)),v-=Math.max(0,Math.ceil(v*.1)),y-=Math.max(0,Math.ceil(y*.1)),N=Yr(un.relativeLuminance2(b,v,y),un.relativeLuminance2(d,_,x));return(b<<24|v<<16|y<<8|255)>>>0}e.reduceLuminance=s;function a(c,h,p){let d=c>>24&255,_=c>>16&255,x=c>>8&255,b=h>>24&255,v=h>>16&255,y=h>>8&255,N=Yr(un.relativeLuminance2(b,v,y),un.relativeLuminance2(d,_,x));for(;N >>0}e.increaseLuminance=a;function o(c){return[c>>24&255,c>>16&255,c>>8&255,c&255]}e.toChannels=o})(mu||(mu={}));function Qs(e){let t=e.toString(16);return t.length<2?"0"+t:t}function Yr(e,t){return e 0&&(A>_||x[A].getTrimmedLength()===0);A--)E++;E>0&&(c.push(h+x.length-E),c.push(E)),h+=x.length-1}return c}function z2(e,t){let i=[],s=0,a=t[s],o=0;for(let c=0;c=i[1]&&e>=s[0]&&t<=s[1]:t>i[1]&&t=i[0]&&e=i[0]}};sp=si([Me(1,My),Me(2,dn),Me(3,Qr),Me(4,ca),Me(5,Ro),Me(6,cl)],sp);function Eb(e,t,i){for(;e.length0&&(this._flat[s]=c),c}let a=e;t&&(a+="B"),i&&(a+="I");let o=this._holey.get(a);if(o===void 0){let c=0;t&&(c|=1),i&&(c|=2),o=this._measure(e,c),o>0&&this._holey.set(a,o)}return o}_measure(e,t){let i=this._measureElements[t];return i.textContent=e.repeat(32),i.offsetWidth/32}},e2=class{constructor(){this.clear()}clear(){this.hasSelection=!1,this.columnSelectMode=!1,this.viewportStartRow=0,this.viewportEndRow=0,this.viewportCappedStartRow=0,this.viewportCappedEndRow=0,this.startCol=0,this.endCol=0,this.selectionStart=void 0,this.selectionEnd=void 0}update(e,t,i,s=!1){if(this.selectionStart=t,this.selectionEnd=i,!t||!i||t[0]===i[0]&&t[1]===i[1]){this.clear();return}let a=e.buffers.active.ydisp,o=t[1]-a,c=i[1]-a,h=Math.max(o,0),p=Math.min(c,e.rows-1);if(h>=e.rows||p<0){this.clear();return}this.hasSelection=!0,this.columnSelectMode=s,this.viewportStartRow=o,this.viewportEndRow=c,this.viewportCappedStartRow=h,this.viewportCappedEndRow=p,this.startCol=t[0],this.endCol=i[0]}isCellSelected(e,t,i){return this.hasSelection?(i-=e.buffer.active.viewportY,this.columnSelectMode?this.startCol<=this.endCol?t>=this.startCol&&i>=this.viewportCappedStartRow&&tt?"A":"B"}function s0(e,t,i,s,a,o){let c=e,h=t,p="";for(;(c!==i||h!==s)&&h>=0&&h=i[1]&&e>=s[0]&&t<=s[1]:t>i[1]&&t=i[0]&&e=i[0]}};sp=oi([Me(1,Dy),Me(2,pn),Me(3,Qr),Me(4,ca),Me(5,Ro),Me(6,ol)],sp);function Nb(e,t,i){for(;e.length0&&(this._flat[s]=c),c}let a=e;t&&(a+="B"),i&&(a+="I");let o=this._holey.get(a);if(o===void 0){let c=0;t&&(c|=1),i&&(c|=2),o=this._measure(e,c),o>0&&this._holey.set(a,o)}return o}_measure(e,t){let i=this._measureElements[t];return i.textContent=e.repeat(32),i.offsetWidth/32}},i2=class{constructor(){this.clear()}clear(){this.hasSelection=!1,this.columnSelectMode=!1,this.viewportStartRow=0,this.viewportEndRow=0,this.viewportCappedStartRow=0,this.viewportCappedEndRow=0,this.startCol=0,this.endCol=0,this.selectionStart=void 0,this.selectionEnd=void 0}update(e,t,i,s=!1){if(this.selectionStart=t,this.selectionEnd=i,!t||!i||t[0]===i[0]&&t[1]===i[1]){this.clear();return}let a=e.buffers.active.ydisp,o=t[1]-a,c=i[1]-a,h=Math.max(o,0),p=Math.min(c,e.rows-1);if(h>=e.rows||p<0){this.clear();return}this.hasSelection=!0,this.columnSelectMode=s,this.viewportStartRow=o,this.viewportEndRow=c,this.viewportCappedStartRow=h,this.viewportCappedEndRow=p,this.startCol=t[0],this.endCol=i[0]}isCellSelected(e,t,i){return this.hasSelection?(i-=e.buffer.active.viewportY,this.columnSelectMode?this.startCol<=this.endCol?t>=this.startCol&&i>=this.viewportCappedStartRow&&tt?"A":"B"}function a0(e,t,i,s,a,o){let c=e,h=t,p="";for(;(c!==i||h!==s)&&h>=0&&h>22||1}return a&&a.push(i),o}};function O2(e,t,i,s,a,o){let c=[];for(let h=0;h