familiar-vtt 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of familiar-vtt might be problematic. Click here for more details.
- package/.github/workflows/publish.yml +23 -0
- package/.mcp.json +11 -0
- package/ARCHITECTURE.md +320 -0
- package/BUSINESS.md +547 -0
- package/CLAUDE.md +118 -0
- package/COMMUNITY_RESEARCH.md +225 -0
- package/LICENSE +21 -0
- package/TOOLS_REFERENCE.md +224 -0
- package/dist/module/main.js +1016 -0
- package/dist/module/main.js.map +7 -0
- package/dist/server/server/config.d.ts +22 -0
- package/dist/server/server/config.js +33 -0
- package/dist/server/server/config.js.map +1 -0
- package/dist/server/server/connector.d.ts +26 -0
- package/dist/server/server/connector.js +179 -0
- package/dist/server/server/connector.js.map +1 -0
- package/dist/server/server/http-transport.d.ts +10 -0
- package/dist/server/server/http-transport.js +43 -0
- package/dist/server/server/http-transport.js.map +1 -0
- package/dist/server/server/index.d.ts +2 -0
- package/dist/server/server/index.js +59 -0
- package/dist/server/server/index.js.map +1 -0
- package/dist/server/server/logger.d.ts +6 -0
- package/dist/server/server/logger.js +14 -0
- package/dist/server/server/logger.js.map +1 -0
- package/dist/server/server/tools/bundles.d.ts +56 -0
- package/dist/server/server/tools/bundles.js +187 -0
- package/dist/server/server/tools/bundles.js.map +1 -0
- package/dist/server/server/tools/canvas-environment.d.ts +3 -0
- package/dist/server/server/tools/canvas-environment.js +151 -0
- package/dist/server/server/tools/canvas-environment.js.map +1 -0
- package/dist/server/server/tools/canvas.d.ts +3 -0
- package/dist/server/server/tools/canvas.js +134 -0
- package/dist/server/server/tools/canvas.js.map +1 -0
- package/dist/server/server/tools/cards.d.ts +3 -0
- package/dist/server/server/tools/cards.js +35 -0
- package/dist/server/server/tools/cards.js.map +1 -0
- package/dist/server/server/tools/characters.d.ts +3 -0
- package/dist/server/server/tools/characters.js +115 -0
- package/dist/server/server/tools/characters.js.map +1 -0
- package/dist/server/server/tools/chat.d.ts +3 -0
- package/dist/server/server/tools/chat.js +23 -0
- package/dist/server/server/tools/chat.js.map +1 -0
- package/dist/server/server/tools/combat.d.ts +3 -0
- package/dist/server/server/tools/combat.js +38 -0
- package/dist/server/server/tools/combat.js.map +1 -0
- package/dist/server/server/tools/compendium.d.ts +3 -0
- package/dist/server/server/tools/compendium.js +33 -0
- package/dist/server/server/tools/compendium.js.map +1 -0
- package/dist/server/server/tools/dice.d.ts +3 -0
- package/dist/server/server/tools/dice.js +12 -0
- package/dist/server/server/tools/dice.js.map +1 -0
- package/dist/server/server/tools/effects.d.ts +3 -0
- package/dist/server/server/tools/effects.js +54 -0
- package/dist/server/server/tools/effects.js.map +1 -0
- package/dist/server/server/tools/ember-events.d.ts +3 -0
- package/dist/server/server/tools/ember-events.js +34 -0
- package/dist/server/server/tools/ember-events.js.map +1 -0
- package/dist/server/server/tools/folders.d.ts +3 -0
- package/dist/server/server/tools/folders.js +30 -0
- package/dist/server/server/tools/folders.js.map +1 -0
- package/dist/server/server/tools/helpers.d.ts +10 -0
- package/dist/server/server/tools/helpers.js +13 -0
- package/dist/server/server/tools/helpers.js.map +1 -0
- package/dist/server/server/tools/items.d.ts +3 -0
- package/dist/server/server/tools/items.js +23 -0
- package/dist/server/server/tools/items.js.map +1 -0
- package/dist/server/server/tools/journals.d.ts +3 -0
- package/dist/server/server/tools/journals.js +71 -0
- package/dist/server/server/tools/journals.js.map +1 -0
- package/dist/server/server/tools/macros.d.ts +3 -0
- package/dist/server/server/tools/macros.js +32 -0
- package/dist/server/server/tools/macros.js.map +1 -0
- package/dist/server/server/tools/playlists.d.ts +3 -0
- package/dist/server/server/tools/playlists.js +39 -0
- package/dist/server/server/tools/playlists.js.map +1 -0
- package/dist/server/server/tools/regions.d.ts +3 -0
- package/dist/server/server/tools/regions.js +65 -0
- package/dist/server/server/tools/regions.js.map +1 -0
- package/dist/server/server/tools/registry.d.ts +11 -0
- package/dist/server/server/tools/registry.js +73 -0
- package/dist/server/server/tools/registry.js.map +1 -0
- package/dist/server/server/tools/scenes.d.ts +3 -0
- package/dist/server/server/tools/scenes.js +142 -0
- package/dist/server/server/tools/scenes.js.map +1 -0
- package/dist/server/server/tools/tables.d.ts +3 -0
- package/dist/server/server/tools/tables.js +22 -0
- package/dist/server/server/tools/tables.js.map +1 -0
- package/dist/server/server/tools/world.d.ts +3 -0
- package/dist/server/server/tools/world.js +26 -0
- package/dist/server/server/tools/world.js.map +1 -0
- package/dist/server/shared/constants.d.ts +16 -0
- package/dist/server/shared/constants.js +17 -0
- package/dist/server/shared/constants.js.map +1 -0
- package/dist/server/shared/html.d.ts +4 -0
- package/dist/server/shared/html.js +9 -0
- package/dist/server/shared/html.js.map +1 -0
- package/dist/server/shared/protocol.d.ts +184 -0
- package/dist/server/shared/protocol.js +58 -0
- package/dist/server/shared/protocol.js.map +1 -0
- package/dist/server/shared/types.d.ts +21 -0
- package/dist/server/shared/types.js +2 -0
- package/dist/server/shared/types.js.map +1 -0
- package/module.json +21 -0
- package/package.json +81 -0
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
var Ho=Object.defineProperty;var Fo=(t,e)=>{for(var n in e)Ho(t,n,{get:e[n],enumerable:!0})};var y="familiar";var A={};Fo(A,{BRAND:()=>ms,DIRTY:()=>ge,EMPTY_PATH:()=>Go,INVALID:()=>v,NEVER:()=>Ys,OK:()=>O,ParseStatus:()=>j,Schema:()=>T,ZodAny:()=>pe,ZodArray:()=>ce,ZodBigInt:()=>ye,ZodBoolean:()=>ve,ZodBranded:()=>Je,ZodCatch:()=>Me,ZodDate:()=>be,ZodDefault:()=>Pe,ZodDiscriminatedUnion:()=>at,ZodEffects:()=>W,ZodEnum:()=>Se,ZodError:()=>H,ZodFirstPartyTypeKind:()=>b,ZodFunction:()=>dt,ZodIntersection:()=>Te,ZodIssueCode:()=>p,ZodLazy:()=>Ce,ZodLiteral:()=>Ee,ZodMap:()=>He,ZodNaN:()=>Ve,ZodNativeEnum:()=>_e,ZodNever:()=>J,ZodNull:()=>xe,ZodNullable:()=>te,ZodNumber:()=>he,ZodObject:()=>F,ZodOptional:()=>B,ZodParsedType:()=>g,ZodPipeline:()=>Ye,ZodPromise:()=>ue,ZodReadonly:()=>Ie,ZodRecord:()=>ct,ZodSchema:()=>T,ZodSet:()=>Fe,ZodString:()=>le,ZodSymbol:()=>Ne,ZodTransformer:()=>W,ZodTuple:()=>ee,ZodType:()=>T,ZodUndefined:()=>we,ZodUnion:()=>ke,ZodUnknown:()=>ae,ZodVoid:()=>qe,addIssueToContext:()=>f,any:()=>ks,array:()=>Ss,bigint:()=>ys,boolean:()=>sn,coerce:()=>Js,custom:()=>nn,date:()=>vs,datetimeRegex:()=>en,defaultErrorMap:()=>oe,discriminatedUnion:()=>Is,effect:()=>Vs,enum:()=>qs,function:()=>Os,getErrorMap:()=>je,getParsedType:()=>X,instanceof:()=>gs,intersection:()=>As,isAborted:()=>ot,isAsync:()=>Oe,isDirty:()=>st,isValid:()=>de,late:()=>fs,lazy:()=>$s,literal:()=>Ns,makeIssue:()=>Ze,map:()=>Ds,nan:()=>hs,nativeEnum:()=>Hs,never:()=>Cs,null:()=>xs,nullable:()=>Us,number:()=>on,object:()=>_s,objectUtil:()=>Et,oboolean:()=>Zs,onumber:()=>Ws,optional:()=>zs,ostring:()=>Ks,pipeline:()=>Bs,preprocess:()=>Gs,promise:()=>Fs,quotelessJson:()=>Vo,record:()=>Ls,set:()=>js,setErrorMap:()=>Uo,strictObject:()=>Ps,string:()=>rn,symbol:()=>bs,transformer:()=>Vs,tuple:()=>Rs,undefined:()=>ws,union:()=>Ms,unknown:()=>Ts,util:()=>C,void:()=>Es});var C;(function(t){t.assertEqual=i=>{};function e(i){}t.assertIs=e;function n(i){throw new Error}t.assertNever=n,t.arrayToEnum=i=>{let o={};for(let s of i)o[s]=s;return o},t.getValidEnumValues=i=>{let o=t.objectKeys(i).filter(a=>typeof i[i[a]]!="number"),s={};for(let a of o)s[a]=i[a];return t.objectValues(s)},t.objectValues=i=>t.objectKeys(i).map(function(o){return i[o]}),t.objectKeys=typeof Object.keys=="function"?i=>Object.keys(i):i=>{let o=[];for(let s in i)Object.prototype.hasOwnProperty.call(i,s)&&o.push(s);return o},t.find=(i,o)=>{for(let s of i)if(o(s))return s},t.isInteger=typeof Number.isInteger=="function"?i=>Number.isInteger(i):i=>typeof i=="number"&&Number.isFinite(i)&&Math.floor(i)===i;function r(i,o=" | "){return i.map(s=>typeof s=="string"?`'${s}'`:s).join(o)}t.joinValues=r,t.jsonStringifyReplacer=(i,o)=>typeof o=="bigint"?o.toString():o})(C||(C={}));var Et;(function(t){t.mergeShapes=(e,n)=>({...e,...n})})(Et||(Et={}));var g=C.arrayToEnum(["string","nan","number","integer","float","boolean","date","bigint","symbol","function","undefined","null","array","object","unknown","promise","void","never","map","set"]),X=t=>{switch(typeof t){case"undefined":return g.undefined;case"string":return g.string;case"number":return Number.isNaN(t)?g.nan:g.number;case"boolean":return g.boolean;case"function":return g.function;case"bigint":return g.bigint;case"symbol":return g.symbol;case"object":return Array.isArray(t)?g.array:t===null?g.null:t.then&&typeof t.then=="function"&&t.catch&&typeof t.catch=="function"?g.promise:typeof Map<"u"&&t instanceof Map?g.map:typeof Set<"u"&&t instanceof Set?g.set:typeof Date<"u"&&t instanceof Date?g.date:g.object;default:return g.unknown}};var p=C.arrayToEnum(["invalid_type","invalid_literal","custom","invalid_union","invalid_union_discriminator","invalid_enum_value","unrecognized_keys","invalid_arguments","invalid_return_type","invalid_date","invalid_string","too_small","too_big","invalid_intersection_types","not_multiple_of","not_finite"]),Vo=t=>JSON.stringify(t,null,2).replace(/"([^"]+)":/g,"$1:"),H=class t extends Error{get errors(){return this.issues}constructor(e){super(),this.issues=[],this.addIssue=r=>{this.issues=[...this.issues,r]},this.addIssues=(r=[])=>{this.issues=[...this.issues,...r]};let n=new.target.prototype;Object.setPrototypeOf?Object.setPrototypeOf(this,n):this.__proto__=n,this.name="ZodError",this.issues=e}format(e){let n=e||function(o){return o.message},r={_errors:[]},i=o=>{for(let s of o.issues)if(s.code==="invalid_union")s.unionErrors.map(i);else if(s.code==="invalid_return_type")i(s.returnTypeError);else if(s.code==="invalid_arguments")i(s.argumentsError);else if(s.path.length===0)r._errors.push(n(s));else{let a=r,c=0;for(;c<s.path.length;){let l=s.path[c];c===s.path.length-1?(a[l]=a[l]||{_errors:[]},a[l]._errors.push(n(s))):a[l]=a[l]||{_errors:[]},a=a[l],c++}}};return i(this),r}static assert(e){if(!(e instanceof t))throw new Error(`Not a ZodError: ${e}`)}toString(){return this.message}get message(){return JSON.stringify(this.issues,C.jsonStringifyReplacer,2)}get isEmpty(){return this.issues.length===0}flatten(e=n=>n.message){let n={},r=[];for(let i of this.issues)if(i.path.length>0){let o=i.path[0];n[o]=n[o]||[],n[o].push(e(i))}else r.push(e(i));return{formErrors:r,fieldErrors:n}}get formErrors(){return this.flatten()}};H.create=t=>new H(t);var zo=(t,e)=>{let n;switch(t.code){case p.invalid_type:t.received===g.undefined?n="Required":n=`Expected ${t.expected}, received ${t.received}`;break;case p.invalid_literal:n=`Invalid literal value, expected ${JSON.stringify(t.expected,C.jsonStringifyReplacer)}`;break;case p.unrecognized_keys:n=`Unrecognized key(s) in object: ${C.joinValues(t.keys,", ")}`;break;case p.invalid_union:n="Invalid input";break;case p.invalid_union_discriminator:n=`Invalid discriminator value. Expected ${C.joinValues(t.options)}`;break;case p.invalid_enum_value:n=`Invalid enum value. Expected ${C.joinValues(t.options)}, received '${t.received}'`;break;case p.invalid_arguments:n="Invalid function arguments";break;case p.invalid_return_type:n="Invalid function return type";break;case p.invalid_date:n="Invalid date";break;case p.invalid_string:typeof t.validation=="object"?"includes"in t.validation?(n=`Invalid input: must include "${t.validation.includes}"`,typeof t.validation.position=="number"&&(n=`${n} at one or more positions greater than or equal to ${t.validation.position}`)):"startsWith"in t.validation?n=`Invalid input: must start with "${t.validation.startsWith}"`:"endsWith"in t.validation?n=`Invalid input: must end with "${t.validation.endsWith}"`:C.assertNever(t.validation):t.validation!=="regex"?n=`Invalid ${t.validation}`:n="Invalid";break;case p.too_small:t.type==="array"?n=`Array must contain ${t.exact?"exactly":t.inclusive?"at least":"more than"} ${t.minimum} element(s)`:t.type==="string"?n=`String must contain ${t.exact?"exactly":t.inclusive?"at least":"over"} ${t.minimum} character(s)`:t.type==="number"?n=`Number must be ${t.exact?"exactly equal to ":t.inclusive?"greater than or equal to ":"greater than "}${t.minimum}`:t.type==="bigint"?n=`Number must be ${t.exact?"exactly equal to ":t.inclusive?"greater than or equal to ":"greater than "}${t.minimum}`:t.type==="date"?n=`Date must be ${t.exact?"exactly equal to ":t.inclusive?"greater than or equal to ":"greater than "}${new Date(Number(t.minimum))}`:n="Invalid input";break;case p.too_big:t.type==="array"?n=`Array must contain ${t.exact?"exactly":t.inclusive?"at most":"less than"} ${t.maximum} element(s)`:t.type==="string"?n=`String must contain ${t.exact?"exactly":t.inclusive?"at most":"under"} ${t.maximum} character(s)`:t.type==="number"?n=`Number must be ${t.exact?"exactly":t.inclusive?"less than or equal to":"less than"} ${t.maximum}`:t.type==="bigint"?n=`BigInt must be ${t.exact?"exactly":t.inclusive?"less than or equal to":"less than"} ${t.maximum}`:t.type==="date"?n=`Date must be ${t.exact?"exactly":t.inclusive?"smaller than or equal to":"smaller than"} ${new Date(Number(t.maximum))}`:n="Invalid input";break;case p.custom:n="Invalid input";break;case p.invalid_intersection_types:n="Intersection results could not be merged";break;case p.not_multiple_of:n=`Number must be a multiple of ${t.multipleOf}`;break;case p.not_finite:n="Number must be finite";break;default:n=e.defaultError,C.assertNever(t)}return{message:n}},oe=zo;var Zt=oe;function Uo(t){Zt=t}function je(){return Zt}var Ze=t=>{let{data:e,path:n,errorMaps:r,issueData:i}=t,o=[...n,...i.path||[]],s={...i,path:o};if(i.message!==void 0)return{...i,path:o,message:i.message};let a="",c=r.filter(l=>!!l).slice().reverse();for(let l of c)a=l(s,{data:e,defaultError:a}).message;return{...i,path:o,message:a}},Go=[];function f(t,e){let n=je(),r=Ze({issueData:e,data:t.data,path:t.path,errorMaps:[t.common.contextualErrorMap,t.schemaErrorMap,n,n===oe?void 0:oe].filter(i=>!!i)});t.common.issues.push(r)}var j=class t{constructor(){this.value="valid"}dirty(){this.value==="valid"&&(this.value="dirty")}abort(){this.value!=="aborted"&&(this.value="aborted")}static mergeArray(e,n){let r=[];for(let i of n){if(i.status==="aborted")return v;i.status==="dirty"&&e.dirty(),r.push(i.value)}return{status:e.value,value:r}}static async mergeObjectAsync(e,n){let r=[];for(let i of n){let o=await i.key,s=await i.value;r.push({key:o,value:s})}return t.mergeObjectSync(e,r)}static mergeObjectSync(e,n){let r={};for(let i of n){let{key:o,value:s}=i;if(o.status==="aborted"||s.status==="aborted")return v;o.status==="dirty"&&e.dirty(),s.status==="dirty"&&e.dirty(),o.value!=="__proto__"&&(typeof s.value<"u"||i.alwaysSet)&&(r[o.value]=s.value)}return{status:e.value,value:r}}},v=Object.freeze({status:"aborted"}),ge=t=>({status:"dirty",value:t}),O=t=>({status:"valid",value:t}),ot=t=>t.status==="aborted",st=t=>t.status==="dirty",de=t=>t.status==="valid",Oe=t=>typeof Promise<"u"&&t instanceof Promise;var h;(function(t){t.errToObj=e=>typeof e=="string"?{message:e}:e||{},t.toString=e=>typeof e=="string"?e:e?.message})(h||(h={}));var K=class{constructor(e,n,r,i){this._cachedPath=[],this.parent=e,this.data=n,this._path=r,this._key=i}get path(){return this._cachedPath.length||(Array.isArray(this._key)?this._cachedPath.push(...this._path,...this._key):this._cachedPath.push(...this._path,this._key)),this._cachedPath}},Jt=(t,e)=>{if(de(e))return{success:!0,data:e.value};if(!t.common.issues.length)throw new Error("Validation failed but no issues detected.");return{success:!1,get error(){if(this._error)return this._error;let n=new H(t.common.issues);return this._error=n,this._error}}};function k(t){if(!t)return{};let{errorMap:e,invalid_type_error:n,required_error:r,description:i}=t;if(e&&(n||r))throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`);return e?{errorMap:e,description:i}:{errorMap:(s,a)=>{let{message:c}=t;return s.code==="invalid_enum_value"?{message:c??a.defaultError}:typeof a.data>"u"?{message:c??r??a.defaultError}:s.code!=="invalid_type"?{message:a.defaultError}:{message:c??n??a.defaultError}},description:i}}var T=class{get description(){return this._def.description}_getType(e){return X(e.data)}_getOrReturnCtx(e,n){return n||{common:e.parent.common,data:e.data,parsedType:X(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}_processInputParams(e){return{status:new j,ctx:{common:e.parent.common,data:e.data,parsedType:X(e.data),schemaErrorMap:this._def.errorMap,path:e.path,parent:e.parent}}}_parseSync(e){let n=this._parse(e);if(Oe(n))throw new Error("Synchronous parse encountered promise.");return n}_parseAsync(e){let n=this._parse(e);return Promise.resolve(n)}parse(e,n){let r=this.safeParse(e,n);if(r.success)return r.data;throw r.error}safeParse(e,n){let r={common:{issues:[],async:n?.async??!1,contextualErrorMap:n?.errorMap},path:n?.path||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:X(e)},i=this._parseSync({data:e,path:r.path,parent:r});return Jt(r,i)}"~validate"(e){let n={common:{issues:[],async:!!this["~standard"].async},path:[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:X(e)};if(!this["~standard"].async)try{let r=this._parseSync({data:e,path:[],parent:n});return de(r)?{value:r.value}:{issues:n.common.issues}}catch(r){r?.message?.toLowerCase()?.includes("encountered")&&(this["~standard"].async=!0),n.common={issues:[],async:!0}}return this._parseAsync({data:e,path:[],parent:n}).then(r=>de(r)?{value:r.value}:{issues:n.common.issues})}async parseAsync(e,n){let r=await this.safeParseAsync(e,n);if(r.success)return r.data;throw r.error}async safeParseAsync(e,n){let r={common:{issues:[],contextualErrorMap:n?.errorMap,async:!0},path:n?.path||[],schemaErrorMap:this._def.errorMap,parent:null,data:e,parsedType:X(e)},i=this._parse({data:e,path:r.path,parent:r}),o=await(Oe(i)?i:Promise.resolve(i));return Jt(r,o)}refine(e,n){let r=i=>typeof n=="string"||typeof n>"u"?{message:n}:typeof n=="function"?n(i):n;return this._refinement((i,o)=>{let s=e(i),a=()=>o.addIssue({code:p.custom,...r(i)});return typeof Promise<"u"&&s instanceof Promise?s.then(c=>c?!0:(a(),!1)):s?!0:(a(),!1)})}refinement(e,n){return this._refinement((r,i)=>e(r)?!0:(i.addIssue(typeof n=="function"?n(r,i):n),!1))}_refinement(e){return new W({schema:this,typeName:b.ZodEffects,effect:{type:"refinement",refinement:e}})}superRefine(e){return this._refinement(e)}constructor(e){this.spa=this.safeParseAsync,this._def=e,this.parse=this.parse.bind(this),this.safeParse=this.safeParse.bind(this),this.parseAsync=this.parseAsync.bind(this),this.safeParseAsync=this.safeParseAsync.bind(this),this.spa=this.spa.bind(this),this.refine=this.refine.bind(this),this.refinement=this.refinement.bind(this),this.superRefine=this.superRefine.bind(this),this.optional=this.optional.bind(this),this.nullable=this.nullable.bind(this),this.nullish=this.nullish.bind(this),this.array=this.array.bind(this),this.promise=this.promise.bind(this),this.or=this.or.bind(this),this.and=this.and.bind(this),this.transform=this.transform.bind(this),this.brand=this.brand.bind(this),this.default=this.default.bind(this),this.catch=this.catch.bind(this),this.describe=this.describe.bind(this),this.pipe=this.pipe.bind(this),this.readonly=this.readonly.bind(this),this.isNullable=this.isNullable.bind(this),this.isOptional=this.isOptional.bind(this),this["~standard"]={version:1,vendor:"zod",validate:n=>this["~validate"](n)}}optional(){return B.create(this,this._def)}nullable(){return te.create(this,this._def)}nullish(){return this.nullable().optional()}array(){return ce.create(this)}promise(){return ue.create(this,this._def)}or(e){return ke.create([this,e],this._def)}and(e){return Te.create(this,e,this._def)}transform(e){return new W({...k(this._def),schema:this,typeName:b.ZodEffects,effect:{type:"transform",transform:e}})}default(e){let n=typeof e=="function"?e:()=>e;return new Pe({...k(this._def),innerType:this,defaultValue:n,typeName:b.ZodDefault})}brand(){return new Je({typeName:b.ZodBranded,type:this,...k(this._def)})}catch(e){let n=typeof e=="function"?e:()=>e;return new Me({...k(this._def),innerType:this,catchValue:n,typeName:b.ZodCatch})}describe(e){let n=this.constructor;return new n({...this._def,description:e})}pipe(e){return Ye.create(this,e)}readonly(){return Ie.create(this)}isOptional(){return this.safeParse(void 0).success}isNullable(){return this.safeParse(null).success}},Bo=/^c[^\s-]{8,}$/i,Ko=/^[0-9a-z]+$/,Wo=/^[0-9A-HJKMNP-TV-Z]{26}$/i,Zo=/^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i,Jo=/^[a-z0-9_-]{21}$/i,Yo=/^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/,Qo=/^[-+]?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)?)??$/,Xo=/^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i,es="^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$",St,ts=/^(?:(?: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])$/,ns=/^(?:(?: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])\/(3[0-2]|[12]?[0-9])$/,rs=/^(([0-9a-fA-F]{1,4}:){7,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}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,is=/^(([0-9a-fA-F]{1,4}:){7,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}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/,os=/^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/,ss=/^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/,Qt="((\\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])))",as=new RegExp(`^${Qt}$`);function Xt(t){let e="[0-5]\\d";t.precision?e=`${e}\\.\\d{${t.precision}}`:t.precision==null&&(e=`${e}(\\.\\d+)?`);let n=t.precision?"+":"?";return`([01]\\d|2[0-3]):[0-5]\\d(:${e})${n}`}function cs(t){return new RegExp(`^${Xt(t)}$`)}function en(t){let e=`${Qt}T${Xt(t)}`,n=[];return n.push(t.local?"Z?":"Z"),t.offset&&n.push("([+-]\\d{2}:?\\d{2})"),e=`${e}(${n.join("|")})`,new RegExp(`^${e}$`)}function ds(t,e){return!!((e==="v4"||!e)&&ts.test(t)||(e==="v6"||!e)&&rs.test(t))}function ls(t,e){if(!Yo.test(t))return!1;try{let[n]=t.split(".");if(!n)return!1;let r=n.replace(/-/g,"+").replace(/_/g,"/").padEnd(n.length+(4-n.length%4)%4,"="),i=JSON.parse(atob(r));return!(typeof i!="object"||i===null||"typ"in i&&i?.typ!=="JWT"||!i.alg||e&&i.alg!==e)}catch{return!1}}function ps(t,e){return!!((e==="v4"||!e)&&ns.test(t)||(e==="v6"||!e)&&is.test(t))}var le=class t extends T{_parse(e){if(this._def.coerce&&(e.data=String(e.data)),this._getType(e)!==g.string){let o=this._getOrReturnCtx(e);return f(o,{code:p.invalid_type,expected:g.string,received:o.parsedType}),v}let r=new j,i;for(let o of this._def.checks)if(o.kind==="min")e.data.length<o.value&&(i=this._getOrReturnCtx(e,i),f(i,{code:p.too_small,minimum:o.value,type:"string",inclusive:!0,exact:!1,message:o.message}),r.dirty());else if(o.kind==="max")e.data.length>o.value&&(i=this._getOrReturnCtx(e,i),f(i,{code:p.too_big,maximum:o.value,type:"string",inclusive:!0,exact:!1,message:o.message}),r.dirty());else if(o.kind==="length"){let s=e.data.length>o.value,a=e.data.length<o.value;(s||a)&&(i=this._getOrReturnCtx(e,i),s?f(i,{code:p.too_big,maximum:o.value,type:"string",inclusive:!0,exact:!0,message:o.message}):a&&f(i,{code:p.too_small,minimum:o.value,type:"string",inclusive:!0,exact:!0,message:o.message}),r.dirty())}else if(o.kind==="email")Xo.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"email",code:p.invalid_string,message:o.message}),r.dirty());else if(o.kind==="emoji")St||(St=new RegExp(es,"u")),St.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"emoji",code:p.invalid_string,message:o.message}),r.dirty());else if(o.kind==="uuid")Zo.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"uuid",code:p.invalid_string,message:o.message}),r.dirty());else if(o.kind==="nanoid")Jo.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"nanoid",code:p.invalid_string,message:o.message}),r.dirty());else if(o.kind==="cuid")Bo.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"cuid",code:p.invalid_string,message:o.message}),r.dirty());else if(o.kind==="cuid2")Ko.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"cuid2",code:p.invalid_string,message:o.message}),r.dirty());else if(o.kind==="ulid")Wo.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"ulid",code:p.invalid_string,message:o.message}),r.dirty());else if(o.kind==="url")try{new URL(e.data)}catch{i=this._getOrReturnCtx(e,i),f(i,{validation:"url",code:p.invalid_string,message:o.message}),r.dirty()}else o.kind==="regex"?(o.regex.lastIndex=0,o.regex.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"regex",code:p.invalid_string,message:o.message}),r.dirty())):o.kind==="trim"?e.data=e.data.trim():o.kind==="includes"?e.data.includes(o.value,o.position)||(i=this._getOrReturnCtx(e,i),f(i,{code:p.invalid_string,validation:{includes:o.value,position:o.position},message:o.message}),r.dirty()):o.kind==="toLowerCase"?e.data=e.data.toLowerCase():o.kind==="toUpperCase"?e.data=e.data.toUpperCase():o.kind==="startsWith"?e.data.startsWith(o.value)||(i=this._getOrReturnCtx(e,i),f(i,{code:p.invalid_string,validation:{startsWith:o.value},message:o.message}),r.dirty()):o.kind==="endsWith"?e.data.endsWith(o.value)||(i=this._getOrReturnCtx(e,i),f(i,{code:p.invalid_string,validation:{endsWith:o.value},message:o.message}),r.dirty()):o.kind==="datetime"?en(o).test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{code:p.invalid_string,validation:"datetime",message:o.message}),r.dirty()):o.kind==="date"?as.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{code:p.invalid_string,validation:"date",message:o.message}),r.dirty()):o.kind==="time"?cs(o).test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{code:p.invalid_string,validation:"time",message:o.message}),r.dirty()):o.kind==="duration"?Qo.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"duration",code:p.invalid_string,message:o.message}),r.dirty()):o.kind==="ip"?ds(e.data,o.version)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"ip",code:p.invalid_string,message:o.message}),r.dirty()):o.kind==="jwt"?ls(e.data,o.alg)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"jwt",code:p.invalid_string,message:o.message}),r.dirty()):o.kind==="cidr"?ps(e.data,o.version)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"cidr",code:p.invalid_string,message:o.message}),r.dirty()):o.kind==="base64"?os.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"base64",code:p.invalid_string,message:o.message}),r.dirty()):o.kind==="base64url"?ss.test(e.data)||(i=this._getOrReturnCtx(e,i),f(i,{validation:"base64url",code:p.invalid_string,message:o.message}),r.dirty()):C.assertNever(o);return{status:r.value,value:e.data}}_regex(e,n,r){return this.refinement(i=>e.test(i),{validation:n,code:p.invalid_string,...h.errToObj(r)})}_addCheck(e){return new t({...this._def,checks:[...this._def.checks,e]})}email(e){return this._addCheck({kind:"email",...h.errToObj(e)})}url(e){return this._addCheck({kind:"url",...h.errToObj(e)})}emoji(e){return this._addCheck({kind:"emoji",...h.errToObj(e)})}uuid(e){return this._addCheck({kind:"uuid",...h.errToObj(e)})}nanoid(e){return this._addCheck({kind:"nanoid",...h.errToObj(e)})}cuid(e){return this._addCheck({kind:"cuid",...h.errToObj(e)})}cuid2(e){return this._addCheck({kind:"cuid2",...h.errToObj(e)})}ulid(e){return this._addCheck({kind:"ulid",...h.errToObj(e)})}base64(e){return this._addCheck({kind:"base64",...h.errToObj(e)})}base64url(e){return this._addCheck({kind:"base64url",...h.errToObj(e)})}jwt(e){return this._addCheck({kind:"jwt",...h.errToObj(e)})}ip(e){return this._addCheck({kind:"ip",...h.errToObj(e)})}cidr(e){return this._addCheck({kind:"cidr",...h.errToObj(e)})}datetime(e){return typeof e=="string"?this._addCheck({kind:"datetime",precision:null,offset:!1,local:!1,message:e}):this._addCheck({kind:"datetime",precision:typeof e?.precision>"u"?null:e?.precision,offset:e?.offset??!1,local:e?.local??!1,...h.errToObj(e?.message)})}date(e){return this._addCheck({kind:"date",message:e})}time(e){return typeof e=="string"?this._addCheck({kind:"time",precision:null,message:e}):this._addCheck({kind:"time",precision:typeof e?.precision>"u"?null:e?.precision,...h.errToObj(e?.message)})}duration(e){return this._addCheck({kind:"duration",...h.errToObj(e)})}regex(e,n){return this._addCheck({kind:"regex",regex:e,...h.errToObj(n)})}includes(e,n){return this._addCheck({kind:"includes",value:e,position:n?.position,...h.errToObj(n?.message)})}startsWith(e,n){return this._addCheck({kind:"startsWith",value:e,...h.errToObj(n)})}endsWith(e,n){return this._addCheck({kind:"endsWith",value:e,...h.errToObj(n)})}min(e,n){return this._addCheck({kind:"min",value:e,...h.errToObj(n)})}max(e,n){return this._addCheck({kind:"max",value:e,...h.errToObj(n)})}length(e,n){return this._addCheck({kind:"length",value:e,...h.errToObj(n)})}nonempty(e){return this.min(1,h.errToObj(e))}trim(){return new t({...this._def,checks:[...this._def.checks,{kind:"trim"}]})}toLowerCase(){return new t({...this._def,checks:[...this._def.checks,{kind:"toLowerCase"}]})}toUpperCase(){return new t({...this._def,checks:[...this._def.checks,{kind:"toUpperCase"}]})}get isDatetime(){return!!this._def.checks.find(e=>e.kind==="datetime")}get isDate(){return!!this._def.checks.find(e=>e.kind==="date")}get isTime(){return!!this._def.checks.find(e=>e.kind==="time")}get isDuration(){return!!this._def.checks.find(e=>e.kind==="duration")}get isEmail(){return!!this._def.checks.find(e=>e.kind==="email")}get isURL(){return!!this._def.checks.find(e=>e.kind==="url")}get isEmoji(){return!!this._def.checks.find(e=>e.kind==="emoji")}get isUUID(){return!!this._def.checks.find(e=>e.kind==="uuid")}get isNANOID(){return!!this._def.checks.find(e=>e.kind==="nanoid")}get isCUID(){return!!this._def.checks.find(e=>e.kind==="cuid")}get isCUID2(){return!!this._def.checks.find(e=>e.kind==="cuid2")}get isULID(){return!!this._def.checks.find(e=>e.kind==="ulid")}get isIP(){return!!this._def.checks.find(e=>e.kind==="ip")}get isCIDR(){return!!this._def.checks.find(e=>e.kind==="cidr")}get isBase64(){return!!this._def.checks.find(e=>e.kind==="base64")}get isBase64url(){return!!this._def.checks.find(e=>e.kind==="base64url")}get minLength(){let e=null;for(let n of this._def.checks)n.kind==="min"&&(e===null||n.value>e)&&(e=n.value);return e}get maxLength(){let e=null;for(let n of this._def.checks)n.kind==="max"&&(e===null||n.value<e)&&(e=n.value);return e}};le.create=t=>new le({checks:[],typeName:b.ZodString,coerce:t?.coerce??!1,...k(t)});function us(t,e){let n=(t.toString().split(".")[1]||"").length,r=(e.toString().split(".")[1]||"").length,i=n>r?n:r,o=Number.parseInt(t.toFixed(i).replace(".","")),s=Number.parseInt(e.toFixed(i).replace(".",""));return o%s/10**i}var he=class t extends T{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte,this.step=this.multipleOf}_parse(e){if(this._def.coerce&&(e.data=Number(e.data)),this._getType(e)!==g.number){let o=this._getOrReturnCtx(e);return f(o,{code:p.invalid_type,expected:g.number,received:o.parsedType}),v}let r,i=new j;for(let o of this._def.checks)o.kind==="int"?C.isInteger(e.data)||(r=this._getOrReturnCtx(e,r),f(r,{code:p.invalid_type,expected:"integer",received:"float",message:o.message}),i.dirty()):o.kind==="min"?(o.inclusive?e.data<o.value:e.data<=o.value)&&(r=this._getOrReturnCtx(e,r),f(r,{code:p.too_small,minimum:o.value,type:"number",inclusive:o.inclusive,exact:!1,message:o.message}),i.dirty()):o.kind==="max"?(o.inclusive?e.data>o.value:e.data>=o.value)&&(r=this._getOrReturnCtx(e,r),f(r,{code:p.too_big,maximum:o.value,type:"number",inclusive:o.inclusive,exact:!1,message:o.message}),i.dirty()):o.kind==="multipleOf"?us(e.data,o.value)!==0&&(r=this._getOrReturnCtx(e,r),f(r,{code:p.not_multiple_of,multipleOf:o.value,message:o.message}),i.dirty()):o.kind==="finite"?Number.isFinite(e.data)||(r=this._getOrReturnCtx(e,r),f(r,{code:p.not_finite,message:o.message}),i.dirty()):C.assertNever(o);return{status:i.value,value:e.data}}gte(e,n){return this.setLimit("min",e,!0,h.toString(n))}gt(e,n){return this.setLimit("min",e,!1,h.toString(n))}lte(e,n){return this.setLimit("max",e,!0,h.toString(n))}lt(e,n){return this.setLimit("max",e,!1,h.toString(n))}setLimit(e,n,r,i){return new t({...this._def,checks:[...this._def.checks,{kind:e,value:n,inclusive:r,message:h.toString(i)}]})}_addCheck(e){return new t({...this._def,checks:[...this._def.checks,e]})}int(e){return this._addCheck({kind:"int",message:h.toString(e)})}positive(e){return this._addCheck({kind:"min",value:0,inclusive:!1,message:h.toString(e)})}negative(e){return this._addCheck({kind:"max",value:0,inclusive:!1,message:h.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:0,inclusive:!0,message:h.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:0,inclusive:!0,message:h.toString(e)})}multipleOf(e,n){return this._addCheck({kind:"multipleOf",value:e,message:h.toString(n)})}finite(e){return this._addCheck({kind:"finite",message:h.toString(e)})}safe(e){return this._addCheck({kind:"min",inclusive:!0,value:Number.MIN_SAFE_INTEGER,message:h.toString(e)})._addCheck({kind:"max",inclusive:!0,value:Number.MAX_SAFE_INTEGER,message:h.toString(e)})}get minValue(){let e=null;for(let n of this._def.checks)n.kind==="min"&&(e===null||n.value>e)&&(e=n.value);return e}get maxValue(){let e=null;for(let n of this._def.checks)n.kind==="max"&&(e===null||n.value<e)&&(e=n.value);return e}get isInt(){return!!this._def.checks.find(e=>e.kind==="int"||e.kind==="multipleOf"&&C.isInteger(e.value))}get isFinite(){let e=null,n=null;for(let r of this._def.checks){if(r.kind==="finite"||r.kind==="int"||r.kind==="multipleOf")return!0;r.kind==="min"?(n===null||r.value>n)&&(n=r.value):r.kind==="max"&&(e===null||r.value<e)&&(e=r.value)}return Number.isFinite(n)&&Number.isFinite(e)}};he.create=t=>new he({checks:[],typeName:b.ZodNumber,coerce:t?.coerce||!1,...k(t)});var ye=class t extends T{constructor(){super(...arguments),this.min=this.gte,this.max=this.lte}_parse(e){if(this._def.coerce)try{e.data=BigInt(e.data)}catch{return this._getInvalidInput(e)}if(this._getType(e)!==g.bigint)return this._getInvalidInput(e);let r,i=new j;for(let o of this._def.checks)o.kind==="min"?(o.inclusive?e.data<o.value:e.data<=o.value)&&(r=this._getOrReturnCtx(e,r),f(r,{code:p.too_small,type:"bigint",minimum:o.value,inclusive:o.inclusive,message:o.message}),i.dirty()):o.kind==="max"?(o.inclusive?e.data>o.value:e.data>=o.value)&&(r=this._getOrReturnCtx(e,r),f(r,{code:p.too_big,type:"bigint",maximum:o.value,inclusive:o.inclusive,message:o.message}),i.dirty()):o.kind==="multipleOf"?e.data%o.value!==BigInt(0)&&(r=this._getOrReturnCtx(e,r),f(r,{code:p.not_multiple_of,multipleOf:o.value,message:o.message}),i.dirty()):C.assertNever(o);return{status:i.value,value:e.data}}_getInvalidInput(e){let n=this._getOrReturnCtx(e);return f(n,{code:p.invalid_type,expected:g.bigint,received:n.parsedType}),v}gte(e,n){return this.setLimit("min",e,!0,h.toString(n))}gt(e,n){return this.setLimit("min",e,!1,h.toString(n))}lte(e,n){return this.setLimit("max",e,!0,h.toString(n))}lt(e,n){return this.setLimit("max",e,!1,h.toString(n))}setLimit(e,n,r,i){return new t({...this._def,checks:[...this._def.checks,{kind:e,value:n,inclusive:r,message:h.toString(i)}]})}_addCheck(e){return new t({...this._def,checks:[...this._def.checks,e]})}positive(e){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!1,message:h.toString(e)})}negative(e){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!1,message:h.toString(e)})}nonpositive(e){return this._addCheck({kind:"max",value:BigInt(0),inclusive:!0,message:h.toString(e)})}nonnegative(e){return this._addCheck({kind:"min",value:BigInt(0),inclusive:!0,message:h.toString(e)})}multipleOf(e,n){return this._addCheck({kind:"multipleOf",value:e,message:h.toString(n)})}get minValue(){let e=null;for(let n of this._def.checks)n.kind==="min"&&(e===null||n.value>e)&&(e=n.value);return e}get maxValue(){let e=null;for(let n of this._def.checks)n.kind==="max"&&(e===null||n.value<e)&&(e=n.value);return e}};ye.create=t=>new ye({checks:[],typeName:b.ZodBigInt,coerce:t?.coerce??!1,...k(t)});var ve=class extends T{_parse(e){if(this._def.coerce&&(e.data=!!e.data),this._getType(e)!==g.boolean){let r=this._getOrReturnCtx(e);return f(r,{code:p.invalid_type,expected:g.boolean,received:r.parsedType}),v}return O(e.data)}};ve.create=t=>new ve({typeName:b.ZodBoolean,coerce:t?.coerce||!1,...k(t)});var be=class t extends T{_parse(e){if(this._def.coerce&&(e.data=new Date(e.data)),this._getType(e)!==g.date){let o=this._getOrReturnCtx(e);return f(o,{code:p.invalid_type,expected:g.date,received:o.parsedType}),v}if(Number.isNaN(e.data.getTime())){let o=this._getOrReturnCtx(e);return f(o,{code:p.invalid_date}),v}let r=new j,i;for(let o of this._def.checks)o.kind==="min"?e.data.getTime()<o.value&&(i=this._getOrReturnCtx(e,i),f(i,{code:p.too_small,message:o.message,inclusive:!0,exact:!1,minimum:o.value,type:"date"}),r.dirty()):o.kind==="max"?e.data.getTime()>o.value&&(i=this._getOrReturnCtx(e,i),f(i,{code:p.too_big,message:o.message,inclusive:!0,exact:!1,maximum:o.value,type:"date"}),r.dirty()):C.assertNever(o);return{status:r.value,value:new Date(e.data.getTime())}}_addCheck(e){return new t({...this._def,checks:[...this._def.checks,e]})}min(e,n){return this._addCheck({kind:"min",value:e.getTime(),message:h.toString(n)})}max(e,n){return this._addCheck({kind:"max",value:e.getTime(),message:h.toString(n)})}get minDate(){let e=null;for(let n of this._def.checks)n.kind==="min"&&(e===null||n.value>e)&&(e=n.value);return e!=null?new Date(e):null}get maxDate(){let e=null;for(let n of this._def.checks)n.kind==="max"&&(e===null||n.value<e)&&(e=n.value);return e!=null?new Date(e):null}};be.create=t=>new be({checks:[],coerce:t?.coerce||!1,typeName:b.ZodDate,...k(t)});var Ne=class extends T{_parse(e){if(this._getType(e)!==g.symbol){let r=this._getOrReturnCtx(e);return f(r,{code:p.invalid_type,expected:g.symbol,received:r.parsedType}),v}return O(e.data)}};Ne.create=t=>new Ne({typeName:b.ZodSymbol,...k(t)});var we=class extends T{_parse(e){if(this._getType(e)!==g.undefined){let r=this._getOrReturnCtx(e);return f(r,{code:p.invalid_type,expected:g.undefined,received:r.parsedType}),v}return O(e.data)}};we.create=t=>new we({typeName:b.ZodUndefined,...k(t)});var xe=class extends T{_parse(e){if(this._getType(e)!==g.null){let r=this._getOrReturnCtx(e);return f(r,{code:p.invalid_type,expected:g.null,received:r.parsedType}),v}return O(e.data)}};xe.create=t=>new xe({typeName:b.ZodNull,...k(t)});var pe=class extends T{constructor(){super(...arguments),this._any=!0}_parse(e){return O(e.data)}};pe.create=t=>new pe({typeName:b.ZodAny,...k(t)});var ae=class extends T{constructor(){super(...arguments),this._unknown=!0}_parse(e){return O(e.data)}};ae.create=t=>new ae({typeName:b.ZodUnknown,...k(t)});var J=class extends T{_parse(e){let n=this._getOrReturnCtx(e);return f(n,{code:p.invalid_type,expected:g.never,received:n.parsedType}),v}};J.create=t=>new J({typeName:b.ZodNever,...k(t)});var qe=class extends T{_parse(e){if(this._getType(e)!==g.undefined){let r=this._getOrReturnCtx(e);return f(r,{code:p.invalid_type,expected:g.void,received:r.parsedType}),v}return O(e.data)}};qe.create=t=>new qe({typeName:b.ZodVoid,...k(t)});var ce=class t extends T{_parse(e){let{ctx:n,status:r}=this._processInputParams(e),i=this._def;if(n.parsedType!==g.array)return f(n,{code:p.invalid_type,expected:g.array,received:n.parsedType}),v;if(i.exactLength!==null){let s=n.data.length>i.exactLength.value,a=n.data.length<i.exactLength.value;(s||a)&&(f(n,{code:s?p.too_big:p.too_small,minimum:a?i.exactLength.value:void 0,maximum:s?i.exactLength.value:void 0,type:"array",inclusive:!0,exact:!0,message:i.exactLength.message}),r.dirty())}if(i.minLength!==null&&n.data.length<i.minLength.value&&(f(n,{code:p.too_small,minimum:i.minLength.value,type:"array",inclusive:!0,exact:!1,message:i.minLength.message}),r.dirty()),i.maxLength!==null&&n.data.length>i.maxLength.value&&(f(n,{code:p.too_big,maximum:i.maxLength.value,type:"array",inclusive:!0,exact:!1,message:i.maxLength.message}),r.dirty()),n.common.async)return Promise.all([...n.data].map((s,a)=>i.type._parseAsync(new K(n,s,n.path,a)))).then(s=>j.mergeArray(r,s));let o=[...n.data].map((s,a)=>i.type._parseSync(new K(n,s,n.path,a)));return j.mergeArray(r,o)}get element(){return this._def.type}min(e,n){return new t({...this._def,minLength:{value:e,message:h.toString(n)}})}max(e,n){return new t({...this._def,maxLength:{value:e,message:h.toString(n)}})}length(e,n){return new t({...this._def,exactLength:{value:e,message:h.toString(n)}})}nonempty(e){return this.min(1,e)}};ce.create=(t,e)=>new ce({type:t,minLength:null,maxLength:null,exactLength:null,typeName:b.ZodArray,...k(e)});function $e(t){if(t instanceof F){let e={};for(let n in t.shape){let r=t.shape[n];e[n]=B.create($e(r))}return new F({...t._def,shape:()=>e})}else return t instanceof ce?new ce({...t._def,type:$e(t.element)}):t instanceof B?B.create($e(t.unwrap())):t instanceof te?te.create($e(t.unwrap())):t instanceof ee?ee.create(t.items.map(e=>$e(e))):t}var F=class t extends T{constructor(){super(...arguments),this._cached=null,this.nonstrict=this.passthrough,this.augment=this.extend}_getCached(){if(this._cached!==null)return this._cached;let e=this._def.shape(),n=C.objectKeys(e);return this._cached={shape:e,keys:n},this._cached}_parse(e){if(this._getType(e)!==g.object){let l=this._getOrReturnCtx(e);return f(l,{code:p.invalid_type,expected:g.object,received:l.parsedType}),v}let{status:r,ctx:i}=this._processInputParams(e),{shape:o,keys:s}=this._getCached(),a=[];if(!(this._def.catchall instanceof J&&this._def.unknownKeys==="strip"))for(let l in i.data)s.includes(l)||a.push(l);let c=[];for(let l of s){let m=o[l],w=i.data[l];c.push({key:{status:"valid",value:l},value:m._parse(new K(i,w,i.path,l)),alwaysSet:l in i.data})}if(this._def.catchall instanceof J){let l=this._def.unknownKeys;if(l==="passthrough")for(let m of a)c.push({key:{status:"valid",value:m},value:{status:"valid",value:i.data[m]}});else if(l==="strict")a.length>0&&(f(i,{code:p.unrecognized_keys,keys:a}),r.dirty());else if(l!=="strip")throw new Error("Internal ZodObject error: invalid unknownKeys value.")}else{let l=this._def.catchall;for(let m of a){let w=i.data[m];c.push({key:{status:"valid",value:m},value:l._parse(new K(i,w,i.path,m)),alwaysSet:m in i.data})}}return i.common.async?Promise.resolve().then(async()=>{let l=[];for(let m of c){let w=await m.key,I=await m.value;l.push({key:w,value:I,alwaysSet:m.alwaysSet})}return l}).then(l=>j.mergeObjectSync(r,l)):j.mergeObjectSync(r,c)}get shape(){return this._def.shape()}strict(e){return h.errToObj,new t({...this._def,unknownKeys:"strict",...e!==void 0?{errorMap:(n,r)=>{let i=this._def.errorMap?.(n,r).message??r.defaultError;return n.code==="unrecognized_keys"?{message:h.errToObj(e).message??i}:{message:i}}}:{}})}strip(){return new t({...this._def,unknownKeys:"strip"})}passthrough(){return new t({...this._def,unknownKeys:"passthrough"})}extend(e){return new t({...this._def,shape:()=>({...this._def.shape(),...e})})}merge(e){return new t({unknownKeys:e._def.unknownKeys,catchall:e._def.catchall,shape:()=>({...this._def.shape(),...e._def.shape()}),typeName:b.ZodObject})}setKey(e,n){return this.augment({[e]:n})}catchall(e){return new t({...this._def,catchall:e})}pick(e){let n={};for(let r of C.objectKeys(e))e[r]&&this.shape[r]&&(n[r]=this.shape[r]);return new t({...this._def,shape:()=>n})}omit(e){let n={};for(let r of C.objectKeys(this.shape))e[r]||(n[r]=this.shape[r]);return new t({...this._def,shape:()=>n})}deepPartial(){return $e(this)}partial(e){let n={};for(let r of C.objectKeys(this.shape)){let i=this.shape[r];e&&!e[r]?n[r]=i:n[r]=i.optional()}return new t({...this._def,shape:()=>n})}required(e){let n={};for(let r of C.objectKeys(this.shape))if(e&&!e[r])n[r]=this.shape[r];else{let o=this.shape[r];for(;o instanceof B;)o=o._def.innerType;n[r]=o}return new t({...this._def,shape:()=>n})}keyof(){return tn(C.objectKeys(this.shape))}};F.create=(t,e)=>new F({shape:()=>t,unknownKeys:"strip",catchall:J.create(),typeName:b.ZodObject,...k(e)});F.strictCreate=(t,e)=>new F({shape:()=>t,unknownKeys:"strict",catchall:J.create(),typeName:b.ZodObject,...k(e)});F.lazycreate=(t,e)=>new F({shape:t,unknownKeys:"strip",catchall:J.create(),typeName:b.ZodObject,...k(e)});var ke=class extends T{_parse(e){let{ctx:n}=this._processInputParams(e),r=this._def.options;function i(o){for(let a of o)if(a.result.status==="valid")return a.result;for(let a of o)if(a.result.status==="dirty")return n.common.issues.push(...a.ctx.common.issues),a.result;let s=o.map(a=>new H(a.ctx.common.issues));return f(n,{code:p.invalid_union,unionErrors:s}),v}if(n.common.async)return Promise.all(r.map(async o=>{let s={...n,common:{...n.common,issues:[]},parent:null};return{result:await o._parseAsync({data:n.data,path:n.path,parent:s}),ctx:s}})).then(i);{let o,s=[];for(let c of r){let l={...n,common:{...n.common,issues:[]},parent:null},m=c._parseSync({data:n.data,path:n.path,parent:l});if(m.status==="valid")return m;m.status==="dirty"&&!o&&(o={result:m,ctx:l}),l.common.issues.length&&s.push(l.common.issues)}if(o)return n.common.issues.push(...o.ctx.common.issues),o.result;let a=s.map(c=>new H(c));return f(n,{code:p.invalid_union,unionErrors:a}),v}}get options(){return this._def.options}};ke.create=(t,e)=>new ke({options:t,typeName:b.ZodUnion,...k(e)});var se=t=>t instanceof Ce?se(t.schema):t instanceof W?se(t.innerType()):t instanceof Ee?[t.value]:t instanceof Se?t.options:t instanceof _e?C.objectValues(t.enum):t instanceof Pe?se(t._def.innerType):t instanceof we?[void 0]:t instanceof xe?[null]:t instanceof B?[void 0,...se(t.unwrap())]:t instanceof te?[null,...se(t.unwrap())]:t instanceof Je||t instanceof Ie?se(t.unwrap()):t instanceof Me?se(t._def.innerType):[],at=class t extends T{_parse(e){let{ctx:n}=this._processInputParams(e);if(n.parsedType!==g.object)return f(n,{code:p.invalid_type,expected:g.object,received:n.parsedType}),v;let r=this.discriminator,i=n.data[r],o=this.optionsMap.get(i);return o?n.common.async?o._parseAsync({data:n.data,path:n.path,parent:n}):o._parseSync({data:n.data,path:n.path,parent:n}):(f(n,{code:p.invalid_union_discriminator,options:Array.from(this.optionsMap.keys()),path:[r]}),v)}get discriminator(){return this._def.discriminator}get options(){return this._def.options}get optionsMap(){return this._def.optionsMap}static create(e,n,r){let i=new Map;for(let o of n){let s=se(o.shape[e]);if(!s.length)throw new Error(`A discriminator value for key \`${e}\` could not be extracted from all schema options`);for(let a of s){if(i.has(a))throw new Error(`Discriminator property ${String(e)} has duplicate value ${String(a)}`);i.set(a,o)}}return new t({typeName:b.ZodDiscriminatedUnion,discriminator:e,options:n,optionsMap:i,...k(r)})}};function _t(t,e){let n=X(t),r=X(e);if(t===e)return{valid:!0,data:t};if(n===g.object&&r===g.object){let i=C.objectKeys(e),o=C.objectKeys(t).filter(a=>i.indexOf(a)!==-1),s={...t,...e};for(let a of o){let c=_t(t[a],e[a]);if(!c.valid)return{valid:!1};s[a]=c.data}return{valid:!0,data:s}}else if(n===g.array&&r===g.array){if(t.length!==e.length)return{valid:!1};let i=[];for(let o=0;o<t.length;o++){let s=t[o],a=e[o],c=_t(s,a);if(!c.valid)return{valid:!1};i.push(c.data)}return{valid:!0,data:i}}else return n===g.date&&r===g.date&&+t==+e?{valid:!0,data:t}:{valid:!1}}var Te=class extends T{_parse(e){let{status:n,ctx:r}=this._processInputParams(e),i=(o,s)=>{if(ot(o)||ot(s))return v;let a=_t(o.value,s.value);return a.valid?((st(o)||st(s))&&n.dirty(),{status:n.value,value:a.data}):(f(r,{code:p.invalid_intersection_types}),v)};return r.common.async?Promise.all([this._def.left._parseAsync({data:r.data,path:r.path,parent:r}),this._def.right._parseAsync({data:r.data,path:r.path,parent:r})]).then(([o,s])=>i(o,s)):i(this._def.left._parseSync({data:r.data,path:r.path,parent:r}),this._def.right._parseSync({data:r.data,path:r.path,parent:r}))}};Te.create=(t,e,n)=>new Te({left:t,right:e,typeName:b.ZodIntersection,...k(n)});var ee=class t extends T{_parse(e){let{status:n,ctx:r}=this._processInputParams(e);if(r.parsedType!==g.array)return f(r,{code:p.invalid_type,expected:g.array,received:r.parsedType}),v;if(r.data.length<this._def.items.length)return f(r,{code:p.too_small,minimum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),v;!this._def.rest&&r.data.length>this._def.items.length&&(f(r,{code:p.too_big,maximum:this._def.items.length,inclusive:!0,exact:!1,type:"array"}),n.dirty());let o=[...r.data].map((s,a)=>{let c=this._def.items[a]||this._def.rest;return c?c._parse(new K(r,s,r.path,a)):null}).filter(s=>!!s);return r.common.async?Promise.all(o).then(s=>j.mergeArray(n,s)):j.mergeArray(n,o)}get items(){return this._def.items}rest(e){return new t({...this._def,rest:e})}};ee.create=(t,e)=>{if(!Array.isArray(t))throw new Error("You must pass an array of schemas to z.tuple([ ... ])");return new ee({items:t,typeName:b.ZodTuple,rest:null,...k(e)})};var ct=class t extends T{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){let{status:n,ctx:r}=this._processInputParams(e);if(r.parsedType!==g.object)return f(r,{code:p.invalid_type,expected:g.object,received:r.parsedType}),v;let i=[],o=this._def.keyType,s=this._def.valueType;for(let a in r.data)i.push({key:o._parse(new K(r,a,r.path,a)),value:s._parse(new K(r,r.data[a],r.path,a)),alwaysSet:a in r.data});return r.common.async?j.mergeObjectAsync(n,i):j.mergeObjectSync(n,i)}get element(){return this._def.valueType}static create(e,n,r){return n instanceof T?new t({keyType:e,valueType:n,typeName:b.ZodRecord,...k(r)}):new t({keyType:le.create(),valueType:e,typeName:b.ZodRecord,...k(n)})}},He=class extends T{get keySchema(){return this._def.keyType}get valueSchema(){return this._def.valueType}_parse(e){let{status:n,ctx:r}=this._processInputParams(e);if(r.parsedType!==g.map)return f(r,{code:p.invalid_type,expected:g.map,received:r.parsedType}),v;let i=this._def.keyType,o=this._def.valueType,s=[...r.data.entries()].map(([a,c],l)=>({key:i._parse(new K(r,a,r.path,[l,"key"])),value:o._parse(new K(r,c,r.path,[l,"value"]))}));if(r.common.async){let a=new Map;return Promise.resolve().then(async()=>{for(let c of s){let l=await c.key,m=await c.value;if(l.status==="aborted"||m.status==="aborted")return v;(l.status==="dirty"||m.status==="dirty")&&n.dirty(),a.set(l.value,m.value)}return{status:n.value,value:a}})}else{let a=new Map;for(let c of s){let l=c.key,m=c.value;if(l.status==="aborted"||m.status==="aborted")return v;(l.status==="dirty"||m.status==="dirty")&&n.dirty(),a.set(l.value,m.value)}return{status:n.value,value:a}}}};He.create=(t,e,n)=>new He({valueType:e,keyType:t,typeName:b.ZodMap,...k(n)});var Fe=class t extends T{_parse(e){let{status:n,ctx:r}=this._processInputParams(e);if(r.parsedType!==g.set)return f(r,{code:p.invalid_type,expected:g.set,received:r.parsedType}),v;let i=this._def;i.minSize!==null&&r.data.size<i.minSize.value&&(f(r,{code:p.too_small,minimum:i.minSize.value,type:"set",inclusive:!0,exact:!1,message:i.minSize.message}),n.dirty()),i.maxSize!==null&&r.data.size>i.maxSize.value&&(f(r,{code:p.too_big,maximum:i.maxSize.value,type:"set",inclusive:!0,exact:!1,message:i.maxSize.message}),n.dirty());let o=this._def.valueType;function s(c){let l=new Set;for(let m of c){if(m.status==="aborted")return v;m.status==="dirty"&&n.dirty(),l.add(m.value)}return{status:n.value,value:l}}let a=[...r.data.values()].map((c,l)=>o._parse(new K(r,c,r.path,l)));return r.common.async?Promise.all(a).then(c=>s(c)):s(a)}min(e,n){return new t({...this._def,minSize:{value:e,message:h.toString(n)}})}max(e,n){return new t({...this._def,maxSize:{value:e,message:h.toString(n)}})}size(e,n){return this.min(e,n).max(e,n)}nonempty(e){return this.min(1,e)}};Fe.create=(t,e)=>new Fe({valueType:t,minSize:null,maxSize:null,typeName:b.ZodSet,...k(e)});var dt=class t extends T{constructor(){super(...arguments),this.validate=this.implement}_parse(e){let{ctx:n}=this._processInputParams(e);if(n.parsedType!==g.function)return f(n,{code:p.invalid_type,expected:g.function,received:n.parsedType}),v;function r(a,c){return Ze({data:a,path:n.path,errorMaps:[n.common.contextualErrorMap,n.schemaErrorMap,je(),oe].filter(l=>!!l),issueData:{code:p.invalid_arguments,argumentsError:c}})}function i(a,c){return Ze({data:a,path:n.path,errorMaps:[n.common.contextualErrorMap,n.schemaErrorMap,je(),oe].filter(l=>!!l),issueData:{code:p.invalid_return_type,returnTypeError:c}})}let o={errorMap:n.common.contextualErrorMap},s=n.data;if(this._def.returns instanceof ue){let a=this;return O(async function(...c){let l=new H([]),m=await a._def.args.parseAsync(c,o).catch(x=>{throw l.addIssue(r(c,x)),l}),w=await Reflect.apply(s,this,m);return await a._def.returns._def.type.parseAsync(w,o).catch(x=>{throw l.addIssue(i(w,x)),l})})}else{let a=this;return O(function(...c){let l=a._def.args.safeParse(c,o);if(!l.success)throw new H([r(c,l.error)]);let m=Reflect.apply(s,this,l.data),w=a._def.returns.safeParse(m,o);if(!w.success)throw new H([i(m,w.error)]);return w.data})}}parameters(){return this._def.args}returnType(){return this._def.returns}args(...e){return new t({...this._def,args:ee.create(e).rest(ae.create())})}returns(e){return new t({...this._def,returns:e})}implement(e){return this.parse(e)}strictImplement(e){return this.parse(e)}static create(e,n,r){return new t({args:e||ee.create([]).rest(ae.create()),returns:n||ae.create(),typeName:b.ZodFunction,...k(r)})}},Ce=class extends T{get schema(){return this._def.getter()}_parse(e){let{ctx:n}=this._processInputParams(e);return this._def.getter()._parse({data:n.data,path:n.path,parent:n})}};Ce.create=(t,e)=>new Ce({getter:t,typeName:b.ZodLazy,...k(e)});var Ee=class extends T{_parse(e){if(e.data!==this._def.value){let n=this._getOrReturnCtx(e);return f(n,{received:n.data,code:p.invalid_literal,expected:this._def.value}),v}return{status:"valid",value:e.data}}get value(){return this._def.value}};Ee.create=(t,e)=>new Ee({value:t,typeName:b.ZodLiteral,...k(e)});function tn(t,e){return new Se({values:t,typeName:b.ZodEnum,...k(e)})}var Se=class t extends T{_parse(e){if(typeof e.data!="string"){let n=this._getOrReturnCtx(e),r=this._def.values;return f(n,{expected:C.joinValues(r),received:n.parsedType,code:p.invalid_type}),v}if(this._cache||(this._cache=new Set(this._def.values)),!this._cache.has(e.data)){let n=this._getOrReturnCtx(e),r=this._def.values;return f(n,{received:n.data,code:p.invalid_enum_value,options:r}),v}return O(e.data)}get options(){return this._def.values}get enum(){let e={};for(let n of this._def.values)e[n]=n;return e}get Values(){let e={};for(let n of this._def.values)e[n]=n;return e}get Enum(){let e={};for(let n of this._def.values)e[n]=n;return e}extract(e,n=this._def){return t.create(e,{...this._def,...n})}exclude(e,n=this._def){return t.create(this.options.filter(r=>!e.includes(r)),{...this._def,...n})}};Se.create=tn;var _e=class extends T{_parse(e){let n=C.getValidEnumValues(this._def.values),r=this._getOrReturnCtx(e);if(r.parsedType!==g.string&&r.parsedType!==g.number){let i=C.objectValues(n);return f(r,{expected:C.joinValues(i),received:r.parsedType,code:p.invalid_type}),v}if(this._cache||(this._cache=new Set(C.getValidEnumValues(this._def.values))),!this._cache.has(e.data)){let i=C.objectValues(n);return f(r,{received:r.data,code:p.invalid_enum_value,options:i}),v}return O(e.data)}get enum(){return this._def.values}};_e.create=(t,e)=>new _e({values:t,typeName:b.ZodNativeEnum,...k(e)});var ue=class extends T{unwrap(){return this._def.type}_parse(e){let{ctx:n}=this._processInputParams(e);if(n.parsedType!==g.promise&&n.common.async===!1)return f(n,{code:p.invalid_type,expected:g.promise,received:n.parsedType}),v;let r=n.parsedType===g.promise?n.data:Promise.resolve(n.data);return O(r.then(i=>this._def.type.parseAsync(i,{path:n.path,errorMap:n.common.contextualErrorMap})))}};ue.create=(t,e)=>new ue({type:t,typeName:b.ZodPromise,...k(e)});var W=class extends T{innerType(){return this._def.schema}sourceType(){return this._def.schema._def.typeName===b.ZodEffects?this._def.schema.sourceType():this._def.schema}_parse(e){let{status:n,ctx:r}=this._processInputParams(e),i=this._def.effect||null,o={addIssue:s=>{f(r,s),s.fatal?n.abort():n.dirty()},get path(){return r.path}};if(o.addIssue=o.addIssue.bind(o),i.type==="preprocess"){let s=i.transform(r.data,o);if(r.common.async)return Promise.resolve(s).then(async a=>{if(n.value==="aborted")return v;let c=await this._def.schema._parseAsync({data:a,path:r.path,parent:r});return c.status==="aborted"?v:c.status==="dirty"?ge(c.value):n.value==="dirty"?ge(c.value):c});{if(n.value==="aborted")return v;let a=this._def.schema._parseSync({data:s,path:r.path,parent:r});return a.status==="aborted"?v:a.status==="dirty"?ge(a.value):n.value==="dirty"?ge(a.value):a}}if(i.type==="refinement"){let s=a=>{let c=i.refinement(a,o);if(r.common.async)return Promise.resolve(c);if(c instanceof Promise)throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead.");return a};if(r.common.async===!1){let a=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});return a.status==="aborted"?v:(a.status==="dirty"&&n.dirty(),s(a.value),{status:n.value,value:a.value})}else return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(a=>a.status==="aborted"?v:(a.status==="dirty"&&n.dirty(),s(a.value).then(()=>({status:n.value,value:a.value}))))}if(i.type==="transform")if(r.common.async===!1){let s=this._def.schema._parseSync({data:r.data,path:r.path,parent:r});if(!de(s))return v;let a=i.transform(s.value,o);if(a instanceof Promise)throw new Error("Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.");return{status:n.value,value:a}}else return this._def.schema._parseAsync({data:r.data,path:r.path,parent:r}).then(s=>de(s)?Promise.resolve(i.transform(s.value,o)).then(a=>({status:n.value,value:a})):v);C.assertNever(i)}};W.create=(t,e,n)=>new W({schema:t,typeName:b.ZodEffects,effect:e,...k(n)});W.createWithPreprocess=(t,e,n)=>new W({schema:e,effect:{type:"preprocess",transform:t},typeName:b.ZodEffects,...k(n)});var B=class extends T{_parse(e){return this._getType(e)===g.undefined?O(void 0):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}};B.create=(t,e)=>new B({innerType:t,typeName:b.ZodOptional,...k(e)});var te=class extends T{_parse(e){return this._getType(e)===g.null?O(null):this._def.innerType._parse(e)}unwrap(){return this._def.innerType}};te.create=(t,e)=>new te({innerType:t,typeName:b.ZodNullable,...k(e)});var Pe=class extends T{_parse(e){let{ctx:n}=this._processInputParams(e),r=n.data;return n.parsedType===g.undefined&&(r=this._def.defaultValue()),this._def.innerType._parse({data:r,path:n.path,parent:n})}removeDefault(){return this._def.innerType}};Pe.create=(t,e)=>new Pe({innerType:t,typeName:b.ZodDefault,defaultValue:typeof e.default=="function"?e.default:()=>e.default,...k(e)});var Me=class extends T{_parse(e){let{ctx:n}=this._processInputParams(e),r={...n,common:{...n.common,issues:[]}},i=this._def.innerType._parse({data:r.data,path:r.path,parent:{...r}});return Oe(i)?i.then(o=>({status:"valid",value:o.status==="valid"?o.value:this._def.catchValue({get error(){return new H(r.common.issues)},input:r.data})})):{status:"valid",value:i.status==="valid"?i.value:this._def.catchValue({get error(){return new H(r.common.issues)},input:r.data})}}removeCatch(){return this._def.innerType}};Me.create=(t,e)=>new Me({innerType:t,typeName:b.ZodCatch,catchValue:typeof e.catch=="function"?e.catch:()=>e.catch,...k(e)});var Ve=class extends T{_parse(e){if(this._getType(e)!==g.nan){let r=this._getOrReturnCtx(e);return f(r,{code:p.invalid_type,expected:g.nan,received:r.parsedType}),v}return{status:"valid",value:e.data}}};Ve.create=t=>new Ve({typeName:b.ZodNaN,...k(t)});var ms=Symbol("zod_brand"),Je=class extends T{_parse(e){let{ctx:n}=this._processInputParams(e),r=n.data;return this._def.type._parse({data:r,path:n.path,parent:n})}unwrap(){return this._def.type}},Ye=class t extends T{_parse(e){let{status:n,ctx:r}=this._processInputParams(e);if(r.common.async)return(async()=>{let o=await this._def.in._parseAsync({data:r.data,path:r.path,parent:r});return o.status==="aborted"?v:o.status==="dirty"?(n.dirty(),ge(o.value)):this._def.out._parseAsync({data:o.value,path:r.path,parent:r})})();{let i=this._def.in._parseSync({data:r.data,path:r.path,parent:r});return i.status==="aborted"?v:i.status==="dirty"?(n.dirty(),{status:"dirty",value:i.value}):this._def.out._parseSync({data:i.value,path:r.path,parent:r})}}static create(e,n){return new t({in:e,out:n,typeName:b.ZodPipeline})}},Ie=class extends T{_parse(e){let n=this._def.innerType._parse(e),r=i=>(de(i)&&(i.value=Object.freeze(i.value)),i);return Oe(n)?n.then(i=>r(i)):r(n)}unwrap(){return this._def.innerType}};Ie.create=(t,e)=>new Ie({innerType:t,typeName:b.ZodReadonly,...k(e)});function Yt(t,e){let n=typeof t=="function"?t(e):typeof t=="string"?{message:t}:t;return typeof n=="string"?{message:n}:n}function nn(t,e={},n){return t?pe.create().superRefine((r,i)=>{let o=t(r);if(o instanceof Promise)return o.then(s=>{if(!s){let a=Yt(e,r),c=a.fatal??n??!0;i.addIssue({code:"custom",...a,fatal:c})}});if(!o){let s=Yt(e,r),a=s.fatal??n??!0;i.addIssue({code:"custom",...s,fatal:a})}}):pe.create()}var fs={object:F.lazycreate},b;(function(t){t.ZodString="ZodString",t.ZodNumber="ZodNumber",t.ZodNaN="ZodNaN",t.ZodBigInt="ZodBigInt",t.ZodBoolean="ZodBoolean",t.ZodDate="ZodDate",t.ZodSymbol="ZodSymbol",t.ZodUndefined="ZodUndefined",t.ZodNull="ZodNull",t.ZodAny="ZodAny",t.ZodUnknown="ZodUnknown",t.ZodNever="ZodNever",t.ZodVoid="ZodVoid",t.ZodArray="ZodArray",t.ZodObject="ZodObject",t.ZodUnion="ZodUnion",t.ZodDiscriminatedUnion="ZodDiscriminatedUnion",t.ZodIntersection="ZodIntersection",t.ZodTuple="ZodTuple",t.ZodRecord="ZodRecord",t.ZodMap="ZodMap",t.ZodSet="ZodSet",t.ZodFunction="ZodFunction",t.ZodLazy="ZodLazy",t.ZodLiteral="ZodLiteral",t.ZodEnum="ZodEnum",t.ZodEffects="ZodEffects",t.ZodNativeEnum="ZodNativeEnum",t.ZodOptional="ZodOptional",t.ZodNullable="ZodNullable",t.ZodDefault="ZodDefault",t.ZodCatch="ZodCatch",t.ZodPromise="ZodPromise",t.ZodBranded="ZodBranded",t.ZodPipeline="ZodPipeline",t.ZodReadonly="ZodReadonly"})(b||(b={}));var gs=(t,e={message:`Input not instance of ${t.name}`})=>nn(n=>n instanceof t,e),rn=le.create,on=he.create,hs=Ve.create,ys=ye.create,sn=ve.create,vs=be.create,bs=Ne.create,ws=we.create,xs=xe.create,ks=pe.create,Ts=ae.create,Cs=J.create,Es=qe.create,Ss=ce.create,_s=F.create,Ps=F.strictCreate,Ms=ke.create,Is=at.create,As=Te.create,Rs=ee.create,Ls=ct.create,Ds=He.create,js=Fe.create,Os=dt.create,$s=Ce.create,Ns=Ee.create,qs=Se.create,Hs=_e.create,Fs=ue.create,Vs=W.create,zs=B.create,Us=te.create,Gs=W.createWithPreprocess,Bs=Ye.create,Ks=()=>rn().optional(),Ws=()=>on().optional(),Zs=()=>sn().optional(),Js={string:(t=>le.create({...t,coerce:!0})),number:(t=>he.create({...t,coerce:!0})),boolean:(t=>ve.create({...t,coerce:!0})),bigint:(t=>ye.create({...t,coerce:!0})),date:(t=>be.create({...t,coerce:!0}))};var Ys=v;var ne={QUERY:"mcp-query",RESPONSE:"mcp-response",PING:"ping",PONG:"pong"},Qs=A.object({type:A.literal(ne.QUERY),id:A.string(),data:A.object({method:A.string(),params:A.record(A.unknown()).optional()})}),Xs=A.object({type:A.literal(ne.RESPONSE),id:A.string(),data:A.object({success:A.boolean(),result:A.unknown().optional(),error:A.string().optional()})}),ea=A.object({type:A.literal(ne.PING),id:A.string()}),ta=A.object({type:A.literal(ne.PONG),id:A.string()}),yc=A.discriminatedUnion("type",[Qs,Xs,ea,ta]);var lt=class{constructor(e,n){this.port=e;this.handler=n}ws=null;reconnectAttempts=0;reconnectTimer=null;status="disconnected";setStatus(e){this.status=e,Hooks.call("familiar:mcpStatus",e)}connect(){let e=`ws://localhost:${String(this.port)}`;console.log(`${y} | Connecting to ${e}`),this.setStatus("connecting"),this.ws=new WebSocket(e),this.ws.addEventListener("open",()=>{console.log(`${y} | Connected to MCP server`),this.reconnectAttempts=0,this.setStatus("connected"),ui.notifications?.info("Foundry MCP: Connected to AI bridge")}),this.ws.addEventListener("message",n=>{this.handleMessage(n.data)}),this.ws.addEventListener("close",()=>{console.log(`${y} | Disconnected from MCP server`),this.ws=null,this.setStatus("disconnected"),this.scheduleReconnect()}),this.ws.addEventListener("error",n=>{console.error(`${y} | WebSocket error`,n)})}disconnect(){this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(1e3,"Module shutting down"),this.ws=null)}async handleMessage(e){let n;try{n=JSON.parse(e)}catch{console.warn(`${y} | Invalid JSON received`);return}let r=n;if(r.type===ne.PING){this.send({type:ne.PONG,id:r.id});return}if(r.type===ne.QUERY&&r.data)try{let i=await this.handler(r.data.method,r.data.params);this.send({type:ne.RESPONSE,id:r.id,data:{success:!0,result:i}})}catch(i){let o=i instanceof Error?i.message:"Unknown error";console.error(`${y} | Query error [${r.data.method}]:`,i),this.send({type:ne.RESPONSE,id:r.id,data:{success:!1,error:o}})}}send(e){this.ws?.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(e))}scheduleReconnect(){if(this.reconnectAttempts>=5){console.warn(`${y} | Max reconnect attempts reached (${String(5)})`),ui.notifications?.warn("Foundry MCP: Could not reconnect to AI bridge. Is the MCP server running?");return}let e=Math.min(1e3*2**this.reconnectAttempts,3e4);this.reconnectAttempts++,console.log(`${y} | Reconnecting in ${String(e)}ms (attempt ${String(this.reconnectAttempts)}/${String(5)})`),this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},e)}};var D=t=>({Authorization:`Bearer ${t}`,"Content-Type":"application/json"}),V=t=>{let e=t;return!e.data||!Array.isArray(e.data)?[]:e.data.map(n=>({id:n.id,name:n.id}))};var an={xai:{id:"xai",name:"xAI (Grok)",description:"Grok 3 and Grok 3 Mini by xAI",chatEndpoint:"https://api.x.ai/v1/chat/completions",modelsEndpoint:"https://api.x.ai/v1/models",apiKeyPlaceholder:"xai-...",defaultModel:"grok-3-latest",requiresApiKey:!0,staticModels:[{id:"grok-3-latest",name:"Grok 3"},{id:"grok-3-mini-latest",name:"Grok 3 Mini"},{id:"grok-3-fast-latest",name:"Grok 3 Fast"}],buildHeaders:D,parseModelsResponse:V},cohere:{id:"cohere",name:"Cohere",description:"Command R+ \u2014 strong tool use and RAG",chatEndpoint:"https://api.cohere.com/v2/chat",modelsEndpoint:"https://api.cohere.com/v2/models",apiKeyPlaceholder:"...",defaultModel:"command-r-plus",requiresApiKey:!0,staticModels:[{id:"command-r-plus",name:"Command R+"},{id:"command-r",name:"Command R"},{id:"command-a-03-2025",name:"Command A"}],buildHeaders:D,parseModelsResponse:V},perplexity:{id:"perplexity",name:"Perplexity",description:"Sonar models \u2014 AI search with real-time web access",chatEndpoint:"https://api.perplexity.ai/chat/completions",modelsEndpoint:null,apiKeyPlaceholder:"pplx-...",defaultModel:"sonar-pro",requiresApiKey:!0,staticModels:[{id:"sonar-pro",name:"Sonar Pro"},{id:"sonar",name:"Sonar"},{id:"sonar-deep-research",name:"Sonar Deep Research"}],buildHeaders:D,parseModelsResponse:()=>[]},fireworks:{id:"fireworks",name:"Fireworks AI",description:"Fast inference \u2014 Llama, Mixtral, and more",chatEndpoint:"https://api.fireworks.ai/inference/v1/chat/completions",modelsEndpoint:"https://api.fireworks.ai/inference/v1/models",apiKeyPlaceholder:"fw_...",defaultModel:"accounts/fireworks/models/llama-v3p3-70b-instruct",requiresApiKey:!0,staticModels:[{id:"accounts/fireworks/models/llama-v3p3-70b-instruct",name:"Llama 3.3 70B"},{id:"accounts/fireworks/models/deepseek-v3",name:"DeepSeek V3"},{id:"accounts/fireworks/models/qwen2p5-72b-instruct",name:"Qwen 2.5 72B"}],buildHeaders:D,parseModelsResponse:V},cerebras:{id:"cerebras",name:"Cerebras",description:"Ultra-fast inference \u2014 fastest Llama speeds available",chatEndpoint:"https://api.cerebras.ai/v1/chat/completions",modelsEndpoint:"https://api.cerebras.ai/v1/models",apiKeyPlaceholder:"csk-...",defaultModel:"llama-3.3-70b",requiresApiKey:!0,staticModels:[{id:"llama-3.3-70b",name:"Llama 3.3 70B"},{id:"llama-3.1-8b",name:"Llama 3.1 8B"},{id:"qwen-2.5-32b",name:"Qwen 2.5 32B"}],buildHeaders:D,parseModelsResponse:V},sambanova:{id:"sambanova",name:"SambaNova",description:"Fast inference with free tier \u2014 Llama, DeepSeek",chatEndpoint:"https://api.sambanova.ai/v1/chat/completions",modelsEndpoint:"https://api.sambanova.ai/v1/models",apiKeyPlaceholder:"...",defaultModel:"Meta-Llama-3.3-70B-Instruct",requiresApiKey:!0,staticModels:[{id:"Meta-Llama-3.3-70B-Instruct",name:"Llama 3.3 70B"},{id:"DeepSeek-V3-0324",name:"DeepSeek V3"},{id:"DeepSeek-R1",name:"DeepSeek R1"},{id:"Qwen2.5-72B-Instruct",name:"Qwen 2.5 72B"}],buildHeaders:D,parseModelsResponse:V},lmstudio:{id:"lmstudio",name:"LM Studio (Local)",description:"Run models locally via LM Studio \u2014 free, private",chatEndpoint:"http://localhost:1234/v1/chat/completions",modelsEndpoint:"http://localhost:1234/v1/models",apiKeyPlaceholder:"Not required",defaultModel:"local-model",requiresApiKey:!1,staticModels:[{id:"local-model",name:"Loaded Model"}],buildHeaders:()=>({"Content-Type":"application/json"}),parseModelsResponse:V},ollama:{id:"ollama",name:"Ollama (Local)",description:"Run models locally \u2014 free, private, no API key needed",chatEndpoint:"http://localhost:11434/v1/chat/completions",modelsEndpoint:"http://localhost:11434/api/tags",apiKeyPlaceholder:"Not required",defaultModel:"llama3.2",requiresApiKey:!1,staticModels:[{id:"llama3.2",name:"Llama 3.2"},{id:"mistral",name:"Mistral"},{id:"gemma2",name:"Gemma 2"},{id:"qwen2.5",name:"Qwen 2.5"}],buildHeaders:()=>({"Content-Type":"application/json"}),parseModelsResponse:t=>{let e=t;return!e.models||!Array.isArray(e.models)?[]:e.models.map(n=>({id:n.name,name:n.name}))}}};var ze={openrouter:{id:"openrouter",name:"OpenRouter (Recommended)",description:"One key, 300+ models \u2014 Claude, GPT-4, Gemini, Llama, and more",chatEndpoint:"https://openrouter.ai/api/v1/chat/completions",modelsEndpoint:"https://openrouter.ai/api/v1/models",apiKeyPlaceholder:"sk-or-v1-...",defaultModel:"anthropic/claude-sonnet-4",requiresApiKey:!0,staticModels:[{id:"anthropic/claude-sonnet-4",name:"Claude Sonnet 4"},{id:"anthropic/claude-haiku-4",name:"Claude Haiku 4"},{id:"openai/gpt-4o",name:"GPT-4o"},{id:"openai/gpt-4.1",name:"GPT-4.1"},{id:"google/gemini-2.5-pro-preview",name:"Gemini 2.5 Pro"},{id:"google/gemini-2.5-flash-preview",name:"Gemini 2.5 Flash"},{id:"meta-llama/llama-4-maverick",name:"Llama 4 Maverick"},{id:"deepseek/deepseek-chat-v3",name:"DeepSeek V3"}],buildHeaders:t=>({Authorization:`Bearer ${t}`,"Content-Type":"application/json","HTTP-Referer":"https://github.com/Ryanjansen92/familiar","X-Title":"Familiar - AI DM Assistant"}),parseModelsResponse:V},anthropic:{id:"anthropic",name:"Anthropic",description:"Claude models \u2014 Opus, Sonnet, Haiku",chatEndpoint:"https://api.anthropic.com/v1/messages",modelsEndpoint:null,apiKeyPlaceholder:"sk-ant-api03-...",defaultModel:"claude-sonnet-4-20250514",requiresApiKey:!0,staticModels:[{id:"claude-opus-4-20250514",name:"Claude Opus 4"},{id:"claude-sonnet-4-20250514",name:"Claude Sonnet 4"},{id:"claude-haiku-4-20250514",name:"Claude Haiku 4"}],buildHeaders:t=>({"x-api-key":t,"Content-Type":"application/json","anthropic-version":"2023-06-01","anthropic-dangerous-direct-browser-access":"true"}),parseModelsResponse:()=>[]},openai:{id:"openai",name:"OpenAI",description:"GPT-4o, GPT-4.1, o3, o4-mini",chatEndpoint:"https://api.openai.com/v1/chat/completions",modelsEndpoint:"https://api.openai.com/v1/models",apiKeyPlaceholder:"sk-...",defaultModel:"gpt-4o",requiresApiKey:!0,staticModels:[{id:"gpt-4o",name:"GPT-4o"},{id:"gpt-4.1",name:"GPT-4.1"},{id:"gpt-4o-mini",name:"GPT-4o Mini"},{id:"o3",name:"o3"},{id:"o4-mini",name:"o4-mini"}],buildHeaders:D,parseModelsResponse:V},google:{id:"google",name:"Google",description:"Gemini 2.5 Pro, Gemini 2.5 Flash",chatEndpoint:"https://generativelanguage.googleapis.com/v1beta/chat/completions",modelsEndpoint:"https://generativelanguage.googleapis.com/v1beta/models",apiKeyPlaceholder:"AIza...",defaultModel:"gemini-2.5-flash",requiresApiKey:!0,staticModels:[{id:"gemini-2.5-pro",name:"Gemini 2.5 Pro"},{id:"gemini-2.5-flash",name:"Gemini 2.5 Flash"}],buildHeaders:t=>({"Content-Type":"application/json","x-goog-api-key":t}),parseModelsResponse:t=>{let e=t;return!e.models||!Array.isArray(e.models)?[]:e.models.filter(n=>(n.supportedGenerationMethods?.includes("generateContent")??!1)||(n.supportedGenerationMethods?.includes("streamGenerateContent")??!1)).map(n=>({id:n.name.replace("models/",""),name:n.displayName}))}},groq:{id:"groq",name:"Groq",description:"Llama, Mistral, Gemma \u2014 fast and cheap",chatEndpoint:"https://api.groq.com/openai/v1/chat/completions",modelsEndpoint:"https://api.groq.com/openai/v1/models",apiKeyPlaceholder:"gsk_...",defaultModel:"llama-3.3-70b-versatile",requiresApiKey:!0,staticModels:[{id:"llama-3.3-70b-versatile",name:"Llama 3.3 70B"},{id:"llama-3.1-8b-instant",name:"Llama 3.1 8B"},{id:"gemma2-9b-it",name:"Gemma 2 9B"},{id:"mixtral-8x7b-32768",name:"Mixtral 8x7B"}],buildHeaders:D,parseModelsResponse:V},mistral:{id:"mistral",name:"Mistral",description:"Mistral Large, Medium, Small",chatEndpoint:"https://api.mistral.ai/v1/chat/completions",modelsEndpoint:"https://api.mistral.ai/v1/models",apiKeyPlaceholder:"...",defaultModel:"mistral-large-latest",requiresApiKey:!0,staticModels:[{id:"mistral-large-latest",name:"Mistral Large"},{id:"mistral-medium-latest",name:"Mistral Medium"},{id:"mistral-small-latest",name:"Mistral Small"}],buildHeaders:D,parseModelsResponse:V},togetherai:{id:"togetherai",name:"Together AI",description:"Open source models \u2014 Llama, Qwen, DeepSeek",chatEndpoint:"https://api.together.xyz/v1/chat/completions",modelsEndpoint:"https://api.together.xyz/v1/models",apiKeyPlaceholder:"...",defaultModel:"meta-llama/Llama-3.3-70B-Instruct-Turbo",requiresApiKey:!0,staticModels:[{id:"meta-llama/Llama-3.3-70B-Instruct-Turbo",name:"Llama 3.3 70B Instruct Turbo"},{id:"Qwen/Qwen2.5-72B-Instruct-Turbo",name:"Qwen 2.5 72B Instruct"},{id:"deepseek-ai/DeepSeek-V3",name:"DeepSeek V3"}],buildHeaders:D,parseModelsResponse:V},deepseek:{id:"deepseek",name:"DeepSeek",description:"DeepSeek V3 and R1 \u2014 powerful and very affordable",chatEndpoint:"https://api.deepseek.com/chat/completions",modelsEndpoint:"https://api.deepseek.com/models",apiKeyPlaceholder:"sk-...",defaultModel:"deepseek-chat",requiresApiKey:!0,staticModels:[{id:"deepseek-chat",name:"DeepSeek V3 (Chat)"},{id:"deepseek-reasoner",name:"DeepSeek R1 (Reasoning)"}],buildHeaders:D,parseModelsResponse:V},...an},ia=Object.keys(ze);function Z(t){let e=ze[t],n=ze.openrouter;return e??n}var Ue={elevenlabs:{id:"elevenlabs",name:"ElevenLabs (Recommended)",description:"10,000+ voices, best quality TTS \u2014 perfect for NPC dialogue and sound effects",ttsEndpoint:"https://api.elevenlabs.io/v1/text-to-speech",voicesEndpoint:"https://api.elevenlabs.io/v1/voices",apiKeyPlaceholder:"sk_...",requiresApiKey:!0,defaultVoice:"JBFqnCBsd6RMkjVDRZzb",staticVoices:[{id:"JBFqnCBsd6RMkjVDRZzb",name:"George (Warm Narrator)"},{id:"TX3LPaxmHKxFdv7VOQHJ",name:"Liam (Articulate)"},{id:"XB0fDUnXU5powFXDhCwa",name:"Charlotte (Seductive)"},{id:"pFZP5JQG7iQjIQuC4Bku",name:"Lily (Warm)"},{id:"nPczCjzI2devNBz1zQrb",name:"Brian (Deep)"},{id:"N2lVS1w4EtoT3dr4eOWO",name:"Callum (Intense)"},{id:"cjVigY5qzO86Huf0OWal",name:"Eric (Friendly)"},{id:"iP95p4xoKVk53GoZ742B",name:"Chris (Casual)"},{id:"onwK4e9ZLuTAKqWW03F9",name:"Daniel (Authoritative)"},{id:"EXAVITQu4vr4xnSDxMaL",name:"Sarah (Soft)"}],buildHeaders:t=>({"xi-api-key":t,"Content-Type":"application/json"}),parseVoicesResponse:t=>{let e=t;return!e.voices||!Array.isArray(e.voices)?[]:e.voices.map(n=>({id:n.voice_id,name:n.category?`${n.name} (${n.category})`:n.name}))}},cartesia:{id:"cartesia",name:"Cartesia (Sonic)",description:"40ms latency \u2014 fastest TTS, perfect for real-time NPC dialogue",ttsEndpoint:"https://api.cartesia.ai/tts/bytes",voicesEndpoint:"https://api.cartesia.ai/voices",apiKeyPlaceholder:"...",requiresApiKey:!0,defaultVoice:"a0e99841-438c-4a64-b679-ae501e7d6091",staticVoices:[{id:"a0e99841-438c-4a64-b679-ae501e7d6091",name:"Barbershop Man"},{id:"79a125e8-cd45-4c13-8a67-188112f4dd22",name:"British Lady"},{id:"87748186-23bb-4571-8b62-a61ce5ec610e",name:"Calm Lady"},{id:"ee7ea9f8-c0c1-498c-9f43-e0a2e8b787b3",name:"Wise Man"},{id:"f114a467-c40a-4db8-964d-aaba89cd08fa",name:"Wise Guide Man"}],buildHeaders:t=>({"X-API-Key":t,"Cartesia-Version":"2024-06-10","Content-Type":"application/json"}),parseVoicesResponse:t=>{let e=t;return Array.isArray(e)?e.map(n=>({id:n.id,name:n.name})):[]}},playht:{id:"playht",name:"PlayHT",description:"600+ voices, 140+ languages \u2014 great variety at lower cost",ttsEndpoint:"https://api.play.ht/api/v2/tts/stream",voicesEndpoint:"https://api.play.ht/api/v2/voices",apiKeyPlaceholder:"...",requiresApiKey:!0,defaultVoice:"s3://voice-cloning-zero-shot/775ae416-49bb-4fb6-bd45-740f205d3672/original/manifest.json",staticVoices:[{id:"s3://voice-cloning-zero-shot/775ae416-49bb-4fb6-bd45-740f205d3672/original/manifest.json",name:"Michael (American Male)"},{id:"s3://voice-cloning-zero-shot/e040bd1b-f190-4bdb-83f0-75ef85b18f84/original/manifest.json",name:"Jennifer (American Female)"}],buildHeaders:t=>({Authorization:`Bearer ${t}`,"Content-Type":"application/json","X-USER-ID":"familiar"}),parseVoicesResponse:t=>{let e=t;return Array.isArray(e)?e.map(n=>({id:n.id,name:n.name})):[]}},openai_tts:{id:"openai_tts",name:"OpenAI TTS",description:"GPT-4o-mini TTS \u2014 uses your existing OpenAI key",ttsEndpoint:"https://api.openai.com/v1/audio/speech",voicesEndpoint:null,apiKeyPlaceholder:"sk-...",requiresApiKey:!0,defaultVoice:"nova",staticVoices:[{id:"alloy",name:"Alloy (Neutral)"},{id:"ash",name:"Ash (Warm Male)"},{id:"ballad",name:"Ballad (Soft)"},{id:"coral",name:"Coral (Warm Female)"},{id:"echo",name:"Echo (Male)"},{id:"fable",name:"Fable (Storyteller)"},{id:"nova",name:"Nova (Friendly Female)"},{id:"onyx",name:"Onyx (Deep Male)"},{id:"sage",name:"Sage (Wise)"},{id:"shimmer",name:"Shimmer (Bright Female)"}],buildHeaders:D,parseVoicesResponse:()=>[]}},oa=Object.keys(Ue);function me(t){let e=Ue[t],n=Ue.elevenlabs;return e??n}var Ge={openai_image:{id:"openai_image",name:"OpenAI (DALL-E / GPT Image)",description:"GPT Image \u2014 best quality, uses your existing OpenAI key",generateEndpoint:"https://api.openai.com/v1/images/generations",apiKeyPlaceholder:"sk-...",requiresApiKey:!0,defaultModel:"gpt-image-1",staticModels:[{id:"gpt-image-1",name:"GPT Image 1 (Best Quality)"},{id:"dall-e-3",name:"DALL-E 3"},{id:"dall-e-2",name:"DALL-E 2"}],buildHeaders:D},stability:{id:"stability",name:"Stability AI",description:"Stable Diffusion 3.5 + Stable Audio \u2014 images and sound effects",generateEndpoint:"https://api.stability.ai/v2beta/stable-image/generate/core",apiKeyPlaceholder:"sk-...",requiresApiKey:!0,defaultModel:"core",staticModels:[{id:"ultra",name:"Stable Image Ultra (Best)"},{id:"core",name:"Stable Image Core (Fast)"},{id:"sd3",name:"Stable Diffusion 3.5"}],buildHeaders:D},falai:{id:"falai",name:"fal.ai (Flux)",description:"Flux models \u2014 fast, cheap, great for battlemaps and tokens",generateEndpoint:"https://fal.run/fal-ai/flux/dev",apiKeyPlaceholder:"(UUID key)",requiresApiKey:!0,defaultModel:"fal-ai/flux/dev",staticModels:[{id:"fal-ai/flux-pro/v1.1-ultra",name:"Flux Pro 1.1 Ultra (Best)"},{id:"fal-ai/flux-pro/v1.1",name:"Flux Pro 1.1"},{id:"fal-ai/flux/dev",name:"Flux Dev (Good & Fast)"},{id:"fal-ai/flux/schnell",name:"Flux Schnell (Fastest)"}],buildHeaders:t=>({Authorization:`Key ${t}`,"Content-Type":"application/json"})},leonardo:{id:"leonardo",name:"Leonardo AI",description:"Fantasy art specialization \u2014 great for D&D character portraits and tokens",generateEndpoint:"https://cloud.leonardo.ai/api/rest/v1/generations",apiKeyPlaceholder:"...",requiresApiKey:!0,defaultModel:"auto",staticModels:[{id:"auto",name:"Auto (Best Match)"},{id:"leonardo-phoenix",name:"Leonardo Phoenix"},{id:"leonardo-lightning",name:"Leonardo Lightning (Fast)"}],buildHeaders:D},replicate:{id:"replicate",name:"Replicate",description:"Custom & fine-tuned models \u2014 200+ community image models",generateEndpoint:"https://api.replicate.com/v1/predictions",apiKeyPlaceholder:"r8_...",requiresApiKey:!0,defaultModel:"black-forest-labs/flux-1.1-pro",staticModels:[{id:"black-forest-labs/flux-1.1-pro",name:"Flux 1.1 Pro"},{id:"black-forest-labs/flux-schnell",name:"Flux Schnell"},{id:"stability-ai/sdxl",name:"SDXL"}],buildHeaders:D}},sa=Object.keys(Ge);function Ae(t){let e=Ge[t],n=Ge.openai_image;return e??n}var pt={"mcp-claude-desktop":{id:"mcp-claude-desktop",name:"Claude Desktop \u2014 Your Subscription (Pro / Max / Team)",description:"Use Claude Desktop with your existing subscription. No API key needed \u2014 124 tools available.",icon:"fas fa-wand-sparkles",appName:"Claude Desktop",appDownloadUrl:"https://claude.ai/download",setupMode:"config-file",configPaths:{win32:"%APPDATA%\\Claude\\claude_desktop_config.json",darwin:"~/Library/Application Support/Claude/claude_desktop_config.json",linux:"~/.config/Claude/claude_desktop_config.json"},configSnippet:{mcpServers:{familiar:{command:"npx",args:["-y","familiar-vtt"]}}},openInstructions:{win32:"Open File Explorer \u2192 paste in the address bar: %APPDATA%\\Claude \u2192 open claude_desktop_config.json with Notepad",darwin:"Open Finder \u2192 Go \u2192 Go to Folder \u2192 paste: ~/Library/Application Support/Claude",linux:"Open a terminal \u2192 nano ~/.config/Claude/claude_desktop_config.json"}},"mcp-claude-code":{id:"mcp-claude-code",name:"Claude Code (CLI) \u2014 Your Subscription (Pro / Max / Team)",description:"Use Claude Code in your terminal with your existing subscription. No API key needed \u2014 124 tools via the command line.",icon:"fas fa-terminal",appName:"Claude Code",appDownloadUrl:"https://code.claude.com/docs/en/setup",setupMode:"cli-command",cliCommand:"claude mcp add --scope user familiar -- npx -y familiar-vtt"},"mcp-gemini-cli":{id:"mcp-gemini-cli",name:"Gemini CLI \u2014 Your Subscription (Google)",description:"Use Gemini CLI with your Google account. No API key needed \u2014 124 tools via the command line.",icon:"fas fa-terminal",appName:"Gemini CLI",appDownloadUrl:"https://github.com/google-gemini/gemini-cli",setupMode:"config-file",configPaths:{win32:"%USERPROFILE%\\.gemini\\settings.json",darwin:"~/.gemini/settings.json",linux:"~/.gemini/settings.json"},configSnippet:{mcpServers:{familiar:{command:"npx",args:["-y","familiar-vtt"]}}},openInstructions:{win32:"Open File Explorer \u2192 paste in the address bar: %USERPROFILE%\\.gemini \u2192 open settings.json with Notepad",darwin:"Open Finder \u2192 Go \u2192 Go to Folder \u2192 paste: ~/.gemini",linux:"Open a terminal \u2192 nano ~/.gemini/settings.json"}},"mcp-codex":{id:"mcp-codex",name:"Codex CLI \u2014 Your Subscription (OpenAI)",description:"Use OpenAI Codex CLI with your existing subscription. No API key needed \u2014 124 tools via the command line.",icon:"fas fa-terminal",appName:"Codex CLI",appDownloadUrl:"https://github.com/openai/codex",setupMode:"cli-command",cliCommand:"codex mcp add familiar -- npx -y familiar-vtt"},"mcp-windsurf":{id:"mcp-windsurf",name:"Windsurf \u2014 Your Subscription (Free / Pro / Teams)",description:"Use Windsurf with your existing subscription. No API key needed \u2014 124 tools available in your IDE.",icon:"fas fa-code",appName:"Windsurf",appDownloadUrl:"https://windsurf.com",setupMode:"config-file",configPaths:{win32:"%USERPROFILE%\\.codeium\\windsurf\\mcp_config.json",darwin:"~/.codeium/windsurf/mcp_config.json",linux:"~/.codeium/windsurf/mcp_config.json"},configSnippet:{mcpServers:{familiar:{command:"npx",args:["-y","familiar-vtt"]}}},openInstructions:{win32:"Open File Explorer \u2192 paste in the address bar: %USERPROFILE%\\.codeium\\windsurf \u2192 open mcp_config.json with Notepad (create the file if it does not exist)",darwin:"Open Finder \u2192 Go \u2192 Go to Folder \u2192 paste: ~/.codeium/windsurf \u2192 open mcp_config.json (create the file if it does not exist)",linux:"Open a terminal \u2192 nano ~/.codeium/windsurf/mcp_config.json (create the file if it does not exist)"}},"mcp-vscode":{id:"mcp-vscode",name:"VS Code + Copilot \u2014 Your Subscription (GitHub Copilot)",description:"Use GitHub Copilot in VS Code with your existing subscription. No API key needed \u2014 124 tools in your editor.",icon:"fas fa-code",appName:"VS Code",appDownloadUrl:"https://code.visualstudio.com",setupMode:"config-file",configPaths:{win32:".vscode\\mcp.json",darwin:".vscode/mcp.json",linux:".vscode/mcp.json"},configSnippet:{servers:{familiar:{type:"stdio",command:"npx",args:["-y","familiar-vtt"]}}},openInstructions:{win32:'Open VS Code \u2192 Ctrl+Shift+P \u2192 search "MCP: Add Server" \u2192 choose "stdio" \u2192 or manually create .vscode\\mcp.json in your project',darwin:'Open VS Code \u2192 Cmd+Shift+P \u2192 search "MCP: Add Server" \u2192 choose "stdio" \u2192 or manually create .vscode/mcp.json in your project',linux:'Open VS Code \u2192 Ctrl+Shift+P \u2192 search "MCP: Add Server" \u2192 choose "stdio" \u2192 or manually create .vscode/mcp.json in your project'}},"mcp-perplexity":{id:"mcp-perplexity",name:"Perplexity \u2014 Your Subscription (Pro)",description:"Use Perplexity Desktop (macOS) with your existing subscription. No API key needed \u2014 124 tools via MCP Connectors. Requires the PerplexityXPC helper app.",icon:"fas fa-search",appName:"Perplexity",appDownloadUrl:"https://www.perplexity.ai/hub/getting-started/perplexity-mac-app",setupMode:"app-settings",openInstructions:{win32:"Perplexity MCP is currently only supported on macOS.",darwin:'Open Perplexity \u2192 install PerplexityXPC when prompted \u2192 Account Settings \u2192 Connectors \u2192 Add Connector \u2192 on the Simple tab enter "familiar" as Server Name and "npx -y familiar-vtt" as the command \u2192 click Save \u2192 enable the connector under Sources',linux:"Perplexity MCP is currently only supported on macOS."}},"mcp-cursor":{id:"mcp-cursor",name:"Cursor \u2014 Your Subscription (Pro / Teams)",description:"Use Cursor with your existing subscription. No API key needed \u2014 124 tools available in your IDE.",icon:"fas fa-code",appName:"Cursor",appDownloadUrl:"https://cursor.com",setupMode:"config-file",configPaths:{win32:"%USERPROFILE%\\.cursor\\mcp.json",darwin:"~/.cursor/mcp.json",linux:"~/.cursor/mcp.json"},configSnippet:{mcpServers:{familiar:{command:"npx",args:["-y","familiar-vtt"]}}},openInstructions:{win32:"Open File Explorer \u2192 paste in the address bar: %USERPROFILE%\\.cursor \u2192 open mcp.json with Notepad (create the file if it does not exist)",darwin:"Open Finder \u2192 Go \u2192 Go to Folder \u2192 paste: ~/.cursor \u2192 open mcp.json (create the file if it does not exist)",linux:"Open a terminal \u2192 nano ~/.cursor/mcp.json (create the file if it does not exist)"}},"mcp-chatgpt":{id:"mcp-chatgpt",name:"ChatGPT Desktop \u2014 Your Subscription (Plus / Pro / Team)",description:"Use ChatGPT Desktop with your existing subscription via an HTTP tunnel. Requires ngrok or cloudflared \u2014 124 tools available.",icon:"fas fa-comments",appName:"ChatGPT Desktop",appDownloadUrl:"https://openai.com/chatgpt/download/",setupMode:"http-tunnel",openInstructions:{win32:"Open ChatGPT Desktop \u2192 Settings \u2192 Apps \u2192 Advanced \u2192 Developer Mode \u2192 enable \u2192 Create Connector \u2192 paste your tunnel URL + /mcp",darwin:"Open ChatGPT Desktop \u2192 Settings \u2192 Apps \u2192 Advanced \u2192 Developer Mode \u2192 enable \u2192 Create Connector \u2192 paste your tunnel URL + /mcp",linux:"Open ChatGPT Desktop \u2192 Settings \u2192 Apps \u2192 Advanced \u2192 Developer Mode \u2192 enable \u2192 Create Connector \u2192 paste your tunnel URL + /mcp"}},"mcp-lechat":{id:"mcp-lechat",name:"Le Chat (Mistral) \u2014 Your Subscription",description:"Use Mistral Le Chat with your existing subscription via an HTTP tunnel. Requires ngrok or cloudflared \u2014 124 tools available.",icon:"fas fa-robot",appName:"Le Chat",appDownloadUrl:"https://chat.mistral.ai",setupMode:"http-tunnel",openInstructions:{win32:"Open Le Chat \u2192 click the Toggle panel icon \u2192 Intelligence \u2192 Connectors \u2192 Custom MCP Connector \u2192 paste your tunnel URL + /mcp",darwin:"Open Le Chat \u2192 click the Toggle panel icon \u2192 Intelligence \u2192 Connectors \u2192 Custom MCP Connector \u2192 paste your tunnel URL + /mcp",linux:"Open Le Chat \u2192 click the Toggle panel icon \u2192 Intelligence \u2192 Connectors \u2192 Custom MCP Connector \u2192 paste your tunnel URL + /mcp"}},"mcp-copilot":{id:"mcp-copilot",name:"Copilot Studio \u2014 Your Subscription (Microsoft 365)",description:"Use Microsoft Copilot Studio with your existing subscription via an HTTP tunnel. Requires ngrok or cloudflared \u2014 124 tools available.",icon:"fas fa-microchip",appName:"Copilot Studio",appDownloadUrl:"https://copilotstudio.microsoft.com",setupMode:"http-tunnel",openInstructions:{win32:"Open Copilot Studio \u2192 your Agent \u2192 Tools \u2192 Add a tool \u2192 Model Context Protocol \u2192 paste your tunnel URL + /mcp",darwin:"Open Copilot Studio \u2192 your Agent \u2192 Tools \u2192 Add a tool \u2192 Model Context Protocol \u2192 paste your tunnel URL + /mcp",linux:"Open Copilot Studio \u2192 your Agent \u2192 Tools \u2192 Add a tool \u2192 Model Context Protocol \u2192 paste your tunnel URL + /mcp"}}};function Re(t){return t in pt}function Qe(t){return pt[t]}function R(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function re(t){return t.replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}function P(t,e,n,r){let i=t.querySelector(e);if(!i)return;let o=n==="success"?"fas fa-check-circle":n==="error"?"fas fa-exclamation-circle":"fas fa-spinner fa-spin";i.innerHTML=`<span class="status-indicator ${n}"><i class="${o}"></i> ${R(r)}</span>`}function Xe(t,e){let n=t.querySelector(e);n&&(n.innerHTML="")}async function cn(t){let e=t.querySelector("#fmcp-provider"),n=t.querySelector("#fmcp-apikey");if(!e)return;let r=Z(e.value),i=(n?.value??"").trim();if(r.requiresApiKey&&!i){P(t,"#fmcp-test-status","error","Please enter an API key first.");return}P(t,"#fmcp-test-status","loading","Testing connection...");try{let o=r.buildHeaders(i),s;if(r.id==="anthropic")s=await fetch(r.chatEndpoint,{method:"POST",headers:o,body:JSON.stringify({model:r.defaultModel,max_tokens:10,messages:[{role:"user",content:"Hi"}]})});else{let a=r.modelsEndpoint??r.chatEndpoint;s=await fetch(a,{method:"GET",headers:o})}if(s.ok)P(t,"#fmcp-test-status","success","Connected! Fetching models..."),await Mt(t);else{let a=await s.text(),c=`HTTP ${String(s.status)}`;try{let l=JSON.parse(a);l.error?.message&&(c=l.error.message)}catch{a&&(c=a.slice(0,200))}P(t,"#fmcp-test-status","error",`Connection failed: ${c}`)}}catch(o){let s=o instanceof Error?o.message:"Unknown error";P(t,"#fmcp-test-status","error",`Network error: ${s}`)}}async function Mt(t){let e=t.querySelector("#fmcp-provider"),n=t.querySelector("#fmcp-apikey"),r=t.querySelector("#fmcp-model");if(!e||!r)return;let i=Z(e.value),o=(n?.value??"").trim();if(!i.modelsEndpoint){P(t,"#fmcp-test-status","error","This provider does not support model listing. Use the static model list.");return}if(i.requiresApiKey&&!o){P(t,"#fmcp-test-status","error","Please enter an API key to fetch models.");return}P(t,"#fmcp-test-status","loading","Fetching models...");try{let s=await fetch(i.modelsEndpoint,{method:"GET",headers:i.buildHeaders(o)});if(!s.ok){let w=(await s.json()).error?.message??`HTTP ${String(s.status)}`;P(t,"#fmcp-test-status","error",`Failed to fetch models: ${w}`);return}let a=await s.json(),c=i.parseModelsResponse(a);if(c.length===0){P(t,"#fmcp-test-status","error","No models found. Using static model list.");return}let l=r.value;r.innerHTML=Y(c,l),P(t,"#fmcp-test-status","success",`Found ${String(c.length)} models.`)}catch(s){let a=s instanceof Error?s.message:"Unknown error";P(t,"#fmcp-test-status","error",`Failed to fetch models: ${a}`)}}async function dn(t){let e=t.querySelector("#fmcp-voice-provider"),n=t.querySelector("#fmcp-voice-apikey");if(!e)return;let r=me(e.value),i=(n?.value??"").trim();if(r.requiresApiKey&&!i){P(t,"#fmcp-voice-test-status","error","Please enter an API key first.");return}P(t,"#fmcp-voice-test-status","loading","Testing voice connection...");try{let o=r.voicesEndpoint??r.ttsEndpoint,s=await fetch(o,{method:"GET",headers:r.buildHeaders(i)});if(s.ok)P(t,"#fmcp-voice-test-status","success","Connected! Fetching voices..."),await It(t);else{let a=await s.text(),c=`HTTP ${String(s.status)}`;try{let l=JSON.parse(a);l.error?.message?c=l.error.message:l.detail?.message&&(c=l.detail.message)}catch{a&&(c=a.slice(0,200))}P(t,"#fmcp-voice-test-status","error",`Connection failed: ${c}`)}}catch(o){let s=o instanceof Error?o.message:"Unknown error";P(t,"#fmcp-voice-test-status","error",`Network error: ${s}`)}}async function It(t){let e=t.querySelector("#fmcp-voice-provider"),n=t.querySelector("#fmcp-voice-apikey"),r=t.querySelector("#fmcp-voice-id");if(!e||!r)return;let i=me(e.value),o=(n?.value??"").trim();if(!i.voicesEndpoint){P(t,"#fmcp-voice-test-status","success","Connected! Using built-in voice list.");return}if(!(i.requiresApiKey&&!o))try{let s=await fetch(i.voicesEndpoint,{method:"GET",headers:i.buildHeaders(o)});if(!s.ok)return;let a=await s.json(),c=i.parseVoicesResponse(a);if(c.length>0){let l=r.value;r.innerHTML=Y(c,l),P(t,"#fmcp-voice-test-status","success",`Found ${String(c.length)} voices.`)}}catch{}}async function ln(t){let e=t.querySelector("#fmcp-image-provider"),n=t.querySelector("#fmcp-image-apikey");if(!e)return;let r=Ae(e.value),i=(n?.value??"").trim();if(r.requiresApiKey&&!i){P(t,"#fmcp-image-test-status","error","Please enter an API key first.");return}P(t,"#fmcp-image-test-status","loading","Testing image connection...");try{let o,s="GET";if(r.id==="openai_image")o="https://api.openai.com/v1/models";else if(r.id==="stability")o="https://api.stability.ai/v1/engines/list";else if(r.id==="falai"){if(i.length>10){P(t,"#fmcp-image-test-status","success","API key saved. Will test on first generation.");return}P(t,"#fmcp-image-test-status","error","API key looks too short.");return}else o=r.generateEndpoint,s="GET";let a=await fetch(o,{method:s,headers:r.buildHeaders(i)});a.ok?P(t,"#fmcp-image-test-status","success","Connected successfully!"):a.status===401||a.status===403?P(t,"#fmcp-image-test-status","error","Invalid API key."):P(t,"#fmcp-image-test-status","success","API key accepted. Ready to generate.")}catch(o){let s=o instanceof Error?o.message:"Unknown error";P(t,"#fmcp-image-test-status","error",`Network error: ${s}`)}}function At(){let t=navigator.userAgent.toLowerCase();return t.includes("mac")?"darwin":t.includes("linux")?"linux":"win32"}function Rt(t,e,n="Download"){return`
|
|
2
|
+
<div class="fmcp-wizard-step">
|
|
3
|
+
<div class="fmcp-step-number">${String(t)}</div>
|
|
4
|
+
<div class="fmcp-step-content">
|
|
5
|
+
<h4>${n} ${R(e.appName)}</h4>
|
|
6
|
+
<p>${n} ${n==="Install"?"":"and install "}${R(e.appName)} if you haven't already.</p>
|
|
7
|
+
<a href="${re(e.appDownloadUrl)}" target="_blank" rel="noopener" class="btn" style="display:inline-flex;align-items:center;gap:6px;margin-top:4px;">
|
|
8
|
+
<i class="fas fa-download"></i> ${n} ${R(e.appName)}
|
|
9
|
+
</a>
|
|
10
|
+
</div>
|
|
11
|
+
</div>`}function ut(t,e,n){let r=n??`Start a new conversation in ${R(e.appName)} and start chatting.`;return`
|
|
12
|
+
<div class="fmcp-wizard-step">
|
|
13
|
+
<div class="fmcp-step-number">${String(t)}</div>
|
|
14
|
+
<div class="fmcp-step-content">
|
|
15
|
+
<h4>Verify Connection</h4>
|
|
16
|
+
<p>${r}</p>
|
|
17
|
+
<div id="fmcp-mcp-status" style="margin-top:8px;">
|
|
18
|
+
<span class="fmcp-status-dot disconnected"></span>
|
|
19
|
+
<span id="fmcp-mcp-status-text">Waiting for connection...</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</div>`}function Lt(t){let e=Qe(t);return e?e.setupMode==="cli-command"?aa(e):e.setupMode==="app-settings"?ca(e):e.setupMode==="http-tunnel"?la(e):da(e):""}function aa(t){let e=t.cliCommand??"";return`
|
|
23
|
+
${Rt(1,t,"Install")}
|
|
24
|
+
|
|
25
|
+
<div class="fmcp-wizard-step">
|
|
26
|
+
<div class="fmcp-step-number">2</div>
|
|
27
|
+
<div class="fmcp-step-content">
|
|
28
|
+
<h4>Add Familiar</h4>
|
|
29
|
+
<p>Open a terminal and run this command:</p>
|
|
30
|
+
<div class="fmcp-code-block">
|
|
31
|
+
<code id="fmcp-config-json">${R(e)}</code>
|
|
32
|
+
<button type="button" class="fmcp-copy-btn" data-copy="config">
|
|
33
|
+
<i class="fas fa-copy"></i> Copy
|
|
34
|
+
</button>
|
|
35
|
+
</div>
|
|
36
|
+
<p style="font-size:0.85em;color:var(--color-text-dark-secondary,#666);margin-top:6px;">
|
|
37
|
+
That's it \u2014 one command and you're done.
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
${ut(3,t,`Start ${R(t.appName)} in your project directory and start chatting.`)}
|
|
43
|
+
`}function ca(t){let e=t.openInstructions?.[At()]??"";return`
|
|
44
|
+
${Rt(1,t)}
|
|
45
|
+
|
|
46
|
+
<div class="fmcp-wizard-step">
|
|
47
|
+
<div class="fmcp-step-number">2</div>
|
|
48
|
+
<div class="fmcp-step-content">
|
|
49
|
+
<h4>Enable Developer Mode</h4>
|
|
50
|
+
<p>${R(e)}</p>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<div class="fmcp-wizard-step">
|
|
55
|
+
<div class="fmcp-step-number">3</div>
|
|
56
|
+
<div class="fmcp-step-content">
|
|
57
|
+
<h4>Add Familiar as a Connector</h4>
|
|
58
|
+
<p>Go to <strong>Apps & Connectors</strong> \u2192 click <strong>Add</strong> \u2192 create a new Connector:</p>
|
|
59
|
+
<ul style="margin:8px 0 8px 20px;">
|
|
60
|
+
<li><strong>Name:</strong> Familiar</li>
|
|
61
|
+
<li><strong>Command:</strong></li>
|
|
62
|
+
</ul>
|
|
63
|
+
<div class="fmcp-code-block">
|
|
64
|
+
<code id="fmcp-config-json">npx -y familiar-vtt</code>
|
|
65
|
+
<button type="button" class="fmcp-copy-btn" data-copy="config">
|
|
66
|
+
<i class="fas fa-copy"></i> Copy
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
${ut(4,t)}
|
|
73
|
+
`}function da(t){let e=JSON.stringify(t.configSnippet,null,2),n=At(),r=t.configPaths?.[n]??"",i=t.openInstructions?.[n]??"";return`
|
|
74
|
+
${Rt(1,t)}
|
|
75
|
+
|
|
76
|
+
<div class="fmcp-wizard-step">
|
|
77
|
+
<div class="fmcp-step-number">2</div>
|
|
78
|
+
<div class="fmcp-step-content">
|
|
79
|
+
<h4>Open the config file</h4>
|
|
80
|
+
<p>${R(i)}</p>
|
|
81
|
+
<div class="fmcp-code-block">
|
|
82
|
+
<code>${R(r)}</code>
|
|
83
|
+
<button type="button" class="fmcp-copy-btn" data-copy="path">
|
|
84
|
+
<i class="fas fa-copy"></i> Copy path
|
|
85
|
+
</button>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="fmcp-wizard-step">
|
|
91
|
+
<div class="fmcp-step-number">3</div>
|
|
92
|
+
<div class="fmcp-step-content">
|
|
93
|
+
<h4>Paste this config</h4>
|
|
94
|
+
<p>Replace the contents of the file with this JSON:</p>
|
|
95
|
+
<div class="fmcp-code-block">
|
|
96
|
+
<code id="fmcp-config-json">${R(e)}</code>
|
|
97
|
+
<button type="button" class="fmcp-copy-btn" data-copy="config">
|
|
98
|
+
<i class="fas fa-copy"></i> Copy JSON
|
|
99
|
+
</button>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
${ut(4,t,`Fully close ${R(t.appName)} and reopen it.`)}
|
|
105
|
+
`}function la(t){let e=t.openInstructions?.[At()]??"";return`
|
|
106
|
+
<div class="fmcp-wizard-step">
|
|
107
|
+
<div class="fmcp-step-number">1</div>
|
|
108
|
+
<div class="fmcp-step-content">
|
|
109
|
+
<h4>Start Familiar in HTTP mode</h4>
|
|
110
|
+
<p>Open a terminal and start the server with the <code>--http</code> flag:</p>
|
|
111
|
+
<div class="fmcp-code-block">
|
|
112
|
+
<code id="fmcp-config-json">npx -y familiar-vtt --http 3006</code>
|
|
113
|
+
<button type="button" class="fmcp-copy-btn" data-copy="config">
|
|
114
|
+
<i class="fas fa-copy"></i> Copy
|
|
115
|
+
</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
|
|
120
|
+
<div class="fmcp-wizard-step">
|
|
121
|
+
<div class="fmcp-step-number">2</div>
|
|
122
|
+
<div class="fmcp-step-content">
|
|
123
|
+
<h4>Start a tunnel</h4>
|
|
124
|
+
<p>Make your local server reachable via a public HTTPS URL:</p>
|
|
125
|
+
<div class="fmcp-code-block">
|
|
126
|
+
<code>ngrok http 3006</code>
|
|
127
|
+
<button type="button" class="fmcp-copy-btn" data-copy="tunnel">
|
|
128
|
+
<i class="fas fa-copy"></i> Copy
|
|
129
|
+
</button>
|
|
130
|
+
</div>
|
|
131
|
+
<p style="font-size:0.85em;color:var(--color-text-dark-secondary,#666);margin-top:6px;">
|
|
132
|
+
Or use <code>cloudflared tunnel --url http://localhost:3006</code> as an alternative. Copy the HTTPS URL you receive.
|
|
133
|
+
</p>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div class="fmcp-wizard-step">
|
|
138
|
+
<div class="fmcp-step-number">3</div>
|
|
139
|
+
<div class="fmcp-step-content">
|
|
140
|
+
<h4>Paste the tunnel URL in ${R(t.appName)}</h4>
|
|
141
|
+
<p>${R(e)}</p>
|
|
142
|
+
<p style="font-size:0.85em;color:var(--color-text-dark-secondary,#666);margin-top:6px;">
|
|
143
|
+
Example URL: <code>https://abc123.ngrok-free.app/mcp</code>
|
|
144
|
+
</p>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
${ut(4,t)}
|
|
149
|
+
`}function Dt(t){t.querySelectorAll(".fmcp-copy-btn").forEach(e=>{e.addEventListener("click",()=>{pa(e)})}),pn(t),Hooks.on("familiar:mcpStatus",()=>{pn(t)})}async function pa(t){let n=t.closest(".fmcp-code-block")?.querySelector("code");if(!n)return;let r=t.innerHTML;await navigator.clipboard.writeText(n.textContent),t.innerHTML='<i class="fas fa-check"></i> Copied!',setTimeout(()=>{t.innerHTML=r},2e3)}function pn(t){let e=t.querySelector("#fmcp-mcp-status .fmcp-status-dot"),n=t.querySelector("#fmcp-mcp-status-text");if(!e||!n)return;let i=globalThis.familiar?.connection?.status??"disconnected";e.className=`fmcp-status-dot ${i}`,i==="connected"?n.textContent="Connected! Familiar is ready to use.":i==="connecting"?n.textContent="Connecting...":n.textContent="Waiting for connection..."}function un(t,e,n,r){let i=t.querySelector("#fmcp-provider-desc"),o=t.querySelector("#fmcp-api-fields"),s=t.querySelector("#fmcp-mcp-wizard"),a=t.querySelector("#fmcp-model-fieldset"),c=t.querySelector("#fmcp-features-fieldset");if(Re(e)){let l=Qe(e);i&&(i.textContent=l?.description??""),o&&(o.style.display="none"),s&&(s.style.display="",s.innerHTML=Lt(e),Dt(t)),a&&(a.style.display="none"),c&&(c.style.display="none")}else{let l=Z(e);i&&(i.textContent=l.description),o&&(o.style.display=""),s&&(s.style.display="none"),a&&(a.style.display=""),c&&(c.style.display="");let m=t.querySelector("#fmcp-apikey-group");m&&(m.style.display=l.requiresApiKey?"":"none"),n&&(n.placeholder=l.apiKeyPlaceholder,n.value=""),r&&(r.innerHTML=Y(l.staticModels,l.defaultModel))}Xe(t,"#fmcp-test-status")}var mn=`
|
|
150
|
+
.familiar-settings-container {
|
|
151
|
+
padding: 8px;
|
|
152
|
+
overflow-y: auto;
|
|
153
|
+
max-height: calc(100vh - 120px);
|
|
154
|
+
}
|
|
155
|
+
.familiar-settings-container fieldset {
|
|
156
|
+
border: 1px solid var(--color-border-light-2, #999);
|
|
157
|
+
border-radius: 4px;
|
|
158
|
+
padding: 8px 12px;
|
|
159
|
+
margin-bottom: 12px;
|
|
160
|
+
}
|
|
161
|
+
.familiar-settings-container legend {
|
|
162
|
+
font-weight: bold;
|
|
163
|
+
font-size: 0.95em;
|
|
164
|
+
padding: 0 6px;
|
|
165
|
+
}
|
|
166
|
+
.familiar-settings-container .form-group {
|
|
167
|
+
display: flex;
|
|
168
|
+
flex-wrap: wrap;
|
|
169
|
+
align-items: center;
|
|
170
|
+
margin: 6px 0;
|
|
171
|
+
}
|
|
172
|
+
.familiar-settings-container .form-group > label {
|
|
173
|
+
flex: 0 0 130px;
|
|
174
|
+
font-weight: 500;
|
|
175
|
+
}
|
|
176
|
+
.familiar-settings-container .form-group .form-fields {
|
|
177
|
+
flex: 1;
|
|
178
|
+
display: flex;
|
|
179
|
+
gap: 4px;
|
|
180
|
+
align-items: center;
|
|
181
|
+
}
|
|
182
|
+
.familiar-settings-container .form-group .form-fields input,
|
|
183
|
+
.familiar-settings-container .form-group .form-fields select {
|
|
184
|
+
flex: 1;
|
|
185
|
+
}
|
|
186
|
+
.familiar-settings-container .hint {
|
|
187
|
+
flex: 0 0 100%;
|
|
188
|
+
font-size: 0.85em;
|
|
189
|
+
color: var(--color-text-dark-secondary, #666);
|
|
190
|
+
margin: 2px 0 4px 130px;
|
|
191
|
+
}
|
|
192
|
+
.familiar-settings-container .provider-desc {
|
|
193
|
+
flex: 0 0 100%;
|
|
194
|
+
font-size: 0.85em;
|
|
195
|
+
color: var(--color-text-dark-secondary, #666);
|
|
196
|
+
margin: 0 0 4px 130px;
|
|
197
|
+
font-style: italic;
|
|
198
|
+
}
|
|
199
|
+
.familiar-settings-container .status-indicator {
|
|
200
|
+
display: inline-flex;
|
|
201
|
+
align-items: center;
|
|
202
|
+
gap: 6px;
|
|
203
|
+
padding: 4px 10px;
|
|
204
|
+
border-radius: 3px;
|
|
205
|
+
font-size: 0.85em;
|
|
206
|
+
margin: 4px 0 4px 130px;
|
|
207
|
+
}
|
|
208
|
+
.familiar-settings-container .status-indicator.success {
|
|
209
|
+
background: rgba(0, 180, 0, 0.1);
|
|
210
|
+
color: #0a7a0a;
|
|
211
|
+
}
|
|
212
|
+
.familiar-settings-container .status-indicator.error {
|
|
213
|
+
background: rgba(220, 0, 0, 0.1);
|
|
214
|
+
color: #a00;
|
|
215
|
+
}
|
|
216
|
+
.familiar-settings-container .status-indicator.loading {
|
|
217
|
+
background: rgba(0, 100, 200, 0.1);
|
|
218
|
+
color: #0064c8;
|
|
219
|
+
}
|
|
220
|
+
.familiar-settings-container .temp-value {
|
|
221
|
+
min-width: 36px;
|
|
222
|
+
text-align: center;
|
|
223
|
+
font-weight: bold;
|
|
224
|
+
}
|
|
225
|
+
.familiar-settings-container .form-buttons {
|
|
226
|
+
display: flex;
|
|
227
|
+
justify-content: flex-end;
|
|
228
|
+
gap: 8px;
|
|
229
|
+
margin-top: 12px;
|
|
230
|
+
padding-top: 8px;
|
|
231
|
+
border-top: 1px solid var(--color-border-light-2, #999);
|
|
232
|
+
}
|
|
233
|
+
`;function Y(t,e){let n=t.some(i=>i.id===e),r="";e&&!n&&(r+=`<option value="${re(e)}" selected>${re(e)}</option>`);for(let i of t){let o=i.id===e?"selected":"";r+=`<option value="${re(i.id)}" ${o}>${re(i.name)}</option>`}return r}function fn(t){let e=t("aiProvider"),n=t("aiApiKey"),r=t("aiModel"),i=t("aiTemperature"),o=t("aiMaxTokens"),s=t("aiStreaming"),a=Z(e),c=t("voiceEnabled"),l=t("voiceProvider"),m=t("voiceApiKey"),w=t("voiceId"),I=me(l),x=t("imageEnabled"),L=t("imageProvider"),G=t("imageApiKey"),ie=t("imageModel"),it=Ae(L),Ro=Object.values(pt).map(N=>`<option value="${N.id}" ${N.id===e?"selected":""}>${N.name}</option>`).join(`
|
|
234
|
+
`),Lo=Object.values(ze).map(N=>`<option value="${N.id}" ${N.id===e?"selected":""}>${N.name}</option>`).join(`
|
|
235
|
+
`),Do=`
|
|
236
|
+
<optgroup label="Je Abonnement (Geen API Key)">
|
|
237
|
+
${Ro}
|
|
238
|
+
</optgroup>
|
|
239
|
+
<optgroup label="API Providers (Betaald per Token)">
|
|
240
|
+
${Lo}
|
|
241
|
+
</optgroup>
|
|
242
|
+
`,De=Re(e),jo=Y(a.staticModels,r),Oo=Object.values(Ue).map(N=>`<option value="${N.id}" ${N.id===l?"selected":""}>${N.name}</option>`).join(`
|
|
243
|
+
`),$o=Y(I.staticVoices,w),No=Object.values(Ge).map(N=>`<option value="${N.id}" ${N.id===L?"selected":""}>${N.name}</option>`).join(`
|
|
244
|
+
`),qo=Y(it.staticModels,ie);return`
|
|
245
|
+
<style>${mn}</style>
|
|
246
|
+
|
|
247
|
+
<form autocomplete="off">
|
|
248
|
+
<fieldset>
|
|
249
|
+
<legend><i class="fas fa-plug"></i> Provider</legend>
|
|
250
|
+
|
|
251
|
+
<div class="form-group">
|
|
252
|
+
<label for="fmcp-provider">AI Provider</label>
|
|
253
|
+
<div class="form-fields">
|
|
254
|
+
<select id="fmcp-provider" name="aiProvider">
|
|
255
|
+
${Do}
|
|
256
|
+
</select>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
<p class="provider-desc" id="fmcp-provider-desc">${De?Qe(e)?.description??"":a.description}</p>
|
|
260
|
+
|
|
261
|
+
<!-- API mode (hidden for MCP providers) -->
|
|
262
|
+
<div id="fmcp-api-fields" ${De?'style="display:none"':""}>
|
|
263
|
+
<div class="form-group" id="fmcp-apikey-group" ${a.requiresApiKey?"":'style="display:none"'}>
|
|
264
|
+
<label for="fmcp-apikey">API Key</label>
|
|
265
|
+
<div class="form-fields">
|
|
266
|
+
<input type="password" id="fmcp-apikey" name="aiApiKey"
|
|
267
|
+
value="${re(n)}"
|
|
268
|
+
placeholder="${a.apiKeyPlaceholder}"
|
|
269
|
+
autocomplete="off" spellcheck="false" />
|
|
270
|
+
</div>
|
|
271
|
+
<p class="hint">Stored locally in your browser. Never sent to our servers.</p>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<div class="form-group">
|
|
275
|
+
<label></label>
|
|
276
|
+
<div class="form-fields">
|
|
277
|
+
<button type="button" id="fmcp-test-btn" class="btn">
|
|
278
|
+
<i class="fas fa-flask"></i> Test Connection
|
|
279
|
+
</button>
|
|
280
|
+
</div>
|
|
281
|
+
</div>
|
|
282
|
+
<div id="fmcp-test-status"></div>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<!-- MCP wizard (hidden for API providers) -->
|
|
286
|
+
<div id="fmcp-mcp-wizard" ${De?"":'style="display:none"'}>
|
|
287
|
+
${De?Lt(e):""}
|
|
288
|
+
</div>
|
|
289
|
+
</fieldset>
|
|
290
|
+
|
|
291
|
+
<fieldset id="fmcp-model-fieldset" ${De?'style="display:none"':""}>
|
|
292
|
+
<legend><i class="fas fa-brain"></i> Model</legend>
|
|
293
|
+
|
|
294
|
+
<div class="form-group">
|
|
295
|
+
<label for="fmcp-model">Model</label>
|
|
296
|
+
<div class="form-fields">
|
|
297
|
+
<select id="fmcp-model" name="aiModel">
|
|
298
|
+
${jo}
|
|
299
|
+
</select>
|
|
300
|
+
<button type="button" id="fmcp-fetch-models-btn" class="btn" title="Refresh model list from provider">
|
|
301
|
+
<i class="fas fa-sync"></i>
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
304
|
+
<p class="hint">Select or fetch available models from your provider.</p>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<div class="form-group">
|
|
308
|
+
<label for="fmcp-temperature">Temperature</label>
|
|
309
|
+
<div class="form-fields">
|
|
310
|
+
<input type="range" id="fmcp-temperature" name="aiTemperature"
|
|
311
|
+
min="0" max="2" step="0.1" value="${String(i)}" />
|
|
312
|
+
<span class="temp-value" id="fmcp-temp-display">${String(i)}</span>
|
|
313
|
+
</div>
|
|
314
|
+
<p class="hint">Higher = more creative responses. Default: 0.8</p>
|
|
315
|
+
</div>
|
|
316
|
+
|
|
317
|
+
<div class="form-group">
|
|
318
|
+
<label for="fmcp-maxtokens">Max Tokens</label>
|
|
319
|
+
<div class="form-fields">
|
|
320
|
+
<input type="number" id="fmcp-maxtokens" name="aiMaxTokens"
|
|
321
|
+
min="256" max="200000" step="256" value="${String(o)}" />
|
|
322
|
+
</div>
|
|
323
|
+
<p class="hint">Maximum response length. Default: 4096</p>
|
|
324
|
+
</div>
|
|
325
|
+
</fieldset>
|
|
326
|
+
|
|
327
|
+
<fieldset id="fmcp-features-fieldset" ${De?'style="display:none"':""}>
|
|
328
|
+
<legend><i class="fas fa-sliders-h"></i> Features</legend>
|
|
329
|
+
|
|
330
|
+
<div class="form-group">
|
|
331
|
+
<label for="fmcp-streaming">Streaming</label>
|
|
332
|
+
<div class="form-fields">
|
|
333
|
+
<input type="checkbox" id="fmcp-streaming" name="aiStreaming"
|
|
334
|
+
${s?"checked":""} />
|
|
335
|
+
<span>Show AI responses word-by-word as they're generated (like ChatGPT)</span>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</fieldset>
|
|
339
|
+
|
|
340
|
+
<fieldset>
|
|
341
|
+
<legend><i class="fas fa-microphone"></i> Voice (NPC Dialogue)</legend>
|
|
342
|
+
|
|
343
|
+
<div class="form-group">
|
|
344
|
+
<label for="fmcp-voice-enabled">Enable Voice</label>
|
|
345
|
+
<div class="form-fields">
|
|
346
|
+
<input type="checkbox" id="fmcp-voice-enabled" name="voiceEnabled"
|
|
347
|
+
${c?"checked":""} />
|
|
348
|
+
<span>Speak NPC dialogue aloud with AI voices</span>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<div id="fmcp-voice-settings" ${c?"":'style="display:none"'}>
|
|
353
|
+
<div class="form-group">
|
|
354
|
+
<label for="fmcp-voice-provider">Voice Provider</label>
|
|
355
|
+
<div class="form-fields">
|
|
356
|
+
<select id="fmcp-voice-provider" name="voiceProvider">
|
|
357
|
+
${Oo}
|
|
358
|
+
</select>
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
<p class="provider-desc" id="fmcp-voice-provider-desc">${I.description}</p>
|
|
362
|
+
|
|
363
|
+
<div class="form-group" id="fmcp-voice-apikey-group" ${I.requiresApiKey?"":'style="display:none"'}>
|
|
364
|
+
<label for="fmcp-voice-apikey">API Key</label>
|
|
365
|
+
<div class="form-fields">
|
|
366
|
+
<input type="password" id="fmcp-voice-apikey" name="voiceApiKey"
|
|
367
|
+
value="${re(m)}"
|
|
368
|
+
placeholder="${I.apiKeyPlaceholder}"
|
|
369
|
+
autocomplete="off" spellcheck="false" />
|
|
370
|
+
</div>
|
|
371
|
+
<p class="hint">Separate key for voice. Stored locally in your browser.</p>
|
|
372
|
+
</div>
|
|
373
|
+
|
|
374
|
+
<div class="form-group">
|
|
375
|
+
<label for="fmcp-voice-id">Default Voice</label>
|
|
376
|
+
<div class="form-fields">
|
|
377
|
+
<select id="fmcp-voice-id" name="voiceId">
|
|
378
|
+
${$o}
|
|
379
|
+
</select>
|
|
380
|
+
<button type="button" id="fmcp-fetch-voices-btn" class="btn" title="Fetch available voices">
|
|
381
|
+
<i class="fas fa-sync"></i>
|
|
382
|
+
</button>
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
|
|
386
|
+
<div class="form-group">
|
|
387
|
+
<label></label>
|
|
388
|
+
<div class="form-fields">
|
|
389
|
+
<button type="button" id="fmcp-test-voice-btn" class="btn">
|
|
390
|
+
<i class="fas fa-flask"></i> Test Voice
|
|
391
|
+
</button>
|
|
392
|
+
</div>
|
|
393
|
+
</div>
|
|
394
|
+
<div id="fmcp-voice-test-status"></div>
|
|
395
|
+
</div>
|
|
396
|
+
</fieldset>
|
|
397
|
+
|
|
398
|
+
<fieldset>
|
|
399
|
+
<legend><i class="fas fa-image"></i> Image Generation (Battlemaps, Portraits)</legend>
|
|
400
|
+
|
|
401
|
+
<div class="form-group">
|
|
402
|
+
<label for="fmcp-image-enabled">Enable Images</label>
|
|
403
|
+
<div class="form-fields">
|
|
404
|
+
<input type="checkbox" id="fmcp-image-enabled" name="imageEnabled"
|
|
405
|
+
${x?"checked":""} />
|
|
406
|
+
<span>Generate battlemaps, portraits, and token art</span>
|
|
407
|
+
</div>
|
|
408
|
+
</div>
|
|
409
|
+
|
|
410
|
+
<div id="fmcp-image-settings" ${x?"":'style="display:none"'}>
|
|
411
|
+
<div class="form-group">
|
|
412
|
+
<label for="fmcp-image-provider">Image Provider</label>
|
|
413
|
+
<div class="form-fields">
|
|
414
|
+
<select id="fmcp-image-provider" name="imageProvider">
|
|
415
|
+
${No}
|
|
416
|
+
</select>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
<p class="provider-desc" id="fmcp-image-provider-desc">${it.description}</p>
|
|
420
|
+
|
|
421
|
+
<div class="form-group" id="fmcp-image-apikey-group" ${it.requiresApiKey?"":'style="display:none"'}>
|
|
422
|
+
<label for="fmcp-image-apikey">API Key</label>
|
|
423
|
+
<div class="form-fields">
|
|
424
|
+
<input type="password" id="fmcp-image-apikey" name="imageApiKey"
|
|
425
|
+
value="${re(G)}"
|
|
426
|
+
placeholder="${it.apiKeyPlaceholder}"
|
|
427
|
+
autocomplete="off" spellcheck="false" />
|
|
428
|
+
</div>
|
|
429
|
+
<p class="hint">Separate key for images. Stored locally in your browser.</p>
|
|
430
|
+
</div>
|
|
431
|
+
|
|
432
|
+
<div class="form-group">
|
|
433
|
+
<label for="fmcp-image-model">Model</label>
|
|
434
|
+
<div class="form-fields">
|
|
435
|
+
<select id="fmcp-image-model" name="imageModel">
|
|
436
|
+
${qo}
|
|
437
|
+
</select>
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
440
|
+
|
|
441
|
+
<div class="form-group">
|
|
442
|
+
<label></label>
|
|
443
|
+
<div class="form-fields">
|
|
444
|
+
<button type="button" id="fmcp-test-image-btn" class="btn">
|
|
445
|
+
<i class="fas fa-flask"></i> Test Connection
|
|
446
|
+
</button>
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
<div id="fmcp-image-test-status"></div>
|
|
450
|
+
</div>
|
|
451
|
+
</fieldset>
|
|
452
|
+
|
|
453
|
+
<div class="form-buttons">
|
|
454
|
+
<button type="button" id="fmcp-cancel-btn" class="btn">
|
|
455
|
+
<i class="fas fa-times"></i> Cancel
|
|
456
|
+
</button>
|
|
457
|
+
<button type="button" id="fmcp-save-btn" class="btn btn-primary">
|
|
458
|
+
<i class="fas fa-save"></i> Save Settings
|
|
459
|
+
</button>
|
|
460
|
+
</div>
|
|
461
|
+
</form>
|
|
462
|
+
`}var ua=foundry.applications.api.ApplicationV2,mt=class extends ua{static DEFAULT_OPTIONS={id:"familiar-ai-settings",classes:["familiar-settings"],window:{title:"AI DM Settings",icon:"fas fa-robot",resizable:!0},position:{width:560,height:700}};async _renderHTML(e,n){let r=document.createElement("div");return r.classList.add("familiar-settings-container"),r.innerHTML=fn(i=>this.getSetting(i)),r}_replaceHTML(e,n,r){n.replaceChildren(e),this.activateListeners(n)}activateListeners(e){let n=e.querySelector("#fmcp-provider"),r=e.querySelector("#fmcp-apikey"),i=e.querySelector("#fmcp-model"),o=e.querySelector("#fmcp-temperature"),s=e.querySelector("#fmcp-temp-display");n?.addEventListener("change",()=>{un(e,n.value,r,i)}),n&&Re(n.value)&&Dt(e),o?.addEventListener("input",()=>{s&&(s.textContent=o.value)}),e.querySelector("#fmcp-test-btn")?.addEventListener("click",()=>{cn(e)}),e.querySelector("#fmcp-fetch-models-btn")?.addEventListener("click",()=>{Mt(e)});let a=e.querySelector("#fmcp-voice-enabled"),c=e.querySelector("#fmcp-voice-provider"),l=e.querySelector("#fmcp-voice-apikey");a?.addEventListener("change",()=>{let x=e.querySelector("#fmcp-voice-settings");x&&(x.style.display=a.checked?"":"none")}),c?.addEventListener("change",()=>{let x=me(c.value),L=e.querySelector("#fmcp-voice-provider-desc"),G=e.querySelector("#fmcp-voice-apikey-group"),ie=e.querySelector("#fmcp-voice-id");L&&(L.textContent=x.description),G&&(G.style.display=x.requiresApiKey?"":"none"),l&&(l.placeholder=x.apiKeyPlaceholder,l.value=""),ie&&(ie.innerHTML=Y(x.staticVoices,x.defaultVoice)),Xe(e,"#fmcp-voice-test-status")}),e.querySelector("#fmcp-fetch-voices-btn")?.addEventListener("click",()=>{It(e)}),e.querySelector("#fmcp-test-voice-btn")?.addEventListener("click",()=>{dn(e)});let m=e.querySelector("#fmcp-image-enabled"),w=e.querySelector("#fmcp-image-provider"),I=e.querySelector("#fmcp-image-apikey");m?.addEventListener("change",()=>{let x=e.querySelector("#fmcp-image-settings");x&&(x.style.display=m.checked?"":"none")}),w?.addEventListener("change",()=>{let x=Ae(w.value),L=e.querySelector("#fmcp-image-provider-desc"),G=e.querySelector("#fmcp-image-apikey-group"),ie=e.querySelector("#fmcp-image-model");L&&(L.textContent=x.description),G&&(G.style.display=x.requiresApiKey?"":"none"),I&&(I.placeholder=x.apiKeyPlaceholder,I.value=""),ie&&(ie.innerHTML=Y(x.staticModels,x.defaultModel)),Xe(e,"#fmcp-image-test-status")}),e.querySelector("#fmcp-test-image-btn")?.addEventListener("click",()=>{ln(e)}),e.querySelector("#fmcp-save-btn")?.addEventListener("click",()=>{this.saveSettings(e)}),e.querySelector("#fmcp-cancel-btn")?.addEventListener("click",()=>{this.close()})}async saveSettings(e){let n=e.querySelector("#fmcp-provider"),r=e.querySelector("#fmcp-apikey"),i=e.querySelector("#fmcp-model"),o=e.querySelector("#fmcp-temperature"),s=e.querySelector("#fmcp-maxtokens"),a=e.querySelector("#fmcp-streaming"),c=n?.value??"openrouter";await this.setSetting("aiProvider",c),Re(c)||(await this.setSetting("aiApiKey",(r?.value??"").trim()),await this.setSetting("aiModel",i?.value??""),await this.setSetting("aiTemperature",Number.parseFloat(o?.value??"0.8")),await this.setSetting("aiMaxTokens",Number.parseInt(s?.value??"4096",10)),await this.setSetting("aiStreaming",a?.checked??!0));let l=e.querySelector("#fmcp-voice-enabled"),m=e.querySelector("#fmcp-voice-provider"),w=e.querySelector("#fmcp-voice-apikey"),I=e.querySelector("#fmcp-voice-id");await this.setSetting("voiceEnabled",l?.checked??!1),await this.setSetting("voiceProvider",m?.value??"elevenlabs"),await this.setSetting("voiceApiKey",(w?.value??"").trim()),await this.setSetting("voiceId",I?.value??"");let x=e.querySelector("#fmcp-image-enabled"),L=e.querySelector("#fmcp-image-provider"),G=e.querySelector("#fmcp-image-apikey"),ie=e.querySelector("#fmcp-image-model");await this.setSetting("imageEnabled",x?.checked??!1),await this.setSetting("imageProvider",L?.value??"openai_image"),await this.setSetting("imageApiKey",(G?.value??"").trim()),await this.setSetting("imageModel",ie?.value??""),ui.notifications?.info("Foundry MCP: AI settings saved."),this.close()}getSetting(e){return game.settings.get(y,e)}async setSetting(e,n){await game.settings.set(y,e,n)}};function u(t,e){let n=t?.[e];if(typeof n!="string")throw new Error(`Missing or invalid string param: ${e}`);return n}function E(t,e){let n=t?.[e];if(n!=null){if(typeof n!="string")throw new Error(`Invalid string param: ${e}`);return n}}function M(t,e){let n=t?.[e];if(typeof n!="number")throw new Error(`Missing or invalid number param: ${e}`);return n}function S(t,e){let n=t?.[e];if(n!=null){if(typeof n!="number")throw new Error(`Invalid number param: ${e}`);return n}}function z(t,e){let n=t?.[e];if(n!=null){if(typeof n!="boolean")throw new Error(`Invalid boolean param: ${e}`);return n}}function q(t,e){let n=t?.[e];if(!Array.isArray(n))throw new Error(`Missing or invalid array param: ${e}`);return n}function U(t,e){let n=t?.[e];if(typeof n!="object"||n===null||Array.isArray(n))throw new Error(`Missing or invalid object param: ${e}`);return n}function jt(t,e){let n=t?.[e];if(n!=null){if(typeof n!="object"||Array.isArray(n))throw new Error(`Invalid object param: ${e}`);return n}}function ft(t,e){let n=t?.[e];if(n!=null){if(!Array.isArray(n))throw new Error(`Invalid array param: ${e}`);return n}}function gn(){let t=game.users.contents.filter(e=>e.active).map(e=>({name:e.name,isGM:e.isGM,id:e.id}));return{world:{title:game.world.title,id:game.world.id},system:{id:game.system.id,title:game.system.title,version:game.system.version},foundryVersion:game.version,paused:game.paused,connectedUsers:t}}function hn(t){return game.togglePause(t,!0),{paused:game.paused}}function yn(){let t=game.time.worldTime,e=Math.floor(t/86400),n=Math.floor(t%86400/3600),r=Math.floor(t%3600/60),i=t%60;return{worldTime:t,formatted:`Day ${e}, ${String(n).padStart(2,"0")}:${String(r).padStart(2,"0")}:${String(i).padStart(2,"0")}`,days:e,hours:n,minutes:r,seconds:i}}async function vn(t,e,n,r){let i=(t??0)+(e??0)*60+(n??0)*3600+(r??0)*86400;if(i<=0)throw new Error("Must advance by a positive duration. Provide seconds, minutes, hours, or days.");let o=await game.time.advance(i),s=Math.floor(o/86400),a=Math.floor(o%86400/3600),c=Math.floor(o%3600/60);return{advanced:i,worldTime:o,formatted:`Day ${s}, ${String(a).padStart(2,"0")}:${String(c).padStart(2,"0")}`}}function Q(t){let e=game.actors.get(t)??game.actors.getName(t);if(!e)throw new Error(`Actor not found: ${t}`);return e}function bn(t){let e=t.system,n={id:t.id,name:t.name,type:t.type,img:t.img};if(e.abilities&&(n.abilities=Object.fromEntries(Object.entries(e.abilities).map(([i,o])=>[i,{value:o.value,mod:o.mod,save:o.save}]))),e.attributes){let{hp:i,ac:o,movement:s}=e.attributes;n.hp={value:i.value,max:i.max,temp:i.temp},n.ac=o.value,n.movement=s}e.details&&(n.level=e.details.level,n.race=e.details.race,n.background=e.details.background);let r={};for(let i of t.items.contents){let o=i.type;r[o]??=[],r[o].push({id:i.id,name:i.name,type:i.type,img:i.img})}return n.items=r,n.effects=t.effects.contents.map(i=>({id:i.id,name:i.name,disabled:i.disabled,icon:i.icon??i.img})),n}function wn(t){return bn(Q(t))}function xn(t){let e=game.actors.contents;return t==="character"?e=e.filter(n=>n.type==="character"):t==="npc"&&(e=e.filter(n=>n.type==="npc")),e.map(n=>({id:n.id,name:n.name,type:n.type,img:n.img}))}function kn(){let t=game.users.contents.filter(n=>!n.isGM).map(n=>n.id);return game.actors.contents.filter(n=>t.some(r=>{let i=n.ownership[r];return i!==void 0&&i>=3})).map(n=>bn(n))}async function Tn(t,e){let n=Q(t);return await n.update(e),{updated:!0,name:n.name,id:n.id}}function Cn(t,e,n){let r=Q(t),i=r.items.contents;if(n&&(i=i.filter(s=>s.type===n)),e){let s=e.toLowerCase();i=i.filter(a=>a.name.toLowerCase().includes(s))}let o=i.slice(0,50).map(s=>{let a={id:s.id,name:s.name,type:s.type,img:s.img},c=s.system;return c.equipped!==void 0&&(a.equipped=c.equipped),c.quantity!==void 0&&(a.quantity=c.quantity),c.level!==void 0&&(a.spellLevel=c.level),c.preparation!==void 0&&(a.prepared=c.preparation.prepared),a});return{actor:r.name,resultCount:o.length,results:o}}function En(t,e){let n=Q(t),r=n.items.contents.find(i=>i.id===e);if(!r)throw new Error(`Item not found: ${e} on actor ${n.name}`);return{id:r.id,name:r.name,type:r.type,img:r.img,system:r.system}}async function Sn(t,e,n){let r=Q(t),i=game.packs.get(e);if(!i)throw new Error(`Pack not found: ${e}`);let o=await i.getDocument(n);if(!o)throw new Error(`Entry not found: ${n} in pack ${e}`);let s=o.toObject(),a=await r.createEmbeddedDocuments("Item",[s]);if(a.length===0)throw new Error(`Failed to add item to ${r.name}`);let c=a[0];return{added:!0,actor:r.name,item:{id:c.id,name:c.name,type:c.type}}}async function _n(t,e){let n=Q(t),r=[];for(let i of e){let o=n.items.contents.find(s=>s.id===i);if(o)r.push(o.id);else{let s=n.items.contents.find(a=>a.name===i);if(s)r.push(s.id);else throw new Error(`Item not found on ${n.name}: ${i}`)}}return await n.deleteEmbeddedDocuments("Item",r),{deleted:!0,actor:n.name,count:r.length,ids:r}}async function Pn(t,e){let n=Q(t),r=n.name;return await n.update({name:e,"prototypeToken.name":e}),{renamed:!0,oldName:r,newName:e,id:n.id}}async function Mn(t,e,n,r,i){let o={name:t,type:e??"npc"};if(n&&(o.system=n),r&&(o.img=r),i){let a=game.folders.get(i)??game.folders.contents.find(c=>c.type==="Actor"&&c.name.toLowerCase()===i.toLowerCase());a&&(o.folder=a.id)}let s=await Actor.create(o);return{created:!0,id:s.id,name:s.name,type:s.type}}async function In(t){let e=Q(t),{name:n,id:r}=e;return await e.delete(),{deleted:!0,name:n,id:r}}async function An(t,e){let n=Q(t),r=await n.clone({name:e??`${n.name} (Copy)`},{save:!0});return{duplicated:!0,original:{id:n.id,name:n.name},copy:{id:r.id,name:r.name}}}function Rn(t){let e=game.users.contents.find(n=>n.id===t)??game.users.contents.find(n=>n.name.toLowerCase()===t.toLowerCase());if(!e){let n=game.users.contents.map(r=>`${r.name} (${r.id})`).join(", ");throw new Error(`User not found: ${t}. Available: ${n}`)}return e}async function Ln(t,e,n){let r=Q(t),i=Rn(e);await r.update({ownership:{...r.ownership,[i.id]:n}});let o={0:"NONE",1:"LIMITED",2:"OBSERVER",3:"OWNER"};return{updated:!0,actor:r.name,user:i.name,level:o[n]??String(n)}}async function Dn(t,e){let n=Q(t),r=Rn(e);return await n.update({ownership:{...n.ownership,[r.id]:0}}),{removed:!0,actor:n.name,user:r.name}}function ma(t){let e={id:t.id,name:t.name,x:t.x,y:t.y,hidden:t.hidden,elevation:t.elevation};if(t.actor){let n=t.actor.system.attributes?.hp;n&&(e.hp={value:n.value,max:n.max}),e.actorId=t.actor.id,e.actorName=t.actor.name}return e}function Le(){let t=game.scenes.active;if(!t)throw new Error("No active scene");return t}function jn(){let t=Le();return{id:t.id,name:t.name,darkness:t.darkness,weather:t.weather||"(none)",grid:{size:t.grid.size,units:t.grid.units,distance:t.grid.distance},dimensions:{width:t.dimensions.width,height:t.dimensions.height,sceneX:t.dimensions.sceneX,sceneY:t.dimensions.sceneY,sceneWidth:t.dimensions.sceneWidth,sceneHeight:t.dimensions.sceneHeight},tokens:t.tokens.contents.map(ma)}}function On(){return game.scenes.contents.map(t=>({id:t.id,name:t.name,active:t.active,navigation:t.navigation}))}async function $n(t){let e=Le(),n=[];for(let i of t){let o=game.actors.get(i.actorId);if(!o)throw new Error(`Actor not found: ${i.actorId}`);let s=await o.getTokenDocument({x:i.x,y:i.y,hidden:i.hidden??!1,disposition:i.disposition,name:i.name??o.name});n.push(s.toObject())}let r=await e.createEmbeddedDocuments("Token",n);return{created:r.length,tokens:r.map(i=>({id:i.id,name:i.name,x:i.x,y:i.y}))}}async function Nn(t){let e=Le(),n=t.map(r=>{let i=e.tokens.contents.find(o=>o.id===r||o.name.toLowerCase()===r.toLowerCase());if(!i)throw new Error(`Token not found: ${r}`);return i.id});return await e.deleteEmbeddedDocuments("Token",n),{deleted:n.length,ids:n}}function Ot(t){let n=Le().tokens.contents.find(r=>r.id===t||r.name.toLowerCase()===t.toLowerCase());if(!n)throw new Error(`Token not found: ${t}`);return n}async function qn(t,e,n){let r=Ot(t);return await r.update({x:e,y:n}),{moved:!0,name:r.name,x:e,y:n}}async function Hn(t,e){let n=Ot(t);return await n.update(e),{updated:!0,name:n.name,id:n.id,updates:e}}async function Fn(t){let e=game.scenes.contents.find(n=>n.id===t||n.name.toLowerCase()===t.toLowerCase());if(!e){let n=game.scenes.contents.map(r=>r.name).join(", ");throw new Error(`Scene not found: ${t}. Available: ${n}`)}return await e.activate(),{activated:!0,name:e.name,id:e.id}}async function Vn(t,e){let n=Le(),r=Math.max(0,Math.min(1,t));return e?canvas.effects.animateDarkness(r,{duration:1e4}):await n.update({darkness:r}),{darkness:r,scene:n.name,animated:e??!1}}async function zn(){let t=Le();return await canvas.fog.reset(),{reset:!0,scene:t.name}}async function Un(t){let e=Le();return await e.update({weather:t}),{weather:t||"(none)",scene:e.name}}function Gn(t,e,n,r){let i={};return t!==void 0&&(i.x=t),e!==void 0&&(i.y=e),n!==void 0&&(i.scale=n),canvas.pan(i),{panned:!0,x:t,y:e,scale:n}}function Bn(){let t=CONFIG.weatherEffects??{};return{effects:Object.entries(t).map(([e,n])=>({id:e,label:n.label??e}))}}function Kn(t){let e=game.scenes.contents.find(n=>n.id===t||n.name.toLowerCase()===t.toLowerCase());if(!e){let n=game.scenes.contents.map(r=>r.name).join(", ");throw new Error(`Scene not found: ${t}. Available: ${n}`)}return e}async function Wn(t,e,n,r,i,o){let s={name:t};e&&(s.width=e),n&&(s.height=n),(r||i)&&(s.grid={...r?{size:r}:{},...i?{distance:i}:{}}),o&&(s.background={src:o});let a=await Scene.create(s);return{created:!0,id:a.id,name:a.name}}async function Zn(t,e){let n=Kn(t);return await n.update(e),{updated:!0,id:n.id,name:n.name}}async function Jn(t){let e=Kn(t),{name:n,id:r}=e;return await e.delete(),{deleted:!0,name:n,id:r}}async function Yn(t,e){let n=Ot(t),r=CONFIG.statusEffects.find(i=>i.id.toLowerCase()===e.toLowerCase()||i.name.toLowerCase()===e.toLowerCase());if(!r){let i=CONFIG.statusEffects.map(o=>o.id).join(", ");throw new Error(`Unknown condition: ${e}. Available: ${i}`)}if(!n.actor)throw new Error(`Token "${n.name}" has no linked actor`);return await n.actor.toggleStatusEffect(r.id),{toggled:!0,token:n.name,condition:r.id}}async function Qn(t,e){let n=new Roll(t);return await n.evaluate(),e&&await n.toMessage({flavor:`MCP Roll: ${t}`}),{formula:n.formula,total:n.total,result:n.result,dice:n.dice.map(r=>r.results.map(i=>i.result))}}async function Xn(t,e,n,r){let i={content:t};if(e){let o=game.actors.getName(e);o?i.speaker={alias:o.name,actor:o.id}:i.speaker={alias:e}}if(n&&n.length>0){let o=[];for(let s of n){let a=game.users.contents.find(c=>c.name.toLowerCase()===s.toLowerCase()||c.id===s);a&&o.push(a.id)}if(o.length===0)throw new Error(`No valid whisper targets found: ${n.join(", ")}`);i.whisper=o}return r&&(i.type=3),await ChatMessage.create(i),{sent:!0,content:t,speaker:i.speaker?.alias??"GM",whisperTo:n??[],isEmote:r??!1}}function er(t){let e=game.messages.contents,n=e.slice(-t);return{count:n.length,total:e.length,messages:n.map(r=>({id:r.id,content:r.content,speaker:r.speaker?.alias??"Unknown",timestamp:r.timestamp,whisper:r.whisper,type:r.type}))}}function tr(){let t=game.playlists.contents;return{count:t.length,playlists:t.map(e=>({id:e.id,name:e.name,playing:e.playing,mode:e.mode,sounds:e.sounds.contents.map(n=>({id:n.id,name:n.name,path:n.path,playing:n.playing,volume:n.volume,repeat:n.repeat}))}))}}async function nr(t){let e=gt(t);return await e.playAll(),{playing:!0,playlist:e.name,id:e.id}}async function rr(t){let e=gt(t);return await e.stopAll(),{playing:!1,playlist:e.name,id:e.id}}async function ir(t,e){let n=gt(t),r=ar(n,e);return await n.playSound(r),{playing:!0,playlist:n.name,track:r.name}}async function or(t,e){let n=gt(t),r=ar(n,e);return await n.stopSound(r),{playing:!1,playlist:n.name,track:r.name}}async function sr(){let t=game.playlists.contents.filter(e=>e.playing);for(let e of t)await e.stopAll();return{stopped:t.length,playlists:t.map(e=>e.name)}}function gt(t){let e=game.playlists.get(t)??game.playlists.getName(t);if(!e)throw new Error(`Playlist not found: ${t}`);return e}function ar(t,e){let n=t.sounds.contents.find(r=>r.id===e||r.name.toLowerCase()===e.toLowerCase());if(!n)throw new Error(`Track "${e}" not found in playlist "${t.name}"`);return n}function cr(){let t=game.tables.contents;return{count:t.length,tables:t.map(e=>({id:e.id,name:e.name,description:e.description??"",formula:e.formula,replacement:e.replacement,resultsCount:e.results.contents.length,folder:e.folder?.name??null}))}}function dr(t){let e=pr(t);return{id:e.id,name:e.name,description:e.description??"",formula:e.formula,replacement:e.replacement,results:e.results.contents.map(n=>({id:n.id,text:n.text,range:n.range,weight:n.weight,drawn:n.drawn,type:n.type}))}}async function lr(t,e){let n=pr(t),r=await n.draw({displayChat:e??!0});return{table:n.name,roll:{formula:r.roll.formula,total:r.roll.total},results:r.results.map(i=>({id:i.id,text:i.text,range:i.range}))}}function pr(t){let e=game.tables.get(t)??game.tables.getName(t);if(!e)throw new Error(`Rollable table not found: ${t}`);return e}function ur(){let t=game.cards.contents;return{count:t.length,stacks:t.map(e=>({id:e.id,name:e.name,type:e.type,description:e.description??"",cardCount:e.cards.contents.length,availableCards:e.availableCards.length,drawnCards:e.drawnCards.length,folder:e.folder?.name??null}))}}function mr(t){let e=et(t);return{id:e.id,name:e.name,type:e.type,description:e.description??"",cards:e.cards.contents.map(n=>({id:n.id,name:n.name,face:n.face,faces:n.faces,drawn:n.drawn,back:n.back?.img??null,img:n.img??null}))}}async function fr(t,e,n){let r=et(t),i=et(e),o=await r.deal([i],n);return{from:r.name,to:i.name,count:n,result:o}}async function gr(t){let e=et(t);return await e.shuffle(),{shuffled:!0,stack:e.name,cardCount:e.cards.contents.length}}async function hr(t){let e=et(t);return await e.recall(),{reset:!0,stack:e.name,cardCount:e.cards.contents.length}}function et(t){let e=game.cards.get(t)??game.cards.getName(t);if(!e)throw new Error(`Card stack not found: ${t}`);return e}function yr(){let t=game.macros.contents;return{count:t.length,macros:t.map(e=>({id:e.id,name:e.name,type:e.type,command:e.command,author:e.author?.name??"Unknown",folder:e.folder?.name??null}))}}async function vr(t){let e=game.macros.get(t)??game.macros.getName(t);if(!e)throw new Error(`Macro not found: ${t}`);return await e.execute(),{executed:!0,macro:e.name,type:e.type}}async function br(t,e,n="script"){let r=await Macro.create({name:t,type:n,command:e,scope:"global"});return{created:!0,id:r.id,name:r.name,type:r.type,command:r.command}}async function wr(t){let e=game.macros.get(t)??game.macros.getName(t);if(!e)throw new Error(`Macro not found: ${t}`);let n=e.name;return await e.delete(),{deleted:!0,name:n}}function xr(){return{"send-chat-message":t=>Xn(u(t,"content"),E(t,"speaker"),t?.whisperTo,z(t,"isEmote")),"get-recent-messages":t=>Promise.resolve(er(S(t,"count")??20)),"list-playlists":()=>Promise.resolve(tr()),"play-playlist":t=>nr(u(t,"identifier")),"stop-playlist":t=>rr(u(t,"identifier")),"play-track":t=>ir(u(t,"playlistIdentifier"),u(t,"trackIdentifier")),"stop-track":t=>or(u(t,"playlistIdentifier"),u(t,"trackIdentifier")),"stop-all-playlists":()=>sr(),"list-rollable-tables":()=>Promise.resolve(cr()),"get-rollable-table":t=>Promise.resolve(dr(u(t,"identifier"))),"roll-table":t=>lr(u(t,"identifier"),z(t,"showInChat")),"list-card-stacks":()=>Promise.resolve(ur()),"get-card-stack":t=>Promise.resolve(mr(u(t,"identifier"))),"draw-cards":t=>fr(u(t,"fromIdentifier"),u(t,"toIdentifier"),M(t,"count")),"shuffle-deck":t=>gr(u(t,"identifier")),"reset-deck":t=>hr(u(t,"identifier")),"list-macros":()=>Promise.resolve(yr()),"execute-macro":t=>vr(u(t,"identifier")),"create-macro":t=>br(u(t,"name"),u(t,"command"),E(t,"type")),"delete-macro":t=>wr(u(t,"identifier"))}}async function kr(t,e,n){let r=game.packs.get(t);if(!r){let s=game.packs.contents.filter(a=>a.metadata.type==="Actor").map(a=>a.collection).join(", ");throw new Error(`Pack not found: ${t}. Actor packs: ${s}`)}let i={};n&&(i.name=n,i["prototypeToken.name"]=n);let o=await game.actors.importFromCompendium(r,e,i);return{created:!0,id:o.id,name:o.name,type:o.type}}function Tr(){return game.packs.contents.map(t=>({collection:t.collection,label:t.metadata.label,type:t.metadata.type,system:t.metadata.system}))}async function fa(t,e,n){let r=[];for(let i of game.packs.contents){if(e&&i.metadata.type!==e||n&&i.metadata.type!=="Actor")continue;let o=await i.getIndex();for(let s of o)s.name.toLowerCase().includes(t)&&r.push({id:s._id,name:s.name,type:s.type??i.metadata.type,pack:i.collection,img:s.img})}return r}function ga(t,e,n,r){let i=t.system,o=i.details,s=i.traits;return!(e!==void 0&&o?.cr!==e||n&&!(o?.type?.value??"").toLowerCase().includes(n.toLowerCase())||r&&(s?.size??"").toLowerCase()!==r.toLowerCase())}async function Cr(t,e,n,r,i){let o=n!==void 0||r!==void 0||i!==void 0,s=await fa(t.toLowerCase(),e,o);if(!o)return{query:t,resultCount:s.length,results:s.slice(0,25)};let a=[];for(let c of s.slice(0,100)){let l=game.packs.get(c.pack);if(!l)continue;let m=await l.getDocument(c.id);m&&ga(m,n,r,i)&&a.push(c)}return{query:t,filters:{cr:n,creatureType:r,size:i},resultCount:a.length,results:a.slice(0,25)}}async function Er(t,e){let n=game.packs.get(t);if(!n){let i=game.packs.contents.map(o=>o.collection).join(", ");throw new Error(`Pack not found: ${t}. Available: ${i}`)}let r=await n.getDocument(e);if(!r)throw new Error(`Entry not found: ${e} in pack ${t}`);return r.toObject()}function tt(){let t=game.combats.active;if(!t)throw new Error("No active combat encounter");return t}function Sr(t){let e={id:t.id,name:t.name,initiative:t.initiative,hidden:t.hidden,defeated:t.defeated,tokenId:t.tokenId,actorId:t.actorId};if(t.actor){let n=t.actor.system.attributes?.hp;n&&(e.hp={value:n.value,max:n.max})}return e}function Be(t){return{id:t.id,round:t.round,turn:t.turn,started:t.started,current:t.combatant?Sr(t.combatant):null,combatants:t.turns.map(Sr)}}async function _r(t){let e=game.scenes.active;if(!e)throw new Error("No active scene");let n=await Combat.create({scene:e.id,active:!0});if(t.length>0){let r=t.map(i=>{let o=e.tokens.contents.find(s=>s.id===i||s.name.toLowerCase()===i.toLowerCase());if(!o)throw new Error(`Token not found: ${i}`);return{tokenId:o.id,sceneId:e.id,actorId:o.actorId??void 0,hidden:o.hidden}});await n.createEmbeddedDocuments("Combatant",r)}return Be(n)}async function Pr(){let t=tt(),e=t.id;return await t.delete(),{ended:!0,combatId:e}}function Mr(){let t=game.combats.active;return t?{active:!0,...Be(t)}:{active:!1}}function $t(t){return t.actor?.system.abilities!==void 0}async function Ir(t){let e=tt();if(t&&t.length>0){let n=t.filter(r=>{let i=e.combatants.get(r);return i&&$t(i)});n.length>0&&await e.rollInitiative(n)}else{let n=e.combatants.filter(r=>r.initiative===null&&$t(r)).map(r=>r.id);n.length>0&&await e.rollInitiative(n)}return Be(e)}async function Ar(){let t=tt(),e=t.combatants.filter(n=>n.initiative===null&&!n.isOwner&&$t(n)).map(n=>n.id);return e.length>0&&await t.rollInitiative(e),Be(t)}async function Rr(){let t=tt();return t.started?await t.nextTurn():await t.startCombat(),Be(t)}async function Lr(){let t=tt();return await t.nextRound(),Be(t)}function Dr(){return CONFIG.statusEffects.map(t=>({id:t.id,name:t.name,icon:t.icon}))}function Ke(t){let e=game.journal.get(t)??game.journal.getName(t);if(!e)throw new Error(`Journal entry not found: ${t}`);return e}function ha(t){return{id:t.id,name:t.name,folder:t.folder?.name??null,pageCount:t.pages.contents.length,pages:t.pages.contents.map(e=>({id:e.id,name:e.name,type:e.type}))}}function jr(t){let e=game.journal.contents;if(t){let n=t.toLowerCase();e=e.filter(r=>r.folder?.name.toLowerCase().includes(n))}return e.map(n=>ha(n))}function Or(t){let e=t.toLowerCase(),n=[];for(let r of game.journal.contents){let i=r.name.toLowerCase().includes(e),o=[];for(let s of r.pages.contents){if(s.name.toLowerCase().includes(e)){o.push({id:s.id,name:s.name,type:s.type,match:"title"});continue}let a=s.text?.content??"";if(a.toLowerCase().includes(e)){let c=a.toLowerCase().indexOf(e),l=Math.max(0,c-80),m=Math.min(a.length,c+e.length+80),w=(l>0?"...":"")+Nt(a.slice(l,m))+(m<a.length?"...":"");o.push({id:s.id,name:s.name,type:s.type,match:"content",snippet:w})}}(i||o.length>0)&&n.push({id:r.id,name:r.name,folder:r.folder?.name??null,titleMatch:i,matchingPages:o})}return{query:t,resultCount:n.length,results:n.slice(0,25)}}function $r(t){let e=Ke(t);return{id:e.id,name:e.name,folder:e.folder?.name??null,pages:e.pages.contents.sort((n,r)=>n.sort-r.sort).map(n=>({id:n.id,name:n.name,type:n.type,content:n.text?.content?Nt(n.text.content):null,image:n.image?.src??null}))}}function Nr(t,e){let n=Ke(t),r=n.pages.contents.find(i=>i.id===e);if(!r)throw new Error(`Page not found: ${e} in journal ${n.name}`);return{id:r.id,name:r.name,type:r.type,content:r.text?.content?Nt(r.text.content):null,rawHtml:r.text?.content??null,image:r.image?.src??null,video:r.video?.src??null}}function ya(t){return(game.folders.get(t)??game.folders.contents.find(n=>n.type==="JournalEntry"&&n.name.toLowerCase()===t.toLowerCase()))?.id}async function qr(t,e,n){let r={name:t};e&&(r.folder=ya(e)??e);let i=await JournalEntry.create(r);return n&&n.length>0&&await i.createEmbeddedDocuments("JournalEntryPage",n.map(o=>({name:o.name,type:"text",text:{content:o.content}}))),{created:!0,id:i.id,name:i.name,folder:i.folder?.name??null,pageCount:i.pages.contents.length}}async function Hr(t,e,n,r){let i=Ke(t),s=(await i.createEmbeddedDocuments("JournalEntryPage",[{name:e,type:r??"text",text:{content:n}}]))[0];if(!s)throw new Error("Failed to create journal page");return{created:!0,journal:i.name,page:{id:s.id,name:s.name,type:s.type}}}async function Fr(t,e,n){let r=Ke(t),i=r.pages.contents.find(o=>o.id===e);if(!i)throw new Error(`Page not found: ${e} in journal ${r.name}`);return await i.update({text:{content:n}}),{updated:!0,journal:r.name,page:{id:i.id,name:i.name}}}async function Vr(t){let e=Ke(t),{name:n,id:r}=e;return await e.delete(),{deleted:!0,name:n,id:r}}async function zr(t,e){let n=Ke(t);if(!n.pages.contents.find(i=>i.id===e))throw new Error(`Page not found: ${e} in journal ${n.name}`);return await n.deleteEmbeddedDocuments("JournalEntryPage",[e]),{deleted:!0,journal:n.name,pageId:e}}function Nt(t){return t.replace(/<br\s*\/?>/gi,`
|
|
463
|
+
`).replace(/<\/p>/gi,`
|
|
464
|
+
|
|
465
|
+
`).replace(/<[^>]+>/g,"").replace(/ /g," ").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/\n{3,}/g,`
|
|
466
|
+
|
|
467
|
+
`).trim()}function Ur(t,e,n){let r=canvas.app.view;if(!r)throw new Error("Canvas not available \u2014 no active scene or canvas not ready");let i=n??.65;return canvas.app.renderer.render(canvas.stage),{imageData:r.toDataURL("image/jpeg",i).split(",")[1],mimeType:"image/jpeg",width:r.width,height:r.height}}function We(){let t=globalThis.ember;if(!t?.narrative?.events)throw new Error("Ember module is not active or not loaded. Enable the Ember module in Foundry.");return t}function ht(){return We().api.systems.EmberNarrativeEvent.STEPS}function qt(t){let e=ht();return t===e.NONE?"none":t===e.AVAILABLE?"available":t===e.ACTIVE?"active":t===e.COMPLETE?"complete":`unknown(${String(t)})`}function Gr(){let t=We(),e=ht(),n=Object.entries(t.narrative.events).map(([i,o])=>({id:i,label:o.label,type:o.type,step:qt(o.step),stepNumber:o.step,ongoing:o.ongoing,isActive:o.step===e.ACTIVE,isComplete:o.step===e.COMPLETE})),r=t.state.event;return{count:n.length,currentEvent:r?{id:r.id,label:r.label}:null,events:n}}function Br(t){let e=We(),n=yt(e,t);if(!n)throw new Error(`Event not found: "${t}". Use list-events to see available events.`);let r=e.state.events[n.id];return{id:n.id,label:n.label,type:n.type,step:qt(n.step),stepNumber:n.step,ongoing:n.ongoing,state:r??null}}async function Kr(t){let e=We(),n=yt(e,t);if(!n)throw new Error(`Event not found: "${t}". Use list-events to see available events.`);let r=ht();if(e.state.event?.ongoing&&e.state.event.id!==n.id)throw new Error(`Cannot begin "${n.label}" \u2014 event "${e.state.event.label}" is still active. Complete it first.`);if(n.step<=r.NONE||n.step===r.AVAILABLE)e.state.event?.id===n.id&&(e.state.event=null),await n.begin();else if(n.step===r.COMPLETE)await n.setStep(r.ACTIVE);else return{alreadyActive:!0,id:n.id,label:n.label,message:`Event "${n.label}" is already active.`};return{begun:!0,id:n.id,label:n.label,step:qt(n.step)}}async function Wr(t){let e=We(),n;if(t){if(n=yt(e,t),!n)throw new Error(`Event not found: "${t}". Use list-events to see available events.`)}else if(n=e.state.event,!n)throw new Error("No active event to complete. Specify an event identifier or begin an event first.");let r=ht();if(n.step===r.COMPLETE)return{alreadyComplete:!0,id:n.id,label:n.label};let o=e.state.events[n.id]?.priorScene;if(typeof n.complete=="function")await n.complete();else if(await n.setStep(r.COMPLETE),o){let s=game.scenes.contents.find(a=>a.id===o||a.name===o);s&&await s.activate()}return{completed:!0,id:n.id,label:n.label,returnedToScene:o??null}}async function Zr(t,e){let n=We(),r=yt(n,t);if(!r)throw new Error(`Event not found: "${t}". Use list-events to see available events.`);return await n.state.recordEventOutcomes(r,e),{updated:!0,id:r.id,label:r.label,outcomes:e}}function yt(t,e){if(t.narrative.events[e])return t.narrative.events[e];let n=e.toLowerCase();for(let r of Object.values(t.narrative.events))if(r.label.toLowerCase()===n)return r;for(let r of Object.values(t.narrative.events))if(r.label.toLowerCase().includes(n))return r;return null}function vt(t){let e=game.actors.get(t)??game.actors.getName(t);if(!e)throw new Error(`Actor not found: ${t}`);return e}function Jr(t){let e=vt(t);return{actor:e.name,effects:e.effects.contents.map(n=>({id:n.id,name:n.name,disabled:n.disabled,icon:n.icon??n.img,changes:n.changes,duration:n.duration,origin:n.origin,transfer:n.transfer}))}}async function Yr(t,e){let n=vt(t),i=(await n.createEmbeddedDocuments("ActiveEffect",[e]))[0];if(!i)throw new Error("Failed to create active effect");return{created:!0,actor:n.name,effect:{id:i.id,name:i.name}}}async function Qr(t,e,n){let r=vt(t),i=r.effects.contents.find(o=>o.id===e);if(!i)throw new Error(`Effect not found: ${e} on actor ${r.name}`);return await i.update(n),{updated:!0,actor:r.name,effect:{id:i.id,name:i.name}}}async function Xr(t,e){let n=vt(t);if(!n.effects.contents.find(i=>i.id===e))throw new Error(`Effect not found: ${e} on actor ${n.name}`);return await n.deleteEmbeddedDocuments("ActiveEffect",[e]),{deleted:!0,actor:n.name,effectId:e}}async function ei(t,e,n,r,i){let o={name:t,type:e};if(n&&(o.system=n),r&&(o.img=r),i){let a=game.folders.get(i)??game.folders.contents.find(c=>c.type==="Item"&&c.name.toLowerCase()===i.toLowerCase());a&&(o.folder=a.id)}let s=await Item.create(o);return{created:!0,id:s.id,name:s.name,type:s.type}}async function ti(t){let e=game.items.get(t)??game.items.getName(t);if(!e)throw new Error(`Item not found: ${t}`);let n=e.name,r=e.id;return await e.delete(),{deleted:!0,name:n,id:r}}function ni(t){let e=game.folders.contents;if(t){let n=t.toLowerCase();e=e.filter(r=>r.type.toLowerCase()===n)}return e.map(n=>({id:n.id,name:n.name,type:n.type,parent:n.parent?.name??null,depth:n.depth}))}async function ri(t,e,n){let r={name:t,type:e};if(n){let o=game.folders.get(n)??game.folders.contents.find(s=>s.type===e&&s.name.toLowerCase()===n.toLowerCase());o&&(r.parent=o.id)}let i=await Folder.create(r);return{created:!0,id:i.id,name:i.name,type:i.type}}async function ii(t){let e=game.folders.get(t)??game.folders.contents.find(i=>i.name.toLowerCase()===t.toLowerCase());if(!e)throw new Error(`Folder not found: ${t}`);let n=e.name,r=e.id;return await e.delete(),{deleted:!0,name:n,id:r}}function Ht(){let t=game.scenes.active;if(!t)throw new Error("No active scene");return t}function oi(){let t=Ht();return{scene:t.name,regions:t.regions.contents.map(e=>({id:e.id,name:e.name,color:e.color,locked:e.locked,visibility:e.visibility,shapes:e.shapes,behaviorCount:e.behaviors.contents.length,behaviors:e.behaviors.contents.map(n=>({id:n.id,name:n.name,type:n.type,disabled:n.disabled}))}))}}async function si(t,e,n,r){let i=Ht(),o={name:t,shapes:e};n&&(o.color=n),r&&(o.behaviors=r);let a=(await i.createEmbeddedDocuments("Region",[o]))[0];if(!a)throw new Error("Failed to create region");return{created:!0,id:a.id,name:a.name,scene:i.name}}async function ai(t){let e=Ht();return await e.deleteEmbeddedDocuments("Region",t),{deleted:t.length,ids:t,scene:e.name}}function fe(t){return t&&(/^#[\da-f]{8}$/i.test(t)?t.slice(0,7):t)}function _(){let t=game.scenes.active;if(!t)throw new Error("No active scene");return t}function $(t,e,n){let r=t.find(i=>i.id===e);if(!r)throw new Error(`${n} not found: ${e}`);return r}function ci(t){return{id:t.id,type:t.t,x:t.x,y:t.y,distance:t.distance,direction:t.direction,angle:t.angle,width:t.width,borderColor:t.borderColor,fillColor:t.fillColor,hidden:t.hidden}}function di(){return _().templates.contents.map(ci)}async function li(t,e,n,r,i,o,s,a,c){let l=_(),m={t,x:e,y:n,distance:r};i!==void 0&&(m.direction=i),o!==void 0&&(m.angle=o),s!==void 0&&(m.width=s),a!==void 0&&(m.borderColor=fe(a)),c!==void 0&&(m.fillColor=fe(c));let w=await l.createEmbeddedDocuments("MeasuredTemplate",[m]);if(!w[0])throw new Error("Failed to create template \u2014 check parameters (e.g., hex colors must be 6-char #RRGGBB)");let I=w[0];return{created:!0,...ci(I)}}async function pi(t,e){let n=_(),r=$(n.templates.contents,t,"Template");return await r.update(e),{updated:!0,id:r.id}}async function mi(t){let e=_();for(let n of t)$(e.templates.contents,n,"Template");return await e.deleteEmbeddedDocuments("MeasuredTemplate",t),{deleted:t.length,ids:t}}function fi(t){return{id:t.id,src:t.texture?.src,x:t.x,y:t.y,width:t.width,height:t.height,rotation:t.rotation,alpha:t.alpha,hidden:t.hidden,overhead:t.overhead}}function gi(){return _().tiles.contents.map(fi)}async function hi(t,e,n,r,i,o,s,a,c){let l=_(),m={texture:{src:t},x:e,y:n,width:r,height:i};o!==void 0&&(m.overhead=o),s!==void 0&&(m.hidden=s),a!==void 0&&(m.alpha=a),c!==void 0&&(m.rotation=c);let I=(await l.createEmbeddedDocuments("Tile",[m]))[0];return{created:!0,...fi(I)}}async function yi(t,e){let n=_(),r=$(n.tiles.contents,t,"Tile");return await r.update(e),{updated:!0,id:r.id}}async function vi(t){let e=_();for(let n of t)$(e.tiles.contents,n,"Tile");return await e.deleteEmbeddedDocuments("Tile",t),{deleted:t.length,ids:t}}function bi(t){return{id:t.id,type:t.shape?.type,x:t.x,y:t.y,width:t.shape?.width,height:t.shape?.height,strokeColor:t.strokeColor,fillColor:t.fillColor,text:t.text,hidden:t.hidden}}function wi(){return _().drawings.contents.map(bi)}async function xi(t,e,n,r,i,o,s,a,c,l,m,w){let I=_(),x={shape:{type:t,width:r,height:i},x:e,y:n};o!==void 0&&(x.strokeColor=fe(o)),s!==void 0&&(x.fillColor=fe(s)),a!==void 0&&(x.fillAlpha=a),c!==void 0&&(x.strokeWidth=c),l!==void 0&&(x.text=l),m!==void 0&&(x.fontSize=m),w!==void 0&&(x.textColor=fe(w));let L=await I.createEmbeddedDocuments("Drawing",[x]);if(!L[0])throw new Error("Failed to create drawing \u2014 check parameters (unsupported shape type or invalid colors)");let G=L[0];return{created:!0,...bi(G)}}async function ki(t,e){let n=_(),r=$(n.drawings.contents,t,"Drawing");return await r.update(e),{updated:!0,id:r.id}}async function Ti(t){let e=_();for(let n of t)$(e.drawings.contents,n,"Drawing");return await e.deleteEmbeddedDocuments("Drawing",t),{deleted:t.length,ids:t}}function va(t){return{id:t.id,c:t.c,light:t.light,move:t.move,sight:t.sight,sound:t.sound,door:t.door,ds:t.ds}}function Ci(){let t=_();return{count:t.walls.contents.length,walls:t.walls.contents.map(va)}}var ba={basic:{light:20,move:20,sight:20,sound:20},terrain:{light:20,move:20,sight:10,sound:20},invisible:{light:0,move:20,sight:0,sound:0},ethereal:{light:20,move:0,sight:20,sound:20},window:{light:0,move:20,sight:10,sound:0},door:{light:20,move:20,sight:20,sound:20,door:1},secret:{light:20,move:20,sight:20,sound:20,door:2}};async function Ei(t){let e=_(),n=t.map(i=>{let o=i.preset?ba[i.preset]??{}:{},s={c:i.c,...o};return i.light!==void 0&&(s.light=i.light),i.move!==void 0&&(s.move=i.move),i.sight!==void 0&&(s.sight=i.sight),i.sound!==void 0&&(s.sound=i.sound),i.door!==void 0&&(s.door=i.door),i.ds!==void 0&&(s.ds=i.ds),s}),r=await e.createEmbeddedDocuments("Wall",n);return{created:r.length,walls:r.map(i=>({id:i.id,c:i.c,door:i.door,ds:i.ds}))}}async function Si(t,e){let n=_(),r=$(n.walls.contents,t,"Wall");return await r.update(e),{updated:!0,id:r.id}}async function _i(t,e){let n=_(),r=$(n.walls.contents,t,"Wall");if(r.door===0)throw new Error(`Wall ${t} is not a door`);let i=e??(r.ds===0?1:0);await r.update({ds:i});let o=["closed","open","locked"];return{toggled:!0,id:r.id,doorState:o[i]??i}}async function Pi(){let t=_(),e=t.walls.contents.filter(r=>r.door>0&&r.ds!==0);if(e.length===0)return{closed:0,message:"No open/locked doors found"};let n=e.map(r=>({_id:r.id,ds:0}));return await t.updateEmbeddedDocuments("Wall",n),{closed:e.length,message:`Closed ${e.length} door(s)`}}async function Mi(t){let e=_();for(let n of t)$(e.walls.contents,n,"Wall");return await e.deleteEmbeddedDocuments("Wall",t),{deleted:t.length,ids:t}}function Ii(t){return{id:t.id,x:t.x,y:t.y,dim:t.config?.dim,bright:t.config?.bright,color:t.config?.color,alpha:t.config?.alpha,angle:t.config?.angle,negative:t.config?.negative,animation:t.config?.animation,hidden:t.hidden}}function Ai(){return _().lights.contents.map(Ii)}async function Ri(t,e,n,r,i,o,s,a,c,l,m,w){let I=_(),x={dim:n,bright:r};i!==void 0&&(x.color=fe(i)),o!==void 0&&(x.alpha=o),s!==void 0&&(x.angle=s),a!==void 0&&(x.negative=a),c!==void 0&&(x.animation={type:c,speed:l??5,intensity:m??5});let L={x:t,y:e,config:x};w!==void 0&&(L.hidden=w);let G=await I.createEmbeddedDocuments("AmbientLight",[L]);return{created:!0,...Ii(G[0])}}async function Li(t,e){let n=_(),r=$(n.lights.contents,t,"Light");return await r.update(e),{updated:!0,id:r.id}}async function Di(t){let e=_();for(let n of t)$(e.lights.contents,n,"Light");return await e.deleteEmbeddedDocuments("AmbientLight",t),{deleted:t.length,ids:t}}function ji(t){return{id:t.id,x:t.x,y:t.y,path:t.path,radius:t.radius,volume:t.volume,easing:t.easing,hidden:t.hidden}}function Oi(){return _().sounds.contents.map(ji)}async function $i(t,e,n,r,i,o,s){let a=_(),c={x:t,y:e,path:n,radius:r};i!==void 0&&(c.volume=i),o!==void 0&&(c.easing=o),s!==void 0&&(c.hidden=s);let l=await a.createEmbeddedDocuments("AmbientSound",[c]);return{created:!0,...ji(l[0])}}async function Ni(t,e){let n=$(_().sounds.contents,t,"Sound");return await n.update(e),{updated:!0,id:n.id}}async function qi(t){let e=_();for(let n of t)$(e.sounds.contents,n,"Sound");return await e.deleteEmbeddedDocuments("AmbientSound",t),{deleted:t.length,ids:t}}function Hi(t){return{id:t.id,x:t.x,y:t.y,label:t.label,entryId:t.entryId,pageId:t.pageId,iconSize:t.iconSize,textColor:t.textColor,hidden:t.hidden}}function Fi(){return _().notes.contents.map(Hi)}async function Vi(t,e,n,r,i,o,s,a){let c=_(),l={x:t,y:e};n!==void 0&&(l.text=n),r!==void 0&&(l.entryId=r),i!==void 0&&(l.pageId=i),o!==void 0&&(l.iconSize=o),s!==void 0&&(l.textColor=fe(s)),a!==void 0&&(l.hidden=a);let m=await c.createEmbeddedDocuments("Note",[l]);if(!m[0])throw new Error("Failed to create note \u2014 check parameters");return{created:!0,...Hi(m[0])}}async function zi(t,e){let n=$(_().notes.contents,t,"Note");return await n.update(e),{updated:!0,id:n.id}}async function Ui(t){let e=_();for(let n of t)$(e.notes.contents,n,"Note");return await e.deleteEmbeddedDocuments("Note",t),{deleted:t.length,ids:t}}function bt(){let t={"get-world-info":()=>Promise.resolve(gn()),"toggle-pause":e=>Promise.resolve(hn(z(e,"paused"))),"get-time":()=>Promise.resolve(yn()),"advance-time":e=>vn(S(e,"seconds"),S(e,"minutes"),S(e,"hours"),S(e,"days")),"get-character":e=>Promise.resolve(wn(u(e,"identifier"))),"list-characters":e=>Promise.resolve(xn(E(e,"type")??"all")),"list-party-members":()=>Promise.resolve(kn()),"update-character":e=>Tn(u(e,"identifier"),U(e,"updates")),"search-character-items":e=>Promise.resolve(Cn(u(e,"identifier"),E(e,"query"),E(e,"type"))),"get-character-item":e=>Promise.resolve(En(u(e,"identifier"),u(e,"itemId"))),"add-compendium-to-character":e=>Sn(u(e,"identifier"),u(e,"pack"),u(e,"id")),"delete-character-items":e=>_n(u(e,"identifier"),q(e,"itemIds")),"rename-character":e=>Pn(u(e,"identifier"),u(e,"name")),"get-current-scene":()=>Promise.resolve(jn()),"list-scenes":()=>Promise.resolve(On()),"move-token":e=>qn(u(e,"identifier"),M(e,"x"),M(e,"y")),"update-token":e=>Hn(u(e,"identifier"),U(e,"updates")),"switch-scene":e=>Fn(u(e,"identifier")),"pan-canvas":e=>Promise.resolve(Gn(S(e,"x"),S(e,"y"),S(e,"scale"),S(e,"duration"))),"toggle-condition":e=>Yn(u(e,"identifier"),u(e,"condition")),"set-darkness":e=>Vn(M(e,"darkness"),z(e,"animate")),"reset-fog":()=>zn(),"set-weather":e=>Un(u(e,"effect")),"get-weather-effects":()=>Promise.resolve(Bn()),"roll-dice":e=>Qn(u(e,"formula"),z(e,"showInChat")??!0),"search-compendium":e=>Cr(u(e,"query"),E(e,"type"),S(e,"cr"),E(e,"creatureType"),E(e,"size")),"get-compendium-entry":e=>Er(u(e,"pack"),u(e,"id")),"list-compendium-packs":()=>Promise.resolve(Tr()),"create-actor-from-compendium":e=>kr(u(e,"pack"),u(e,"id"),E(e,"name")),"create-tokens":e=>$n(q(e,"tokens")),"delete-tokens":e=>Nn(q(e,"identifiers")),"start-combat":e=>_r(q(e,"tokenIds")),"end-combat":()=>Pr(),"get-combat":()=>Promise.resolve(Mr()),"roll-initiative":e=>Ir(ft(e,"combatantIds")),"roll-npc-initiative":()=>Ar(),"next-turn":()=>Rr(),"next-round":()=>Lr(),"get-available-conditions":()=>Promise.resolve(Dr()),"list-journals":e=>Promise.resolve(jr(E(e,"folder"))),"search-journals":e=>Promise.resolve(Or(u(e,"query"))),"get-journal":e=>Promise.resolve($r(u(e,"identifier"))),"get-journal-page":e=>Promise.resolve(Nr(u(e,"identifier"),u(e,"pageId"))),"create-journal":e=>qr(u(e,"name"),E(e,"folder"),ft(e,"pages")),"create-journal-page":e=>Hr(u(e,"identifier"),u(e,"name"),u(e,"content"),E(e,"type")),"update-journal-page":e=>Fr(u(e,"identifier"),u(e,"pageId"),u(e,"content")),"delete-journal":e=>Vr(u(e,"identifier")),"delete-journal-page":e=>zr(u(e,"identifier"),u(e,"pageId")),"create-actor":e=>Mn(u(e,"name"),E(e,"type"),jt(e,"system"),E(e,"img"),E(e,"folder")),"delete-actor":e=>In(u(e,"identifier")),"duplicate-actor":e=>An(u(e,"identifier"),E(e,"name")),"assign-ownership":e=>Ln(u(e,"identifier"),u(e,"userIdentifier"),M(e,"level")),"remove-ownership":e=>Dn(u(e,"identifier"),u(e,"userIdentifier")),"create-scene":e=>Wn(u(e,"name"),S(e,"width"),S(e,"height"),S(e,"gridSize"),S(e,"gridDistance"),E(e,"background")),"update-scene":e=>Zn(u(e,"identifier"),U(e,"updates")),"delete-scene":e=>Jn(u(e,"identifier")),"capture-screen":e=>Promise.resolve(Ur(S(e,"maxWidth"),S(e,"maxHeight"),S(e,"quality"))),"list-templates":()=>Promise.resolve(di()),"create-template":e=>li(u(e,"type"),M(e,"x"),M(e,"y"),M(e,"distance"),S(e,"direction"),S(e,"angle"),S(e,"width"),E(e,"borderColor"),E(e,"fillColor")),"update-template":e=>pi(u(e,"id"),U(e,"updates")),"delete-templates":e=>mi(q(e,"ids")),"list-tiles":()=>Promise.resolve(gi()),"create-tile":e=>hi(u(e,"src"),M(e,"x"),M(e,"y"),M(e,"width"),M(e,"height"),z(e,"overhead"),z(e,"hidden"),S(e,"alpha"),S(e,"rotation")),"update-tile":e=>yi(u(e,"id"),U(e,"updates")),"delete-tiles":e=>vi(q(e,"ids")),"list-drawings":()=>Promise.resolve(wi()),"create-drawing":e=>xi(u(e,"type"),M(e,"x"),M(e,"y"),M(e,"width"),M(e,"height"),E(e,"strokeColor"),E(e,"fillColor"),S(e,"fillAlpha"),S(e,"strokeWidth"),E(e,"text"),S(e,"fontSize"),E(e,"textColor")),"update-drawing":e=>ki(u(e,"id"),U(e,"updates")),"delete-drawings":e=>Ti(q(e,"ids")),"list-walls":()=>Promise.resolve(Ci()),"create-walls":e=>Ei(q(e,"walls")),"update-wall":e=>Si(u(e,"id"),U(e,"updates")),"toggle-door":e=>_i(u(e,"id"),S(e,"state")),"close-all-doors":()=>Pi(),"delete-walls":e=>Mi(q(e,"ids")),"list-lights":()=>Promise.resolve(Ai()),"create-light":e=>Ri(M(e,"x"),M(e,"y"),M(e,"dim"),M(e,"bright"),E(e,"color"),S(e,"alpha"),S(e,"angle"),z(e,"negative"),E(e,"animationType"),S(e,"animationSpeed"),S(e,"animationIntensity"),z(e,"hidden")),"update-light":e=>Li(u(e,"id"),U(e,"updates")),"delete-lights":e=>Di(q(e,"ids")),"list-sounds":()=>Promise.resolve(Oi()),"create-sound":e=>$i(M(e,"x"),M(e,"y"),u(e,"path"),M(e,"radius"),S(e,"volume"),z(e,"easing"),z(e,"hidden")),"update-sound":e=>Ni(u(e,"id"),U(e,"updates")),"delete-sounds":e=>qi(q(e,"ids")),"list-notes":()=>Promise.resolve(Fi()),"create-note":e=>Vi(M(e,"x"),M(e,"y"),E(e,"label"),E(e,"entryId"),E(e,"pageId"),S(e,"iconSize"),E(e,"textColor"),z(e,"hidden")),"update-note":e=>zi(u(e,"id"),U(e,"updates")),"delete-notes":e=>Ui(q(e,"ids")),"list-effects":e=>Promise.resolve(Jr(u(e,"identifier"))),"create-effect":e=>Yr(u(e,"identifier"),U(e,"effectData")),"update-effect":e=>Qr(u(e,"identifier"),u(e,"effectId"),U(e,"updates")),"delete-effect":e=>Xr(u(e,"identifier"),u(e,"effectId")),"create-item":e=>ei(u(e,"name"),u(e,"type"),jt(e,"system"),E(e,"img"),E(e,"folder")),"delete-item":e=>ti(u(e,"identifier")),"list-folders":e=>Promise.resolve(ni(E(e,"type"))),"create-folder":e=>ri(u(e,"name"),u(e,"type"),E(e,"parent")),"delete-folder":e=>ii(u(e,"identifier")),"list-regions":()=>Promise.resolve(oi()),"create-region":e=>si(u(e,"name"),q(e,"shapes"),E(e,"color"),ft(e,"behaviors")),"delete-regions":e=>ai(q(e,"ids")),"list-events":()=>Promise.resolve(Gr()),"get-event":e=>Promise.resolve(Br(u(e,"identifier"))),"begin-event":e=>Kr(u(e,"identifier")),"complete-event":e=>Wr(E(e,"identifier")),"set-event-outcome":e=>Zr(u(e,"identifier"),U(e,"outcomes")),...xr()};return(e,n)=>{let r=t[e];if(!r)return Promise.reject(new Error(`Unknown query method: ${e}`));console.log(`${y} | Handling query: ${e}`);try{return r(n)}catch(i){return Promise.reject(i instanceof Error?i:new Error(String(i)))}}}function d(t,e,n){return{type:"function",function:{name:t,description:e,parameters:n??{type:"object",properties:{}}}}}var Gi=[d("get-world-info","Get Foundry VTT world info: system, world name, version, connected players"),d("toggle-pause","Pause or unpause the game. Omit paused to toggle.",{type:"object",properties:{paused:{type:"boolean",description:"true to pause, false to unpause, omit to toggle"}}}),d("get-time","Get the current in-game time (world clock)"),d("advance-time","Advance the in-game clock. Use for long rests (8 hours), travel, or time passage.",{type:"object",properties:{seconds:{type:"number",description:"Seconds to advance"},minutes:{type:"number",description:"Minutes to advance"},hours:{type:"number",description:"Hours to advance (e.g., 8 for long rest)"},days:{type:"number",description:"Days to advance"}}})];var Bi=[d("get-character","Get a full character sheet by name or Foundry actor ID (stats, items, spells, features)",{type:"object",properties:{identifier:{type:"string",description:"Character name or Foundry actor ID"}},required:["identifier"]}),d("list-characters","List all actors in the world. Optionally filter by type.",{type:"object",properties:{type:{type:"string",enum:["all","character","npc"],description:"Filter by actor type (default: all)"}}}),d("list-party-members","Get all player-owned characters (the party)"),d("update-character","Update a character: HP, ability scores, spell slots, etc.",{type:"object",properties:{identifier:{type:"string",description:"Character name or Foundry actor ID"},updates:{type:"object",description:'Foundry update data (e.g., {"system.attributes.hp.value": 25})',additionalProperties:!0}},required:["identifier","updates"]}),d("search-character-items","Search a character's inventory, spells, and features. Returns compact results (max 50).",{type:"object",properties:{identifier:{type:"string",description:"Character name or Foundry actor ID"},query:{type:"string",description:'Filter by name substring (e.g., "sword", "fire")'},type:{type:"string",enum:["weapon","spell","feat","equipment","consumable","tool","loot","class","subclass","background","race"],description:"Filter by item type"}},required:["identifier"]}),d("get-character-item","Get full details of a specific item, spell, or feature on a character",{type:"object",properties:{identifier:{type:"string",description:"Character name or Foundry actor ID"},itemId:{type:"string",description:"Item ID (from search-character-items results)"}},required:["identifier","itemId"]}),d("add-compendium-to-character","Add an item or spell from a compendium pack to a character",{type:"object",properties:{identifier:{type:"string",description:"Character name or Foundry actor ID"},pack:{type:"string",description:'Compendium pack key (e.g., "dnd5e.spells")'},id:{type:"string",description:"Entry ID from search-compendium results"}},required:["identifier","pack","id"]}),d("delete-character-items","Remove items from a character by ID or name",{type:"object",properties:{identifier:{type:"string",description:"Character name or Foundry actor ID"},itemIds:{type:"array",items:{type:"string"},description:"Item IDs or names to remove"}},required:["identifier","itemIds"]}),d("rename-character","Rename an actor and its prototype token",{type:"object",properties:{identifier:{type:"string",description:"Character name or Foundry actor ID"},name:{type:"string",description:"New name for the character"}},required:["identifier","name"]}),d("create-actor","Create a new custom actor (NPC or character) \u2014 not from compendium",{type:"object",properties:{name:{type:"string",description:"Actor name"},type:{type:"string",enum:["character","npc"],description:"Actor type (default: npc)"},system:{type:"object",description:"System data (e.g., ability scores, HP)",additionalProperties:!0},img:{type:"string",description:"Token/portrait image path"},folder:{type:"string",description:"Folder name or ID"}},required:["name"]}),d("delete-actor","Delete an actor from the world permanently",{type:"object",properties:{identifier:{type:"string",description:"Actor name or Foundry actor ID"}},required:["identifier"]}),d("duplicate-actor","Clone an actor to create a variant (e.g., quick NPC variants)",{type:"object",properties:{identifier:{type:"string",description:"Actor name or Foundry actor ID to clone"},name:{type:"string",description:'Name for the copy (default: "<original> (Copy)")'}},required:["identifier"]}),d("assign-ownership","Give a player ownership/access to an actor. Permission levels: 0=NONE, 1=LIMITED, 2=OBSERVER, 3=OWNER",{type:"object",properties:{identifier:{type:"string",description:"Actor name or Foundry actor ID"},user:{type:"string",description:"Player name or user ID"},level:{type:"number",description:"Permission level: 0=NONE, 1=LIMITED, 2=OBSERVER, 3=OWNER",minimum:0,maximum:3}},required:["identifier","user","level"]}),d("remove-ownership","Revoke a player's access to an actor (sets permission to NONE)",{type:"object",properties:{identifier:{type:"string",description:"Actor name or Foundry actor ID"},user:{type:"string",description:"Player name or user ID"}},required:["identifier","user"]})];var Ki=[d("get-current-scene","Get the active scene with all tokens (position, HP, conditions)"),d("list-scenes","List all scenes in the world"),d("switch-scene","Switch the active scene by name or ID",{type:"object",properties:{identifier:{type:"string",description:"Scene name or ID"}},required:["identifier"]}),d("move-token","Move a token to x,y coordinates on the current scene",{type:"object",properties:{identifier:{type:"string",description:"Token name or ID"},x:{type:"number",description:"Target X coordinate"},y:{type:"number",description:"Target Y coordinate"}},required:["identifier","x","y"]}),d("update-token","Update token properties (hidden, elevation, size, rotation, disposition)",{type:"object",properties:{identifier:{type:"string",description:"Token name or ID"},hidden:{type:"boolean",description:"Hide/show the token"},elevation:{type:"number",description:"Token elevation in feet"},width:{type:"number",description:"Token width in grid units"},height:{type:"number",description:"Token height in grid units"},rotation:{type:"number",description:"Token rotation in degrees (0-360)"},disposition:{type:"number",description:"Token disposition: -2=secret, -1=hostile, 0=neutral, 1=friendly"}},required:["identifier"]}),d("toggle-condition","Apply or remove a status effect on a token (e.g., prone, poisoned)",{type:"object",properties:{identifier:{type:"string",description:"Token name or ID"},condition:{type:"string",description:'Condition name (e.g., "prone", "poisoned", "blinded")'}},required:["identifier","condition"]}),d("create-tokens","Place one or more tokens on the active scene. Supports batch placement for encounter setup.",{type:"object",properties:{tokens:{type:"array",description:"Array of tokens to place",items:{type:"object",properties:{actorId:{type:"string",description:"World actor ID"},x:{type:"number",description:"X coordinate"},y:{type:"number",description:"Y coordinate"},hidden:{type:"boolean",description:"Hidden from players (default: false)"},disposition:{type:"number",description:"-2=secret, -1=hostile, 0=neutral, 1=friendly"},name:{type:"string",description:"Override token name"}},required:["actorId","x","y"]}}},required:["tokens"]}),d("delete-tokens","Remove tokens from the active scene by name or ID",{type:"object",properties:{identifiers:{type:"array",items:{type:"string"},description:"Token names or IDs to delete"}},required:["identifiers"]}),d("create-scene","Create a new scene for world building",{type:"object",properties:{name:{type:"string",description:"Scene name"},width:{type:"number",description:"Scene width in pixels (default: 4000)"},height:{type:"number",description:"Scene height in pixels (default: 3000)"},gridSize:{type:"number",description:"Grid square size in pixels (default: 100)"},gridDistance:{type:"number",description:"Grid distance per square (default: 5)"},background:{type:"string",description:"Background image path"}},required:["name"]}),d("update-scene","Update scene properties (name, grid, dimensions, background, navigation, etc.)",{type:"object",properties:{identifier:{type:"string",description:"Scene name or ID"},updates:{type:"object",description:'Foundry update data (e.g., {"name": "New Name", "navigation": true})',additionalProperties:!0}},required:["identifier","updates"]}),d("delete-scene","Delete a scene from the world permanently",{type:"object",properties:{identifier:{type:"string",description:"Scene name or ID"}},required:["identifier"]}),d("pan-canvas","Pan and zoom the canvas camera to a position. Useful after scene switches.",{type:"object",properties:{x:{type:"number",description:"Target X coordinate"},y:{type:"number",description:"Target Y coordinate"},scale:{type:"number",description:"Zoom level (0.3=far, 1.0=normal, 2.0=close)"},duration:{type:"number",description:"Animation duration in ms (default: 1000)"}}}),d("set-darkness","Set scene darkness level (0=daylight, 1=full darkness). Use animate for smooth day/night transitions.",{type:"object",properties:{darkness:{type:"number",description:"Darkness level: 0=daylight, 0.5=twilight, 1=full darkness",minimum:0,maximum:1},animate:{type:"boolean",description:"Smoothly animate the transition (default: false)"}},required:["darkness"]}),d("reset-fog","Reset fog of war exploration for the active scene (clears all revealed areas)"),d("get-weather-effects","List available weather effects that can be applied to a scene"),d("set-weather","Set weather effect on the active scene (rain, fog, snow, etc.). Use empty string to clear weather.",{type:"object",properties:{effect:{type:"string",description:'Weather effect ID (e.g., "rain", "fog", "snow") or empty string to clear'}},required:["effect"]}),d("capture-screen","Capture a screenshot of what the GM currently sees in Foundry VTT",{type:"object",properties:{maxWidth:{type:"number",description:"Max image width in pixels (default: 1920)"},maxHeight:{type:"number",description:"Max image height in pixels (default: 1080)"},quality:{type:"number",description:"JPEG quality 0.1-1.0 (default: 0.65)",minimum:.1,maximum:1}}})];var Wi=[d("start-combat","Start a new combat encounter. Pass token names or IDs to add as combatants.",{type:"object",properties:{tokenIds:{type:"array",items:{type:"string"},description:"Token names or IDs to add as combatants"}},required:["tokenIds"]}),d("end-combat","End the active combat encounter"),d("get-combat","Get the current combat state (round, turn, combatants with initiative and HP)"),d("roll-initiative","Roll initiative for combatants. Omit combatantIds to roll for all.",{type:"object",properties:{combatantIds:{type:"array",items:{type:"string"},description:"Specific combatant IDs to roll for (omit to roll for all)"}}}),d("roll-npc-initiative","Roll initiative for all NPC combatants only"),d("next-turn","Advance to the next turn in combat (starts combat if not yet started)"),d("next-round","Advance to the next round in combat"),d("get-available-conditions","List all available status conditions (prone, poisoned, blinded, etc.)")];var Zi=[d("search-compendium","Search compendium packs for items, creatures, spells, etc. by name. Supports creature filters.",{type:"object",properties:{query:{type:"string",description:'Search term (e.g., "fireball", "goblin", "longsword")'},type:{type:"string",description:'Filter by pack type: "Actor", "Item", "JournalEntry", etc.'},cr:{type:"number",description:"Filter creatures by Challenge Rating (e.g., 0.25, 1, 5)"},creatureType:{type:"string",description:'Filter creatures by type (e.g., "beast", "undead", "fiend")'},size:{type:"string",enum:["tiny","sm","med","lg","huge","grg"],description:"Filter creatures by size"}},required:["query"]}),d("get-compendium-entry","Get the full details of a compendium entry (stat block, spell description, item properties)",{type:"object",properties:{pack:{type:"string",description:'Pack collection key (e.g., "dnd5e.monsters")'},id:{type:"string",description:"Entry ID from search results"}},required:["pack","id"]}),d("list-compendium-packs","List all available compendium packs (monsters, spells, items, etc.)"),d("create-actor-from-compendium","Import a creature or NPC from a compendium pack into the world as a new actor",{type:"object",properties:{pack:{type:"string",description:'Pack collection key (e.g., "dnd5e.monsters")'},id:{type:"string",description:"Entry ID from search results"},name:{type:"string",description:'Override actor name (e.g., "Goblin Archer")'}},required:["pack","id"]})];var Ji=[d("roll-dice","Roll a dice formula (e.g., 1d20+5, 2d6+3, 4d6kh3). Result is shown in Foundry chat by default.",{type:"object",properties:{formula:{type:"string",description:'Dice formula (e.g., "1d20+5", "2d6+3", "4d6kh3")'},showInChat:{type:"boolean",description:"Show the roll result in Foundry chat (default: true)"}},required:["formula"]})];var Yi=[d("list-journals","List all journal entries in the world. Optionally filter by folder name.",{type:"object",properties:{folder:{type:"string",description:"Filter by folder name (substring match)"}}}),d("search-journals","Search journal entries by title and page content. Returns matching entries with snippets.",{type:"object",properties:{query:{type:"string",description:"Search term to find in journal titles and page content"}},required:["query"]}),d("get-journal","Get a full journal entry with all page content (HTML stripped to plain text)",{type:"object",properties:{identifier:{type:"string",description:"Journal entry name or Foundry ID"}},required:["identifier"]}),d("get-journal-page","Get a single page from a journal entry with full content",{type:"object",properties:{identifier:{type:"string",description:"Journal entry name or Foundry ID"},pageId:{type:"string",description:"Page ID (from list-journals or get-journal results)"}},required:["identifier","pageId"]}),d("create-journal","Create a new journal entry, optionally with initial pages",{type:"object",properties:{name:{type:"string",description:"Journal entry name"},folder:{type:"string",description:"Folder name or ID to place the journal in"},pages:{type:"array",description:"Initial pages to create (each with name and HTML/text content)",items:{type:"object",properties:{name:{type:"string"},content:{type:"string"}},required:["name","content"]}}},required:["name"]}),d("create-journal-page","Add a new page to an existing journal entry",{type:"object",properties:{identifier:{type:"string",description:"Journal entry name or Foundry ID"},name:{type:"string",description:"Page name"},content:{type:"string",description:"Page content (HTML or plain text)"},type:{type:"string",enum:["text","image","video","pdf"],description:"Page type (default: text)"}},required:["identifier","name","content"]}),d("update-journal-page","Update the content of a journal page",{type:"object",properties:{identifier:{type:"string",description:"Journal entry name or Foundry ID"},pageId:{type:"string",description:"Page ID (from get-journal results)"},content:{type:"string",description:"New page content (HTML or plain text)"}},required:["identifier","pageId","content"]}),d("delete-journal","Delete a journal entry and all its pages",{type:"object",properties:{identifier:{type:"string",description:"Journal entry name or Foundry ID"}},required:["identifier"]}),d("delete-journal-page","Delete a single page from a journal entry",{type:"object",properties:{identifier:{type:"string",description:"Journal entry name or Foundry ID"},pageId:{type:"string",description:"Page ID to delete"}},required:["identifier","pageId"]})];var Qi=[d("list-templates","List all measured templates (spell areas) on the active scene"),d("create-template","Create a measured template for spell areas and AoE effects (e.g., Fireball=circle 20ft)",{type:"object",properties:{type:{type:"string",enum:["circle","cone","ray","rect"],description:"Template shape"},x:{type:"number",description:"Origin X coordinate"},y:{type:"number",description:"Origin Y coordinate"},distance:{type:"number",description:"Radius or length in feet (e.g., 20 for Fireball)"},direction:{type:"number",description:"Direction in degrees (0=south, 90=west, 180=north, 270=east)"},angle:{type:"number",description:"Cone angle in degrees (default: 53 for standard cone)"},width:{type:"number",description:"Ray width in feet (default: 5)"},borderColor:{type:"string",description:'Border hex color (e.g., "#FF0000")'},fillColor:{type:"string",description:'Fill hex color (e.g., "#FF880088")'}},required:["type","x","y","distance"]}),d("update-template","Update a measured template (move, resize, recolor)",{type:"object",properties:{id:{type:"string",description:"Template ID"},updates:{type:"object",description:"Properties to update",additionalProperties:!0}},required:["id","updates"]}),d("delete-templates","Remove measured templates from the active scene",{type:"object",properties:{ids:{type:"array",items:{type:"string"},description:"Template IDs to delete"}},required:["ids"]}),d("list-tiles","List all tiles on the active scene"),d("create-tile","Place a tile image on the active scene (decorative elements, overlays, roof tiles)",{type:"object",properties:{src:{type:"string",description:"Image file path (relative to Foundry data)"},x:{type:"number",description:"X coordinate"},y:{type:"number",description:"Y coordinate"},width:{type:"number",description:"Tile width in pixels"},height:{type:"number",description:"Tile height in pixels"},overhead:{type:"boolean",description:"Render above tokens (default: false)"},hidden:{type:"boolean",description:"Hidden from players (default: false)"},alpha:{type:"number",description:"Opacity 0-1 (default: 1)"},rotation:{type:"number",description:"Rotation in degrees"}},required:["src","x","y","width","height"]}),d("update-tile","Update a tile (move, resize, change opacity, toggle overhead)",{type:"object",properties:{id:{type:"string",description:"Tile ID"},updates:{type:"object",description:"Properties to update",additionalProperties:!0}},required:["id","updates"]}),d("delete-tiles","Remove tiles from the active scene",{type:"object",properties:{ids:{type:"array",items:{type:"string"},description:"Tile IDs to delete"}},required:["ids"]}),d("list-drawings","List all drawings on the active scene"),d("create-drawing","Draw a shape on the canvas (rectangles, circles, area markers)",{type:"object",properties:{type:{type:"string",enum:["r","e","p","f"],description:"Shape type: r=rectangle, e=ellipse, p=polygon, f=freehand"},x:{type:"number",description:"X coordinate"},y:{type:"number",description:"Y coordinate"},width:{type:"number",description:"Width in pixels"},height:{type:"number",description:"Height in pixels"},strokeColor:{type:"string",description:'Stroke hex color (e.g., "#FF0000")'},fillColor:{type:"string",description:"Fill hex color"},fillAlpha:{type:"number",description:"Fill opacity 0-1"},strokeWidth:{type:"number",description:"Stroke width in pixels"},text:{type:"string",description:"Text content (for text drawings)"},fontSize:{type:"number",description:"Font size in pixels"},textColor:{type:"string",description:"Text hex color"}},required:["type","x","y","width","height"]}),d("update-drawing","Update a drawing (move, resize, recolor, change text)",{type:"object",properties:{id:{type:"string",description:"Drawing ID"},updates:{type:"object",description:"Properties to update",additionalProperties:!0}},required:["id","updates"]}),d("delete-drawings","Remove drawings from the active scene",{type:"object",properties:{ids:{type:"array",items:{type:"string"},description:"Drawing IDs to delete"}},required:["ids"]})];var Xi=[d("list-walls","List all walls on the active scene (vision/movement/sound blocking lines)"),d("create-walls","Create walls on the active scene (batch supported). Use presets for common wall types.",{type:"object",properties:{walls:{type:"array",description:"Array of walls to create",items:{type:"object",properties:{c:{type:"array",items:{type:"number"},description:"Wall endpoints [x1, y1, x2, y2]"},preset:{type:"string",enum:["basic","terrain","invisible","ethereal","window","door","secret"],description:"Wall type preset"},light:{type:"number",description:"Light blocking: 0=none, 10=limited, 20=normal"},move:{type:"number",description:"Movement blocking: 0=none, 10=limited, 20=normal"},sight:{type:"number",description:"Sight blocking: 0=none, 10=limited, 20=normal"},sound:{type:"number",description:"Sound blocking: 0=none, 10=limited, 20=normal"},door:{type:"number",description:"Door type: 0=none, 1=door, 2=secret"},ds:{type:"number",description:"Door state: 0=closed, 1=open, 2=locked"}},required:["c"]}}},required:["walls"]}),d("update-wall","Update a wall segment (change blocking type, convert to door, etc.)",{type:"object",properties:{id:{type:"string",description:"Wall ID"},updates:{type:"object",description:"Properties to update",additionalProperties:!0}},required:["id","updates"]}),d("toggle-door","Open, close, or lock a door on the active scene",{type:"object",properties:{id:{type:"string",description:"Wall/door ID"},state:{type:"number",description:"Door state: 0=closed, 1=open, 2=locked. Omit to toggle open/closed."}},required:["id"]}),d("close-all-doors","Force close all doors on the active scene (useful for resetting a dungeon)"),d("delete-walls","Remove walls from the active scene",{type:"object",properties:{ids:{type:"array",items:{type:"string"},description:"Wall IDs to delete"}},required:["ids"]}),d("list-lights","List all ambient lights on the active scene"),d("create-light","Place an ambient light source on the scene (torches, magical lights, campfires, darkness)",{type:"object",properties:{x:{type:"number",description:"X coordinate"},y:{type:"number",description:"Y coordinate"},dim:{type:"number",description:"Dim light radius in feet"},bright:{type:"number",description:"Bright light radius in feet"},color:{type:"string",description:'Light hex color (e.g., "#ff9329" for torchlight)'},alpha:{type:"number",description:"Color intensity 0-1 (default: 0.5)"},angle:{type:"number",description:"Emission angle in degrees (360=omnidirectional)"},negative:{type:"boolean",description:"Creates darkness instead of light"},animationType:{type:"string",description:"Animation: torch, pulse, chroma, wave, fog, sunburst, dome, emanation, ghostly, energy, roiling, hole, vortex"},animationSpeed:{type:"number",description:"Animation speed 1-10 (default: 5)"},animationIntensity:{type:"number",description:"Animation intensity 1-10 (default: 5)"},hidden:{type:"boolean",description:"Hidden from players"}},required:["x","y","dim","bright"]}),d("update-light","Update an ambient light (move, change radius, color, animation)",{type:"object",properties:{id:{type:"string",description:"Light ID"},updates:{type:"object",description:"Properties to update",additionalProperties:!0}},required:["id","updates"]}),d("delete-lights","Remove ambient lights from the active scene",{type:"object",properties:{ids:{type:"array",items:{type:"string"},description:"Light IDs to delete"}},required:["ids"]}),d("list-sounds","List all ambient sounds on the active scene"),d("create-sound","Place an ambient sound source on the scene (environmental audio, music zones)",{type:"object",properties:{x:{type:"number",description:"X coordinate"},y:{type:"number",description:"Y coordinate"},path:{type:"string",description:"Audio file path (relative to Foundry data)"},radius:{type:"number",description:"Hearing radius in feet"},volume:{type:"number",description:"Volume 0-1 (default: 1)"},easing:{type:"boolean",description:"Volume eases with distance (default: true)"},hidden:{type:"boolean",description:"Hidden from players"}},required:["x","y","path","radius"]}),d("update-sound","Update an ambient sound (move, change volume, radius)",{type:"object",properties:{id:{type:"string",description:"Sound ID"},updates:{type:"object",description:"Properties to update",additionalProperties:!0}},required:["id","updates"]}),d("delete-sounds","Remove ambient sounds from the active scene",{type:"object",properties:{ids:{type:"array",items:{type:"string"},description:"Sound IDs to delete"}},required:["ids"]}),d("list-notes","List all map pins/notes on the active scene"),d("create-note","Place a map pin on the scene, optionally linked to a journal entry",{type:"object",properties:{x:{type:"number",description:"X coordinate"},y:{type:"number",description:"Y coordinate"},label:{type:"string",description:"Pin label text"},entryId:{type:"string",description:"Journal entry ID to link"},pageId:{type:"string",description:"Journal page ID to link"},iconSize:{type:"number",description:"Icon size in pixels (default: 40)"},textColor:{type:"string",description:"Label text hex color"},hidden:{type:"boolean",description:"Hidden from players"}},required:["x","y"]}),d("update-note","Update a map pin/note (move, change label, link journal)",{type:"object",properties:{id:{type:"string",description:"Note ID"},updates:{type:"object",description:"Properties to update",additionalProperties:!0}},required:["id","updates"]}),d("delete-notes","Remove map pins/notes from the active scene",{type:"object",properties:{ids:{type:"array",items:{type:"string"},description:"Note IDs to delete"}},required:["ids"]})];var eo=[d("send-chat-message","Send a chat message in Foundry. Use for narration, in-character dialogue, whispers, or emotes.",{type:"object",properties:{content:{type:"string",description:"Message content (supports HTML)"},speaker:{type:"string",description:"Speaker name or character name (default: GM)"},whisperTo:{type:"array",items:{type:"string"},description:"Player names to whisper to. Omit for public message."},isEmote:{type:"boolean",description:"Send as an emote/action message (default: false)"}},required:["content"]}),d("get-recent-messages","Get recent chat messages from Foundry.",{type:"object",properties:{count:{type:"number",description:"Number of recent messages to retrieve (default: 20, max: 100)"}}})];var to=[d("list-playlists","List all playlists with their tracks and playing status."),d("play-playlist","Start playing a playlist by name or ID.",{type:"object",properties:{identifier:{type:"string",description:"Playlist name or Foundry ID"}},required:["identifier"]}),d("stop-playlist","Stop a playing playlist by name or ID.",{type:"object",properties:{identifier:{type:"string",description:"Playlist name or Foundry ID"}},required:["identifier"]}),d("play-track","Play a specific track within a playlist.",{type:"object",properties:{playlistIdentifier:{type:"string",description:"Playlist name or ID"},trackIdentifier:{type:"string",description:"Track name or ID"}},required:["playlistIdentifier","trackIdentifier"]}),d("stop-track","Stop a specific track within a playlist.",{type:"object",properties:{playlistIdentifier:{type:"string",description:"Playlist name or ID"},trackIdentifier:{type:"string",description:"Track name or ID"}},required:["playlistIdentifier","trackIdentifier"]}),d("stop-all-playlists","Stop all currently playing playlists.")];var no=[d("list-rollable-tables","List all rollable tables in the world."),d("get-rollable-table","Get a rollable table with all its entries/results.",{type:"object",properties:{identifier:{type:"string",description:"Table name or Foundry ID"}},required:["identifier"]}),d("roll-table","Roll on a rollable table and get the result. Optionally shows in Foundry chat.",{type:"object",properties:{identifier:{type:"string",description:"Table name or Foundry ID"},showInChat:{type:"boolean",description:"Show the roll result in Foundry chat (default: true)"}},required:["identifier"]})];var ro=[d("list-card-stacks","List all card stacks (decks, hands, piles) in the world."),d("get-card-stack","Get a card stack with all its cards.",{type:"object",properties:{identifier:{type:"string",description:"Card stack name or Foundry ID"}},required:["identifier"]}),d("draw-cards","Draw/deal cards from one stack to another.",{type:"object",properties:{fromIdentifier:{type:"string",description:"Source stack name or ID (e.g., the deck)"},toIdentifier:{type:"string",description:"Destination stack name or ID (e.g., a hand or pile)"},count:{type:"number",description:"Number of cards to draw"}},required:["fromIdentifier","toIdentifier","count"]}),d("shuffle-deck","Shuffle a card stack (randomize card order).",{type:"object",properties:{identifier:{type:"string",description:"Card stack name or Foundry ID"}},required:["identifier"]}),d("reset-deck","Reset a card stack (recall all cards back to the deck).",{type:"object",properties:{identifier:{type:"string",description:"Card stack name or Foundry ID"}},required:["identifier"]})];var io=[d("list-macros","List all macros in the world."),d("execute-macro","Execute a macro by name or ID. Use with caution \u2014 macros run arbitrary code.",{type:"object",properties:{identifier:{type:"string",description:"Macro name or Foundry ID"}},required:["identifier"]}),d("create-macro",'Create a new macro. Use "script" for JavaScript, "chat" for chat commands.',{type:"object",properties:{name:{type:"string",description:"Macro name"},command:{type:"string",description:"Macro command/code to execute"},type:{type:"string",enum:["script","chat"],description:"Macro type (default: script)"}},required:["name","command"]}),d("delete-macro","Delete a macro by name or ID.",{type:"object",properties:{identifier:{type:"string",description:"Macro name or Foundry ID"}},required:["identifier"]})];var oo=[d("list-effects","List all active effects (buffs/debuffs) on an actor",{type:"object",properties:{identifier:{type:"string",description:"Actor name or Foundry actor ID"}},required:["identifier"]}),d("create-effect","Add an active effect (buff/debuff) to an actor. Change modes: 0=CUSTOM, 1=MULTIPLY, 2=ADD, 3=DOWNGRADE, 4=UPGRADE, 5=OVERRIDE",{type:"object",properties:{identifier:{type:"string",description:"Actor name or Foundry actor ID"},name:{type:"string",description:'Effect name (e.g., "Bless", "Rage", "Shield of Faith")'},changes:{type:"array",description:"Attribute changes this effect applies",items:{type:"object",properties:{key:{type:"string",description:'System data path (e.g., "system.attributes.ac.bonus")'},mode:{type:"number",description:"Change mode: 0=CUSTOM, 1=MULTIPLY, 2=ADD, 3=DOWNGRADE, 4=UPGRADE, 5=OVERRIDE"},value:{type:"string",description:'Value to apply (e.g., "2", "1d4")'}},required:["key","mode","value"]}},icon:{type:"string",description:"Effect icon path"},disabled:{type:"boolean",description:"Create in disabled state (default: false)"},duration:{type:"object",description:"Effect duration",properties:{rounds:{type:"number"},turns:{type:"number"},seconds:{type:"number"}}}},required:["identifier","name"]}),d("update-effect","Update an active effect on an actor",{type:"object",properties:{identifier:{type:"string",description:"Actor name or Foundry actor ID"},effectId:{type:"string",description:"Effect ID (from list-effects)"},updates:{type:"object",description:"Update data (e.g., {disabled: true})",additionalProperties:!0}},required:["identifier","effectId","updates"]}),d("delete-effect","Remove an active effect from an actor",{type:"object",properties:{identifier:{type:"string",description:"Actor name or Foundry actor ID"},effectId:{type:"string",description:"Effect ID (from list-effects)"}},required:["identifier","effectId"]})];var so=[d("create-item","Create a standalone world item (weapon, spell, equipment, etc.)",{type:"object",properties:{name:{type:"string",description:"Item name"},type:{type:"string",description:"Item type (weapon, spell, equipment, consumable, tool, loot, feat, class, subclass, background)"},system:{type:"object",description:"System-specific data",additionalProperties:!0},img:{type:"string",description:"Item image path"},folder:{type:"string",description:"Folder name or ID"}},required:["name","type"]}),d("delete-item","Delete a standalone item from the world permanently",{type:"object",properties:{identifier:{type:"string",description:"Item name or Foundry ID"}},required:["identifier"]})];var ao=[d("list-folders","List all folders in the world, optionally filtered by document type",{type:"object",properties:{type:{type:"string",enum:["Actor","Item","Scene","JournalEntry","RollableTable","Macro","Cards","Playlist"],description:"Filter by document type"}}}),d("create-folder","Create a new folder for organizing documents",{type:"object",properties:{name:{type:"string",description:"Folder name"},type:{type:"string",enum:["Actor","Item","Scene","JournalEntry","RollableTable","Macro","Cards","Playlist"],description:"Document type this folder holds"},parent:{type:"string",description:"Parent folder name or ID for nesting"}},required:["name","type"]}),d("delete-folder","Delete a folder from the world permanently",{type:"object",properties:{identifier:{type:"string",description:"Folder name or Foundry ID"}},required:["identifier"]})];var co=[d("list-regions","List all regions (v13 trigger zones) on the active scene"),d("create-region","Create a trigger region on the active scene (v13). Regions can trigger behaviors when tokens enter/exit.",{type:"object",properties:{name:{type:"string",description:"Region name"},shapes:{type:"array",description:"Region shapes (rectangle, circle, ellipse, or polygon)",items:{type:"object",properties:{type:{type:"string",enum:["rectangle","circle","ellipse","polygon"],description:"Shape type"},x:{type:"number",description:"X position or center X"},y:{type:"number",description:"Y position or center Y"},width:{type:"number",description:"Width (rectangle)"},height:{type:"number",description:"Height (rectangle)"},radius:{type:"number",description:"Radius (circle)"},radiusX:{type:"number",description:"Horizontal radius (ellipse)"},radiusY:{type:"number",description:"Vertical radius (ellipse)"},points:{type:"array",items:{type:"number"},description:"Flat array of x,y pairs (polygon)"},rotation:{type:"number",description:"Rotation in degrees"},hole:{type:"boolean",description:"Cut out this shape (default: false)"}},required:["type"]}},color:{type:"string",description:'Display color (hex, e.g., "#ff0000")'},behaviors:{type:"array",description:"Behaviors to attach to the region",items:{type:"object",properties:{name:{type:"string"},type:{type:"string",description:"Behavior type (e.g., executeMacro, teleportToken, pauseGame)"},disabled:{type:"boolean"},system:{type:"object",description:"Behavior-specific configuration",additionalProperties:!0}},required:["type"]}}},required:["name","shapes"]}),d("delete-regions","Delete regions from the active scene",{type:"object",properties:{ids:{type:"array",items:{type:"string"},description:"Region IDs to delete"}},required:["ids"]})];var lo=[d("list-events","List all Ember campaign quest events with their current status. Requires Ember module."),d("get-event","Get details of a specific Ember quest event by ID or name. Requires Ember module.",{type:"object",properties:{identifier:{type:"string",description:"Event ID or name (partial match supported)"}},required:["identifier"]}),d("begin-event","Begin an Ember quest event. Switches scene, spawns NPCs, starts music, opens journal. Requires Ember module.",{type:"object",properties:{identifier:{type:"string",description:"Event ID or name (partial match supported)"}},required:["identifier"]}),d("complete-event","Complete an Ember quest event. If no identifier given, completes the currently active event.",{type:"object",properties:{identifier:{type:"string",description:"Event ID or name. Omit to complete the currently active event."}}}),d("set-event-outcome","Set outcomes on an Ember quest event (e.g., mark a choice the party made).",{type:"object",properties:{identifier:{type:"string",description:"Event ID or name"},outcomes:{type:"object",description:"Outcome key-value pairs to set on the event",additionalProperties:!0}},required:["identifier","outcomes"]})];var po=new Set(["dice","world","chat","combat","characters"]),mo=[{id:"dice",name:"Dice",description:"Roll dice formulas (1d20+5, 4d6kh3, etc.)",tools:Ji},{id:"world",name:"World & System",description:"World info, pause, in-game time",tools:Gi},{id:"chat",name:"Chat Messages",description:"Send chat messages, get message history",tools:eo},{id:"combat",name:"Combat & Initiative",description:"Start/end combat, initiative, rounds, turns, conditions, HP tracking",tools:Wi},{id:"characters",name:"Characters & Actors",description:"Get/create/update actors, inventory, HP, abilities, ownership",tools:Bi},{id:"scenes",name:"Scenes & Tokens",description:"Scene management, token placement/movement, darkness, weather, screenshots",tools:Ki},{id:"compendium",name:"Compendium & Rules",description:"Search/lookup SRD content (spells, monsters, items), import actors",tools:Zi},{id:"journals",name:"Journals & Notes",description:"Create/read/update journal entries and pages, search notes",tools:Yi},{id:"canvas",name:"Canvas Drawing",description:"Measurement templates (spell AoE), tiles, drawings on the game canvas",tools:Qi},{id:"canvas-environment",name:"Canvas Environment",description:"Walls, doors, lights, ambient sounds, map notes (vision/movement blocking)",tools:Xi},{id:"playlists",name:"Audio & Playlists",description:"Background music, sound effects, playlist and track management",tools:to},{id:"tables",name:"Rollable Tables",description:"Random tables for loot, encounters, names",tools:no},{id:"cards",name:"Card Decks",description:"Card stacks, drawing, shuffling, dealing",tools:ro},{id:"macros",name:"Macros",description:"List, create, execute, delete macros (automation scripts)",tools:io},{id:"effects",name:"Active Effects",description:"Buffs, debuffs, and status effects on actors",tools:oo},{id:"items",name:"Standalone Items",description:"Create and manage world-level items",tools:so},{id:"folders",name:"Folders",description:"Organize documents into folder hierarchies",tools:ao},{id:"regions",name:"Regions",description:"Trigger zones for scene automation (Foundry v13+)",tools:co},{id:"ember-events",name:"Ember Events",description:"Quest and event tracking (Ember module integration)",tools:lo}],uo=new Map(mo.map(t=>[t.id,t])),wa=[d("list-tool-bundles","List available tool bundles. Tools are organized into domain bundles that can be enabled on demand. Core bundles (dice, world, chat, combat, characters) are always enabled. Enable additional bundles with enable-tool-bundle when you need their capabilities."),d("enable-tool-bundle","Enable a tool bundle to access its tools. Call list-tool-bundles first to see available bundles. You can enable multiple bundles by calling this multiple times. Once enabled, the tools become available for use.",{type:"object",properties:{bundleId:{type:"string",description:'Bundle ID to enable (e.g., "scenes", "compendium", "playlists")'}},required:["bundleId"]})],wt=class{enabledBundles=new Set(po);getActiveTools(){let e=[...wa];for(let n of this.enabledBundles){let r=uo.get(n);r&&e.push(...r.tools)}return e}enableBundle(e){let n=uo.get(e);return n?this.enabledBundles.has(e)?{enabled:!0,toolNames:n.tools.map(r=>r.function.name),error:"Bundle already enabled"}:(this.enabledBundles.add(e),{enabled:!0,toolNames:n.tools.map(r=>r.function.name)}):{enabled:!1,toolNames:[],error:`Unknown bundle: ${e}`}}listBundles(){return mo.map(e=>({id:e.id,name:e.name,description:e.description,toolCount:e.tools.length,enabled:this.enabledBundles.has(e.id)}))}reset(){this.enabledBundles=new Set(po)}};function xa(t){if(!t.startsWith("data: "))return null;let e=t.slice(6);if(e==="[DONE]")return"done";try{return JSON.parse(e)}catch{return null}}function fo(t){let e=t.split(`
|
|
468
|
+
|
|
469
|
+
`),n=e.pop()??"",r=[];for(let i of e)for(let o of i.split(`
|
|
470
|
+
`)){let s=xa(o);if(s==="done")return{chunks:r,remaining:n,done:!0};s&&r.push(s)}return{chunks:r,remaining:n,done:!1}}async function*go(t){let e=t.body;if(!e)throw new Error("Response body is null");let n=e.getReader(),r=new TextDecoder,i="";try{let o=await n.read();for(;!o.done;){i+=r.decode(o.value,{stream:!0});let s=fo(i);i=s.remaining;for(let a of s.chunks)yield a;if(s.done)return;o=await n.read()}if(i.trim()){let s=fo(i+`
|
|
471
|
+
|
|
472
|
+
`);for(let a of s.chunks)yield a}}finally{n.releaseLock()}}function ho(){let t="",e=new Map;return{feed(n){let r=n.choices[0];if(r){if(r.delta.content)return t+=r.delta.content,r.delta.content;if(r.delta.tool_calls)for(let i of r.delta.tool_calls){let o=e.get(i.index);o?i.function?.arguments&&(o.arguments+=i.function.arguments):e.set(i.index,{id:i.id??"",name:i.function?.name??"",arguments:i.function?.arguments??""})}}},getToolCalls(){return[...e.values()]},getContent(){return t},reset(){t="",e.clear()}}}async function yo(t,e,n,r,i){let o=n.find(w=>w.role==="system"),s=n.filter(w=>w.role!=="system"),a=r.map(w=>({name:w.function.name,description:w.function.description,input_schema:w.function.parameters})),c=ka(s),l={model:e.model,messages:c,tools:a,max_tokens:e.maxTokens,temperature:e.temperature,stream:i};o&&(l.system=o.content);let m=await fetch(t.chatEndpoint,{method:"POST",headers:t.buildHeaders(e.apiKey),body:JSON.stringify(l)});if(!i&&m.ok){let w=await m.json(),I=Ta(w);return new Response(JSON.stringify(I),{status:200,headers:{"Content-Type":"application/json"}})}if(i&&m.ok&&m.body){let w=Ca(m.body);return new Response(w,{status:200,headers:{"Content-Type":"text/event-stream"}})}return m}function ka(t){let e=[];for(let n of t)if(n.role==="user")e.push({role:"user",content:n.content});else if(n.role==="assistant"){let r=[];if(n.content&&r.push({type:"text",text:n.content}),n.tool_calls)for(let i of n.tool_calls)r.push({type:"tool_use",id:i.id,name:i.function.name,input:JSON.parse(i.function.arguments)});e.push({role:"assistant",content:r})}else n.role==="tool"&&e.push({role:"user",content:[{type:"tool_result",tool_use_id:n.tool_call_id,content:n.content}]});return e}function Ta(t){let e=null,n=[];for(let r of t.content)r.type==="text"?e=(e??"")+(r.text??""):r.type==="tool_use"&&n.push({id:r.id??"",type:"function",function:{name:r.name??"",arguments:JSON.stringify(r.input)}});return{id:t.id,choices:[{index:0,message:{role:"assistant",content:e,tool_calls:n.length>0?n:void 0},finish_reason:t.stop_reason==="tool_use"?"tool_calls":"stop"}],usage:t.usage?{prompt_tokens:t.usage.input_tokens,completion_tokens:t.usage.output_tokens,total_tokens:t.usage.input_tokens+t.usage.output_tokens}:void 0}}function Ca(t){let e=t.getReader(),n=new TextDecoder,r=new TextEncoder,i="",o=0,s=!1;return new ReadableStream({async pull(a){if(s)return;let{done:c,value:l}=await e.read();if(c){s=!0,a.enqueue(r.encode(`data: [DONE]
|
|
473
|
+
|
|
474
|
+
`)),a.close();return}i+=n.decode(l,{stream:!0});let m=i.split(`
|
|
475
|
+
`);i=m.pop()??"";for(let w of m){if(!w.startsWith("data: "))continue;let I=w.slice(6);if(I==="[DONE]"){s=!0,a.enqueue(r.encode(`data: [DONE]
|
|
476
|
+
|
|
477
|
+
`)),a.close();return}try{let x=JSON.parse(I),L=Ea(x,o);L&&(L.incrementToolIndex&&o++,a.enqueue(r.encode(`data: ${JSON.stringify(L.chunk)}
|
|
478
|
+
|
|
479
|
+
`)))}catch{}}}})}function Ea(t,e){let n=`anthropic-${Date.now().toString()}-${Math.random().toString(36).slice(2,8)}`;switch(t.type){case"content_block_start":return t.content_block?.type==="tool_use"?{chunk:{id:n,choices:[{index:0,delta:{tool_calls:[{index:e,id:t.content_block.id,type:"function",function:{name:t.content_block.name,arguments:""}}]},finish_reason:null}]},incrementToolIndex:!0}:null;case"content_block_delta":return t.delta?.type==="text_delta"&&t.delta.text?{chunk:{id:n,choices:[{index:0,delta:{content:t.delta.text},finish_reason:null}]}}:t.delta?.type==="input_json_delta"&&t.delta.partial_json?{chunk:{id:n,choices:[{index:0,delta:{tool_calls:[{index:e-1,function:{arguments:t.delta.partial_json}}]},finish_reason:null}]}}:null;case"message_delta":return{chunk:{id:n,choices:[{index:0,delta:{},finish_reason:"stop"}]}};default:return null}}var Sa=15,_a=`You are an AI Dungeon Master assistant for Foundry VTT. You have access to 124 tools across 19 domains, organized into bundles for efficient loading.
|
|
480
|
+
|
|
481
|
+
Your core tools (always available): dice, world, chat, combat, characters.
|
|
482
|
+
Additional bundles can be enabled on demand \u2014 call list-tool-bundles to see what's available, then enable-tool-bundle to activate what you need.
|
|
483
|
+
|
|
484
|
+
Your role:
|
|
485
|
+
- Help the DM run sessions by managing the VTT
|
|
486
|
+
- Control tokens, scenes, combat, lighting, music, and more
|
|
487
|
+
- Look up rules, spells, monsters from the compendium
|
|
488
|
+
- Roll dice, track HP, manage conditions
|
|
489
|
+
- Create atmospheric moments with lighting, weather, and sound
|
|
490
|
+
- Be creative, dramatic, and fun \u2014 you're a DM assistant!
|
|
491
|
+
|
|
492
|
+
Guidelines:
|
|
493
|
+
- Enable the right tool bundle before attempting domain-specific actions
|
|
494
|
+
- Use get-current-scene to understand token positions before moving tokens
|
|
495
|
+
- Use search-compendium before creating creatures (to get proper stat blocks)
|
|
496
|
+
- Use descriptive narration in chat messages for dramatic effect
|
|
497
|
+
- Keep tool calls efficient \u2014 batch operations when possible
|
|
498
|
+
- If a tool fails, explain what happened and suggest alternatives`,Ft=class{queryHandler;config=null;bundleManager=new wt;constructor(){this.queryHandler=bt()}loadConfig(){if(this.config={providerId:game.settings.get(y,"aiProvider"),apiKey:game.settings.get(y,"aiApiKey"),model:game.settings.get(y,"aiModel"),temperature:game.settings.get(y,"aiTemperature"),maxTokens:game.settings.get(y,"aiMaxTokens"),streaming:game.settings.get(y,"aiStreaming")},!this.config.model){let e=Z(this.config.providerId);this.config.model=e.defaultModel}}isConfigured(){return this.config||this.loadConfig(),!Z(this.config.providerId).requiresApiKey||this.config.apiKey.length>0}async chat(e,n){this.config||this.loadConfig();let r=this.config,i=Z(r.providerId),o=this.ensureSystemPrompt(e);for(let s=0;s<Sa;s++){let a=r.streaming?await this.streamRequest(i,r,o,n):await this.nonStreamRequest(i,r,o);if(o.push({role:"assistant",content:a.content,tool_calls:a.toolCalls}),!a.toolCalls||a.toolCalls.length===0){n?.onComplete?.(a.content??"");break}for(let c of a.toolCalls){n?.onToolCallStart?.(c.function.name,c.id);let l=await this.executeToolSafe(c);n?.onToolCallEnd?.(c.function.name,l),o.push({role:"tool",tool_call_id:c.id,content:l})}}return o}resetBundles(){this.bundleManager.reset()}async executeToolSafe(e){try{let n=JSON.parse(e.function.arguments);if(console.log(`${y} | AI executing tool: ${e.function.name}`,n),e.function.name==="list-tool-bundles")return JSON.stringify(this.bundleManager.listBundles());if(e.function.name==="enable-tool-bundle"){let i=this.bundleManager.enableBundle(n.bundleId);return JSON.stringify(i)}let r=await this.queryHandler(e.function.name,n);return JSON.stringify(r)}catch(n){return console.error(`${y} | Tool error: ${e.function.name}`,n),JSON.stringify({error:n instanceof Error?n.message:String(n)})}}async streamRequest(e,n,r,i){let o=await this.fetchChatCompletion(e,n,r,!0);if(!o.ok){let l=await o.text(),m=new Error(`${e.name} API error ${String(o.status)}: ${l}`);throw i?.onError?.(m),m}let s=ho();for await(let l of go(o)){let m=s.feed(l);m&&i?.onToken?.(m)}let a=s.getToolCalls(),c=a.length>0?a.map(l=>({id:l.id,type:"function",function:{name:l.name,arguments:l.arguments}})):void 0;return{content:s.getContent()||null,toolCalls:c}}async nonStreamRequest(e,n,r){let i=await this.fetchChatCompletion(e,n,r,!1);if(!i.ok){let a=await i.text();throw new Error(`${e.name} API error ${String(i.status)}: ${a}`)}let s=(await i.json()).choices[0];if(!s)throw new Error("No response choice from API");return{content:s.message.content,toolCalls:s.message.tool_calls}}async fetchChatCompletion(e,n,r,i){let o=this.bundleManager.getActiveTools();if(e.id==="anthropic")return yo(e,n,r,o,i);let s={model:n.model,messages:r.map(a=>Pa(a)),tools:o,temperature:n.temperature,max_tokens:n.maxTokens,stream:i};return fetch(e.chatEndpoint,{method:"POST",headers:e.buildHeaders(n.apiKey),body:JSON.stringify(s)})}ensureSystemPrompt(e){return e.some(r=>r.role==="system")?[...e]:[{role:"system",content:_a},...e]}};function Pa(t){switch(t.role){case"system":return{role:"system",content:t.content};case"user":return{role:"user",content:t.content};case"assistant":{let e={role:"assistant",content:t.content};return t.tool_calls&&(e.tool_calls=t.tool_calls),e}case"tool":return{role:"tool",tool_call_id:t.tool_call_id,content:t.content}}}var vo=null;function xt(){return vo??=new Ft,vo}var bo=`
|
|
499
|
+
/* ---- Container ---- */
|
|
500
|
+
.fmcp-chat {
|
|
501
|
+
display: flex;
|
|
502
|
+
flex-direction: column;
|
|
503
|
+
height: 100%;
|
|
504
|
+
background: #2b2a27;
|
|
505
|
+
color: #e8e4de;
|
|
506
|
+
font-family: var(--font-primary, 'Signika', sans-serif);
|
|
507
|
+
font-size: 13px;
|
|
508
|
+
line-height: 1.5;
|
|
509
|
+
overflow: hidden;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/* ---- Header ---- */
|
|
513
|
+
.fmcp-chat-header {
|
|
514
|
+
display: flex;
|
|
515
|
+
align-items: center;
|
|
516
|
+
justify-content: space-between;
|
|
517
|
+
padding: 8px 12px;
|
|
518
|
+
background: #242320;
|
|
519
|
+
border-bottom: 1px solid rgba(255,245,230,0.08);
|
|
520
|
+
flex-shrink: 0;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.fmcp-chat-title {
|
|
524
|
+
font-weight: 600;
|
|
525
|
+
font-size: 14px;
|
|
526
|
+
color: #d4cfc6;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
.fmcp-chat-title i {
|
|
530
|
+
margin-right: 6px;
|
|
531
|
+
color: #da7756;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.fmcp-chat-actions {
|
|
535
|
+
display: flex;
|
|
536
|
+
gap: 4px;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.fmcp-chat-actions button {
|
|
540
|
+
background: transparent;
|
|
541
|
+
border: 1px solid rgba(255,245,230,0.1);
|
|
542
|
+
color: #8a8680;
|
|
543
|
+
cursor: pointer;
|
|
544
|
+
padding: 4px 8px;
|
|
545
|
+
border-radius: 4px;
|
|
546
|
+
font-size: 12px;
|
|
547
|
+
transition: all 0.15s ease;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
.fmcp-chat-actions button:hover {
|
|
551
|
+
background: rgba(255,245,230,0.06);
|
|
552
|
+
color: #c8c0b8;
|
|
553
|
+
border-color: rgba(255,245,230,0.2);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/* ---- Messages area ---- */
|
|
557
|
+
.fmcp-chat-messages {
|
|
558
|
+
flex: 1;
|
|
559
|
+
overflow-y: auto;
|
|
560
|
+
padding: 12px;
|
|
561
|
+
display: flex;
|
|
562
|
+
flex-direction: column;
|
|
563
|
+
gap: 8px;
|
|
564
|
+
scroll-behavior: smooth;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
.fmcp-chat-messages::-webkit-scrollbar {
|
|
568
|
+
width: 6px;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.fmcp-chat-messages::-webkit-scrollbar-track {
|
|
572
|
+
background: transparent;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.fmcp-chat-messages::-webkit-scrollbar-thumb {
|
|
576
|
+
background: rgba(255,245,230,0.12);
|
|
577
|
+
border-radius: 3px;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/* ---- Input area ---- */
|
|
581
|
+
.fmcp-chat-input-area {
|
|
582
|
+
display: flex;
|
|
583
|
+
align-items: flex-end;
|
|
584
|
+
gap: 6px;
|
|
585
|
+
padding: 10px 12px;
|
|
586
|
+
background: #242320;
|
|
587
|
+
border-top: 1px solid rgba(255,245,230,0.08);
|
|
588
|
+
flex-shrink: 0;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.fmcp-chat-input {
|
|
592
|
+
flex: 1;
|
|
593
|
+
background: #302f2b;
|
|
594
|
+
border: 1px solid rgba(255,245,230,0.12);
|
|
595
|
+
border-radius: 8px;
|
|
596
|
+
padding: 8px 12px;
|
|
597
|
+
color: #e8e4de;
|
|
598
|
+
font-family: var(--font-primary, 'Signika', sans-serif);
|
|
599
|
+
font-size: 13px;
|
|
600
|
+
line-height: 1.4;
|
|
601
|
+
resize: none;
|
|
602
|
+
min-height: 36px;
|
|
603
|
+
max-height: 120px;
|
|
604
|
+
overflow-y: auto;
|
|
605
|
+
outline: none;
|
|
606
|
+
transition: border-color 0.15s ease;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.fmcp-chat-input:focus {
|
|
610
|
+
border-color: rgba(218,119,86,0.5);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.fmcp-chat-input::placeholder {
|
|
614
|
+
color: #6a6660;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.fmcp-chat-input:disabled {
|
|
618
|
+
opacity: 0.5;
|
|
619
|
+
cursor: not-allowed;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.fmcp-chat-send {
|
|
623
|
+
background: #da7756;
|
|
624
|
+
border: none;
|
|
625
|
+
border-radius: 8px;
|
|
626
|
+
padding: 8px 12px;
|
|
627
|
+
color: white;
|
|
628
|
+
cursor: pointer;
|
|
629
|
+
font-size: 14px;
|
|
630
|
+
transition: all 0.15s ease;
|
|
631
|
+
flex-shrink: 0;
|
|
632
|
+
height: 36px;
|
|
633
|
+
display: flex;
|
|
634
|
+
align-items: center;
|
|
635
|
+
justify-content: center;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.fmcp-chat-send:hover {
|
|
639
|
+
background: #c46848;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
.fmcp-chat-send:disabled {
|
|
643
|
+
background: #4a4540;
|
|
644
|
+
cursor: not-allowed;
|
|
645
|
+
opacity: 0.5;
|
|
646
|
+
}
|
|
647
|
+
`;var wo=`
|
|
648
|
+
/* ---- Welcome message ---- */
|
|
649
|
+
.fmcp-welcome {
|
|
650
|
+
text-align: center;
|
|
651
|
+
padding: 32px 16px;
|
|
652
|
+
color: #8a8680;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
.fmcp-welcome i {
|
|
656
|
+
font-size: 32px;
|
|
657
|
+
color: #da7756;
|
|
658
|
+
margin-bottom: 12px;
|
|
659
|
+
display: block;
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
.fmcp-welcome h3 {
|
|
663
|
+
color: #d4cfc6;
|
|
664
|
+
margin: 0 0 8px;
|
|
665
|
+
font-size: 16px;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.fmcp-welcome p {
|
|
669
|
+
margin: 4px 0;
|
|
670
|
+
font-size: 12px;
|
|
671
|
+
line-height: 1.6;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/* ---- Message bubbles ---- */
|
|
675
|
+
.fmcp-msg {
|
|
676
|
+
max-width: 92%;
|
|
677
|
+
animation: fmcp-fade-in 0.2s ease;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
@keyframes fmcp-fade-in {
|
|
681
|
+
from { opacity: 0; transform: translateY(4px); }
|
|
682
|
+
to { opacity: 1; transform: translateY(0); }
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
.fmcp-msg-user {
|
|
686
|
+
align-self: flex-end;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.fmcp-msg-user .fmcp-msg-bubble {
|
|
690
|
+
background: #6b4c3a;
|
|
691
|
+
border-radius: 12px 12px 4px 12px;
|
|
692
|
+
padding: 8px 12px;
|
|
693
|
+
color: #f0e8e0;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
.fmcp-msg-assistant {
|
|
697
|
+
align-self: flex-start;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
.fmcp-msg-assistant .fmcp-msg-bubble {
|
|
701
|
+
background: #353430;
|
|
702
|
+
border-radius: 12px 12px 12px 4px;
|
|
703
|
+
padding: 10px 14px;
|
|
704
|
+
border-left: 3px solid #da7756;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/* ---- Message content (markdown) ---- */
|
|
708
|
+
.fmcp-msg-content {
|
|
709
|
+
word-wrap: break-word;
|
|
710
|
+
overflow-wrap: break-word;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
.fmcp-msg-content p {
|
|
714
|
+
margin: 0 0 8px;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
.fmcp-msg-content p:last-child {
|
|
718
|
+
margin-bottom: 0;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
.fmcp-msg-content h1, .fmcp-msg-content h2, .fmcp-msg-content h3 {
|
|
722
|
+
margin: 12px 0 6px;
|
|
723
|
+
color: #e8e0d6;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.fmcp-msg-content h1 { font-size: 16px; }
|
|
727
|
+
.fmcp-msg-content h2 { font-size: 15px; }
|
|
728
|
+
.fmcp-msg-content h3 { font-size: 14px; }
|
|
729
|
+
|
|
730
|
+
.fmcp-msg-content strong {
|
|
731
|
+
color: #f0e8dd;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
.fmcp-msg-content code {
|
|
735
|
+
background: rgba(0,0,0,0.25);
|
|
736
|
+
padding: 1px 5px;
|
|
737
|
+
border-radius: 3px;
|
|
738
|
+
font-family: var(--font-mono, 'Fira Code', monospace);
|
|
739
|
+
font-size: 12px;
|
|
740
|
+
color: #e0c8a8;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.fmcp-msg-content pre {
|
|
744
|
+
background: #1a1918;
|
|
745
|
+
border: 1px solid rgba(255,245,230,0.08);
|
|
746
|
+
border-radius: 6px;
|
|
747
|
+
padding: 10px 12px;
|
|
748
|
+
overflow-x: auto;
|
|
749
|
+
margin: 8px 0;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
.fmcp-msg-content pre code {
|
|
753
|
+
background: none;
|
|
754
|
+
padding: 0;
|
|
755
|
+
font-size: 12px;
|
|
756
|
+
color: #d8d0c8;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
.fmcp-msg-content blockquote {
|
|
760
|
+
border-left: 3px solid #da7756;
|
|
761
|
+
margin: 8px 0;
|
|
762
|
+
padding: 4px 12px;
|
|
763
|
+
color: #a8a098;
|
|
764
|
+
background: rgba(218,119,86,0.06);
|
|
765
|
+
border-radius: 0 4px 4px 0;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
.fmcp-msg-content ul, .fmcp-msg-content ol {
|
|
769
|
+
margin: 6px 0;
|
|
770
|
+
padding-left: 20px;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.fmcp-msg-content li {
|
|
774
|
+
margin: 2px 0;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
.fmcp-msg-content a {
|
|
778
|
+
color: #da9070;
|
|
779
|
+
text-decoration: underline;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
.fmcp-msg-content hr {
|
|
783
|
+
border: none;
|
|
784
|
+
border-top: 1px solid rgba(255,245,230,0.1);
|
|
785
|
+
margin: 10px 0;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/* ---- Tool calls ---- */
|
|
789
|
+
.fmcp-tool-group {
|
|
790
|
+
display: flex;
|
|
791
|
+
flex-wrap: wrap;
|
|
792
|
+
gap: 4px;
|
|
793
|
+
margin: 8px 0;
|
|
794
|
+
padding: 6px 0;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
.fmcp-tool-call {
|
|
798
|
+
display: inline-flex;
|
|
799
|
+
align-items: center;
|
|
800
|
+
gap: 5px;
|
|
801
|
+
padding: 3px 8px;
|
|
802
|
+
border-radius: 10px;
|
|
803
|
+
font-size: 11px;
|
|
804
|
+
font-weight: 500;
|
|
805
|
+
background: rgba(218,119,86,0.1);
|
|
806
|
+
color: #c09880;
|
|
807
|
+
border: 1px solid rgba(218,119,86,0.2);
|
|
808
|
+
cursor: pointer;
|
|
809
|
+
transition: all 0.15s ease;
|
|
810
|
+
position: relative;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
.fmcp-tool-call:hover {
|
|
814
|
+
background: rgba(218,119,86,0.18);
|
|
815
|
+
border-color: rgba(218,119,86,0.35);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
.fmcp-tool-call i {
|
|
819
|
+
font-size: 10px;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
.fmcp-tool-running {
|
|
823
|
+
color: #d4b060;
|
|
824
|
+
border-color: rgba(212,176,96,0.3);
|
|
825
|
+
background: rgba(212,176,96,0.08);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
.fmcp-tool-running .fmcp-tool-status {
|
|
829
|
+
animation: fmcp-spin 1s linear infinite;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
@keyframes fmcp-spin {
|
|
833
|
+
from { transform: rotate(0deg); }
|
|
834
|
+
to { transform: rotate(360deg); }
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
.fmcp-tool-done {
|
|
838
|
+
color: #7ab87a;
|
|
839
|
+
border-color: rgba(122,184,122,0.3);
|
|
840
|
+
background: rgba(122,184,122,0.08);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
.fmcp-tool-error {
|
|
844
|
+
color: #d07060;
|
|
845
|
+
border-color: rgba(208,112,96,0.3);
|
|
846
|
+
background: rgba(208,112,96,0.08);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/* Tool call result popup */
|
|
850
|
+
.fmcp-tool-result {
|
|
851
|
+
display: none;
|
|
852
|
+
position: absolute;
|
|
853
|
+
bottom: calc(100% + 6px);
|
|
854
|
+
left: 0;
|
|
855
|
+
background: #1a1918;
|
|
856
|
+
border: 1px solid rgba(255,245,230,0.15);
|
|
857
|
+
border-radius: 6px;
|
|
858
|
+
padding: 8px 10px;
|
|
859
|
+
font-size: 11px;
|
|
860
|
+
font-family: var(--font-mono, monospace);
|
|
861
|
+
color: #c8c0b8;
|
|
862
|
+
max-width: 400px;
|
|
863
|
+
max-height: 200px;
|
|
864
|
+
overflow: auto;
|
|
865
|
+
white-space: pre-wrap;
|
|
866
|
+
word-break: break-all;
|
|
867
|
+
z-index: 1000;
|
|
868
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
.fmcp-tool-call:hover .fmcp-tool-result {
|
|
872
|
+
display: block;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/* ---- Streaming indicator ---- */
|
|
876
|
+
.fmcp-streaming-cursor::after {
|
|
877
|
+
content: '\u258B';
|
|
878
|
+
animation: fmcp-blink 0.8s steps(2) infinite;
|
|
879
|
+
color: #da7756;
|
|
880
|
+
margin-left: 1px;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
@keyframes fmcp-blink {
|
|
884
|
+
0%, 100% { opacity: 1; }
|
|
885
|
+
50% { opacity: 0; }
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/* ---- Typing indicator (before first token) ---- */
|
|
889
|
+
.fmcp-thinking {
|
|
890
|
+
display: flex;
|
|
891
|
+
align-items: center;
|
|
892
|
+
gap: 4px;
|
|
893
|
+
color: #8a8680;
|
|
894
|
+
font-size: 12px;
|
|
895
|
+
padding: 4px 0;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
.fmcp-thinking-dots span {
|
|
899
|
+
display: inline-block;
|
|
900
|
+
width: 5px;
|
|
901
|
+
height: 5px;
|
|
902
|
+
border-radius: 50%;
|
|
903
|
+
background: #da7756;
|
|
904
|
+
animation: fmcp-dot-pulse 1.4s ease-in-out infinite;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.fmcp-thinking-dots span:nth-child(2) { animation-delay: 0.2s; }
|
|
908
|
+
.fmcp-thinking-dots span:nth-child(3) { animation-delay: 0.4s; }
|
|
909
|
+
|
|
910
|
+
@keyframes fmcp-dot-pulse {
|
|
911
|
+
0%, 80%, 100% { opacity: 0.2; transform: scale(0.8); }
|
|
912
|
+
40% { opacity: 1; transform: scale(1); }
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/* ---- Error message ---- */
|
|
916
|
+
.fmcp-error {
|
|
917
|
+
background: rgba(208,100,80,0.12);
|
|
918
|
+
border: 1px solid rgba(208,100,80,0.3);
|
|
919
|
+
border-radius: 8px;
|
|
920
|
+
padding: 8px 12px;
|
|
921
|
+
color: #d08070;
|
|
922
|
+
font-size: 12px;
|
|
923
|
+
margin: 4px 0;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.fmcp-error i {
|
|
927
|
+
margin-right: 6px;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/* ---- Timestamp ---- */
|
|
931
|
+
.fmcp-msg-time {
|
|
932
|
+
font-size: 10px;
|
|
933
|
+
color: #5a5650;
|
|
934
|
+
margin-top: 4px;
|
|
935
|
+
text-align: right;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
.fmcp-msg-assistant .fmcp-msg-time {
|
|
939
|
+
text-align: left;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/* ---- Not configured state ---- */
|
|
943
|
+
.fmcp-not-configured {
|
|
944
|
+
text-align: center;
|
|
945
|
+
padding: 32px 16px;
|
|
946
|
+
color: #8a8680;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
.fmcp-not-configured i {
|
|
950
|
+
font-size: 28px;
|
|
951
|
+
color: #d4b060;
|
|
952
|
+
margin-bottom: 12px;
|
|
953
|
+
display: block;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
.fmcp-not-configured h3 {
|
|
957
|
+
color: #d4cfc6;
|
|
958
|
+
margin: 0 0 8px;
|
|
959
|
+
font-size: 15px;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
.fmcp-not-configured p {
|
|
963
|
+
margin: 4px 0;
|
|
964
|
+
font-size: 12px;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.fmcp-btn-configure {
|
|
968
|
+
display: inline-block;
|
|
969
|
+
margin-top: 12px;
|
|
970
|
+
background: #da7756;
|
|
971
|
+
color: white;
|
|
972
|
+
border: none;
|
|
973
|
+
border-radius: 6px;
|
|
974
|
+
padding: 8px 16px;
|
|
975
|
+
cursor: pointer;
|
|
976
|
+
font-size: 13px;
|
|
977
|
+
transition: background 0.15s ease;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
.fmcp-btn-configure:hover {
|
|
981
|
+
background: #c46848;
|
|
982
|
+
}
|
|
983
|
+
`;var xo=bo+wo;function Vt(t){if(!t)return"";let e=R(t.replace(/\r\n/g,`
|
|
984
|
+
`)),n=[],r=[];e=Ma(e,n),e=Ia(e,r),e=Aa(e);let i=La(e.split(`
|
|
985
|
+
`));return i=Oa(i,n,r),i=i.replace(/<p>(<(?:pre|ul|ol|blockquote|h[1-6]|hr)[^>]*>)/g,"$1"),i=i.replace(/(<\/(?:pre|ul|ol|blockquote|h[1-6])>)<\/p>/g,"$1"),i}function Ma(t,e){return t.replace(/```(\w*)\n([\s\S]*?)```/g,(n,r,i)=>{let o=r?` class="language-${r}"`:"",s=`\0CB${String(e.length)}\0`;return e.push(`<pre><code${o}>${i.trimEnd()}</code></pre>`),s})}function Ia(t,e){return t.replace(/`([^`\n]+)`/g,(n,r)=>{let i=`\0IC${String(e.length)}\0`;return e.push(`<code>${r}</code>`),i})}function Aa(t){return t.replace(/\*\*\*(.+?)\*\*\*/g,"<strong><em>$1</em></strong>").replace(/\*\*(.+?)\*\*/g,"<strong>$1</strong>").replace(/\*(.+?)\*/g,"<em>$1</em>").replace(/\[([^\]]+)\]\(([^)]+)\)/g,'<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>')}var Ra=/^(?:#{1,6} |[-*] |\d+\. |> |---|\*\*\*|___)/;function La(t){let e=[],n=0,r=i=>t[i]??"";for(;n<t.length;){let i=r(n),o=/^(#{1,6}) (.+)$/.exec(i);if(o){let s=(o[1]??"").length;e.push(`<h${String(s)}>${o[2]??""}</h${String(s)}>`),n++;continue}if(i.startsWith("> ")){n=Da(t,n,r,e);continue}if(/^[-*] /.test(i)){n=ko(t,n,r,e,/^[-*] /,"ul");continue}if(/^\d+\. /.test(i)){n=ko(t,n,r,e,/^\d+\. /,"ol");continue}if(/^(?:---|\*\*\*|___)$/.test(i)){e.push("<hr>"),n++;continue}if(i.trim()===""){e.push(""),n++;continue}n=ja(t,n,r,e)}return e.join(`
|
|
986
|
+
`)}function Da(t,e,n,r){let i=[];for(;e<t.length&&n(e).startsWith("> ");)i.push(n(e).slice(5)),e++;return r.push(`<blockquote>${i.join("<br>")}</blockquote>`),e}function ko(t,e,n,r,i,o){let s=[];for(;e<t.length&&i.test(n(e));)s.push(`<li>${n(e).replace(i,"")}</li>`),e++;return r.push(`<${o}>${s.join("")}</${o}>`),e}function ja(t,e,n,r){let i=[];for(;e<t.length;){let o=n(e);if(o.trim()===""||Ra.test(o))break;if(o.includes("\0CB")){if(i.length>0)break;return r.push(o),e+1}i.push(o),e++}return i.length>0&&r.push(`<p>${i.join("<br>")}</p>`),e}function Oa(t,e,n){let r=t;for(let i=0;i<e.length;i++)r=r.replace(`\0CB${String(i)}\0`,e[i]??"");for(let i=0;i<n.length;i++)r=r.replace(`\0IC${String(i)}\0`,n[i]??"");return r}var $a=[["get-","fa-magnifying-glass"],["list-","fa-magnifying-glass"],["search-","fa-magnifying-glass"],["create-","fa-plus"],["add-","fa-plus"],["update-","fa-pen"],["set-","fa-pen"],["rename-","fa-pen"],["delete-","fa-trash-can"],["remove-","fa-trash-can"],["move-","fa-up-down-left-right"],["pan-","fa-up-down-left-right"],["roll-","fa-dice-d20"],["play-","fa-play"],["begin-","fa-play"],["start-","fa-play"],["stop-","fa-stop"],["complete-","fa-flag-checkered"],["end-","fa-flag-checkered"],["toggle-","fa-toggle-on"],["send-","fa-paper-plane"],["switch-","fa-right-left"],["capture-","fa-camera"],["next-","fa-forward-step"],["advance-","fa-clock"],["close-","fa-door-closed"],["shuffle-","fa-shuffle"],["draw-","fa-shuffle"],["reset-","fa-rotate-left"],["execute-","fa-terminal"],["duplicate-","fa-copy"],["assign-","fa-user-plus"]];function zt(t){let e=$a.find(([n])=>t.startsWith(n));return`fa-solid ${e?e[1]:"fa-gear"}`}function Ut(t){return t.replace(/-/g," ").replace(/\b\w/g,e=>e.toUpperCase())}function Gt(t,e=600){return t.length<=e?t:t.slice(0,e)+`
|
|
987
|
+
\u2026 (truncated)`}function Bt(t){let e=new Date(t);return`${String(e.getHours()).padStart(2,"0")}:${String(e.getMinutes()).padStart(2,"0")}`}function kt(){return{currentAssistantEl:null,currentContentEl:null,currentToolGroupEl:null,thinkingEl:null,streamedText:"",hasReceivedTokens:!1}}function To(t,e){let n=document.createElement("div");n.classList.add("fmcp-msg","fmcp-msg-user"),n.innerHTML=`
|
|
988
|
+
<div class="fmcp-msg-bubble">${R(e).replace(/\n/g,"<br>")}</div>
|
|
989
|
+
<div class="fmcp-msg-time">${Bt(Date.now())}</div>
|
|
990
|
+
`,t.appendChild(n),nt(t)}function Co(t,e){e.streamedText="",e.hasReceivedTokens=!1,e.currentContentEl=null,e.currentToolGroupEl=null;let n=document.createElement("div");n.classList.add("fmcp-msg","fmcp-msg-assistant");let r=document.createElement("div");r.classList.add("fmcp-msg-bubble"),n.appendChild(r),e.thinkingEl=document.createElement("div"),e.thinkingEl.classList.add("fmcp-thinking"),e.thinkingEl.innerHTML='<span class="fmcp-thinking-dots"><span></span><span></span><span></span></span>',r.appendChild(e.thinkingEl),e.currentAssistantEl=n,t.appendChild(n),nt(t)}function Eo(t,e,n){t.currentAssistantEl&&(t.hasReceivedTokens||(t.hasReceivedTokens=!0,t.thinkingEl?.remove(),t.thinkingEl=null),Na(t)&&(t.currentContentEl=document.createElement("div"),t.currentContentEl.classList.add("fmcp-msg-content","fmcp-streaming-cursor"),t.streamedText="",t.currentAssistantEl.querySelector(".fmcp-msg-bubble")?.appendChild(t.currentContentEl)),t.streamedText+=n,t.currentContentEl&&(t.currentContentEl.innerHTML=Vt(t.streamedText)),e&&nt(e))}function Na(t){if(!t.currentContentEl)return!0;let e=t.currentAssistantEl?.querySelector(".fmcp-msg-bubble");return e?e.lastElementChild?.classList.contains("fmcp-tool-group")===!0:!0}function So(t,e,n,r,i){if(!t.currentAssistantEl)return;t.currentContentEl?.classList.remove("fmcp-streaming-cursor");let o=t.currentAssistantEl.querySelector(".fmcp-msg-bubble");if(!o)return;(!t.currentToolGroupEl||o.lastElementChild!==t.currentToolGroupEl)&&(t.currentToolGroupEl=document.createElement("div"),t.currentToolGroupEl.classList.add("fmcp-tool-group"),o.appendChild(t.currentToolGroupEl));let s=document.createElement("span");s.classList.add("fmcp-tool-call",`fmcp-tool-${i}`),s.dataset.toolId=r,s.dataset.toolName=n;let a=zt(n),c=i==="running"?"fa-solid fa-spinner fmcp-tool-status":"fa-solid fa-check";s.innerHTML=`<i class="${a}"></i><span>${Ut(n)}</span><i class="${c}"></i>`,t.currentToolGroupEl.appendChild(s),e&&nt(e)}function _o(t,e,n,r){if(!t.currentToolGroupEl)return;let i=t.currentToolGroupEl.querySelectorAll(`.fmcp-tool-running[data-tool-name="${e}"]`),o=i[i.length-1];if(!o)return;o.classList.remove("fmcp-tool-running"),o.classList.add(`fmcp-tool-${n}`);let s=o.querySelector(".fmcp-tool-status");if(s&&(s.className=n==="done"?"fa-solid fa-check":"fa-solid fa-xmark"),r){let a=document.createElement("div");a.classList.add("fmcp-tool-result");try{let c=JSON.parse(r);a.textContent=Gt(JSON.stringify(c,null,2))}catch{a.textContent=Gt(r)}o.appendChild(a)}}function Po(t){if(t.currentContentEl?.classList.remove("fmcp-streaming-cursor"),t.thinkingEl?.remove(),t.thinkingEl=null,t.currentAssistantEl){let e=document.createElement("div");e.classList.add("fmcp-msg-time"),e.textContent=Bt(Date.now()),t.currentAssistantEl.appendChild(e)}t.currentAssistantEl=null,t.currentContentEl=null,t.currentToolGroupEl=null,t.streamedText=""}function Tt(t,e){let n=document.createElement("div");n.classList.add("fmcp-error"),n.innerHTML=`<i class="fa-solid fa-circle-exclamation"></i>${R(e)}`,t.appendChild(n),nt(t)}function Mo(t,e){for(let n of e)if(n.role!=="system"){if(n.role==="user"&&n.content){let r=document.createElement("div");r.classList.add("fmcp-msg","fmcp-msg-user"),r.innerHTML=`<div class="fmcp-msg-bubble">${R(n.content).replace(/\n/g,"<br>")}</div>`,t.appendChild(r)}if(n.role==="assistant"){let r=document.createElement("div");r.classList.add("fmcp-msg","fmcp-msg-assistant");let i=document.createElement("div");if(i.classList.add("fmcp-msg-bubble"),n.content){let o=document.createElement("div");o.classList.add("fmcp-msg-content"),o.innerHTML=Vt(n.content),i.appendChild(o)}if(n.tool_calls&&n.tool_calls.length>0){let o=document.createElement("div");o.classList.add("fmcp-tool-group");for(let s of n.tool_calls){let a=document.createElement("span");a.classList.add("fmcp-tool-call","fmcp-tool-done"),a.innerHTML=`<i class="${zt(s.function.name)}"></i><span>${Ut(s.function.name)}</span><i class="fa-solid fa-check"></i>`,o.appendChild(a)}i.appendChild(o)}r.appendChild(i),t.appendChild(r)}}}function nt(t){requestAnimationFrame(()=>{t.scrollTop=t.scrollHeight})}var qa=foundry.applications.api.ApplicationV2,Kt=class extends qa{static DEFAULT_OPTIONS={id:"familiar-chat",classes:["familiar-chat-window"],window:{title:"AI Dungeon Master",icon:"fas fa-hat-wizard",resizable:!0,contentClasses:["fmcp-chat-window-content"]},position:{width:480,height:640}};apiMessages=[];isProcessing=!1;llmClient;messagesEl=null;inputEl=null;sendBtn=null;streaming=kt();constructor(e){super(e),this.llmClient=xt()}async _renderHTML(e,n){let r=document.createElement("div");r.classList.add("fmcp-chat");let i=document.createElement("style");i.textContent=xo,r.appendChild(i);let o=document.createElement("div");o.classList.add("fmcp-chat-header"),o.innerHTML=`
|
|
991
|
+
<span class="fmcp-chat-title"><i class="fa-solid fa-hat-wizard"></i>AI Dungeon Master</span>
|
|
992
|
+
<div class="fmcp-chat-actions">
|
|
993
|
+
<button class="fmcp-btn-settings" title="AI Settings"><i class="fa-solid fa-gear"></i></button>
|
|
994
|
+
<button class="fmcp-btn-clear" title="Clear chat"><i class="fa-solid fa-trash-can"></i></button>
|
|
995
|
+
</div>
|
|
996
|
+
`,r.appendChild(o);let s=document.createElement("div");s.classList.add("fmcp-chat-messages"),r.appendChild(s),this.apiMessages.length===0?s.innerHTML=this.buildWelcomeHTML():Mo(s,this.apiMessages);let a=document.createElement("div");return a.classList.add("fmcp-chat-input-area"),a.innerHTML=`
|
|
997
|
+
<textarea class="fmcp-chat-input" placeholder="Ask your AI DM\u2026" rows="1"${this.isProcessing?" disabled":""}></textarea>
|
|
998
|
+
<button class="fmcp-chat-send" title="Send (Enter)"${this.isProcessing?" disabled":""}><i class="fa-solid fa-paper-plane"></i></button>
|
|
999
|
+
`,r.appendChild(a),r}_replaceHTML(e,n,r){n.replaceChildren(e),this.activateListeners(n)}activateListeners(e){this.messagesEl=e.querySelector(".fmcp-chat-messages"),this.inputEl=e.querySelector(".fmcp-chat-input"),this.sendBtn=e.querySelector(".fmcp-chat-send"),this.sendBtn?.addEventListener("click",()=>{this.sendMessage()}),this.inputEl?.addEventListener("keydown",n=>{n.key==="Enter"&&!n.shiftKey&&(n.preventDefault(),this.sendMessage())}),this.inputEl?.addEventListener("input",()=>{this.inputEl&&(this.inputEl.style.height="auto",this.inputEl.style.height=`${String(Math.min(this.inputEl.scrollHeight,120))}px`)}),e.querySelector(".fmcp-btn-clear")?.addEventListener("click",()=>{this.clearChat()}),e.querySelector(".fmcp-btn-settings")?.addEventListener("click",()=>{Io()}),e.querySelector(".fmcp-btn-configure")?.addEventListener("click",()=>{Io()}),this.inputEl?.focus()}async sendMessage(){if(this.isProcessing||!this.inputEl||!this.messagesEl)return;let e=this.inputEl.value.trim();if(!e)return;if(this.llmClient.loadConfig(),!this.llmClient.isConfigured()){Tt(this.messagesEl,"AI provider not configured. Click the gear icon to set up your API key.");return}this.inputEl.value="",this.inputEl.style.height="auto";let n=this.messagesEl.querySelector(".fmcp-welcome, .fmcp-not-configured");n&&n.remove();let r={role:"user",content:e};this.apiMessages.push(r),To(this.messagesEl,e),this.setProcessing(!0),this.streaming=kt(),Co(this.messagesEl,this.streaming);try{let i=this.buildStreamCallbacks(),o=await this.llmClient.chat(this.apiMessages,i);this.apiMessages=o}catch(i){let o=i instanceof Error?i.message:String(i);Tt(this.messagesEl,o),console.error(`${y} | Chat error:`,i)}finally{Po(this.streaming),this.setProcessing(!1)}}buildStreamCallbacks(){return{onToken:e=>{Eo(this.streaming,this.messagesEl,e)},onToolCallStart:(e,n)=>{So(this.streaming,this.messagesEl,e,n,"running")},onToolCallEnd:(e,n)=>{_o(this.streaming,e,"done",n)},onComplete:e=>{},onError:e=>{this.messagesEl&&Tt(this.messagesEl,e.message)}}}setProcessing(e){this.isProcessing=e,this.inputEl&&(this.inputEl.disabled=e,e||this.inputEl.focus()),this.sendBtn&&(this.sendBtn.disabled=e)}clearChat(){this.apiMessages=[],this.streaming=kt(),this.isProcessing=!1,this.messagesEl&&(this.messagesEl.innerHTML=this.buildWelcomeHTML()),this.setProcessing(!1)}buildWelcomeHTML(){return this.llmClient.isConfigured()?`
|
|
1000
|
+
<div class="fmcp-welcome">
|
|
1001
|
+
<i class="fa-solid fa-hat-wizard"></i>
|
|
1002
|
+
<h3>AI Dungeon Master</h3>
|
|
1003
|
+
<p>Your arcane assistant awaits your command.</p>
|
|
1004
|
+
<p>Summon creatures, wage battles, conjure music, consult the tomes \u2014 just ask.</p>
|
|
1005
|
+
</div>
|
|
1006
|
+
`:`
|
|
1007
|
+
<div class="fmcp-not-configured">
|
|
1008
|
+
<i class="fa-solid fa-key"></i>
|
|
1009
|
+
<h3>API Key Required</h3>
|
|
1010
|
+
<p>Configure your AI provider to start chatting.</p>
|
|
1011
|
+
<button class="fmcp-btn-configure">Configure AI Provider</button>
|
|
1012
|
+
</div>
|
|
1013
|
+
`}};function Io(){let t=game.settings.menus.get(`${y}.aiSettingsMenu`);t&&new t.type().render({force:!0})}var Ao=null;function rt(){return Ao??=new Kt,Ao}function Ct(){rt().render({force:!0})}var Wt=null;Hooks.once("init",()=>{console.log(`${y} | Initializing`),game.settings.register(y,"wsPort",{name:"WebSocket Port",hint:"Port the MCP server listens on. Must match FAMILIAR_WS_PORT.",scope:"world",config:!0,type:Number,default:3005,requiresReload:!0}),game.settings.register(y,"autoConnect",{name:"Auto-Connect",hint:"Automatically connect to the MCP server when the world loads.",scope:"world",config:!0,type:Boolean,default:!0}),game.settings.registerMenu(y,"aiSettingsMenu",{name:"AI Provider Settings",label:"Configure AI",hint:"Set up your AI provider, API key, and model for the built-in AI DM.",icon:"fas fa-robot",type:mt,restricted:!0}),game.settings.register(y,"aiProvider",{name:"AI Provider",scope:"client",config:!1,type:String,default:"openrouter"}),game.settings.register(y,"aiApiKey",{name:"AI API Key",scope:"client",config:!1,type:String,default:""}),game.settings.register(y,"aiModel",{name:"AI Model",scope:"client",config:!1,type:String,default:""}),game.settings.register(y,"aiTemperature",{name:"Temperature",scope:"client",config:!1,type:Number,default:.8}),game.settings.register(y,"aiMaxTokens",{name:"Max Tokens",scope:"client",config:!1,type:Number,default:4096}),game.settings.register(y,"aiStreaming",{name:"Streaming",scope:"client",config:!1,type:Boolean,default:!0}),game.settings.register(y,"voiceEnabled",{name:"Voice Enabled",scope:"client",config:!1,type:Boolean,default:!1}),game.settings.register(y,"voiceProvider",{name:"Voice Provider",scope:"client",config:!1,type:String,default:"elevenlabs"}),game.settings.register(y,"voiceApiKey",{name:"Voice API Key",scope:"client",config:!1,type:String,default:""}),game.settings.register(y,"voiceId",{name:"Voice ID",scope:"client",config:!1,type:String,default:""}),game.settings.register(y,"imageEnabled",{name:"Image Enabled",scope:"client",config:!1,type:Boolean,default:!1}),game.settings.register(y,"imageProvider",{name:"Image Provider",scope:"client",config:!1,type:String,default:"openai_image"}),game.settings.register(y,"imageApiKey",{name:"Image API Key",scope:"client",config:!1,type:String,default:""}),game.settings.register(y,"imageModel",{name:"Image Model",scope:"client",config:!1,type:String,default:""})});Hooks.once("ready",()=>{if(!game.user?.isGM){console.log(`${y} | Non-GM user, module disabled`);return}if(game.settings.get(y,"autoConnect")){let n=game.settings.get(y,"wsPort"),r=bt();Wt=new lt(n,r),Wt.connect(),console.log(`${y} | Ready \u2014 connecting to MCP server on port ${String(n)}`)}else console.log(`${y} | Auto-connect disabled`);let e=xt();e.loadConfig(),console.log(`${y} | LLM client initialized (configured: ${String(e.isConfigured())})`),globalThis.familiar={get connection(){return Wt},chat:n=>e.chat([{role:"user",content:n}],{onToken:r=>{globalThis.process?.stdout?.write?.(r)},onToolCallStart:r=>console.log(`Tool calling: ${r}`),onToolCallEnd:r=>console.log(`Tool done: ${r}`),onComplete:r=>console.log(`
|
|
1014
|
+
--- Response complete ---
|
|
1015
|
+
`,r),onError:r=>console.error("AI error:",r)}),client:e,openChat:Ct,getChatApp:rt,getMessages:()=>rt().apiMessages.filter(r=>r.role!=="system").map(r=>{if(r.role==="user")return{role:"user",content:r.content};if(r.role==="assistant"){let i=r.tool_calls?.map(o=>o.function.name)??[];return{role:"assistant",content:r.content,tools:i}}return{role:r.role,content:r.content.slice(0,200)}}),isProcessing:()=>rt().isProcessing},console.log(`${y} | AI Chat: familiar.openChat() or use the scene control button`)});Hooks.once("ready",()=>{if(!game.user?.isGM)return;let t=document.createElement("button");t.id="familiar-chat-btn",t.title="AI Dungeon Master (Ctrl+Shift+Space)",t.innerHTML='<i class="fa-solid fa-hat-wizard"></i>',t.addEventListener("click",()=>Ct()),Object.assign(t.style,{position:"fixed",bottom:"84px",left:"12px",zIndex:"100",width:"42px",height:"42px",borderRadius:"50%",background:"#da7756",color:"#fff",border:"2px solid rgba(255,255,255,0.15)",cursor:"pointer",fontSize:"18px",display:"flex",alignItems:"center",justifyContent:"center",boxShadow:"0 2px 8px rgba(0,0,0,0.4)",transition:"transform 0.15s ease, background 0.15s ease"}),t.addEventListener("mouseenter",()=>{t.style.transform="scale(1.1)",t.style.background="#c46848"}),t.addEventListener("mouseleave",()=>{t.style.transform="scale(1)",t.style.background="#da7756"});let e=document.createElement("span");e.id="familiar-mcp-dot",Object.assign(e.style,{position:"absolute",top:"-2px",right:"-2px",width:"12px",height:"12px",borderRadius:"50%",border:"2px solid #2b2b2b",display:"none"}),t.appendChild(e),Hooks.on("familiar:mcpStatus",(...n)=>{let r=n[0];r==="connected"?(e.style.display="",e.style.background="#a6e3a1"):r==="connecting"?(e.style.display="",e.style.background="#f9e2af"):e.style.display="none"}),document.body.appendChild(t)});Hooks.once("init",()=>{game.keybindings?.register(y,"openChat",{name:"Open AI Chat",hint:"Open or focus the AI Dungeon Master chat window.",editable:[{key:"Space",modifiers:["Control","Shift"]}],onDown:()=>(Ct(),!0),restricted:!0,precedence:0})});export{Wt as connection};
|
|
1016
|
+
//# sourceMappingURL=main.js.map
|