@shotstack/cli 0.5.0 → 0.6.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/SKILL.md +3 -0
- package/dist/shotstack.js +1 -1
- package/package.json +2 -1
- package/references/html5-snippets.md +80 -19
- package/references/motion.md +151 -0
- package/shared/agent-core.md +262 -0
package/SKILL.md
CHANGED
|
@@ -126,6 +126,8 @@ The Shotstack schema does **not** match CSS or web conventions. Composing Edit J
|
|
|
126
126
|
|
|
127
127
|
The full ruleset (asset types, fonts, smart-string clip values, top-5 mistakes) lives in `shared/agent-core.md`. The same file is also returned by the Shotstack MCP server's `get_shotstack_guide` tool, so the conventions are identical across surfaces.
|
|
128
128
|
|
|
129
|
+
**For anything that animates, compose motion from the house tokens** — one duration scale (`base` 0.6 s in / `fast` 0.33 s out), one house ease (`power3.out` / `cubic-bezier(0.16,1,0.3,1)`, no overshoot by default), one stagger (`0.13 s`). The *Motion language* section of `agent-core.md` is the summary; [`references/motion.md`](references/motion.md) has the full GSAP/CSS recipes and the brand kit. **Don't invent a new easing or duration per clip** — shared tokens are what make a multi-clip edit feel like one production.
|
|
130
|
+
|
|
129
131
|
## Exit codes
|
|
130
132
|
|
|
131
133
|
| Code | Meaning |
|
|
@@ -154,6 +156,7 @@ This skill ships sub-references for the gnarly bits:
|
|
|
154
156
|
- [`references/positioning.md`](references/positioning.md) — coordinate model, `position`/`offset` (fraction of frame, +y up), clip bounding box & `fit`, text sizing, transform order
|
|
155
157
|
- [`references/caption.md`](references/caption.md) — sizing per resolution, default style, the 5 named presets, alias pattern (asset type: `rich-caption`)
|
|
156
158
|
- [`references/svg.md`](references/svg.md) — required attrs, supported elements
|
|
159
|
+
- [`references/motion.md`](references/motion.md) — **the house motion language**: one duration scale, one ease, one stagger; choreography recipes (GSAP/CSS), the rich-text/transition mappings, and the brand kit. Read before composing any animation.
|
|
157
160
|
- [`references/html5.md`](references/html5.md) — HTML5 asset: fields, preloaded libs (gsap/d3/anime/lottie), browser harness, sizing, worked examples
|
|
158
161
|
- [`references/html5-snippets.md`](references/html5-snippets.md) — copy-paste motion-graphic clips: kinetic headline, count-up/price odometer, shine sweep, pulsing CTA, film grain
|
|
159
162
|
- [`references/fonts.md`](references/fonts.md) — built-in fonts, Google Fonts URL pattern, custom-font workflow
|
package/dist/shotstack.js
CHANGED
|
@@ -20,7 +20,7 @@ Expecting one of '${t.join("', '")}'`);if(this._lifeCycleHooks[r])this._lifeCycl
|
|
|
20
20
|
`),this.outputHelp({error:!0});let t=i||{},o=t.exitCode||1,n=t.code||"commander.error";this._exit(o,n,r)}_parseOptionsEnv(){this.options.forEach((r)=>{if(r.envVar&&r.envVar in Q.env){let i=r.attributeName();if(this.getOptionValue(i)===void 0||["default","config","env"].includes(this.getOptionValueSource(i)))if(r.required||r.optional)this.emit(`optionEnv:${r.name()}`,Q.env[r.envVar]);else this.emit(`optionEnv:${r.name()}`)}})}_parseOptionsImplied(){let r=new uU(this.options),i=(t)=>{return this.getOptionValue(t)!==void 0&&!["default","implied"].includes(this.getOptionValueSource(t))};this.options.filter((t)=>t.implied!==void 0&&i(t.attributeName())&&r.valueFromOption(this.getOptionValue(t.attributeName()),t)).forEach((t)=>{Object.keys(t.implied).filter((o)=>!i(o)).forEach((o)=>{this.setOptionValueWithSource(o,t.implied[o],"implied")})})}missingArgument(r){let i=`error: missing required argument '${r}'`;this.error(i,{code:"commander.missingArgument"})}optionMissingArgument(r){let i=`error: option '${r.flags}' argument missing`;this.error(i,{code:"commander.optionMissingArgument"})}missingMandatoryOptionValue(r){let i=`error: required option '${r.flags}' not specified`;this.error(i,{code:"commander.missingMandatoryOptionValue"})}_conflictingOption(r,i){let t=($)=>{let g=$.attributeName(),I=this.getOptionValue(g),c=this.options.find((b)=>b.negate&&g===b.attributeName()),_=this.options.find((b)=>!b.negate&&g===b.attributeName());if(c&&(c.presetArg===void 0&&I===!1||c.presetArg!==void 0&&I===c.presetArg))return c;return _||$},o=($)=>{let g=t($),I=g.attributeName();if(this.getOptionValueSource(I)==="env")return`environment variable '${g.envVar}'`;return`option '${g.flags}'`},n=`error: ${o(r)} cannot be used with ${o(i)}`;this.error(n,{code:"commander.conflictingOption"})}unknownOption(r){if(this._allowUnknownOption)return;let i="";if(r.startsWith("--")&&this._showSuggestionAfterError){let o=[],n=this;do{let $=n.createHelp().visibleOptions(n).filter((g)=>g.long).map((g)=>g.long);o=o.concat($),n=n.parent}while(n&&!n._enablePositionalOptions);i=N6(r,o)}let t=`error: unknown option '${r}'${i}`;this.error(t,{code:"commander.unknownOption"})}_excessArguments(r){if(this._allowExcessArguments)return;let i=this.registeredArguments.length,t=i===1?"":"s",n=`error: too many arguments${this.parent?` for '${this.name()}'`:""}. Expected ${i} argument${t} but got ${r.length}.`;this.error(n,{code:"commander.excessArguments"})}unknownCommand(){let r=this.args[0],i="";if(this._showSuggestionAfterError){let o=[];this.createHelp().visibleCommands(this).forEach((n)=>{if(o.push(n.name()),n.alias())o.push(n.alias())}),i=N6(r,o)}let t=`error: unknown command '${r}'${i}`;this.error(t,{code:"commander.unknownCommand"})}version(r,i,t){if(r===void 0)return this._version;this._version=r,i=i||"-V, --version",t=t||"output the version number";let o=this.createOption(i,t);return this._versionOptionName=o.attributeName(),this._registerOption(o),this.on("option:"+o.name(),()=>{this._outputConfiguration.writeOut(`${r}
|
|
21
21
|
`),this._exit(0,"commander.version",r)}),this}description(r,i){if(r===void 0&&i===void 0)return this._description;if(this._description=r,i)this._argsDescription=i;return this}summary(r){if(r===void 0)return this._summary;return this._summary=r,this}alias(r){if(r===void 0)return this._aliases[0];let i=this;if(this.commands.length!==0&&this.commands[this.commands.length-1]._executableHandler)i=this.commands[this.commands.length-1];if(r===i._name)throw Error("Command alias can't be the same as its name");let t=this.parent?._findCommand(r);if(t){let o=[t.name()].concat(t.aliases()).join("|");throw Error(`cannot add alias '${r}' to command '${this.name()}' as already have command '${o}'`)}return i._aliases.push(r),this}aliases(r){if(r===void 0)return this._aliases;return r.forEach((i)=>this.alias(i)),this}usage(r){if(r===void 0){if(this._usage)return this._usage;let i=this.registeredArguments.map((t)=>{return tU(t)});return[].concat(this.options.length||this._helpOption!==null?"[options]":[],this.commands.length?"[command]":[],this.registeredArguments.length?i:[]).join(" ")}return this._usage=r,this}name(r){if(r===void 0)return this._name;return this._name=r,this}nameFromFilename(r){return this._name=Ur.basename(r,Ur.extname(r)),this}executableDir(r){if(r===void 0)return this._executableDir;return this._executableDir=r,this}helpInformation(r){let i=this.createHelp();if(i.helpWidth===void 0)i.helpWidth=r&&r.error?this._outputConfiguration.getErrHelpWidth():this._outputConfiguration.getOutHelpWidth();return i.formatHelp(this,i)}_getHelpContext(r){r=r||{};let i={error:!!r.error},t;if(i.error)t=(o)=>this._outputConfiguration.writeErr(o);else t=(o)=>this._outputConfiguration.writeOut(o);return i.write=r.write||t,i.command=this,i}outputHelp(r){let i;if(typeof r==="function")i=r,r=void 0;let t=this._getHelpContext(r);this._getCommandAndAncestors().reverse().forEach((n)=>n.emit("beforeAllHelp",t)),this.emit("beforeHelp",t);let o=this.helpInformation(t);if(i){if(o=i(o),typeof o!=="string"&&!Buffer.isBuffer(o))throw Error("outputHelp callback must return a string or a Buffer")}if(t.write(o),this._getHelpOption()?.long)this.emit(this._getHelpOption().long);this.emit("afterHelp",t),this._getCommandAndAncestors().forEach((n)=>n.emit("afterAllHelp",t))}helpOption(r,i){if(typeof r==="boolean"){if(r)this._helpOption=this._helpOption??void 0;else this._helpOption=null;return this}return r=r??"-h, --help",i=i??"display help for command",this._helpOption=this.createOption(r,i),this}_getHelpOption(){if(this._helpOption===void 0)this.helpOption(void 0,void 0);return this._helpOption}addHelpOption(r){return this._helpOption=r,this}help(r){this.outputHelp(r);let i=Q.exitCode||0;if(i===0&&r&&typeof r!=="function"&&r.error)i=1;this._exit(i,"commander.help","(outputHelp)")}addHelpText(r,i){let t=["beforeAll","before","after","afterAll"];if(!t.includes(r))throw Error(`Unexpected value for position to addHelpText.
|
|
22
22
|
Expecting one of '${t.join("', '")}'`);let o=`${r}Help`;return this.on(o,(n)=>{let $;if(typeof i==="function")$=i({error:n.error,command:n.command});else $=i;if($)n.write(`${$}
|
|
23
|
-
`)}),this}_outputHelpIfRequested(r){let i=this._getHelpOption();if(i&&r.find((o)=>i.is(o)))this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)")}}function j6(r){return r.map((i)=>{if(!i.startsWith("--inspect"))return i;let t,o="127.0.0.1",n="9229",$;if(($=i.match(/^(--inspect(-brk)?)$/))!==null)t=$[1];else if(($=i.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null)if(t=$[1],/^\d+$/.test($[3]))n=$[3];else o=$[3];else if(($=i.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null)t=$[1],o=$[3],n=$[4];if(t&&n!=="0")return`${t}=${o}:${parseInt(n)+1}`;return i})}oU.Command=st});var O6=Or((_U)=>{var{Argument:J6}=gi(),{Command:r$}=P6(),{CommanderError:IU,InvalidArgumentError:L6}=Pn(),{Help:cU}=dt(),{Option:E6}=ht();_U.program=new r$;_U.createCommand=(r)=>new r$(r);_U.createOption=(r,i)=>new E6(r,i);_U.createArgument=(r,i)=>new J6(r,i);_U.Command=r$;_U.Option=E6;_U.Argument=J6;_U.Help=cU;_U.CommanderError=IU;_U.InvalidArgumentError=L6;_U.InvalidOptionArgumentError=L6});var G6=qb(O6(),1),{program:FS,createCommand:MS,createArgument:RS,createOption:xS,CommanderError:fS,InvalidArgumentError:ZS,InvalidOptionArgumentError:CS,Command:K,Argument:mS,Option:yS,Help:dS}=G6.default;import{readFile as Bw}from"node:fs/promises";import{resolve as Aw}from"node:path";var X6={name:"@shotstack/cli",version:"0.
|
|
23
|
+
`)}),this}_outputHelpIfRequested(r){let i=this._getHelpOption();if(i&&r.find((o)=>i.is(o)))this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)")}}function j6(r){return r.map((i)=>{if(!i.startsWith("--inspect"))return i;let t,o="127.0.0.1",n="9229",$;if(($=i.match(/^(--inspect(-brk)?)$/))!==null)t=$[1];else if(($=i.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null)if(t=$[1],/^\d+$/.test($[3]))n=$[3];else o=$[3];else if(($=i.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null)t=$[1],o=$[3],n=$[4];if(t&&n!=="0")return`${t}=${o}:${parseInt(n)+1}`;return i})}oU.Command=st});var O6=Or((_U)=>{var{Argument:J6}=gi(),{Command:r$}=P6(),{CommanderError:IU,InvalidArgumentError:L6}=Pn(),{Help:cU}=dt(),{Option:E6}=ht();_U.program=new r$;_U.createCommand=(r)=>new r$(r);_U.createOption=(r,i)=>new E6(r,i);_U.createArgument=(r,i)=>new J6(r,i);_U.Command=r$;_U.Option=E6;_U.Argument=J6;_U.Help=cU;_U.CommanderError=IU;_U.InvalidArgumentError=L6;_U.InvalidOptionArgumentError=L6});var G6=qb(O6(),1),{program:FS,createCommand:MS,createArgument:RS,createOption:xS,CommanderError:fS,InvalidArgumentError:ZS,InvalidOptionArgumentError:CS,Command:K,Argument:mS,Option:yS,Help:dS}=G6.default;import{readFile as Bw}from"node:fs/promises";import{resolve as Aw}from"node:path";var X6={name:"@shotstack/cli",version:"0.6.0",description:"Command-line interface for the Shotstack video rendering API.",license:"Apache-2.0",homepage:"https://github.com/shotstack/shotstack-cli",repository:{type:"git",url:"git+https://github.com/shotstack/shotstack-cli.git"},bugs:{url:"https://github.com/shotstack/shotstack-cli/issues"},type:"module",bin:{shotstack:"dist/shotstack.js"},files:["dist/shotstack.js","SKILL.md","shared","references"],scripts:{start:"bun run src/index.ts",build:"bun build src/index.ts --target=node --minify --outfile=dist/shotstack.js --banner='#!/usr/bin/env node' && chmod +x dist/shotstack.js",prepack:"bun run build",typecheck:"tsc --noEmit",test:"bun test"},dependencies:{"@shotstack/schemas":"^1.12.2",commander:"^12.1.0",zod:"^4.4.3"},devDependencies:{"@types/bun":"latest",typescript:"^5.6.0"}};var Jn=X6.version,W6=`shotstack-cli/${Jn} (${process.platform}-${process.arch})`;class lr extends Error{status;body;constructor(r,i,t){super(t);this.status=r;this.body=i;this.name="ApiError"}get isTransient(){return this.status>=500||this.status===429}}function rr(r){let i=r.env.baseUrl.replace(/\/$/,"");async function t(o,n,$){let g=await fetch(`${i}${n}`,{method:o,headers:{"x-api-key":r.apiKey,"x-shotstack-origin":"cli","x-shotstack-environment":r.env.name,"user-agent":W6,...$!==void 0?{"content-type":"application/json"}:{}},body:$!==void 0?JSON.stringify($):void 0}),I=await g.text(),c=I?JU(I):void 0;if(!g.ok)throw new lr(g.status,c??I,`${o} ${n} → ${g.status} ${g.statusText}`);return c}return{post:(o,n)=>t("POST",o,n),get:(o)=>t("GET",o),del:(o)=>t("DELETE",o)}}function JU(r){try{return JSON.parse(r)}catch{return r}}import{mkdir as LU,writeFile as EU,chmod as OU}from"node:fs/promises";import{readFileSync as GU}from"node:fs";import{join as n$,dirname as XU}from"node:path";import{homedir as q6}from"node:os";function Fr(){if(process.platform==="win32"){let r=process.env.APPDATA??n$(q6(),"AppData","Roaming");return n$(r,"shotstack","credentials.json")}return n$(q6(),".shotstack","credentials.json")}function i$(r){try{let i=JSON.parse(GU(r,"utf8"));if(i&&typeof i==="object"&&!Array.isArray(i))return i}catch{}return{}}function Y6(r,i=Fr()){return i$(i)[r]?.trim()||void 0}async function T6(r,i,t=Fr()){let o=i$(t);o[r]=i.trim(),await t$(t,o)}async function Q6(r,i=Fr()){let t=i$(i);if(!(r in t))return!1;return delete t[r],await t$(i,t),!0}async function V6(r=Fr()){await t$(r,{})}async function t$(r,i){await LU(XU(r),{recursive:!0,mode:448}),await EU(r,JSON.stringify(i,null,2)+`
|
|
24
24
|
`,{mode:384}),await OU(r,384).catch(()=>{})}class Mr extends Error{constructor(r){let i=r?` for env "${r}"`:"",t=r?`shotstack login --env ${r}`:"shotstack login";super(`No Shotstack API key found${i}. Run \`${t}\` to save one, or set SHOTSTACK_API_KEY. Get a key at https://shotstack.io.`);this.name="MissingApiKeyError"}}function nr(r,i){let t=process.env.SHOTSTACK_API_KEY?.trim();if(t)return t;let o=Y6(r,i);if(o)return o;throw new Mr(r)}var M=["dev","stage","v1"];class Rr extends Error{constructor(r){super(`Invalid environment "${r}". Use one of: ${M.join(", ")}.`);this.name="InvalidEnvError"}}function Ii(r){let i=r??process.env.SHOTSTACK_ENV?.trim()??"v1";if(!qU(i))throw new Rr(i);return i}function ir(r,i="edit"){let t=WU(i);if(t)return{name:"custom",baseUrl:t};let o=Ii(r);return{name:o,baseUrl:`https://api.shotstack.io/${i}/${o}`}}function WU(r){return(r==="ingest"?process.env.SHOTSTACK_INGEST_BASE_URL:process.env.SHOTSTACK_API_BASE_URL)?.trim()||void 0}function qU(r){return M.includes(r)}function B(r){if(r==="json")return"json";return"text"}function R(r,i,t){if(r==="json")console.log(JSON.stringify(i));else console.log(t)}import{mkdir as YU,appendFile as TU,stat as A6,readFile as QU,writeFile as F6}from"node:fs/promises";import{join as $$,dirname as VU}from"node:path";import{homedir as H6}from"node:os";var HU=1e6,KU=500000,K6=2000;function ci(){if(process.platform==="win32"){let r=process.env.APPDATA??$$(H6(),"AppData","Roaming");return $$(r,"shotstack","log.jsonl")}return $$(H6(),".shotstack","log.jsonl")}function BU(r,i=new Date){let t={ts:i.toISOString(),cmd:r.cmd,args:r.args.map(ZU),exit:r.exit,durationMs:r.durationMs};if(r.renderId)t.renderId=r.renderId;if(r.response!==void 0)t.response=mU(u$(r.response));if(r.error)t.error=AU(r.error);return t}function AU(r){return{name:r.name,message:typeof r.message==="string"?o$(r.message):String(r.message)}}function FU(r){if(r instanceof Error)return{name:r.name,message:r.message};if(r===void 0||r===null)return;return{name:"Unknown",message:String(r)}}async function B6(r,i=ci()){let t=BU(r),o=JSON.stringify(t)+`
|
|
25
25
|
`;if(await YU(VU(i),{recursive:!0,mode:448}),!await MU(i))await F6(i,"",{mode:384});await TU(i,o),await RU(i)}async function MU(r){try{return await A6(r),!0}catch{return!1}}async function RU(r){let i=await A6(r).catch(()=>null);if(!i||i.size<=HU)return;let t=await QU(r),o=t.subarray(Math.max(0,t.length-KU)),n=o.indexOf(10),$=n>=0?o.subarray(n+1):o;await F6(r,$,{mode:384})}var xU=/^https?:\/\//i,fU=/(https?:\/\/[^\s'"<>?]+)\?[^\s'"<>]*?(?:Signature|X-Amz-[A-Za-z0-9-]+|x-amz-security-token)=[^\s'"<>]*/gi;function o$(r){return r.replace(fU,"$1?[redacted-signed]")}function ZU(r){if(typeof r!=="string")return r;if(r.startsWith("-"))return r;if(xU.test(r))return o$(r);if(/[\/\\]/.test(r))return CU(r);return r}function CU(r){let i=r.split(/[\/\\]/);return i[i.length-1]||r}function u$(r){if(typeof r==="string")return o$(r);if(Array.isArray(r))return r.map(u$);if(r&&typeof r==="object"){let i={};for(let[t,o]of Object.entries(r))i[t]=u$(o);return i}return r}function mU(r){let i=JSON.stringify(r);if(i===void 0||i.length<=K6)return r;return i.slice(0,K6)+"...[truncated]"}async function tr(r,i,t){let o=Date.now();try{let n=await t(),$=n.exitCode??0;if(await B6({cmd:r,args:i,exit:$,durationMs:Date.now()-o,renderId:n.renderId,response:n.response}).catch(()=>{}),$!==0)process.exit($)}catch(n){let $=yU(n),g=n instanceof lr?n.body:void 0;throw await B6({cmd:r,args:i,exit:$,durationMs:Date.now()-o,response:g,error:FU(n)}).catch(()=>{}),n}}function yU(r){if(r instanceof lr)return r.isTransient?2:1;if(r instanceof Mr||r instanceof Rr)return 1;if(r instanceof Error&&r.code==="ENOENT")return 1;return 2}function $r(r){let i=process.argv.slice(2),t=i.indexOf(r);return t>=0?i.slice(t+1):i}var dU=new Set(["done","failed"]),hU=3000,M6=new K("status").description("Poll the status of a Shotstack render").argument("<id>","Render ID returned by `shotstack render`").option("--watch","Poll continuously until the render reaches a terminal state").option("--env <name>",`Environment: ${M.join(" | ")}`).option("--output <format>","Output format: text | json","text").action(async(r,i)=>{await tr("status",$r("status"),async()=>{let t=B(i.output),o=ir(i.env),n=nr(o.name),$=rr({apiKey:n,env:o}),g=await g$($,r,t,i.watch===!0),I=g.status==="failed"?1:0;return{renderId:g.id,response:g,exitCode:I}})});async function g$(r,i,t,o){while(!0){let $=(await r.get(`/render/${encodeURIComponent(i)}`)).response;if(R(t,$,vU($)),!o||dU.has($.status))return $;await aU(hU)}}function vU(r){let i=[r.status];if(r.url)i.push(r.url);if(r.error)i.push(`error: ${r.error}`);return i.join(" ")}function aU(r){return new Promise((i)=>setTimeout(i,r))}var u={};br(u,{xor:()=>S_,xid:()=>Rc,void:()=>U_,uuidv7:()=>Qc,uuidv6:()=>Tc,uuidv4:()=>Yc,uuid:()=>qc,util:()=>z,url:()=>Vc,uppercase:()=>sr,unknown:()=>Hr,union:()=>Bt,undefined:()=>__,ulid:()=>Mc,uint64:()=>I_,uint32:()=>u_,tuple:()=>q4,trim:()=>on,treeifyError:()=>N$,transform:()=>Ft,toUpperCase:()=>In,toLowerCase:()=>gn,toJSONSchema:()=>_t,templateLiteral:()=>T_,symbol:()=>c_,superRefine:()=>$6,success:()=>X_,stringbool:()=>M_,stringFormat:()=>pc,string:()=>yn,strictObject:()=>D_,startsWith:()=>nn,slugify:()=>cn,size:()=>Tr,setErrorMap:()=>Yk,set:()=>J_,safeParseAsync:()=>$4,safeParse:()=>t4,safeEncodeAsync:()=>b4,safeEncode:()=>c4,safeDecodeAsync:()=>U4,safeDecode:()=>_4,registry:()=>Hi,regexes:()=>h,regex:()=>ar,refine:()=>t6,record:()=>Y4,readonly:()=>a4,property:()=>ot,promise:()=>Q_,prettifyError:()=>j$,preprocess:()=>x_,prefault:()=>f4,positive:()=>it,pipe:()=>wt,partialRecord:()=>N_,parseAsync:()=>i4,parse:()=>n4,overwrite:()=>p,optional:()=>hn,object:()=>k_,number:()=>e4,nullish:()=>G_,nullable:()=>vn,null:()=>z4,normalize:()=>un,nonpositive:()=>$t,nonoptional:()=>Z4,nonnegative:()=>ut,never:()=>Kt,negative:()=>tt,nativeEnum:()=>L_,nanoid:()=>Bc,nan:()=>W_,multipleOf:()=>jr,minSize:()=>Ir,minLength:()=>kr,mime:()=>$n,meta:()=>A_,maxSize:()=>Pr,maxLength:()=>Qr,map:()=>P_,mac:()=>Zc,lte:()=>v,lt:()=>or,lowercase:()=>pr,looseRecord:()=>j_,looseObject:()=>w_,locales:()=>fn,literal:()=>E_,length:()=>Vr,lazy:()=>r6,ksuid:()=>xc,keyof:()=>e_,jwt:()=>ac,json:()=>R_,iso:()=>Un,ipv6:()=>Cc,ipv4:()=>fc,invertCodec:()=>Y_,intersection:()=>X4,int64:()=>g_,int32:()=>$_,int:()=>Dt,instanceof:()=>F_,includes:()=>rn,httpUrl:()=>Hc,hostname:()=>sc,hex:()=>r_,hash:()=>n_,guid:()=>Wc,gte:()=>Z,gt:()=>gr,globalRegistry:()=>A,getErrorMap:()=>Tk,function:()=>V_,fromJSONSchema:()=>Z_,formatError:()=>Tn,float64:()=>t_,float32:()=>i_,flattenError:()=>Yn,file:()=>O_,exactOptional:()=>A4,enum:()=>At,endsWith:()=>tn,encodeAsync:()=>g4,encode:()=>u4,emoji:()=>Kc,email:()=>Xc,e164:()=>vc,discriminatedUnion:()=>z_,describe:()=>B_,decodeAsync:()=>I4,decode:()=>o4,date:()=>l_,custom:()=>K_,cuid2:()=>Fc,cuid:()=>Ac,core:()=>Dr,config:()=>V,coerce:()=>o6,codec:()=>q_,clone:()=>x,cidrv6:()=>yc,cidrv4:()=>mc,check:()=>H_,catch:()=>y4,boolean:()=>k4,bigint:()=>o_,base64url:()=>hc,base64:()=>dc,array:()=>ri,any:()=>b_,_function:()=>V_,_default:()=>R4,_ZodString:()=>St,ZodXor:()=>E4,ZodXID:()=>Et,ZodVoid:()=>J4,ZodUnknown:()=>j4,ZodUnion:()=>ii,ZodUndefined:()=>w4,ZodUUID:()=>cr,ZodURL:()=>pn,ZodULID:()=>Lt,ZodType:()=>L,ZodTuple:()=>W4,ZodTransform:()=>K4,ZodTemplateLiteral:()=>p4,ZodSymbol:()=>D4,ZodSuccess:()=>C4,ZodStringFormat:()=>q,ZodString:()=>kn,ZodSet:()=>Q4,ZodRecord:()=>ln,ZodRealError:()=>d,ZodReadonly:()=>v4,ZodPromise:()=>n6,ZodPreprocess:()=>h4,ZodPrefault:()=>x4,ZodPipe:()=>ti,ZodOptional:()=>Mt,ZodObject:()=>ni,ZodNumberFormat:()=>Kr,ZodNumber:()=>wn,ZodNullable:()=>F4,ZodNull:()=>S4,ZodNonOptional:()=>Rt,ZodNever:()=>P4,ZodNanoID:()=>jt,ZodNaN:()=>d4,ZodMap:()=>T4,ZodMAC:()=>l4,ZodLiteral:()=>V4,ZodLazy:()=>s4,ZodKSUID:()=>Ot,ZodJWT:()=>Vt,ZodIssueCode:()=>qk,ZodIntersection:()=>G4,ZodISOTime:()=>et,ZodISODuration:()=>kt,ZodISODateTime:()=>Ut,ZodISODate:()=>lt,ZodIPv6:()=>Xt,ZodIPv4:()=>Gt,ZodGUID:()=>dn,ZodFunction:()=>i6,ZodFirstPartyTypeKind:()=>u6,ZodFile:()=>H4,ZodExactOptional:()=>B4,ZodError:()=>Xk,ZodEnum:()=>en,ZodEmoji:()=>Nt,ZodEmail:()=>zt,ZodE164:()=>Qt,ZodDiscriminatedUnion:()=>O4,ZodDefault:()=>M4,ZodDate:()=>sn,ZodCustomStringFormat:()=>Dn,ZodCustom:()=>ui,ZodCodec:()=>$i,ZodCatch:()=>m4,ZodCUID2:()=>Jt,ZodCUID:()=>Pt,ZodCIDRv6:()=>qt,ZodCIDRv4:()=>Wt,ZodBoolean:()=>Sn,ZodBigIntFormat:()=>Ht,ZodBigInt:()=>zn,ZodBase64URL:()=>Tt,ZodBase64:()=>Yt,ZodArray:()=>L4,ZodAny:()=>N4,TimePrecision:()=>qg,NEVER:()=>I$,$output:()=>Lg,$input:()=>Eg,$brand:()=>c$});var Dr={};br(Dr,{version:()=>wu,util:()=>z,treeifyError:()=>N$,toJSONSchema:()=>_t,toDotPath:()=>y6,safeParseAsync:()=>J$,safeParse:()=>P$,safeEncodeAsync:()=>ql,safeEncode:()=>Xl,safeDecodeAsync:()=>Yl,safeDecode:()=>Wl,registry:()=>Hi,regexes:()=>h,process:()=>X,prettifyError:()=>j$,parseAsync:()=>li,parse:()=>Ui,meta:()=>_I,locales:()=>fn,isValidJWT:()=>kc,isValidBase64URL:()=>ec,isValidBase64:()=>Fu,initializeContext:()=>Jr,globalRegistry:()=>A,globalConfig:()=>Gr,formatError:()=>Tn,flattenError:()=>Yn,finalize:()=>Er,extractDefs:()=>Lr,encodeAsync:()=>Ol,encode:()=>Ll,describe:()=>cI,decodeAsync:()=>Gl,decode:()=>El,createToJSONSchemaMethod:()=>UI,createStandardJSONSchemaMethod:()=>bn,config:()=>V,clone:()=>x,_xor:()=>$k,_xid:()=>mi,_void:()=>rI,_uuidv7:()=>Mi,_uuidv6:()=>Fi,_uuidv4:()=>Ai,_uuid:()=>Bi,_url:()=>Cn,_uppercase:()=>sr,_unknown:()=>pg,_union:()=>tk,_undefined:()=>hg,_ulid:()=>Ci,_uint64:()=>yg,_uint32:()=>Rg,_tuple:()=>gk,_trim:()=>on,_transform:()=>ek,_toUpperCase:()=>In,_toLowerCase:()=>gn,_templateLiteral:()=>Jk,_symbol:()=>dg,_superRefine:()=>II,_success:()=>zk,_stringbool:()=>bI,_stringFormat:()=>_n,_string:()=>Gg,_startsWith:()=>nn,_slugify:()=>cn,_size:()=>Tr,_set:()=>_k,_safeParseAsync:()=>dr,_safeParse:()=>yr,_safeEncodeAsync:()=>Ni,_safeEncode:()=>Si,_safeDecodeAsync:()=>ji,_safeDecode:()=>zi,_regex:()=>ar,_refine:()=>gI,_record:()=>Ik,_readonly:()=>Pk,_property:()=>ot,_promise:()=>Ek,_positive:()=>it,_pipe:()=>jk,_parseAsync:()=>mr,_parse:()=>Cr,_overwrite:()=>p,_optional:()=>kk,_number:()=>Hg,_nullable:()=>Dk,_null:()=>vg,_normalize:()=>un,_nonpositive:()=>$t,_nonoptional:()=>Sk,_nonnegative:()=>ut,_never:()=>sg,_negative:()=>tt,_nativeEnum:()=>Uk,_nanoid:()=>xi,_nan:()=>tI,_multipleOf:()=>jr,_minSize:()=>Ir,_minLength:()=>kr,_min:()=>Z,_mime:()=>$n,_maxSize:()=>Pr,_maxLength:()=>Qr,_max:()=>v,_map:()=>ck,_mac:()=>Wg,_lte:()=>v,_lt:()=>or,_lowercase:()=>pr,_literal:()=>lk,_length:()=>Vr,_lazy:()=>Lk,_ksuid:()=>yi,_jwt:()=>nt,_isoTime:()=>Qg,_isoDuration:()=>Vg,_isoDateTime:()=>Yg,_isoDate:()=>Tg,_ipv6:()=>hi,_ipv4:()=>di,_intersection:()=>ok,_int64:()=>mg,_int32:()=>Mg,_int:()=>Bg,_includes:()=>rn,_guid:()=>Zn,_gte:()=>Z,_gt:()=>gr,_float64:()=>Fg,_float32:()=>Ag,_file:()=>uI,_enum:()=>bk,_endsWith:()=>tn,_encodeAsync:()=>Di,_encode:()=>ei,_emoji:()=>Ri,_email:()=>Ki,_e164:()=>rt,_discriminatedUnion:()=>uk,_default:()=>wk,_decodeAsync:()=>wi,_decode:()=>ki,_date:()=>nI,_custom:()=>oI,_cuid2:()=>Zi,_cuid:()=>fi,_coercedString:()=>Xg,_coercedNumber:()=>Kg,_coercedDate:()=>iI,_coercedBoolean:()=>fg,_coercedBigint:()=>Cg,_cidrv6:()=>ai,_cidrv4:()=>vi,_check:()=>Lc,_catch:()=>Nk,_boolean:()=>xg,_bigint:()=>Zg,_base64url:()=>si,_base64:()=>pi,_array:()=>$I,_any:()=>ag,TimePrecision:()=>qg,NEVER:()=>I$,JSONSchemaGenerator:()=>vI,JSONSchema:()=>Ec,Doc:()=>Ei,$output:()=>Lg,$input:()=>Eg,$constructor:()=>U,$brand:()=>c$,$ZodXor:()=>to,$ZodXID:()=>Xu,$ZodVoid:()=>su,$ZodUnknown:()=>au,$ZodUnion:()=>Bn,$ZodUndefined:()=>du,$ZodUUID:()=>Nu,$ZodURL:()=>Pu,$ZodULID:()=>Gu,$ZodType:()=>J,$ZodTuple:()=>Ti,$ZodTransform:()=>Uo,$ZodTemplateLiteral:()=>Jo,$ZodSymbol:()=>yu,$ZodSuccess:()=>So,$ZodStringFormat:()=>W,$ZodString:()=>Yr,$ZodSet:()=>Io,$ZodRegistry:()=>Og,$ZodRecord:()=>oo,$ZodRealError:()=>y,$ZodReadonly:()=>Po,$ZodPromise:()=>Eo,$ZodPreprocess:()=>jo,$ZodPrefault:()=>Do,$ZodPipe:()=>Vi,$ZodOptional:()=>Qi,$ZodObjectJIT:()=>io,$ZodObject:()=>Sc,$ZodNumberFormat:()=>Cu,$ZodNumber:()=>qi,$ZodNullable:()=>eo,$ZodNull:()=>hu,$ZodNonOptional:()=>wo,$ZodNever:()=>pu,$ZodNanoID:()=>Lu,$ZodNaN:()=>No,$ZodMap:()=>go,$ZodMAC:()=>Ku,$ZodLiteral:()=>_o,$ZodLazy:()=>Oo,$ZodKSUID:()=>Wu,$ZodJWT:()=>fu,$ZodIntersection:()=>uo,$ZodISOTime:()=>Tu,$ZodISODuration:()=>Qu,$ZodISODateTime:()=>qu,$ZodISODate:()=>Yu,$ZodIPv6:()=>Hu,$ZodIPv4:()=>Vu,$ZodGUID:()=>zu,$ZodFunction:()=>Lo,$ZodFile:()=>bo,$ZodExactOptional:()=>lo,$ZodError:()=>qn,$ZodEnum:()=>co,$ZodEncodeError:()=>Xr,$ZodEmoji:()=>Ju,$ZodEmail:()=>ju,$ZodE164:()=>xu,$ZodDiscriminatedUnion:()=>$o,$ZodDefault:()=>ko,$ZodDate:()=>ro,$ZodCustomStringFormat:()=>Zu,$ZodCustom:()=>Go,$ZodCodec:()=>An,$ZodCheckUpperCase:()=>_u,$ZodCheckStringFormat:()=>hr,$ZodCheckStartsWith:()=>Uu,$ZodCheckSizeEquals:()=>$u,$ZodCheckRegex:()=>Iu,$ZodCheckProperty:()=>eu,$ZodCheckOverwrite:()=>Du,$ZodCheckNumberFormat:()=>ru,$ZodCheckMultipleOf:()=>s$,$ZodCheckMinSize:()=>tu,$ZodCheckMinLength:()=>ou,$ZodCheckMimeType:()=>ku,$ZodCheckMaxSize:()=>iu,$ZodCheckMaxLength:()=>uu,$ZodCheckLowerCase:()=>cu,$ZodCheckLessThan:()=>Ji,$ZodCheckLengthEquals:()=>gu,$ZodCheckIncludes:()=>bu,$ZodCheckGreaterThan:()=>Li,$ZodCheckEndsWith:()=>lu,$ZodCheckBigIntFormat:()=>nu,$ZodCheck:()=>Y,$ZodCatch:()=>zo,$ZodCUID2:()=>Ou,$ZodCUID:()=>Eu,$ZodCIDRv6:()=>Au,$ZodCIDRv4:()=>Bu,$ZodBoolean:()=>Kn,$ZodBigIntFormat:()=>mu,$ZodBigInt:()=>Yi,$ZodBase64URL:()=>Ru,$ZodBase64:()=>Mu,$ZodAsyncError:()=>ur,$ZodArray:()=>no,$ZodAny:()=>vu});var R6,I$=Object.freeze({status:"aborted"});function U(r,i,t){function o(I,c){if(!I._zod)Object.defineProperty(I,"_zod",{value:{def:c,constr:g,traits:new Set},enumerable:!1});if(I._zod.traits.has(r))return;I._zod.traits.add(r),i(I,c);let _=g.prototype,b=Object.keys(_);for(let e=0;e<b.length;e++){let D=b[e];if(!(D in I))I[D]=_[D].bind(I)}}let n=t?.Parent??Object;class $ extends n{}Object.defineProperty($,"name",{value:r});function g(I){var c;let _=t?.Parent?new $:this;o(_,I),(c=_._zod).deferred??(c.deferred=[]);for(let b of _._zod.deferred)b();return _}return Object.defineProperty(g,"init",{value:o}),Object.defineProperty(g,Symbol.hasInstance,{value:(I)=>{if(t?.Parent&&I instanceof t.Parent)return!0;return I?._zod?.traits?.has(r)}}),Object.defineProperty(g,"name",{value:r}),g}var c$=Symbol("zod_brand");class ur extends Error{constructor(){super("Encountered Promise during synchronous parse. Use .parseAsync() instead.")}}class Xr extends Error{constructor(r){super(`Encountered unidirectional transform during encode: ${r}`);this.name="ZodEncodeError"}}(R6=globalThis).__zod_globalConfig??(R6.__zod_globalConfig={});var Gr=globalThis.__zod_globalConfig;function V(r){if(r)Object.assign(Gr,r);return Gr}var z={};br(z,{unwrapMessage:()=>Ln,uint8ArrayToHex:()=>Pl,uint8ArrayToBase64url:()=>Nl,uint8ArrayToBase64:()=>Z6,stringifyPrimitive:()=>w,slugify:()=>U$,shallowClone:()=>e$,safeExtend:()=>el,required:()=>wl,randomString:()=>gl,propertyKeyTypes:()=>Gn,promiseAllObject:()=>ol,primitiveTypes:()=>k$,prefixIssues:()=>m,pick:()=>bl,partial:()=>Dl,parsedType:()=>S,optionalKeys:()=>D$,omit:()=>Ul,objectClone:()=>tl,numKeys:()=>Il,nullish:()=>wr,normalizeParams:()=>N,mergeDefs:()=>er,merge:()=>kl,jsonStringifyReplacer:()=>xr,joinValues:()=>l,issue:()=>Zr,isPlainObject:()=>zr,isObject:()=>Wr,hexToUint8Array:()=>jl,getSizableOrigin:()=>Xn,getParsedType:()=>cl,getLengthableOrigin:()=>Wn,getEnumValues:()=>En,getElementAtPath:()=>ul,floatSafeRemainder:()=>b$,finalizeIssue:()=>f,extend:()=>ll,explicitlyAborted:()=>z$,escapeRegex:()=>a,esc:()=>_i,defineLazy:()=>E,createTransparentProxy:()=>_l,cloneDef:()=>$l,clone:()=>x,cleanRegex:()=>On,cleanEnum:()=>Sl,captureStackTrace:()=>bi,cached:()=>fr,base64urlToUint8Array:()=>zl,base64ToUint8Array:()=>f6,assignProp:()=>Sr,assertNotEqual:()=>sU,assertNever:()=>nl,assertIs:()=>rl,assertEqual:()=>pU,assert:()=>il,allowsEval:()=>l$,aborted:()=>Nr,NUMBER_FORMAT_RANGES:()=>w$,Class:()=>C6,BIGINT_FORMAT_RANGES:()=>S$});function pU(r){return r}function sU(r){return r}function rl(r){}function nl(r){throw Error("Unexpected value in exhaustive check")}function il(r){}function En(r){let i=Object.values(r).filter((o)=>typeof o==="number");return Object.entries(r).filter(([o,n])=>i.indexOf(+o)===-1).map(([o,n])=>n)}function l(r,i="|"){return r.map((t)=>w(t)).join(i)}function xr(r,i){if(typeof i==="bigint")return i.toString();return i}function fr(r){return{get value(){{let t=r();return Object.defineProperty(this,"value",{value:t}),t}throw Error("cached value already set")}}}function wr(r){return r===null||r===void 0}function On(r){let i=r.startsWith("^")?1:0,t=r.endsWith("$")?r.length-1:r.length;return r.slice(i,t)}function b$(r,i){let t=r/i,o=Math.round(t),n=Number.EPSILON*Math.max(Math.abs(t),1);if(Math.abs(t-o)<n)return 0;return t-o}var x6=Symbol("evaluating");function E(r,i,t){let o=void 0;Object.defineProperty(r,i,{get(){if(o===x6)return;if(o===void 0)o=x6,o=t();return o},set(n){Object.defineProperty(r,i,{value:n})},configurable:!0})}function tl(r){return Object.create(Object.getPrototypeOf(r),Object.getOwnPropertyDescriptors(r))}function Sr(r,i,t){Object.defineProperty(r,i,{value:t,writable:!0,enumerable:!0,configurable:!0})}function er(...r){let i={};for(let t of r){let o=Object.getOwnPropertyDescriptors(t);Object.assign(i,o)}return Object.defineProperties({},i)}function $l(r){return er(r._zod.def)}function ul(r,i){if(!i)return r;return i.reduce((t,o)=>t?.[o],r)}function ol(r){let i=Object.keys(r),t=i.map((o)=>r[o]);return Promise.all(t).then((o)=>{let n={};for(let $=0;$<i.length;$++)n[i[$]]=o[$];return n})}function gl(r=10){let t="";for(let o=0;o<r;o++)t+="abcdefghijklmnopqrstuvwxyz"[Math.floor(Math.random()*26)];return t}function _i(r){return JSON.stringify(r)}function U$(r){return r.toLowerCase().trim().replace(/[^\w\s-]/g,"").replace(/[\s_-]+/g,"-").replace(/^-+|-+$/g,"")}var bi="captureStackTrace"in Error?Error.captureStackTrace:(...r)=>{};function Wr(r){return typeof r==="object"&&r!==null&&!Array.isArray(r)}var l$=fr(()=>{if(Gr.jitless)return!1;if(typeof navigator<"u"&&navigator?.userAgent?.includes("Cloudflare"))return!1;try{return new Function(""),!0}catch(r){return!1}});function zr(r){if(Wr(r)===!1)return!1;let i=r.constructor;if(i===void 0)return!0;if(typeof i!=="function")return!0;let t=i.prototype;if(Wr(t)===!1)return!1;if(Object.prototype.hasOwnProperty.call(t,"isPrototypeOf")===!1)return!1;return!0}function e$(r){if(zr(r))return{...r};if(Array.isArray(r))return[...r];if(r instanceof Map)return new Map(r);if(r instanceof Set)return new Set(r);return r}function Il(r){let i=0;for(let t in r)if(Object.prototype.hasOwnProperty.call(r,t))i++;return i}var cl=(r)=>{let i=typeof r;switch(i){case"undefined":return"undefined";case"string":return"string";case"number":return Number.isNaN(r)?"nan":"number";case"boolean":return"boolean";case"function":return"function";case"bigint":return"bigint";case"symbol":return"symbol";case"object":if(Array.isArray(r))return"array";if(r===null)return"null";if(r.then&&typeof r.then==="function"&&r.catch&&typeof r.catch==="function")return"promise";if(typeof Map<"u"&&r instanceof Map)return"map";if(typeof Set<"u"&&r instanceof Set)return"set";if(typeof Date<"u"&&r instanceof Date)return"date";if(typeof File<"u"&&r instanceof File)return"file";return"object";default:throw Error(`Unknown data type: ${i}`)}},Gn=new Set(["string","number","symbol"]),k$=new Set(["string","number","bigint","boolean","symbol","undefined"]);function a(r){return r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function x(r,i,t){let o=new r._zod.constr(i??r._zod.def);if(!i||t?.parent)o._zod.parent=r;return o}function N(r){let i=r;if(!i)return{};if(typeof i==="string")return{error:()=>i};if(i?.message!==void 0){if(i?.error!==void 0)throw Error("Cannot specify both `message` and `error` params");i.error=i.message}if(delete i.message,typeof i.error==="string")return{...i,error:()=>i.error};return i}function _l(r){let i;return new Proxy({},{get(t,o,n){return i??(i=r()),Reflect.get(i,o,n)},set(t,o,n,$){return i??(i=r()),Reflect.set(i,o,n,$)},has(t,o){return i??(i=r()),Reflect.has(i,o)},deleteProperty(t,o){return i??(i=r()),Reflect.deleteProperty(i,o)},ownKeys(t){return i??(i=r()),Reflect.ownKeys(i)},getOwnPropertyDescriptor(t,o){return i??(i=r()),Reflect.getOwnPropertyDescriptor(i,o)},defineProperty(t,o,n){return i??(i=r()),Reflect.defineProperty(i,o,n)}})}function w(r){if(typeof r==="bigint")return r.toString()+"n";if(typeof r==="string")return`"${r}"`;return`${r}`}function D$(r){return Object.keys(r).filter((i)=>{return r[i]._zod.optin==="optional"&&r[i]._zod.optout==="optional"})}var w$={safeint:[Number.MIN_SAFE_INTEGER,Number.MAX_SAFE_INTEGER],int32:[-2147483648,2147483647],uint32:[0,4294967295],float32:[-340282346638528860000000000000000000000,340282346638528860000000000000000000000],float64:[-Number.MAX_VALUE,Number.MAX_VALUE]},S$={int64:[BigInt("-9223372036854775808"),BigInt("9223372036854775807")],uint64:[BigInt(0),BigInt("18446744073709551615")]};function bl(r,i){let t=r._zod.def,o=t.checks;if(o&&o.length>0)throw Error(".pick() cannot be used on object schemas containing refinements");let $=er(r._zod.def,{get shape(){let g={};for(let I in i){if(!(I in t.shape))throw Error(`Unrecognized key: "${I}"`);if(!i[I])continue;g[I]=t.shape[I]}return Sr(this,"shape",g),g},checks:[]});return x(r,$)}function Ul(r,i){let t=r._zod.def,o=t.checks;if(o&&o.length>0)throw Error(".omit() cannot be used on object schemas containing refinements");let $=er(r._zod.def,{get shape(){let g={...r._zod.def.shape};for(let I in i){if(!(I in t.shape))throw Error(`Unrecognized key: "${I}"`);if(!i[I])continue;delete g[I]}return Sr(this,"shape",g),g},checks:[]});return x(r,$)}function ll(r,i){if(!zr(i))throw Error("Invalid input to extend: expected a plain object");let t=r._zod.def.checks;if(t&&t.length>0){let $=r._zod.def.shape;for(let g in i)if(Object.getOwnPropertyDescriptor($,g)!==void 0)throw Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead.")}let n=er(r._zod.def,{get shape(){let $={...r._zod.def.shape,...i};return Sr(this,"shape",$),$}});return x(r,n)}function el(r,i){if(!zr(i))throw Error("Invalid input to safeExtend: expected a plain object");let t=er(r._zod.def,{get shape(){let o={...r._zod.def.shape,...i};return Sr(this,"shape",o),o}});return x(r,t)}function kl(r,i){if(r._zod.def.checks?.length)throw Error(".merge() cannot be used on object schemas containing refinements. Use .safeExtend() instead.");let t=er(r._zod.def,{get shape(){let o={...r._zod.def.shape,...i._zod.def.shape};return Sr(this,"shape",o),o},get catchall(){return i._zod.def.catchall},checks:i._zod.def.checks??[]});return x(r,t)}function Dl(r,i,t){let n=i._zod.def.checks;if(n&&n.length>0)throw Error(".partial() cannot be used on object schemas containing refinements");let g=er(i._zod.def,{get shape(){let I=i._zod.def.shape,c={...I};if(t)for(let _ in t){if(!(_ in I))throw Error(`Unrecognized key: "${_}"`);if(!t[_])continue;c[_]=r?new r({type:"optional",innerType:I[_]}):I[_]}else for(let _ in I)c[_]=r?new r({type:"optional",innerType:I[_]}):I[_];return Sr(this,"shape",c),c},checks:[]});return x(i,g)}function wl(r,i,t){let o=er(i._zod.def,{get shape(){let n=i._zod.def.shape,$={...n};if(t)for(let g in t){if(!(g in $))throw Error(`Unrecognized key: "${g}"`);if(!t[g])continue;$[g]=new r({type:"nonoptional",innerType:n[g]})}else for(let g in n)$[g]=new r({type:"nonoptional",innerType:n[g]});return Sr(this,"shape",$),$}});return x(i,o)}function Nr(r,i=0){if(r.aborted===!0)return!0;for(let t=i;t<r.issues.length;t++)if(r.issues[t]?.continue!==!0)return!0;return!1}function z$(r,i=0){if(r.aborted===!0)return!0;for(let t=i;t<r.issues.length;t++)if(r.issues[t]?.continue===!1)return!0;return!1}function m(r,i){return i.map((t)=>{var o;return(o=t).path??(o.path=[]),t.path.unshift(r),t})}function Ln(r){return typeof r==="string"?r:r?.message}function f(r,i,t){let o=r.message?r.message:Ln(r.inst?._zod.def?.error?.(r))??Ln(i?.error?.(r))??Ln(t.customError?.(r))??Ln(t.localeError?.(r))??"Invalid input",{inst:n,continue:$,input:g,...I}=r;if(I.path??(I.path=[]),I.message=o,i?.reportInput)I.input=g;return I}function Xn(r){if(r instanceof Set)return"set";if(r instanceof Map)return"map";if(r instanceof File)return"file";return"unknown"}function Wn(r){if(Array.isArray(r))return"array";if(typeof r==="string")return"string";return"unknown"}function S(r){let i=typeof r;switch(i){case"number":return Number.isNaN(r)?"nan":"number";case"object":{if(r===null)return"null";if(Array.isArray(r))return"array";let t=r;if(t&&Object.getPrototypeOf(t)!==Object.prototype&&"constructor"in t&&t.constructor)return t.constructor.name}}return i}function Zr(...r){let[i,t,o]=r;if(typeof i==="string")return{message:i,code:"custom",input:t,inst:o};return{...i}}function Sl(r){return Object.entries(r).filter(([i,t])=>{return Number.isNaN(Number.parseInt(i,10))}).map((i)=>i[1])}function f6(r){let i=atob(r),t=new Uint8Array(i.length);for(let o=0;o<i.length;o++)t[o]=i.charCodeAt(o);return t}function Z6(r){let i="";for(let t=0;t<r.length;t++)i+=String.fromCharCode(r[t]);return btoa(i)}function zl(r){let i=r.replace(/-/g,"+").replace(/_/g,"/"),t="=".repeat((4-i.length%4)%4);return f6(i+t)}function Nl(r){return Z6(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}function jl(r){let i=r.replace(/^0x/,"");if(i.length%2!==0)throw Error("Invalid hex string length");let t=new Uint8Array(i.length/2);for(let o=0;o<i.length;o+=2)t[o/2]=Number.parseInt(i.slice(o,o+2),16);return t}function Pl(r){return Array.from(r).map((i)=>i.toString(16).padStart(2,"0")).join("")}class C6{constructor(...r){}}var m6=(r,i)=>{r.name="$ZodError",Object.defineProperty(r,"_zod",{value:r._zod,enumerable:!1}),Object.defineProperty(r,"issues",{value:i,enumerable:!1}),r.message=JSON.stringify(i,xr,2),Object.defineProperty(r,"toString",{value:()=>r.message,enumerable:!1})},qn=U("$ZodError",m6),y=U("$ZodError",m6,{Parent:Error});function Yn(r,i=(t)=>t.message){let t={},o=[];for(let n of r.issues)if(n.path.length>0)t[n.path[0]]=t[n.path[0]]||[],t[n.path[0]].push(i(n));else o.push(i(n));return{formErrors:o,fieldErrors:t}}function Tn(r,i=(t)=>t.message){let t={_errors:[]},o=(n,$=[])=>{for(let g of n.issues)if(g.code==="invalid_union"&&g.errors.length)g.errors.map((I)=>o({issues:I},[...$,...g.path]));else if(g.code==="invalid_key")o({issues:g.issues},[...$,...g.path]);else if(g.code==="invalid_element")o({issues:g.issues},[...$,...g.path]);else{let I=[...$,...g.path];if(I.length===0)t._errors.push(i(g));else{let c=t,_=0;while(_<I.length){let b=I[_];if(_!==I.length-1)c[b]=c[b]||{_errors:[]};else c[b]=c[b]||{_errors:[]},c[b]._errors.push(i(g));c=c[b],_++}}}};return o(r),t}function N$(r,i=(t)=>t.message){let t={errors:[]},o=(n,$=[])=>{var g,I;for(let c of n.issues)if(c.code==="invalid_union"&&c.errors.length)c.errors.map((_)=>o({issues:_},[...$,...c.path]));else if(c.code==="invalid_key")o({issues:c.issues},[...$,...c.path]);else if(c.code==="invalid_element")o({issues:c.issues},[...$,...c.path]);else{let _=[...$,...c.path];if(_.length===0){t.errors.push(i(c));continue}let b=t,e=0;while(e<_.length){let D=_[e],k=e===_.length-1;if(typeof D==="string")b.properties??(b.properties={}),(g=b.properties)[D]??(g[D]={errors:[]}),b=b.properties[D];else b.items??(b.items=[]),(I=b.items)[D]??(I[D]={errors:[]}),b=b.items[D];if(k)b.errors.push(i(c));e++}}};return o(r),t}function y6(r){let i=[],t=r.map((o)=>typeof o==="object"?o.key:o);for(let o of t)if(typeof o==="number")i.push(`[${o}]`);else if(typeof o==="symbol")i.push(`[${JSON.stringify(String(o))}]`);else if(/[^\w$]/.test(o))i.push(`[${JSON.stringify(o)}]`);else{if(i.length)i.push(".");i.push(o)}return i.join("")}function j$(r){let i=[],t=[...r.issues].sort((o,n)=>(o.path??[]).length-(n.path??[]).length);for(let o of t)if(i.push(`✖ ${o.message}`),o.path?.length)i.push(` → at ${y6(o.path)}`);return i.join(`
|
|
26
26
|
`)}var Cr=(r)=>(i,t,o,n)=>{let $=o?{...o,async:!1}:{async:!1},g=i._zod.run({value:t,issues:[]},$);if(g instanceof Promise)throw new ur;if(g.issues.length){let I=new(n?.Err??r)(g.issues.map((c)=>f(c,$,V())));throw bi(I,n?.callee),I}return g.value},Ui=Cr(y),mr=(r)=>async(i,t,o,n)=>{let $=o?{...o,async:!0}:{async:!0},g=i._zod.run({value:t,issues:[]},$);if(g instanceof Promise)g=await g;if(g.issues.length){let I=new(n?.Err??r)(g.issues.map((c)=>f(c,$,V())));throw bi(I,n?.callee),I}return g.value},li=mr(y),yr=(r)=>(i,t,o)=>{let n=o?{...o,async:!1}:{async:!1},$=i._zod.run({value:t,issues:[]},n);if($ instanceof Promise)throw new ur;return $.issues.length?{success:!1,error:new(r??qn)($.issues.map((g)=>f(g,n,V())))}:{success:!0,data:$.value}},P$=yr(y),dr=(r)=>async(i,t,o)=>{let n=o?{...o,async:!0}:{async:!0},$=i._zod.run({value:t,issues:[]},n);if($ instanceof Promise)$=await $;return $.issues.length?{success:!1,error:new r($.issues.map((g)=>f(g,n,V())))}:{success:!0,data:$.value}},J$=dr(y),ei=(r)=>(i,t,o)=>{let n=o?{...o,direction:"backward"}:{direction:"backward"};return Cr(r)(i,t,n)},Ll=ei(y),ki=(r)=>(i,t,o)=>{return Cr(r)(i,t,o)},El=ki(y),Di=(r)=>async(i,t,o)=>{let n=o?{...o,direction:"backward"}:{direction:"backward"};return mr(r)(i,t,n)},Ol=Di(y),wi=(r)=>async(i,t,o)=>{return mr(r)(i,t,o)},Gl=wi(y),Si=(r)=>(i,t,o)=>{let n=o?{...o,direction:"backward"}:{direction:"backward"};return yr(r)(i,t,n)},Xl=Si(y),zi=(r)=>(i,t,o)=>{return yr(r)(i,t,o)},Wl=zi(y),Ni=(r)=>async(i,t,o)=>{let n=o?{...o,direction:"backward"}:{direction:"backward"};return dr(r)(i,t,n)},ql=Ni(y),ji=(r)=>async(i,t,o)=>{return dr(r)(i,t,o)},Yl=ji(y);var h={};br(h,{xid:()=>G$,uuid7:()=>Hl,uuid6:()=>Vl,uuid4:()=>Ql,uuid:()=>qr,uppercase:()=>p$,unicodeEmail:()=>d6,undefined:()=>v$,ulid:()=>O$,time:()=>f$,string:()=>C$,sha512_hex:()=>ie,sha512_base64url:()=>$e,sha512_base64:()=>te,sha384_hex:()=>sl,sha384_base64url:()=>ne,sha384_base64:()=>re,sha256_hex:()=>vl,sha256_base64url:()=>pl,sha256_base64:()=>al,sha1_hex:()=>yl,sha1_base64url:()=>hl,sha1_base64:()=>dl,rfc5322Email:()=>Bl,number:()=>Qn,null:()=>h$,nanoid:()=>W$,md5_hex:()=>Zl,md5_base64url:()=>ml,md5_base64:()=>Cl,mac:()=>K$,lowercase:()=>a$,ksuid:()=>X$,ipv6:()=>H$,ipv4:()=>V$,integer:()=>y$,idnEmail:()=>Al,httpProtocol:()=>M$,html5Email:()=>Kl,hostname:()=>Rl,hex:()=>fl,guid:()=>Y$,extendedDuration:()=>Tl,emoji:()=>Q$,email:()=>T$,e164:()=>R$,duration:()=>q$,domain:()=>xl,datetime:()=>Z$,date:()=>x$,cuid2:()=>E$,cuid:()=>L$,cidrv6:()=>A$,cidrv4:()=>B$,browserEmail:()=>Fl,boolean:()=>d$,bigint:()=>m$,base64url:()=>Pi,base64:()=>F$});var L$=/^[cC][0-9a-z]{6,}$/,E$=/^[0-9a-z]+$/,O$=/^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/,G$=/^[0-9a-vA-V]{20}$/,X$=/^[A-Za-z0-9]{27}$/,W$=/^[a-zA-Z0-9_-]{21}$/,q$=/^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/,Tl=/^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/,Y$=/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/,qr=(r)=>{if(!r)return/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${r}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`)},Ql=qr(4),Vl=qr(6),Hl=qr(7),T$=/^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/,Kl=/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,Bl=/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,d6=/^[^\s@"]{1,64}@[^\s@]{1,255}$/u,Al=d6,Fl=/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/,Ml="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";function Q$(){return new RegExp(Ml,"u")}var V$=/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/,H$=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/,K$=(r)=>{let i=a(r??":");return new RegExp(`^(?:[0-9A-F]{2}${i}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${i}){5}[0-9a-f]{2}$`)},B$=/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/,A$=/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,F$=/^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/,Pi=/^[A-Za-z0-9_-]*$/,Rl=/^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/,xl=/^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/,M$=/^https?$/,R$=/^\+[1-9]\d{6,14}$/,h6="(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))",x$=new RegExp(`^${h6}$`);function v6(r){return typeof r.precision==="number"?r.precision===-1?"(?:[01]\\d|2[0-3]):[0-5]\\d":r.precision===0?"(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d":`(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d\\.\\d{${r.precision}}`:"(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?"}function f$(r){return new RegExp(`^${v6(r)}$`)}function Z$(r){let i=v6({precision:r.precision}),t=["Z"];if(r.local)t.push("");if(r.offset)t.push("([+-](?:[01]\\d|2[0-3]):[0-5]\\d)");let o=`${i}(?:${t.join("|")})`;return new RegExp(`^${h6}T(?:${o})$`)}var C$=(r)=>{let i=r?`[\\s\\S]{${r?.minimum??0},${r?.maximum??""}}`:"[\\s\\S]*";return new RegExp(`^${i}$`)},m$=/^-?\d+n?$/,y$=/^-?\d+$/,Qn=/^-?\d+(?:\.\d+)?$/,d$=/^(?:true|false)$/i,h$=/^null$/i;var v$=/^undefined$/i;var a$=/^[^A-Z]*$/,p$=/^[^a-z]*$/,fl=/^[0-9a-fA-F]*$/;function Vn(r,i){return new RegExp(`^[A-Za-z0-9+/]{${r}}${i}$`)}function Hn(r){return new RegExp(`^[A-Za-z0-9_-]{${r}}$`)}var Zl=/^[0-9a-fA-F]{32}$/,Cl=Vn(22,"=="),ml=Hn(22),yl=/^[0-9a-fA-F]{40}$/,dl=Vn(27,"="),hl=Hn(27),vl=/^[0-9a-fA-F]{64}$/,al=Vn(43,"="),pl=Hn(43),sl=/^[0-9a-fA-F]{96}$/,re=Vn(64,""),ne=Hn(64),ie=/^[0-9a-fA-F]{128}$/,te=Vn(86,"=="),$e=Hn(86);var Y=U("$ZodCheck",(r,i)=>{var t;r._zod??(r._zod={}),r._zod.def=i,(t=r._zod).onattach??(t.onattach=[])}),p6={number:"number",bigint:"bigint",object:"date"},Ji=U("$ZodCheckLessThan",(r,i)=>{Y.init(r,i);let t=p6[typeof i.value];r._zod.onattach.push((o)=>{let n=o._zod.bag,$=(i.inclusive?n.maximum:n.exclusiveMaximum)??Number.POSITIVE_INFINITY;if(i.value<$)if(i.inclusive)n.maximum=i.value;else n.exclusiveMaximum=i.value}),r._zod.check=(o)=>{if(i.inclusive?o.value<=i.value:o.value<i.value)return;o.issues.push({origin:t,code:"too_big",maximum:typeof i.value==="object"?i.value.getTime():i.value,input:o.value,inclusive:i.inclusive,inst:r,continue:!i.abort})}}),Li=U("$ZodCheckGreaterThan",(r,i)=>{Y.init(r,i);let t=p6[typeof i.value];r._zod.onattach.push((o)=>{let n=o._zod.bag,$=(i.inclusive?n.minimum:n.exclusiveMinimum)??Number.NEGATIVE_INFINITY;if(i.value>$)if(i.inclusive)n.minimum=i.value;else n.exclusiveMinimum=i.value}),r._zod.check=(o)=>{if(i.inclusive?o.value>=i.value:o.value>i.value)return;o.issues.push({origin:t,code:"too_small",minimum:typeof i.value==="object"?i.value.getTime():i.value,input:o.value,inclusive:i.inclusive,inst:r,continue:!i.abort})}}),s$=U("$ZodCheckMultipleOf",(r,i)=>{Y.init(r,i),r._zod.onattach.push((t)=>{var o;(o=t._zod.bag).multipleOf??(o.multipleOf=i.value)}),r._zod.check=(t)=>{if(typeof t.value!==typeof i.value)throw Error("Cannot mix number and bigint in multiple_of check.");if(typeof t.value==="bigint"?t.value%i.value===BigInt(0):b$(t.value,i.value)===0)return;t.issues.push({origin:typeof t.value,code:"not_multiple_of",divisor:i.value,input:t.value,inst:r,continue:!i.abort})}}),ru=U("$ZodCheckNumberFormat",(r,i)=>{Y.init(r,i),i.format=i.format||"float64";let t=i.format?.includes("int"),o=t?"int":"number",[n,$]=w$[i.format];r._zod.onattach.push((g)=>{let I=g._zod.bag;if(I.format=i.format,I.minimum=n,I.maximum=$,t)I.pattern=y$}),r._zod.check=(g)=>{let I=g.value;if(t){if(!Number.isInteger(I)){g.issues.push({expected:o,format:i.format,code:"invalid_type",continue:!1,input:I,inst:r});return}if(!Number.isSafeInteger(I)){if(I>0)g.issues.push({input:I,code:"too_big",maximum:Number.MAX_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:r,origin:o,inclusive:!0,continue:!i.abort});else g.issues.push({input:I,code:"too_small",minimum:Number.MIN_SAFE_INTEGER,note:"Integers must be within the safe integer range.",inst:r,origin:o,inclusive:!0,continue:!i.abort});return}}if(I<n)g.issues.push({origin:"number",input:I,code:"too_small",minimum:n,inclusive:!0,inst:r,continue:!i.abort});if(I>$)g.issues.push({origin:"number",input:I,code:"too_big",maximum:$,inclusive:!0,inst:r,continue:!i.abort})}}),nu=U("$ZodCheckBigIntFormat",(r,i)=>{Y.init(r,i);let[t,o]=S$[i.format];r._zod.onattach.push((n)=>{let $=n._zod.bag;$.format=i.format,$.minimum=t,$.maximum=o}),r._zod.check=(n)=>{let $=n.value;if($<t)n.issues.push({origin:"bigint",input:$,code:"too_small",minimum:t,inclusive:!0,inst:r,continue:!i.abort});if($>o)n.issues.push({origin:"bigint",input:$,code:"too_big",maximum:o,inclusive:!0,inst:r,continue:!i.abort})}}),iu=U("$ZodCheckMaxSize",(r,i)=>{var t;Y.init(r,i),(t=r._zod.def).when??(t.when=(o)=>{let n=o.value;return!wr(n)&&n.size!==void 0}),r._zod.onattach.push((o)=>{let n=o._zod.bag.maximum??Number.POSITIVE_INFINITY;if(i.maximum<n)o._zod.bag.maximum=i.maximum}),r._zod.check=(o)=>{let n=o.value;if(n.size<=i.maximum)return;o.issues.push({origin:Xn(n),code:"too_big",maximum:i.maximum,inclusive:!0,input:n,inst:r,continue:!i.abort})}}),tu=U("$ZodCheckMinSize",(r,i)=>{var t;Y.init(r,i),(t=r._zod.def).when??(t.when=(o)=>{let n=o.value;return!wr(n)&&n.size!==void 0}),r._zod.onattach.push((o)=>{let n=o._zod.bag.minimum??Number.NEGATIVE_INFINITY;if(i.minimum>n)o._zod.bag.minimum=i.minimum}),r._zod.check=(o)=>{let n=o.value;if(n.size>=i.minimum)return;o.issues.push({origin:Xn(n),code:"too_small",minimum:i.minimum,inclusive:!0,input:n,inst:r,continue:!i.abort})}}),$u=U("$ZodCheckSizeEquals",(r,i)=>{var t;Y.init(r,i),(t=r._zod.def).when??(t.when=(o)=>{let n=o.value;return!wr(n)&&n.size!==void 0}),r._zod.onattach.push((o)=>{let n=o._zod.bag;n.minimum=i.size,n.maximum=i.size,n.size=i.size}),r._zod.check=(o)=>{let n=o.value,$=n.size;if($===i.size)return;let g=$>i.size;o.issues.push({origin:Xn(n),...g?{code:"too_big",maximum:i.size}:{code:"too_small",minimum:i.size},inclusive:!0,exact:!0,input:o.value,inst:r,continue:!i.abort})}}),uu=U("$ZodCheckMaxLength",(r,i)=>{var t;Y.init(r,i),(t=r._zod.def).when??(t.when=(o)=>{let n=o.value;return!wr(n)&&n.length!==void 0}),r._zod.onattach.push((o)=>{let n=o._zod.bag.maximum??Number.POSITIVE_INFINITY;if(i.maximum<n)o._zod.bag.maximum=i.maximum}),r._zod.check=(o)=>{let n=o.value;if(n.length<=i.maximum)return;let g=Wn(n);o.issues.push({origin:g,code:"too_big",maximum:i.maximum,inclusive:!0,input:n,inst:r,continue:!i.abort})}}),ou=U("$ZodCheckMinLength",(r,i)=>{var t;Y.init(r,i),(t=r._zod.def).when??(t.when=(o)=>{let n=o.value;return!wr(n)&&n.length!==void 0}),r._zod.onattach.push((o)=>{let n=o._zod.bag.minimum??Number.NEGATIVE_INFINITY;if(i.minimum>n)o._zod.bag.minimum=i.minimum}),r._zod.check=(o)=>{let n=o.value;if(n.length>=i.minimum)return;let g=Wn(n);o.issues.push({origin:g,code:"too_small",minimum:i.minimum,inclusive:!0,input:n,inst:r,continue:!i.abort})}}),gu=U("$ZodCheckLengthEquals",(r,i)=>{var t;Y.init(r,i),(t=r._zod.def).when??(t.when=(o)=>{let n=o.value;return!wr(n)&&n.length!==void 0}),r._zod.onattach.push((o)=>{let n=o._zod.bag;n.minimum=i.length,n.maximum=i.length,n.length=i.length}),r._zod.check=(o)=>{let n=o.value,$=n.length;if($===i.length)return;let g=Wn(n),I=$>i.length;o.issues.push({origin:g,...I?{code:"too_big",maximum:i.length}:{code:"too_small",minimum:i.length},inclusive:!0,exact:!0,input:o.value,inst:r,continue:!i.abort})}}),hr=U("$ZodCheckStringFormat",(r,i)=>{var t,o;if(Y.init(r,i),r._zod.onattach.push((n)=>{let $=n._zod.bag;if($.format=i.format,i.pattern)$.patterns??($.patterns=new Set),$.patterns.add(i.pattern)}),i.pattern)(t=r._zod).check??(t.check=(n)=>{if(i.pattern.lastIndex=0,i.pattern.test(n.value))return;n.issues.push({origin:"string",code:"invalid_format",format:i.format,input:n.value,...i.pattern?{pattern:i.pattern.toString()}:{},inst:r,continue:!i.abort})});else(o=r._zod).check??(o.check=()=>{})}),Iu=U("$ZodCheckRegex",(r,i)=>{hr.init(r,i),r._zod.check=(t)=>{if(i.pattern.lastIndex=0,i.pattern.test(t.value))return;t.issues.push({origin:"string",code:"invalid_format",format:"regex",input:t.value,pattern:i.pattern.toString(),inst:r,continue:!i.abort})}}),cu=U("$ZodCheckLowerCase",(r,i)=>{i.pattern??(i.pattern=a$),hr.init(r,i)}),_u=U("$ZodCheckUpperCase",(r,i)=>{i.pattern??(i.pattern=p$),hr.init(r,i)}),bu=U("$ZodCheckIncludes",(r,i)=>{Y.init(r,i);let t=a(i.includes),o=new RegExp(typeof i.position==="number"?`^.{${i.position}}${t}`:t);i.pattern=o,r._zod.onattach.push((n)=>{let $=n._zod.bag;$.patterns??($.patterns=new Set),$.patterns.add(o)}),r._zod.check=(n)=>{if(n.value.includes(i.includes,i.position))return;n.issues.push({origin:"string",code:"invalid_format",format:"includes",includes:i.includes,input:n.value,inst:r,continue:!i.abort})}}),Uu=U("$ZodCheckStartsWith",(r,i)=>{Y.init(r,i);let t=new RegExp(`^${a(i.prefix)}.*`);i.pattern??(i.pattern=t),r._zod.onattach.push((o)=>{let n=o._zod.bag;n.patterns??(n.patterns=new Set),n.patterns.add(t)}),r._zod.check=(o)=>{if(o.value.startsWith(i.prefix))return;o.issues.push({origin:"string",code:"invalid_format",format:"starts_with",prefix:i.prefix,input:o.value,inst:r,continue:!i.abort})}}),lu=U("$ZodCheckEndsWith",(r,i)=>{Y.init(r,i);let t=new RegExp(`.*${a(i.suffix)}$`);i.pattern??(i.pattern=t),r._zod.onattach.push((o)=>{let n=o._zod.bag;n.patterns??(n.patterns=new Set),n.patterns.add(t)}),r._zod.check=(o)=>{if(o.value.endsWith(i.suffix))return;o.issues.push({origin:"string",code:"invalid_format",format:"ends_with",suffix:i.suffix,input:o.value,inst:r,continue:!i.abort})}});function a6(r,i,t){if(r.issues.length)i.issues.push(...m(t,r.issues))}var eu=U("$ZodCheckProperty",(r,i)=>{Y.init(r,i),r._zod.check=(t)=>{let o=i.schema._zod.run({value:t.value[i.property],issues:[]},{});if(o instanceof Promise)return o.then((n)=>a6(n,t,i.property));a6(o,t,i.property);return}}),ku=U("$ZodCheckMimeType",(r,i)=>{Y.init(r,i);let t=new Set(i.mime);r._zod.onattach.push((o)=>{o._zod.bag.mime=i.mime}),r._zod.check=(o)=>{if(t.has(o.value.type))return;o.issues.push({code:"invalid_value",values:i.mime,input:o.value.type,inst:r,continue:!i.abort})}}),Du=U("$ZodCheckOverwrite",(r,i)=>{Y.init(r,i),r._zod.check=(t)=>{t.value=i.tx(t.value)}});class Ei{constructor(r=[]){if(this.content=[],this.indent=0,this)this.args=r}indented(r){this.indent+=1,r(this),this.indent-=1}write(r){if(typeof r==="function"){r(this,{execution:"sync"}),r(this,{execution:"async"});return}let t=r.split(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shotstack/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Command-line interface for the Shotstack video rendering API.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/shotstack/shotstack-cli",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"files": [
|
|
19
19
|
"dist/shotstack.js",
|
|
20
20
|
"SKILL.md",
|
|
21
|
+
"shared",
|
|
21
22
|
"references"
|
|
22
23
|
],
|
|
23
24
|
"scripts": {
|
|
@@ -4,7 +4,15 @@ Copy-paste `html5` clips that "pop" — kinetic type, count-ups, shine sweeps,
|
|
|
4
4
|
pulsing CTAs, grain. Each is a **single clip**: paste it into a track's
|
|
5
5
|
`clips[]`, set `start`/`length`, and position with `offset`.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Every snippet here is composed from the house **[`motion.md`](motion.md)** tokens —
|
|
8
|
+
one duration scale (`base` 0.6 s in, `fast` 0.33 s out), one house ease
|
|
9
|
+
(`power3.out` / `cubic-bezier(0.16,1,0.3,1)`), one stagger (`0.13 s`), and a single
|
|
10
|
+
shared palette (ink `#141414`, accent `#D96B82`). That shared vocabulary is what
|
|
11
|
+
makes a set of these clips feel like one production. **When you adapt a snippet,
|
|
12
|
+
keep the tokens** — change the words, the colours and the canvas size, not the
|
|
13
|
+
easings and durations. Read [`motion.md`](motion.md) for the why and the full recipe set.
|
|
14
|
+
|
|
15
|
+
Read [`html5.md`](html5.md) for the rules these obey. The non-negotiables:
|
|
8
16
|
|
|
9
17
|
- **Seekable animation only.** GSAP timelines, GSAP tweens (incl. `onUpdate`), anime.js, Lottie, or CSS `@keyframes`. **Never** `setTimeout`/`setInterval`/`requestAnimationFrame`/`Date.now()`/`gsap.call()` — the renderer seeks by absolute time, it doesn't play.
|
|
10
18
|
- **Size the clip to the content, not the canvas.** `html, body` pinned to the clip's `width`/`height`; place with `offset` (`{x:0,y:0}` is centred, `y` positive is up). Use **px**, never `vw`/`vh`/`%`.
|
|
@@ -13,13 +21,43 @@ Read [`html5.md`](html5.md) first for the rules these obey. The non-negotiables:
|
|
|
13
21
|
- **No network.** gsap/anime/d3/lottie are preloaded; external `<script src>`, `fetch`, remote `<img>`, and remote fonts are all CSP-blocked — inline everything as `data:` URIs.
|
|
14
22
|
- **Fonts:** `timeline.fonts[]` does **not** reach `html5` and remote `@font-face` is blocked. These snippets name a display font first but render in the `system-ui` fallback unless you inline the font as a `data:` `@font-face`.
|
|
15
23
|
|
|
16
|
-
Coordinates below assume a **1080×1920 vertical** canvas; adjust `offset` for other sizes.
|
|
24
|
+
Coordinates below assume a **1080×1920 vertical** canvas; adjust `offset` for other sizes.
|
|
25
|
+
|
|
26
|
+
> **Continuous-motion exception.** Looping or drifting effects (the pulse, the sweep, the grain) are *ambient*, not entrances — they correctly use `ease-in-out` / `linear` and their own loop durations rather than the entrance tokens. Everything that *reveals* uses the house entrance tokens.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1. Blur reveal — calm text entrance
|
|
31
|
+
|
|
32
|
+
**Category** entrances · **Use when** the default text reveal; the calm house entrance for a title or line — reach for a punchier one (snippet 2) only with intent · **Canvas** 900×300 · **Tags** text, reveal, entrance, blur
|
|
33
|
+
|
|
34
|
+
The reference entrance: opacity, blur and a 16 px rise settle **together** off one tween (one progress, many channels) on the house ease — no overshoot. Quietly cinematic.
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"asset": {
|
|
39
|
+
"type": "html5",
|
|
40
|
+
"html": "<div class=\"t\">{{title}}</div>",
|
|
41
|
+
"css": "html,body{margin:0;width:900px;height:300px;overflow:hidden;background:transparent;font-family:'Clash Display',system-ui,sans-serif}.t{display:flex;align-items:center;justify-content:center;width:900px;height:300px;font-size:150px;font-weight:600;letter-spacing:-3px;color:#141414;text-align:center}",
|
|
42
|
+
"js": "gsap.from('.t',{opacity:0,y:16,filter:'blur(10px)',duration:0.6,ease:'power3.out'});gsap.to({},{duration:1.5});"
|
|
43
|
+
},
|
|
44
|
+
"start": 0,
|
|
45
|
+
"length": 3,
|
|
46
|
+
"width": 900,
|
|
47
|
+
"height": 300,
|
|
48
|
+
"offset": { "x": 0, "y": 0 }
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`base` (0.6 s) entrance on `power3.out`, then a `hold` (1.5 s) settle. Swap `{{title}}` via top-level `merge[]`. `Clash Display` falls back to `system-ui` unless you inline it as a `data:` `@font-face` (see Fonts above).
|
|
17
53
|
|
|
18
54
|
---
|
|
19
55
|
|
|
20
|
-
##
|
|
56
|
+
## 2. Kinetic headline (word-by-word rise)
|
|
21
57
|
|
|
22
|
-
|
|
58
|
+
**Category** entrances · **Use when** a headline needs energy — a hype/hero title where each word punches up in sequence · **Canvas** 980×420 · **Tags** text, reveal, entrance, stagger, hero
|
|
59
|
+
|
|
60
|
+
Each word springs up in sequence. GSAP timeline, fully seekable, on the house stagger.
|
|
23
61
|
|
|
24
62
|
```json
|
|
25
63
|
{
|
|
@@ -27,7 +65,7 @@ Big title where each word springs up in sequence. GSAP timeline, fully seekable.
|
|
|
27
65
|
"type": "html5",
|
|
28
66
|
"html": "<div class=\"h\"><span>JUST</span> <span>DROPPED</span></div>",
|
|
29
67
|
"css": "html,body{margin:0;width:980px;height:420px;overflow:hidden;background:transparent;font-family:'Anton',system-ui,sans-serif}.h{display:flex;flex-wrap:wrap;gap:0 18px;align-items:center;justify-content:center;width:980px;height:420px;text-transform:uppercase;line-height:0.9}.h span{display:inline-block;font-size:150px;font-weight:800;color:#141414;transform:translateY(120%);opacity:0}",
|
|
30
|
-
"js": "const tl=gsap.timeline();gsap.utils.toArray('.h span').forEach((el,i)=>{tl.to(el,{y:0,opacity:1,duration:0.6,ease:'
|
|
68
|
+
"js": "const tl=gsap.timeline();gsap.utils.toArray('.h span').forEach((el,i)=>{tl.to(el,{y:0,opacity:1,duration:0.6,ease:'power3.out'},i*0.13)});tl.to({},{duration:1.5});"
|
|
31
69
|
},
|
|
32
70
|
"start": 0,
|
|
33
71
|
"length": 3,
|
|
@@ -37,11 +75,13 @@ Big title where each word springs up in sequence. GSAP timeline, fully seekable.
|
|
|
37
75
|
}
|
|
38
76
|
```
|
|
39
77
|
|
|
40
|
-
Each word
|
|
78
|
+
Each word starts `translateY(120%)`, opacity 0; `0.6 s` rise on `power3.out`, words `0.13 s` apart (the house stagger); a trailing empty tween holds the title still. **Punchy variant:** this is the one "hero" spot where a pop is allowed — swap `ease:'power3.out'` for `ease:'back.out(1.4)'` for a gentle overshoot. Keep it to one headline per scene. `Anton` falls back to `system-ui` unless inlined.
|
|
41
79
|
|
|
42
80
|
---
|
|
43
81
|
|
|
44
|
-
##
|
|
82
|
+
## 3. Count-up number / price odometer
|
|
83
|
+
|
|
84
|
+
**Category** data · **Use when** revealing a value, price, stat or metric · **Canvas** 620×220 · **Tags** number, count, price, data · **Merge-friendly** target value
|
|
45
85
|
|
|
46
86
|
Animate a value from 0 to its target. Seek-safe because the count lives in a **tweened object with `onUpdate`** (fires on seek), never a timer.
|
|
47
87
|
|
|
@@ -51,7 +91,7 @@ Animate a value from 0 to its target. Seek-safe because the count lives in a **t
|
|
|
51
91
|
"type": "html5",
|
|
52
92
|
"html": "<div class=\"wrap\"><span class=\"cur\">$</span><span id=\"n\">0</span></div>",
|
|
53
93
|
"css": "html,body{margin:0;width:620px;height:220px;overflow:hidden;background:transparent;font-family:system-ui,sans-serif}.wrap{display:flex;align-items:baseline;justify-content:center;width:620px;height:220px;color:#141414;font-weight:800;font-variant-numeric:tabular-nums}.cur{font-size:70px;margin-right:6px}#n{font-size:150px;letter-spacing:-2px}",
|
|
54
|
-
"js": "const o={v:0};const out=document.getElementById('n');const tl=gsap.timeline();tl.to(o,{v:395,duration:
|
|
94
|
+
"js": "const o={v:0};const out=document.getElementById('n');const tl=gsap.timeline();tl.to(o,{v:395,duration:0.8,ease:'power2.out',onUpdate:()=>{out.textContent=Math.round(o.v)}});tl.to({},{duration:1.5});"
|
|
55
95
|
},
|
|
56
96
|
"start": 0,
|
|
57
97
|
"length": 3,
|
|
@@ -61,20 +101,22 @@ Animate a value from 0 to its target. Seek-safe because the count lives in a **t
|
|
|
61
101
|
}
|
|
62
102
|
```
|
|
63
103
|
|
|
64
|
-
Swap `v:395` for any target
|
|
104
|
+
`slow` (0.8 s) count on `power2.out` (a decelerating settle reads right for a value), then a `hold`. Swap `v:395` for any target; for thousands separators use `Math.round(o.v).toLocaleString()`. Pair with a static label on an adjacent `rich-text` track ("FROM", "AUD").
|
|
65
105
|
|
|
66
106
|
---
|
|
67
107
|
|
|
68
|
-
##
|
|
108
|
+
## 4. Shine / gloss sweep
|
|
69
109
|
|
|
70
|
-
|
|
110
|
+
**Category** graphics (emphasis) · **Use when** adding premium gloss to a product name, logo or card · **Canvas** 860×280 · **Tags** shine, gloss, emphasis, specular
|
|
111
|
+
|
|
112
|
+
A specular highlight slides across text — premium product gloss. *Continuous* motion (the sweep itself), so it uses `ease-in-out` over `slower` (1.0 s), not an entrance token. Pure CSS keyframe (WAAPI-seekable). Put this clip **above** the thing it shines on, or wrap the content in the same clip.
|
|
71
113
|
|
|
72
114
|
```json
|
|
73
115
|
{
|
|
74
116
|
"asset": {
|
|
75
117
|
"type": "html5",
|
|
76
118
|
"html": "<div class=\"plate\"><div class=\"label\">UTOPIA</div><div class=\"shine\"></div></div>",
|
|
77
|
-
"css": "html,body{margin:0;width:860px;height:280px;overflow:hidden;background:transparent;font-family:'Anton',system-ui,sans-serif}.plate{position:relative;width:860px;height:280px;display:flex;align-items:center;justify-content:center;overflow:hidden}.label{font-size:200px;font-weight:800;letter-spacing:6px;color:#141414;text-transform:uppercase}.shine{position:absolute;top:0;left:0;width:240px;height:280px;background:linear-gradient(100deg,transparent,rgba(255,255,255,0.85),transparent);mix-blend-mode:overlay;transform:translateX(-300px) skewX(-12deg);animation:sweep
|
|
119
|
+
"css": "html,body{margin:0;width:860px;height:280px;overflow:hidden;background:transparent;font-family:'Anton',system-ui,sans-serif}.plate{position:relative;width:860px;height:280px;display:flex;align-items:center;justify-content:center;overflow:hidden}.label{font-size:200px;font-weight:800;letter-spacing:6px;color:#141414;text-transform:uppercase}.shine{position:absolute;top:0;left:0;width:240px;height:280px;background:linear-gradient(100deg,transparent,rgba(255,255,255,0.85),transparent);mix-blend-mode:overlay;transform:translateX(-300px) skewX(-12deg);animation:sweep 1s ease-in-out 0.4s 1 both}@keyframes sweep{to{transform:translateX(1100px) skewX(-12deg)}}",
|
|
78
120
|
"js": ""
|
|
79
121
|
},
|
|
80
122
|
"start": 0,
|
|
@@ -85,20 +127,22 @@ A specular highlight that slides across text or a card — premium product gloss
|
|
|
85
127
|
}
|
|
86
128
|
```
|
|
87
129
|
|
|
88
|
-
`mix-blend-mode: overlay` makes the sweep read as a real highlight rather than a white bar. No JS needed.
|
|
130
|
+
`mix-blend-mode: overlay` makes the sweep read as a real highlight rather than a white bar. No JS needed. The `0.4 s` start delay lets the thing it shines on settle first.
|
|
89
131
|
|
|
90
132
|
---
|
|
91
133
|
|
|
92
|
-
##
|
|
134
|
+
## 5. Pulsing CTA button
|
|
135
|
+
|
|
136
|
+
**Category** graphics (emphasis) · **Use when** drawing the eye to a CTA on an end card · **Canvas** 620×170 · **Tags** cta, button, pulse, loop · **Accent** `#D96B82`
|
|
93
137
|
|
|
94
|
-
A "SHOP NOW" pill with a soft breathing glow —
|
|
138
|
+
A "SHOP NOW" pill with a soft breathing glow. *Ambient loop* — `ease-in-out`, its own 1.6 s cycle (exempt from the entrance tokens). CSS keyframes seek cleanly.
|
|
95
139
|
|
|
96
140
|
```json
|
|
97
141
|
{
|
|
98
142
|
"asset": {
|
|
99
143
|
"type": "html5",
|
|
100
144
|
"html": "<div class=\"cta\">SHOP NOW</div>",
|
|
101
|
-
"css": "html,body{margin:0;width:620px;height:170px;overflow:hidden;background:transparent;font-family:system-ui,sans-serif}.cta{width:560px;height:128px;margin:21px auto;display:flex;align-items:center;justify-content:center;border-radius:16px;background:#
|
|
145
|
+
"css": "html,body{margin:0;width:620px;height:170px;overflow:hidden;background:transparent;font-family:system-ui,sans-serif}.cta{width:560px;height:128px;margin:21px auto;display:flex;align-items:center;justify-content:center;border-radius:16px;background:#D96B82;color:#fff;font-size:46px;font-weight:700;letter-spacing:4px;text-transform:uppercase;box-shadow:0 0 0 0 rgba(217,107,130,0.5);animation:pulse 1.6s ease-in-out 0s infinite both}@keyframes pulse{0%,100%{transform:scale(1);box-shadow:0 0 0 0 rgba(217,107,130,0.45)}50%{transform:scale(1.04);box-shadow:0 0 36px 8px rgba(217,107,130,0.35)}}",
|
|
102
146
|
"js": ""
|
|
103
147
|
},
|
|
104
148
|
"start": 0,
|
|
@@ -109,13 +153,15 @@ A "SHOP NOW" pill with a soft breathing glow — draws the eye on an end card. C
|
|
|
109
153
|
}
|
|
110
154
|
```
|
|
111
155
|
|
|
112
|
-
The clip is wider/taller than the pill so the glow has room
|
|
156
|
+
The accent (`#D96B82`) is used here as the single earned colour. The clip is wider/taller than the pill so the glow has room. For a non-looping single pop, replace `infinite` with `1`.
|
|
113
157
|
|
|
114
158
|
---
|
|
115
159
|
|
|
116
|
-
##
|
|
160
|
+
## 6. Film-grain / texture overlay
|
|
117
161
|
|
|
118
|
-
|
|
162
|
+
**Category** atmosphere · **Use when** adding a moody analogue texture over a dark scene · **Canvas** 1080×1920 (full frame) · **Tags** grain, texture, overlay, atmosphere
|
|
163
|
+
|
|
164
|
+
A subtle moving grain over the whole frame. *Continuous drift* — `steps()`/`linear` is correct here (not an entrance). SVG `feTurbulence`, animated by shifting a slightly-oversized layer so it never reveals an edge. Size this one **to the canvas** and put it on a top track at low opacity.
|
|
119
165
|
|
|
120
166
|
```json
|
|
121
167
|
{
|
|
@@ -137,9 +183,24 @@ A `data:` URI is fine **inside an html5 asset's CSS** (it's iframe content, not
|
|
|
137
183
|
|
|
138
184
|
---
|
|
139
185
|
|
|
186
|
+
## Brand kit — re-skin every snippet at once
|
|
187
|
+
|
|
188
|
+
The snippets share one palette (ink `#141414`, accent `#D96B82`) so a set already looks coherent. To re-skin a whole edit to a brand in one place, lift the colours/font into top-level `merge[]` and reference the tokens in each clip's `css` — `merge` find/replace runs over the `html`/`css` strings too:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
"merge": [
|
|
192
|
+
{ "find": "ink", "replace": "#1A1A2E" },
|
|
193
|
+
{ "find": "accent", "replace": "#E8552D" },
|
|
194
|
+
{ "find": "font", "replace": "Anton, system-ui, sans-serif" }
|
|
195
|
+
]
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Then in any snippet's CSS, swap the literal hex for the token: `color:{{ink}}`, `background:{{accent}}`, `font-family:{{font}}`. One edit re-skins every clip. Keep the accent **earned** — a headline word, a number, a CTA, one glow; everything else neutral. (If you ship a snippet *without* a matching `merge[]` entry, leave the literal hex in — an undefined `{{token}}` renders as invalid CSS.)
|
|
199
|
+
|
|
140
200
|
## Composing these
|
|
141
201
|
|
|
142
202
|
- Each snippet is one clip on its own track. Layer order is top-track-first (see `agent-core.md`) — grain and shine go in **early** tracks, backgrounds in **late** ones.
|
|
143
203
|
- They don't overlap on a single track, so `shotstack validate <file>` stays clean. Run it before rendering.
|
|
144
204
|
- Reuse text via top-level `merge[]` (`{{title}}` in the HTML) — see the lower-third example in `html5.md`.
|
|
205
|
+
- Mix calm and punchy deliberately: a `blur-reveal` title, a `kinetic-headline` hero line, a `count-up` stat, a `shine` on the product, a pulsing CTA — all on the same tokens, so the set reads as one piece.
|
|
145
206
|
- Heavier motion = longer render. Preview in `shotstack studio <file>` before spending credits.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Motion language — the house vocabulary
|
|
2
|
+
|
|
3
|
+
A small, closed set of timing, easing and stagger values that **every** animated clip should share. Composing motion from these tokens — instead of inventing a new easing and duration per clip — is what makes a multi-clip edit feel like one production instead of eight unrelated effects. Reach for these before hardcoding a `duration`, an `ease`, or a delay.
|
|
4
|
+
|
|
5
|
+
> The values are video-paced for Shotstack's **second-based** timeline (most UI-motion guidance is frame-based). The GSAP/CSS easings below stand in for spring physics, which the render engine doesn't expose.
|
|
6
|
+
|
|
7
|
+
## The tokens
|
|
8
|
+
|
|
9
|
+
### Duration scale (seconds)
|
|
10
|
+
|
|
11
|
+
| Token | Seconds | Use for |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `instant` | 0.2 | micro shifts, near-imperceptible feedback |
|
|
14
|
+
| `fast` | 0.33 | **exits**, small moves |
|
|
15
|
+
| `base` | 0.6 | **the default entrance** — ~80% of reveals |
|
|
16
|
+
| `slow` | 0.8 | large entrances, hero moves |
|
|
17
|
+
| `slower` | 1.0 | full-scene transitions |
|
|
18
|
+
| `hold` | 1.5 | minimum settled hold before a clip ends |
|
|
19
|
+
|
|
20
|
+
Most entrances are `base` (0.6 s). Exits are deliberately faster than entrances — get out of the way faster than you came in.
|
|
21
|
+
|
|
22
|
+
### Easing — one house curve, no overshoot by default
|
|
23
|
+
|
|
24
|
+
| Where | Entrance / settle | Exit |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **html5 GSAP** | `ease: 'power3.out'` | `ease: 'power2.in'` |
|
|
27
|
+
| **html5 CSS** | `cubic-bezier(0.16, 1, 0.3, 1)` | `cubic-bezier(0.3, 0, 0.8, 0.2)` |
|
|
28
|
+
|
|
29
|
+
`power3.out` / `cubic-bezier(0.16,1,0.3,1)` is the **house ease** — a confident "fast then settle" that reads like a heavily-damped spring with **no bounce**. Use it for anything the eye tracks (position, scale, blur). Never use raw `linear` / `'none'` for motion the eye follows; `linear` is only correct for a continuous drift (grain, marquee, a constant-speed ticker).
|
|
30
|
+
|
|
31
|
+
**Overshoot is opt-in, not default.** A gentle `back.out(1.4)` (GSAP) is reserved for **one** "hero" element per scene where a pop is intended — a logo sting, a headline punch. Calm is the default; energetic is fine when it *earns* it. Never sprinkle bounce across every element.
|
|
32
|
+
|
|
33
|
+
### Stagger — one value
|
|
34
|
+
|
|
35
|
+
`0.13 s` (≈ 4 frames at 30 fps) between siblings in any cascade — words, list items, grouped reveals. One value everywhere; present but never frantic. In GSAP: `stagger: 0.13`. In CSS: `animation-delay` steps of `0.13s` (`0s, 0.13s, 0.26s, …`).
|
|
36
|
+
|
|
37
|
+
### Travel & restraint
|
|
38
|
+
|
|
39
|
+
- **Travel small:** translate **12–24 px**, not 80. The eye should feel the motion, not be dragged by it. Blur reveals: `10 px → 0`. Scale reveals: `0.92 → 1` (below ~0.85 is dramatic-zoom territory — avoid unless intended).
|
|
40
|
+
- **One focal element per moment.** If two things compete, stagger them. Don't animate everything at once.
|
|
41
|
+
- **Let it settle.** After a reveal lands, hold it (a trailing `gsap.to({}, { duration: <hold> })` or enough clip `length`) before the clip ends. Never cut a reveal the instant it arrives.
|
|
42
|
+
|
|
43
|
+
## The three motion layers
|
|
44
|
+
|
|
45
|
+
Shotstack exposes motion at three levels of control. Use the tokens at whichever level fits; reach down a level only when you need more control.
|
|
46
|
+
|
|
47
|
+
| Layer | Control | Where the tokens apply |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| 1 — **clip `transition` / `effect`** | coarse (whole-clip in/out; only `Slow`/`Fast` variants) | pick `fade`, `slide*`, `zoom`; `Slow` ≈ `slow`, default ≈ `base`, `Fast` ≈ `fast` |
|
|
50
|
+
| 2 — **`rich-text` `animation`** | medium (`preset` + `duration` + `style` + `direction`) | set `duration` from the scale; `style:"word"` gives the staggered cascade |
|
|
51
|
+
| 3 — **`html5` GSAP / CSS** | full (any property, any easing) | the full vocabulary below — durations, the house ease, `0.13` stagger |
|
|
52
|
+
|
|
53
|
+
### Layer 2 — rich-text animation, tokenised
|
|
54
|
+
|
|
55
|
+
`animation.preset` maps onto the choreography names; set `duration` from the scale:
|
|
56
|
+
|
|
57
|
+
| Intent | `rich-text` animation |
|
|
58
|
+
|---|---|
|
|
59
|
+
| fade in | `{ "preset": "fadeIn", "duration": 0.6 }` |
|
|
60
|
+
| rise + fade (the workhorse) | `{ "preset": "ascend", "duration": 0.6, "direction": "up" }` |
|
|
61
|
+
| slide in | `{ "preset": "slideIn", "duration": 0.6, "direction": "up" }` |
|
|
62
|
+
| word-by-word cascade | `{ "preset": "shift", "duration": 0.6, "style": "word", "direction": "up" }` |
|
|
63
|
+
| typewriter | `{ "preset": "typewriter", "duration": 0.8, "style": "character" }` |
|
|
64
|
+
|
|
65
|
+
Schema constraints (`@shotstack/schemas` → `RichTextAnimation`): `preset` ∈ `fadeIn · slideIn · typewriter · ascend · shift · movingLetters`; `duration` 0.1–30 s; **`style` (`word`/`character`) applies only to `typewriter` and `shift`** (ignored on the others); `direction` is **required** for `slideIn`, `ascend`, `shift`, `movingLetters`. Rich-text `animation` is **entrance-only** — exits use the clip `transition.out` (Layer 1). Easing on the presets is engine-fixed; for full easing control, drop to html5 (Layer 3).
|
|
66
|
+
|
|
67
|
+
## Choreography recipes (html5 / GSAP)
|
|
68
|
+
|
|
69
|
+
Pure, seekable GSAP — obey [`html5.md`](html5.md) (no `setTimeout`/`rAF`/`Date.now`/`gsap.call()`; size the clip to the content). Each recipe is a named choreography helper you compose rather than reinventing.
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
// entryFadeRise — THE workhorse: rise 12px + fade, house ease, base duration
|
|
73
|
+
gsap.from('.el', { opacity: 0, y: 12, duration: 0.6, ease: 'power3.out' });
|
|
74
|
+
|
|
75
|
+
// entryFade — presence only (overlays, avatars)
|
|
76
|
+
gsap.from('.el', { opacity: 0, duration: 0.6, ease: 'power3.out' });
|
|
77
|
+
|
|
78
|
+
// entryScale — calm scale-up
|
|
79
|
+
gsap.from('.el', { opacity: 0, scale: 0.92, duration: 0.6, ease: 'power3.out' });
|
|
80
|
+
|
|
81
|
+
// blurReveal — opacity + blur + rise off ONE tween (one progress, many channels)
|
|
82
|
+
gsap.from('.el', { opacity: 0, y: 16, filter: 'blur(10px)', duration: 0.6, ease: 'power3.out' });
|
|
83
|
+
|
|
84
|
+
// wordStagger — each word rises in sequence (wrap words in <span>)
|
|
85
|
+
gsap.from('.word', { opacity: 0, yPercent: 120, duration: 0.6, ease: 'power3.out', stagger: 0.13 });
|
|
86
|
+
|
|
87
|
+
// heroReveal — the ONE place a pop is allowed (≤1 per scene)
|
|
88
|
+
gsap.from('.hero', { opacity: 0, y: 16, scale: 0.97, duration: 0.8, ease: 'back.out(1.4)' });
|
|
89
|
+
|
|
90
|
+
// exitFadeFall — fade + drop, faster, ease-in (an exit doesn't settle)
|
|
91
|
+
gsap.to('.el', { opacity: 0, y: 8, duration: 0.33, ease: 'power2.in' });
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Entrance → hold → exit, on one timeline:**
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
const tl = gsap.timeline();
|
|
98
|
+
tl.from('.el', { opacity: 0, y: 12, duration: 0.6, ease: 'power3.out' }) // in (base)
|
|
99
|
+
.to({}, { duration: 1.5 }) // hold
|
|
100
|
+
.to('.el', { opacity: 0, y: 8, duration: 0.33, ease: 'power2.in' }); // out (fast)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### CSS equivalents (no JS)
|
|
104
|
+
|
|
105
|
+
```css
|
|
106
|
+
/* house ease entrance */
|
|
107
|
+
.el { animation: rise 0.6s cubic-bezier(0.16, 1, 0.3, 1) both; }
|
|
108
|
+
@keyframes rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
|
|
109
|
+
|
|
110
|
+
/* staggered siblings — 0.13s steps */
|
|
111
|
+
.w:nth-child(1){animation-delay:0s}.w:nth-child(2){animation-delay:.13s}.w:nth-child(3){animation-delay:.26s}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Brand kit — set the palette once, re-skin everything
|
|
115
|
+
|
|
116
|
+
Don't hardcode hex and font per snippet. Declare the brand as top-level `merge[]` fields and reference the tokens in every clip's `css`/`html` — one edit re-skins the whole video — brand tokens applied once, via Shotstack merge:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
"merge": [
|
|
120
|
+
{ "find": "ink", "replace": "#141414" },
|
|
121
|
+
{ "find": "accent", "replace": "#D96B82" },
|
|
122
|
+
{ "find": "bg", "replace": "#08080A" },
|
|
123
|
+
{ "find": "font", "replace": "Clash Display, system-ui, sans-serif" }
|
|
124
|
+
]
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
```css
|
|
128
|
+
.title { color: {{ink}}; font-family: {{font}}; }
|
|
129
|
+
.cta { background: {{accent}}; }
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`merge` find/replace runs over the whole edit, including `html`/`css` strings, so `{{accent}}` resolves everywhere. Keep one accent and use it sparingly — a headline word, a number, a CTA, one glow — everything else neutral. **Colour is earned, never sprinkled.**
|
|
133
|
+
|
|
134
|
+
## Motion checklist
|
|
135
|
+
|
|
136
|
+
Before rendering anything animated:
|
|
137
|
+
|
|
138
|
+
1. **Durations from the scale?** (`0.6` in, `0.33` out — not `0.45`, `0.7`, `1.2`.)
|
|
139
|
+
2. **House ease, not linear?** `power3.out` / `cubic-bezier(0.16,1,0.3,1)` for tracked motion.
|
|
140
|
+
3. **Overshoot only on one hero element**, if at all.
|
|
141
|
+
4. **Stagger is `0.13`**, applied via `stagger` / `animation-delay`.
|
|
142
|
+
5. **Travel 12–24 px; scale ≥ 0.92.** Restraint over flash.
|
|
143
|
+
6. **One focal element per moment;** competing elements staggered.
|
|
144
|
+
7. **It holds before it cuts** (settled `hold`, not an instant exit).
|
|
145
|
+
8. **Brand colours via `merge[]`**, accent used sparingly.
|
|
146
|
+
|
|
147
|
+
## See also
|
|
148
|
+
|
|
149
|
+
- [`html5-snippets.md`](html5-snippets.md) — ready-made clips that already follow these tokens
|
|
150
|
+
- [`html5.md`](html5.md) — the html5 sandbox rules these obey (seekable-only, sizing, fonts)
|
|
151
|
+
- `shared/agent-core.md` → "Motion language" — the compact token summary (shared with the MCP server)
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Shotstack Agent Core
|
|
2
|
+
|
|
3
|
+
Universal Edit JSON authoring conventions. Read this **before composing any Edit JSON**. The conventions agents most often get wrong are listed here once. The same file ships with both the Shotstack CLI skill and the Shotstack MCP server.
|
|
4
|
+
|
|
5
|
+
## Before composing JSON: check the schema
|
|
6
|
+
|
|
7
|
+
Don't invent property names or enum values. The Shotstack schema is published — fetch one of these before composing JSON from scratch:
|
|
8
|
+
|
|
9
|
+
- <https://shotstack.io/docs/api/api.edit.json> — single-file OpenAPI Schema. Machine-validatable; load it once and validate locally instead of round-tripping the API.
|
|
10
|
+
- <https://shotstack.io/docs/api/> — interactive HTML reference. Fastest for human scanning.
|
|
11
|
+
- <https://shotstack.io/docs/guide/llms-full.txt> — single-file LLM-friendly version of the full guide + reference.
|
|
12
|
+
- <https://github.com/shotstack/oas-api-definition/tree/main/schemas> — raw OpenAPI YAML, source of truth.
|
|
13
|
+
|
|
14
|
+
CSS naming conventions (`alignment`, `vertical: "center"`) **do not** apply. The spec uses precise names that often differ from web/CSS instincts:
|
|
15
|
+
|
|
16
|
+
| You'd guess (wrong) | API uses (right) |
|
|
17
|
+
|---|---|
|
|
18
|
+
| `alignment` | `align` |
|
|
19
|
+
| `align: "center"` (string on rich-text asset) | `align: { "horizontal": "center", "vertical": "middle" }` (object) |
|
|
20
|
+
| `align.vertical: "center"` | `align.vertical: "middle"` |
|
|
21
|
+
| `font.name` | `font.family` |
|
|
22
|
+
| `duration` | `length` |
|
|
23
|
+
| `transitions: [...]` (array) | `transition: { in, out }` (object) |
|
|
24
|
+
| `fit: "cover"` (CSS instinct: scale+crop maintaining aspect) | `fit: "crop"` — Shotstack's `cover` STRETCHES without maintaining aspect ratio |
|
|
25
|
+
|
|
26
|
+
When the API rejects a property, the error message names the field — fix and retry. Don't guess twice.
|
|
27
|
+
|
|
28
|
+
## Track ordering is REVERSED
|
|
29
|
+
|
|
30
|
+
`timeline.tracks` is an array. **The first element (`tracks[0]`) is the TOP layer; the last element is the BOTTOM layer.** This is opposite to most z-index conventions.
|
|
31
|
+
|
|
32
|
+
> "Tracks are layered on top of each other in the same order they are added to the array with the top most track layered over the top of those below it." — [Shotstack docs](https://shotstack.io/docs/guide/architecting-an-application/guidelines/)
|
|
33
|
+
|
|
34
|
+
Practical rule: **put captions, overlays, and titles in early tracks; put video/image backgrounds in later tracks.**
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"timeline": {
|
|
39
|
+
"tracks": [
|
|
40
|
+
{ "clips": [/* TOP — captions */] },
|
|
41
|
+
{ "clips": [/* MIDDLE — title overlay */] },
|
|
42
|
+
{ "clips": [/* BOTTOM — background video */] }
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Smart clip strings
|
|
49
|
+
|
|
50
|
+
These string values are accepted in addition to numbers:
|
|
51
|
+
|
|
52
|
+
| Where | Value | Meaning |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| `clip.start` | `"auto"` | Start when the previous clip on the same track finishes |
|
|
55
|
+
| `clip.start` | `"alias://<name>"` | Inherit start from another clip with `alias: "<name>"` |
|
|
56
|
+
| `clip.length` | `"auto"` | Asset's natural duration. Use for foreground (video, voiceover, scene). |
|
|
57
|
+
| `clip.length` | `"end"` | Until timeline ends, capped at asset duration. Use for background (music, captions, watermark). |
|
|
58
|
+
| `clip.length` | `"alias://<name>"` | Inherit length from another clip |
|
|
59
|
+
|
|
60
|
+
`"end"` does NOT loop short audio — use a numeric `length` if you need precise control.
|
|
61
|
+
|
|
62
|
+
The `alias://` protocol is also used in `rich-caption` `src` to auto-transcribe a referenced audio/video clip — see `references/caption.md`.
|
|
63
|
+
|
|
64
|
+
## Public HTTPS URLs only
|
|
65
|
+
|
|
66
|
+
All asset `src` URLs must be publicly accessible HTTPS. **No local file paths, no `data:` URIs, no signed URLs that expire mid-render.** The render workers fetch assets from the public internet.
|
|
67
|
+
|
|
68
|
+
For test renders without your own assets, use the placeholder library at <https://shotstack-assets.s3.amazonaws.com/> — see `references/asset-library.md`.
|
|
69
|
+
|
|
70
|
+
## Don't overlap clips on the same track
|
|
71
|
+
|
|
72
|
+
Clips on the same track must not have overlapping `start`/`length` ranges — overlapping clips flicker because the engine can't decide which to display. **Anything visible at the same time goes on a separate track.** This is the most common structural mistake.
|
|
73
|
+
|
|
74
|
+
```jsonc
|
|
75
|
+
// WRONG — three simultaneous end-card lines on ONE track (all overlap 8.0–11.0)
|
|
76
|
+
{ "clips": [
|
|
77
|
+
{ "asset": { "type": "rich-text", "text": "TITLE" }, "start": 8.0, "length": 3 },
|
|
78
|
+
{ "asset": { "type": "rich-text", "text": "$395" }, "start": 8.0, "length": 3 },
|
|
79
|
+
{ "asset": { "type": "rich-text", "text": "SHOP NOW" }, "start": 8.0, "length": 3 }
|
|
80
|
+
] }
|
|
81
|
+
|
|
82
|
+
// RIGHT — one track each
|
|
83
|
+
"tracks": [
|
|
84
|
+
{ "clips": [ { "asset": { "type": "rich-text", "text": "TITLE" }, "start": 8.0, "length": 3 } ] },
|
|
85
|
+
{ "clips": [ { "asset": { "type": "rich-text", "text": "$395" }, "start": 8.0, "length": 3 } ] },
|
|
86
|
+
{ "clips": [ { "asset": { "type": "rich-text", "text": "SHOP NOW" }, "start": 8.0, "length": 3 } ] }
|
|
87
|
+
]
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Sequential clips (one finishes, the next starts) **can** share a track — `"start": "auto"` chains them. A cross-fade needs two tracks with a small time overlap and a `transition` on each.
|
|
91
|
+
|
|
92
|
+
**Validate before you render:** `shotstack validate <file>` catches same-track overlaps — plus unloaded fonts, non-public `src` URLs, and wrong property names/enums — offline, no API key, no credits.
|
|
93
|
+
|
|
94
|
+
## Clip motion & rich-text fields (cheatsheet)
|
|
95
|
+
|
|
96
|
+
Compose from this rather than round-tripping the full schema — these are the values renders actually use. (`api.edit.json` stays authoritative; `shotstack validate` checks against it.)
|
|
97
|
+
|
|
98
|
+
**Clip-level (wraps any asset):**
|
|
99
|
+
|
|
100
|
+
| Field | Values |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `fit` | `crop` (fill + crop, default — CSS `object-fit: cover`) · `contain` (letterbox) · `cover` (stretch, ignores aspect) · `none` |
|
|
103
|
+
| `position` | `center` `top` `bottom` `left` `right` `topLeft` `topRight` `bottomLeft` `bottomRight` |
|
|
104
|
+
| `offset` | `{ "x": <−1..1>, "y": <−1..1> }` — fraction of the canvas; **`y` positive = up** |
|
|
105
|
+
| `scale` / `opacity` | number (`1` = the fit result) / `0..1` |
|
|
106
|
+
| `effect` | Ken-Burns drift: `zoomIn` `zoomOut` `slideLeft` `slideRight` `slideUp` `slideDown` — each also `…Slow` / `…Fast` |
|
|
107
|
+
| `filter` | `blur` `boost` `contrast` `darken` `greyscale` `lighten` `muted` `negative` |
|
|
108
|
+
| `transition` | `{ "in": …, "out": … }` ↓ |
|
|
109
|
+
|
|
110
|
+
**`transition.in` / `.out`** — each also takes a `Slow`/`Fast` suffix (e.g. `fadeSlow`, `slideUpFast`):
|
|
111
|
+
`none` `fade` `reveal` `wipeLeft` `wipeRight` `slideLeft` `slideRight` `slideUp` `slideDown` `carouselLeft` `carouselRight` `carouselUp` `carouselDown` `shuffle*` (eight corners, e.g. `shuffleTopRight`) `zoom`.
|
|
112
|
+
|
|
113
|
+
**`rich-text` asset** — the styled-text workhorse (use instead of `text`/`title`):
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"type": "rich-text",
|
|
118
|
+
"text": "UTOPIA",
|
|
119
|
+
"font": { "family": "<loaded-family>", "size": 160, "weight": "700", "color": "#141414", "opacity": 1 },
|
|
120
|
+
"style": { "letterSpacing": 6, "lineHeight": 0.95, "textTransform": "uppercase", "textDecoration": "none" },
|
|
121
|
+
"stroke": { "width": 3, "color": "#000000" },
|
|
122
|
+
"shadow": { "offsetX": 0, "offsetY": 6, "blur": 18, "color": "#000000", "opacity": 0.4 },
|
|
123
|
+
"background": { "color": "#ffffff", "opacity": 1, "borderRadius": 16 },
|
|
124
|
+
"align": { "horizontal": "center", "vertical": "middle" },
|
|
125
|
+
"animation": { "preset": "fadeIn", "duration": 0.6, "style": "word", "direction": "up" }
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`align.horizontal` = `left|center|right`; `align.vertical` = `top|middle|bottom` (**not** `center`). `animation.preset` = `fadeIn` `slideIn` `typewriter` `ascend` `shift` `movingLetters`; `animation.style` = `character|word`; `animation.direction` = `left|right|up|down`. Give the clip a `width`/`height` box so text wraps and aligns where you expect.
|
|
130
|
+
|
|
131
|
+
For motion beyond this (kinetic type, count-ups, shine sweeps, grain, pulsing CTAs) reach for `html5` — see [`references/html5-snippets.md`](../references/html5-snippets.md).
|
|
132
|
+
|
|
133
|
+
## Motion language (house tokens)
|
|
134
|
+
|
|
135
|
+
Compose motion from a **closed set of tokens** so a multi-clip edit feels like one production, not eight unrelated effects. **Don't invent a new easing or duration per clip.** Full recipes (GSAP/CSS, choreography, brand kit) in [`references/motion.md`](../references/motion.md).
|
|
136
|
+
|
|
137
|
+
- **Durations (seconds):** `instant 0.2 · fast 0.33 · base 0.6 · slow 0.8 · slower 1.0 · hold 1.5`. Entrances default to **`base` (0.6)**; **exits are faster** (`fast`).
|
|
138
|
+
- **One house ease, no overshoot:** html5 GSAP `power3.out` (in) / `power2.in` (out); CSS `cubic-bezier(0.16, 1, 0.3, 1)`. Never raw `linear` for tracked motion. A gentle `back.out(1.4)` is reserved for **one** hero element per scene — calm is the default.
|
|
139
|
+
- **One stagger:** `0.13s` between siblings (GSAP `stagger: 0.13`; CSS `animation-delay` steps).
|
|
140
|
+
- **Restraint:** translate 12–24 px (not 80), scale ≥ 0.92, one focal element per moment, let a reveal settle (`hold`) before it cuts.
|
|
141
|
+
- **rich-text `animation`** (entrance-only — exits use the clip `transition.out`): set `duration` from the scale (e.g. `0.6`); `preset:"ascend"` + `direction:"up"` is the rise-and-fade workhorse; for a word cascade use `preset:"shift"` + `style:"word"` + `direction:"up"` (`style` works only on `typewriter`/`shift`; `direction` is required for `slideIn`/`ascend`/`shift`/`movingLetters`).
|
|
142
|
+
- **Brand once:** put palette/font in top-level `merge[]` (`{{ink}}`, `{{accent}}`, `{{font}}`) and reference them in every clip — one edit re-skins the whole video. Accent used sparingly.
|
|
143
|
+
|
|
144
|
+
## Positioning & coordinates
|
|
145
|
+
|
|
146
|
+
`position` picks one of nine anchor points (`center` default; `top` `bottom` `left` `right` `topLeft` `topRight` `bottomLeft` `bottomRight`); `offset` nudges from there. **`offset` is a fraction of the output frame, not a centred −1..+1 grid:** `offset.x` positive → right (× frame width), `offset.y` positive → **up** (× frame height). Range is ±10; anything past ±1 pushes the clip off-frame.
|
|
147
|
+
|
|
148
|
+
Clip-level `width`/`height` (pixels) define a bounding box. For `image`/`video`, `fit` fills it — `crop` (keep aspect, crop overflow) · `contain` (letterbox) · `cover` (**stretch, distorts**) · `none`. For `rich-text`, that same clip box sets the text-wrap width and the area `align` positions within — **size text on the clip, not the asset.** Without `width`/`height` a clip fills the frame, so unsized text centres across the whole output. `scale` then multiplies the result (uniform on both axes).
|
|
149
|
+
|
|
150
|
+
Order of operations: fit → position → offset → rotate → scale. Full reference: [`references/positioning.md`](../references/positioning.md).
|
|
151
|
+
|
|
152
|
+
## Output resolution
|
|
153
|
+
|
|
154
|
+
Pick `output.resolution` (preset) OR `output.size.width`+`output.size.height` (custom):
|
|
155
|
+
|
|
156
|
+
| Preset | Pixels @ fps |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `preview` | 512×288 @ 15 |
|
|
159
|
+
| `mobile` | 640×360 @ 25 |
|
|
160
|
+
| `sd` | 1024×576 @ 25 |
|
|
161
|
+
| `hd` | 1280×720 @ 25 (default) |
|
|
162
|
+
| `1080` | 1920×1080 @ 25 |
|
|
163
|
+
|
|
164
|
+
Custom sizes must be divisible by 2.
|
|
165
|
+
|
|
166
|
+
## Asset types
|
|
167
|
+
|
|
168
|
+
Use only the **current** asset types; the deprecated ones still parse but should not be used in new templates.
|
|
169
|
+
|
|
170
|
+
### Current
|
|
171
|
+
|
|
172
|
+
| Type | Purpose |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `video` | Video file (mp4, mov, webm). |
|
|
175
|
+
| `image` | Static image — `jpg`, `png`, `webp`, `gif`, `bmp`, `tiff`. |
|
|
176
|
+
| `audio` | Audio clip placed at a specific time on the timeline. |
|
|
177
|
+
| `rich-text` | Styled text overlay with full typography control. **Use this instead of `text`/`html`/`title`.** |
|
|
178
|
+
| `svg` | Vector graphics from raw SVG markup. See `references/svg.md`. |
|
|
179
|
+
| `html5` | Self-contained HTML/CSS/JS page rendered in an iframe (motion graphics, charts, animated overlays). Preloads gsap/d3/anime/lottie. See `references/html5.md`. **Never use the deprecated `html` asset.** |
|
|
180
|
+
| `rich-caption` | Word-level animated captions sourced from audio, video, or subtitle files. See `references/caption.md`. |
|
|
181
|
+
| `luma` | Luma matte for masking effects. |
|
|
182
|
+
| `image-to-video` | **AI**: animate a still image into a short video clip. Billed per generation. |
|
|
183
|
+
| `text-to-image` | **AI**: generate an image from a text prompt. Billed per generation. |
|
|
184
|
+
| `text-to-speech` | **AI**: generate speech from a text prompt. Billed per generation. |
|
|
185
|
+
|
|
186
|
+
`timeline.fonts[]` is a separate field for custom font URLs (not an asset type).
|
|
187
|
+
|
|
188
|
+
For background music, **use an `audio` asset on its own track** with `length: "end"`. Do NOT use `timeline.soundtrack` — it is deprecated. The audio asset path supports keyframes, custom timing, fades, and effects; soundtrack does not.
|
|
189
|
+
|
|
190
|
+
### Deprecated — do not use
|
|
191
|
+
|
|
192
|
+
`text`, `title`, `caption`, `html`, `shape`. They still parse but produce inferior output. Replace with:
|
|
193
|
+
|
|
194
|
+
| If you'd use… | Use instead |
|
|
195
|
+
|---|---|
|
|
196
|
+
| `text` or `title` | `rich-text` |
|
|
197
|
+
| `caption` | `rich-caption` |
|
|
198
|
+
| `html` | `html5` (for motion graphics or animated overlays) or `rich-text` (for static styled text) |
|
|
199
|
+
| `shape` | `svg` with `<rect>`, `<circle>`, `<polygon>` etc. |
|
|
200
|
+
| `timeline.soundtrack` | `audio` asset on its own track with `length: "end"` |
|
|
201
|
+
|
|
202
|
+
### AI-generated assets
|
|
203
|
+
|
|
204
|
+
`image-to-video`, `text-to-speech`, and `text-to-image` are billed per generation **even when invoked through the sandbox stage endpoint** (which is otherwise free). They are async — the render submits the AI job and waits. Renders containing AI assets take longer.
|
|
205
|
+
|
|
206
|
+
## The design ladder — escalate from rich-text to html5
|
|
207
|
+
|
|
208
|
+
One overlay is a `rich-text` job. **Several videos "in different styles" is not** — making them all `rich-text` ships one look eight times. Match the asset to the ambition:
|
|
209
|
+
|
|
210
|
+
| Level | Asset | Use for |
|
|
211
|
+
|---|---|---|
|
|
212
|
+
| 1 — type & layout | `rich-text` | Titles, lower-thirds, kickers, captions, price/CTA pills. Fast and reliable; the right default for static styled text. |
|
|
213
|
+
| 2 — shapes | `svg` | Colour panels, rules, badges, frames, geometric accents behind or around type. |
|
|
214
|
+
| 3 — motion graphics | `html5` | Kinetic type, count-ups / price odometers, shine sweeps, animated gradients, film grain, masked reveals, data-driven overlays — anything that should *move* beyond a `transition` or a Ken-Burns `effect`. gsap / anime / d3 / lottie are preloaded. See [`references/html5.md`](../references/html5.md) and the copy-paste clips in [`references/html5-snippets.md`](../references/html5-snippets.md). |
|
|
215
|
+
|
|
216
|
+
**When the brief asks for a *range*, deliberately spread across the ladder.** A strong set: a couple of clean `rich-text` studio cuts, one or two `svg` colour-block promos, and several `html5` pieces carrying the real motion (kinetic headline, price odometer, shine-swept CTA, grain-graded teaser). Reserve the elaborate `html5` treatments for the hero / hype cuts where motion sells the product. If every clip in a "variety" brief is `rich-text`, you have not delivered variety.
|
|
217
|
+
|
|
218
|
+
## Fonts
|
|
219
|
+
|
|
220
|
+
Use **custom Google Fonts via `timeline.fonts[]`**. System fonts (`Arial`, `Helvetica`, `Times New Roman`) are NOT installed and will fail with "Font not found".
|
|
221
|
+
|
|
222
|
+
**CRITICAL: Do NOT construct or fabricate Google Fonts URLs from memory.** Google rotates them (`v26 → v31 → ...`) and the hash filenames change with each version. Any URL you reconstruct from training data is almost certainly a 404. **Use ONLY the verified entries below, copied verbatim.**
|
|
223
|
+
|
|
224
|
+
See `references/fonts.md` for how to source fonts from the Studio SDK catalogue and the built-in fallback fonts.
|
|
225
|
+
|
|
226
|
+
### Usage example
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"timeline": {
|
|
231
|
+
"fonts": [
|
|
232
|
+
{ "src": "https://fonts.gstatic.com/s/montserrat/v31/JTUSjIg1_i6t8kCHKm45xW5rygbi49c.ttf" }
|
|
233
|
+
],
|
|
234
|
+
"tracks": [
|
|
235
|
+
{
|
|
236
|
+
"clips": [{
|
|
237
|
+
"asset": {
|
|
238
|
+
"type": "rich-text",
|
|
239
|
+
"text": "Hello",
|
|
240
|
+
"font": { "family": "JTUSjIg1_i6t8kCHKm45xW5rygbi49c", "size": 60, "weight": "700", "color": "#ffffff" }
|
|
241
|
+
},
|
|
242
|
+
"start": 0, "length": 3
|
|
243
|
+
}]
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
The `font.family` value MUST match the `family` column in the table above (it's the filename basename without `.ttf`). If `family` and the URL's basename don't match, the font will not load.
|
|
251
|
+
|
|
252
|
+
## Top 5 mistakes
|
|
253
|
+
|
|
254
|
+
1. **Reverse track order.** `tracks[0]` is the TOP layer, not the bottom. Captions go in early tracks; backgrounds go in late tracks.
|
|
255
|
+
2. **System fonts.** `Arial`, `Helvetica`, `Times New Roman`, etc. are not installed. Use Google Fonts via `timeline.fonts[]` (preferred) or one of the built-in fonts in `references/fonts.md`.
|
|
256
|
+
3. **Captions fill the whole frame.** A `rich-caption` clip without `width`, `height`, and `fit: "none"` covers the entire output. Use a named preset from `references/caption.md`.
|
|
257
|
+
4. **`<text>` inside an SVG asset.** Raw `<text>` is unsupported. Use a `rich-text` asset for any text content; reserve SVG for shapes only.
|
|
258
|
+
5. **Composing custom caption styles when presets exist.** The five named presets (Nico, Kai, Kapow, Lovely Little Lychee, Rizz) cover the common styles. Use one verbatim from `references/caption.md` unless the user asks for something specific.
|
|
259
|
+
|
|
260
|
+
## Per-topic deep dives
|
|
261
|
+
|
|
262
|
+
For details beyond this core guide (rich-caption presets, SVG constraints, full font URL list, troubleshooting), see the `references/` directory in the Shotstack CLI repo or fetch the topic-specific docs from `https://shotstack.io/docs/guide/llms-full.txt`.
|