1import{r as e,n as t,B as n,x as a,a5 as i,a6 as s,a9 as o,aQ as r}from"./index-DCpYiqWz.js";const l=["carress","grab","milk","stroke","suck","tease"],c=["shock","whip"],d={carress:[4,6,8,9,10],grab:[3,4,6,8,10],milk:[1,2,3,5,6,7,8,9,10],stroke:[1,2,3,5,6,7,10],suck:[1,2,4,5,6,7,8,10],tease:[0,1,3,4,5,6,7,8,9,10],shock:[3,6,10],whip:[1,2,5,6,7,8,9,10]},u="/estim-audio/base.mp3",m="/estim-audio/base_pain.mp3";function p(e){return c.includes(e)?"pain":"pleasure"}const b={engagementMode:"organic",customGuidance:"",displayName:"e-stim",toolName:"stimulate_genitals",masterVolume:70,pleasureMinIntensity:10,pleasureMaxIntensity:100,painMinIntensity:5,painMaxIntensity:60,enablePain:!0,enabledPleasureActions:["carress","grab","milk","stroke","suck","tease"],enabledPainActions:["shock","whip"],ambientLoop:!1,ambientVolume:15,fadeTimeMs:300,outputDeviceId:""},y={settings:{...b},enabled:!1,isPlaying:!1,currentAction:null,currentType:null,currentIntensity:0,currentVolume:0,currentFile:null,ambientActive:!1,lastError:"",listeners:new Set};let x=null,h=null,g="pleasure",f=null,v=!1,M=null,N=0;const A=new Map;let I=Promise.resolve();function w(e){const t={...b,...e||{}};return t.toolName=function(e,t){const n="string"==typeof e?e.trim():"";if(!n)return t;const a=n.toLowerCase().replace(/[^a-z0-9_]/g,"");return!a||/^[0-9]/.test(a)?t:a}(t.toolName,b.toolName),t.displayName=(t.displayName||b.displayName).slice(0,60),t.masterVolume=P(t.masterVolume,0,100),t.pleasureMinIntensity=P(t.pleasureMinIntensity,0,100),t.pleasureMaxIntensity=P(t.pleasureMaxIntensity,0,100),t.painMinIntensity=P(t.painMinIntensity,0,100),t.painMaxIntensity=P(t.painMaxIntensity,0,100),t.pleasureMinIntensity>t.pleasureMaxIntensity&&(t.pleasureMinIntensity=t.pleasureMaxIntensity),t.painMinIntensity>t.painMaxIntensity&&(t.painMinIntensity=t.painMaxIntensity),t.ambientVolume=P(t.ambientVolume,0,100),t.fadeTimeMs=P(t.fadeTimeMs,0,5e3),t.outputDeviceId="string"==typeof t.outputDeviceId?t.outputDeviceId:"",t.enabledPleasureActions=Array.isArray(t.enabledPleasureActions)?t.enabledPleasureActions.filter(e=>"string"==typeof e):[...b.enabledPleasureActions],t.enabledPainActions=Array.isArray(t.enabledPainActions)?t.enabledPainActions.filter(e=>"string"==typeof e):[...b.enabledPainActions],t}function P(e,t,n){const a=Number(e);return Number.isFinite(a)?a<t?t:a>n?n:a:t}function j(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function k(){return{settings:{...y.settings},enabled:y.enabled,isPlaying:y.isPlaying,currentAction:y.currentAction,currentType:y.currentType,currentIntensity:y.currentIntensity,currentVolume:y.currentVolume,currentFile:y.currentFile,ambientActive:y.ambientActive,lastError:y.lastError}}function S(){const e=k();for(const n of y.listeners)try{n(e)}catch(t){console.error("[AudioEstim] listener error",t)}}function C(){const[t,n]=e.useState(k);return e.useEffect(()=>{return e=n,y.listeners.add(e),()=>{y.listeners.delete(e)};var e},[]),t}function T(){return y.settings}function V(e){const t=y.settings,n=w({...t,...e||{}}),a=(s=n,!((i=t).engagementMode===s.engagementMode&&i.customGuidance===s.customGuidance&&i.displayName===s.displayName&&i.toolName===s.toolName&&i.masterVolume===s.masterVolume&&i.pleasureMinIntensity===s.pleasureMinIntensity&&i.pleasureMaxIntensity===s.pleasureMaxIntensity&&i.painMinIntensity===s.painMinIntensity&&i.painMaxIntensity===s.painMaxIntensity&&i.enablePain===s.enablePain&&i.ambientLoop===s.ambientLoop&&i.ambientVolume===s.ambientVolume&&i.fadeTimeMs===s.fadeTimeMs&&i.outputDeviceId===s.outputDeviceId&&j(i.enabledPleasureActions,s.enabledPleasureActions)&&j(i.enabledPainActions,s.enabledPainActions)));var i,s;if(y.settings=n,a){if(x){const e=N*(n.masterVolume/100);y.currentVolume=e,x.volume=e}h&&(h.volume=n.ambientVolume/100*(n.masterVolume/100)),!n.ambientLoop&&y.ambientActive&&R(),t.outputDeviceId!==n.outputDeviceId&&(x&&q
1(x,n.outputDeviceId),h&&q(h,n.outputDeviceId)),S()}return y.settings}function $(e){y.enabled!==e&&(y.enabled=e,S())}function E(e){const t=String(e||"");y.lastError!==t&&(y.lastError=t,S())}async function L(e){const t=I.then(()=>async function(e){const t=y.settings,n=String(e.action||"").toLowerCase();if(!t.enabledPleasureActions.includes(n)&&(!t.enabledPainActions.includes(n)||!t.enablePain)){const e=`Action "${n}" is not enabled.`;return E(e),{ok:!1,error:e}}const a=e.type?e.type:p(n);if("pain"===a&&!t.enablePain){const e="Pain stimulation is disabled in settings.";return E(e),{ok:!1,error:e}}const[i,s]="pain"===a?[t.painMinIntensity,t.painMaxIntensity]:[t.pleasureMinIntensity,t.pleasureMaxIntensity],o=P(e.intensity,0,100),r=(i+o/100*(s-i))/100,l=r*(t.masterVolume/100);N=r;const{file:c,matchedSpeed:b}=function(e,t,n){const a=d[e],i=n??(a?p(e):"pleasure");if(a){const n=Number.isFinite(t)?Math.max(0,Math.min(10,Math.round(t))):5;for(const t of a)if(t>=n)return{file:`/estim-audio/${i}/${e}_${t}.mp3`,matchedSpeed:t};const s=a[a.length-1];return{file:`/estim-audio/${i}/${e}_${s}.mp3`,matchedSpeed:s}}return{file:"pain"===i?m:u,matchedSpeed:null}}(n,e.speed??5,a);await O({fade:!0}),await async function(){if(!h||!y.ambientActive)return;const e=h;y.settings.fadeTimeMs>0&&await H(e,0,y.settings.fadeTimeMs);try{e.pause(),e.volume=0}catch{}v=!0,y.ambientActive=!1,S()}();const g=await B(c);if(!g){const e="HTMLAudioElement is unavailable in this environment.";return E(e),{ok:!1,error:e}}x=g,g.volume=0,g.loop=!0,y.isPlaying=!0,y.currentAction=n,y.currentType=a,y.currentIntensity=o,y.currentVolume=l,y.currentFile=c,y.lastError="",S();try{await g.play()}catch(A){const e=A instanceof Error?A.message:String(A);return F(),E(`Audio blocked: ${e}`),{ok:!1,error:`Audio blocked: ${e}`}}if(f=()=>{if(x===g)try{g.currentTime=0,g.play()}catch{}},g.addEventListener("ended",f),W(g,l,t.fadeTimeMs),e.durationMs&&e.durationMs>0){const t=Math.min(6e4,Math.max(500,e.durationMs));M=setTimeout(()=>{M=null,x===g&&O({fade:!0,resumeAmbient:!0})},t)}return{ok:!0,file:c,volume:l,matchedSpeed:b}}(e));return I=t.catch(()=>{}),t}function D(){if(null!=M&&(clearTimeout(M),M=null),function(){for(const[e,t]of A)clearInterval(e),t&&t();A.clear()}(),x){G(x);try{x.loop=!1,x.pause(),x.volume=0,x.src=""}catch{}x=null}F(),U()}async function O(e={}){if(!x)return;const t=x;x=null,G(t),null!=M&&(clearTimeout(M),M=null),e.fade&&y.settings.fadeTimeMs>0&&await H(t,0,y.settings.fadeTimeMs);try{t.loop=!1,t.pause(),t.volume=0,t.src=""}catch{}F(),e.resumeAmbient&&await U()}function G(e){if(f){try{e.removeEventListener("ended",f)}catch{}f=null}}function F(){(y.isPlaying||null!=y.currentAction||null!=y.currentFile)&&(y.isPlaying=!1,y.currentAction=null,y.currentType=null,y.currentIntensity=0,y.currentVolume=0,y.currentFile=null,N=0,S())}async function _(e="pleasure"){const t=y.settings;if(!t.ambientLoop)return;const n="pain"===e?m:u;if(h&&g===e)return void(h.volume=t.ambientVolume/100*(t.masterVolume/100));await R();const a=await B(n);if(a){a.loop=!0,a.volume=0,h=a,g=e;try{await a.play()}catch(i){return h=null,void E(`Audio blocked: ${i instanceof Error?i.message:String(i)}`)}W(a,t.ambientVolume/100*(t.masterVolume/100),t.fadeTimeMs),y.ambientActive=!0,S()}}async function R(){if(!h)return void(y.ambientActive&&(y.ambientActive=!1,S()));const e=h;h=null,y.settings.fadeTimeMs>0&&await H(e,0,y.settings.fadeTimeMs);try{e.loop=!1,e.pause(),e.volume=0,e.src=""}catch{}v=!1,y.ambientActive=!1,S()}async function U(){if(!v)return;if(v=!1,!y.settings.ambientLoop||x)return;const e=y.settings;if(!h)return void(await _(g));const t=h;t.loop=!0,t.volume=0;try{await t.play()}catch(n){return h=null,void E(`Audio blocked: ${n instanceof Error?n.message:String(n)}`)}if(x){try{t.pause(),t.volume=0}catch{}v=!0}else W(t,e.ambientVolume/100*(e.masterVolume/100),e.fadeTimeMs),y.ambientActive=!0,S()}async function B(e){if("undefined"==typeof Audio)return null;const t=new Audio(e);return t.preload="auto",await q(t,y.settings.outputDeviceId),t}async function q(e,t){if(!t)return;const n=e.setSinkId;if("function"==typeof n)try{await n.call(e,t)}catch(a){console.error("[AudioEstim] setSinkId failed",a)}}function W(e,t,n){if(n<=0)return void(e.volume=t);const a=Math.max(1,Math.floor(n/20)),i=e.volume,s=t-i;let o=0;const r=setInterval(()=>{if(o+=1,e.paused&&t>0)return A.delete(r),void clearInterval(r);e.volume=Math.max(0,Math.min(1,i+s*o/20)),o>=20&&(A.delete(r),clearInterval(r),e.volume=Math.max(0,Math.min(1,t)))},a);A.set(r,null)}function H(e,t,n){return new Promise(a=>{if(n<=0)return e.volume=t,void a();const i=Math.max(1,Math.floor(n/20)),s=e.volume,o=t-s;let r=0;const l=setInterval(()=>{r+=1,e.volume=Math.max(0,Math.min(1,s+o*r/20)),r>=20&&(A.delete(l),clearInterval(l),e.volume=Math.max(0,Math.min(1,t)),a())},i);A.set(l,a)})}
1const z=[{value:"always",label:"Always",desc:"AI must adjust the audio every turn."},{value:"organic",label:"AI decides",desc:"AI adjusts when the scene calls for it."},{value:"custom",label:"Custom",desc:"Use your own instructions instead."}];function Q({label:e,help:n,value:a,min:i,max:s,step:o,disabled:r,onChange:l,onCommit:c}){return t.jsxs("label",{className:"block space-y-1.5",children:[t.jsxs("div",{className:"flex items-center justify-between",children:[t.jsx("span",{className:"text-sm font-medium text-text-primary",children:e}),t.jsx("span",{className:"text-xs text-text-tertiary",children:a})]}),t.jsx("input",{type:"range",className:"w-full accent-orchid-500",min:i,max:s,step:o??1,value:a,disabled:r,onChange:e=>l(Number(e.target.value)),onMouseUp:e=>c(Number(e.target.value)),onTouchEnd:e=>c(Number(e.target.value))}),n&&t.jsx("span",{className:"block text-xs text-text-tertiary",children:n})]})}function Y({label:e,help:n,value:a,placeholder:i,disabled:s,onChange:o,onCommit:r}){return t.jsxs("label",{className:"block space-y-1.5",children:[t.jsx("span",{className:"text-sm font-medium text-text-primary",children:e}),t.jsx("input",{type:"text",spellCheck:!1,autoComplete:"off",className:"w-full rounded-md border border-orchid-500/15 bg-bg-deep px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-orchid-500/40",value:a,placeholder:i,disabled:s,onChange:e=>o(e.target.value),onBlur:r}),n&&t.jsx("span",{className:"block text-xs text-text-tertiary",children:n})]})}function J({label:e,active:a,disabled:i,onToggle:s,onPreview:o}){return t.jsxs("span",{className:"inline-flex items-center rounded-full border text-xs overflow-hidden "+(a?"border-orchid-500/60 bg-orchid-500/20 text-text-primary":"border-orchid-500/15 bg-bg-deep text-text-tertiary"),children:[t.jsx("button",{type:"button",className:"px-3 py-1 hover:bg-orchid-500/10 disabled:opacity-50",disabled:i,onClick:s,children:e}),t.jsx("button",{type:"button","aria-label":`Preview ${e}`,className:"px-2 py-1 border-l border-orchid-500/15 hover:bg-orchid-500/10 disabled:opacity-50",disabled:i,onClick:o,children:t.jsx(n,{className:"w-3 h-3"})})]})}function K({value:n,disabled:a,onChange:i}){const s="undefined"!=typeof navigator&&!!navigator.mediaDevices&&"function"==typeof navigator.mediaDevices.enumerateDevices,[r,l]=e.useState([]),[c,d]=e.useState(!1),[u,m]=e.useState(""),p=e.useCallback(async()=>{if(s)try{const e=(await navigator.mediaDevices.enumerateDevices()).filter(e=>"audiooutput"===e.kind).map(e=>({deviceId:e.deviceId,label:e.label}));l(e),d(e.length>0&&e.every(e=>!e.label)),m("")}catch(e){m(e instanceof Error?e.message:String(e))}},[s]);e.useEffect(()=>{if(!s)return;p();const e=()=>{p()};return navigator.mediaDevices.addEventListener("devicechange",e),()=>navigator.mediaDevices.removeEventListener("devicechange",e)},[s,p]);const b=e.useCallback(async()=>{if(s)try{(await navigator.mediaDevices.getUserMedia({audio:!0})).getTracks().forEach(e=>e.stop()),await p()}catch(e){m(e instanceof Error?e.message:String(e))}},[s,p]);if(!s)return t.jsxs("section",{className:"rounded-xl border border-orchid-500/15 bg-bg-deepest/60 p-4 space-y-2",children:[t.jsx("h3",{className:"text-xs font-semibold uppercase tracking-wider text-text-secondary",children:"Audio output device"}),t.jsx("p",{className:"text-xs text-text-tertiary",children:"This environment doesn't expose audio device enumeration."})]});const y=!n||r.some(e=>e.deviceId===n);return t.jsxs("section",{className:"rounded-xl border border-orchid-500/15 bg-bg-deepest/60 p-4 space-y-3",children:[t.jsxs("div",{className:"flex items-center justify-between",children:[t.jsx("h3",{className:"text-xs font-semibold uppercase tracking-wider text-text-secondary",children:"Audio output device"}),t.jsx("button",{type:"button","aria-label":"Refresh device list",className:"p-1.5 rounded-md text-text-tertiary hover:text-text-primary hover:bg-orchid-500/10 disabled:opacity-50",disabled:a,onClick:()=>{p()},children:t.jsx(o,{className:"w-3.5 h-3.5"})})]}),t.jsx("p",{className:"text-xs text-text-tertiary",children:"Route estim audio to a specific output (e.g. a separate USB interface) so TTS and other audio can keep using your normal speakers."}),t.jsxs("select",{className:"w-full rounded-md border border-orchid-500/15 bg-bg-deep px-3 py-2 text-sm text-text-primary focus:outline-none focus:border-orchid-500/40",value:n,disabled:a,onChange:e=>i(e.target.value),children:[t.jsx("option",{value:"",children:"System default"}),r.map(e=>t.jsx("option",{value:e.deviceId,children:e.label||`Device ${e.deviceId.slice(0,8)||"default"}`},e.deviceId)),!y&&t.jsx("option",{value:n,children:"Saved device (not currently connected)"})]}),c&&t.jsxs("div",{className:"flex items-center justify-between gap-3 rounded-md border border-orchid-500/15 bg-bg-deep/60 px-3 py-2",children:[t.jsx("span",{className:"text-xs text-text-tertiary",children:"Device names are hidden until you grant microphone permission."}),t.jsx("button",{type:"button",className:"text-xs px-2.5 py-1 rounded-md bg-orchid-600 hover:bg-orchid-500 text-white disabled:opacity-50",disabled:a,onClick:()=>{b()},children:"Reveal names"})]}),u&&t.jsx("p",{className:"text-xs text-rose-300",children:u})]})}function X(e=k()){const t=e.settings.displayName||"e-stim",{settings:n}=e,a=n.enabledPleasureActions.length>0||n.enablePain&&n.enabledPainActions.length>0;if(!e.isPlaying&&!e.ambientActive)return a?{toys:[{kind:"toy",id:"audio-estim",name:t,visibility:"visible",statusLabel:"idle",parts:[{label:"state",value:"idle"}],sortOrder:0}],playerInfo:[]}:{toys:[],playerInfo:[]};const i=[];e.currentAction&&i.push({label:"action",value:e.currentAction}),e.currentType&&i.push({label:"type",value:e.currentType}),e.isPlaying&&i.push({label:"intensity",value:Math.round(e.currentIntensity),unit:"%"}),e.ambientActive&&!e.isPlaying&&i.push({label:"state",value:"ambient"});return{toys:[{kind:"toy",id:"audio-estim",name:t,visibility:"visible",statusLabel:e.isPlaying?`${e.currentAction??"playing"} ${Math.round(e.currentIntensity)}%`:"Ambient",parts:i,sortOrder:0}],playerInfo:[]}}const Z="audio_estim_stop";
1function ee(){const e=T(),t=e.displayName||"e-stim",n=e.enabledPleasureActions,a=e.enablePain?e.enabledPainActions:[],i=[...n,...a];return{type:"function",displayLabels:{pending:`Stimulating with the ${t}â¦`,success:`Stimulated with the ${t}`,error:`Couldn't run the ${t}`},function:{name:e.toolName,description:`Play a looping audio stim through the ${t}. The sample loops at the chosen intensity until you call this tool again with a different action, or call the stop tool. Pick an action (rhythm/feel), a speed (0â10), and an intensity (0â100). Intensity is mapped through the user's safety range before playback.`,parameters:{type:"object",properties:{action:{type:"string",enum:i.length>0?i:["tease"],description:`Sensation to play. Pleasure: ${n.join(", ")||"(none)"}${a.length?`. Pain: ${a.join(", ")}`:""}.`},intensity:{type:"number",minimum:0,maximum:100,description:"Strength of the sensation (0 = silent, 100 = strongest within the user's safety range)."},speed:{type:"number",minimum:0,maximum:10,description:"Rhythm of the action sample (0 = slowest, 10 = fastest)."},durationMs:{type:"number",minimum:500,maximum:6e4,description:"Optional: auto-stop after this many milliseconds. Omit to keep looping until the next call or the stop tool."}},required:["action","intensity"]}}}}function te(){const e=T().displayName||"e-stim";return{type:"function",displayLabels:{pending:`Stopping the ${e}â¦`,success:`Stopped the ${e}`,error:`Couldn't stop the ${e}`},function:{name:Z,description:`Stop the ${e} immediately.`,parameters:{type:"object",properties:{}}}}}const ne={manifest:{apiVersion:6,version:"1.0.0",source:"builtin",key:"audio-estim",name:"Audio Estim",category:"toy",icon:"AudioWaveform",description:"Requires audio estim device",showInTopBar:!0,supportsPromptContributions:!0,supportsLifecycleHooks:!0,supportsEmergencyStop:!0,SettingsComponent:({extension:o})=>{const r=C(),[d,u]=e.useState(()=>w(o.resolvedSettings)),[m,p]=e.useState(!1),[y,x]=e.useState(!1),[h,g]=e.useState("");e.useEffect(()=>{V(o.resolvedSettings),u(w(o.resolvedSettings))},[o.resolvedSettings]);const f=async e=>{u(t=>({...t,...e}));try{await o.updateGlobalSettings(e)}catch(t){g(t instanceof Error?t.message:String(t))}},v=async e=>{u(t=>({...t,...e}));try{await o.updatePersonaSettings(e)}catch(t){g(t instanceof Error?t.message:String(t))}},M=async(e,t)=>{x(!0),g("");try{const n=await L({type:t,action:e,intensity:"pain"===t?50:70,speed:5,durationMs:2500});n.ok||g(n.error||"Preview failed.")}finally{x(!1)}};return t.jsxs("div",{className:"space-y-4",children:[t.jsxs("section",{className:"rounded-xl border border-orchid-500/15 bg-bg-deepest/60 p-4 space-y-3",children:[t.jsx("h3",{className:"text-xs font-semibold uppercase tracking-wider text-text-secondary",children:"AI Engagement"}),t.jsx("div",{className:"grid grid-cols-3 gap-2",children:z.map(e=>t.jsxs("button",{type:"button",className:"rounded-md border px-3 py-2 text-left transition-colors "+(d.engagementMode===e.value?"border-orchid-500/60 bg-orchid-500/20":"border-orchid-500/15 bg-bg-deep hover:bg-bg-card"),disabled:y,onClick:()=>v({engagementMode:e.value}),children:[t.jsx("span",{className:"block text-sm font-medium text-text-primary",children:e.label}),t.jsx("span",{className:"block text-[11px] text-text-tertiary mt-0.5",children:e.desc})]},e.value))}),"custom"===d.engagementMode&&t.jsxs("label",{className:"block space-y-1.5",children:[t.jsx("span",{className:"text-sm font-medium text-text-primary",children:"Custom guidance for the AI"}),t.jsx("textarea",{className:"w-full min-h-[96px] rounded-md border border-orchid-500/15 bg-bg-deep px-3 py-2 text-sm text-text-primary placeholder:text-text-tertiary focus:outline-none focus:border-orchid-500/40 resize-y",value:d.customGuidance,placeholder:"e.g. Stay on tease until the user begs; never use shock above intensity 30.",disabled:y,onChange:e=>u(t=>({...t,customGuidance:e.target.value})),onBlur:()=>v({customGuidance:d.customGuidance})})]})]}),t.jsxs("section",{className:"rounded-xl border border-orchid-500/15 bg-bg-deepest/60 p-4 space-y-3",children:[t.jsx("h3",{className:"text-xs font-semibold uppercase tracking-wider text-text-secondary",children:"Volume"}),t.jsx(Q,{label:"Master volume",help:"Final cap on output (combined with per-type min/max).",value:d.masterVolume,min:0,max:100,step:5,disabled:y,onChange:e=>u(t=>({...t,masterVolume:e})),onCommit:e=>f({masterVolume:e})}),t.jsxs("div",{className:"flex gap-2 pt-1",children:[t.jsxs("button",{type:"button",className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-orchid-600 hover:bg-orchid-500 disabled:opacity-50 text-white text-sm font-medium transition-colors",disabled:y,onClick:()=>M("tease","pleasure"),children:[t.jsx(n,{className:"w-4 h-4"}),"Preview tease"]}),t.jsxs("button",{type:"button",className:"inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md border border-rose-500/30 bg-rose-900/30 hover:bg-rose-900/50 disabled:opacity-50 text-sm text-rose-100 transition-colors",disabled:y||!r.isPlaying&&!r.ambientActive,onClick:()=>D(),children:[t.jsx(a,{className:"w-4 h-4"}),"Stop"]})]}),h&&t.jsx("p",{className:"text-xs text-rose-300",children:h})]}),t.jsx(K,{value:d.outputDeviceId,disabled:y,onChange:e=>f({outputDeviceId:e})}),t.jsxs("section",{className:"rounded-xl border border-orchid-500/15 bg-bg-deepest/60 p-4 space-y-3",children:[t.jsxs("button",{type:"button",className:"flex items-center justify-between w-full text-left bg-transparent border-0 p-0 cursor-pointer","aria-expanded":m,onClick:()=>p(e=>!e),children:[t.jsx("span",{className:"text-xs font-semibold uppercase tracking-wider text-text-secondary",children:"Advanced"}),m?t.jsx(i,{className:"w-4 h-4 text-text-tertiary"}):t.jsx(s,{className:"w-4 h-4 text-text-tertiary"})]}),m?t.jsxs("div",{className:"space-y-4",children:[t.jsx(Y,{label:"Toy
1name shown to the AI",help:"What the AI calls this device. Generic noun beats a brand name.",value:d.displayName,placeholder:b.displayName,disabled:y,onChange:e=>u(t=>({...t,displayName:e})),onCommit:()=>f({displayName:d.displayName.trim()||b.displayName})}),t.jsx(Y,{label:"Tool name",help:"Function name the AI sees. Lowercase + underscores only.",value:d.toolName,placeholder:b.toolName,disabled:y,onChange:e=>u(t=>({...t,toolName:e})),onCommit:()=>f({toolName:d.toolName})}),t.jsxs("div",{className:"space-y-2",children:[t.jsx("span",{className:"text-sm font-medium text-text-primary",children:"Pleasure intensity range"}),t.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[t.jsx(Q,{label:"Min",value:d.pleasureMinIntensity,min:0,max:100,step:5,disabled:y,onChange:e=>u(t=>({...t,pleasureMinIntensity:e})),onCommit:e=>f({pleasureMinIntensity:e})}),t.jsx(Q,{label:"Max",value:d.pleasureMaxIntensity,min:0,max:100,step:5,disabled:y,onChange:e=>u(t=>({...t,pleasureMaxIntensity:e})),onCommit:e=>f({pleasureMaxIntensity:e})})]}),t.jsx("span",{className:"text-xs text-text-tertiary",children:"AI intensity 0â100 is rescaled into this band before playback."})]}),t.jsxs("label",{className:"flex items-start gap-3 cursor-pointer",children:[t.jsx("input",{type:"checkbox",className:"mt-1 accent-orchid-500",checked:d.enablePain,disabled:y,onChange:e=>f({enablePain:e.target.checked})}),t.jsxs("span",{className:"space-y-0.5",children:[t.jsx("span",{className:"block text-sm font-medium text-text-primary",children:"Allow pain samples"}),t.jsx("span",{className:"block text-xs text-text-tertiary",children:"When off, shock + whip are removed from the AI tool entirely."})]})]}),d.enablePain&&t.jsxs("div",{className:"space-y-2",children:[t.jsx("span",{className:"text-sm font-medium text-text-primary",children:"Pain intensity range"}),t.jsxs("div",{className:"grid grid-cols-2 gap-3",children:[t.jsx(Q,{label:"Min",value:d.painMinIntensity,min:0,max:100,step:5,disabled:y,onChange:e=>u(t=>({...t,painMinIntensity:e})),onCommit:e=>f({painMinIntensity:e})}),t.jsx(Q,{label:"Max (safety cap)",value:d.painMaxIntensity,min:0,max:100,step:5,disabled:y,onChange:e=>u(t=>({...t,painMaxIntensity:e})),onCommit:e=>f({painMaxIntensity:e})})]}),t.jsx("span",{className:"text-xs text-text-tertiary",children:"Pain volume is hard-capped at Max â the AI cannot exceed it regardless of what it asks for."})]}),t.jsxs("div",{className:"space-y-2",children:[t.jsx("span",{className:"text-sm font-medium text-text-primary",children:"Enabled pleasure actions"}),t.jsx("div",{className:"flex flex-wrap gap-2",children:l.map(e=>t.jsx(J,{label:e,active:d.enabledPleasureActions.includes(e),onToggle:()=>(e=>{const t=d.enabledPleasureActions.includes(e)?d.enabledPleasureActions.filter(t=>t!==e):[...d.enabledPleasureActions,e];f({enabledPleasureActions:t})})(e),onPreview:()=>M(e,"pleasure"),disabled:y},e))})]}),d.enablePain&&t.jsxs("div",{className:"space-y-2",children:[t.jsx("span",{className:"text-sm font-medium text-text-primary",children:"Enabled pain actions"}),t.jsx("div",{className:"flex flex-wrap gap-2",children:c.map(e=>t.jsx(J,{label:e,active:d.enabledPainActions.includes(e),onToggle:()=>(e=>{const t=d.enabledPainActions.includes(e)?d.enabledPainActions.filter(t=>t!==e):[...d.enabledPainActions,e];f({enabledPainActions:t})})(e),onPreview:()=>M(e,"pain"),disabled:y},e))})]}),t.jsxs("label",{className:"flex items-start gap-3 cursor-pointer",children:[t.jsx("input",{type:"checkbox",className:"mt-1 accent-orchid-500",checked:d.ambientLoop,disabled:y,onChange:e=>f({ambientLoop:e.target.checked})}),t.jsxs("span",{className:"space-y-0.5",children:[t.jsx("span",{className:"block text-sm font-medium text-text-primary",children:"Ambient base loop"}),t.jsx("span",{className:"block text-xs text-text-tertiary",children:"Plays base.mp3 quietly underneath whenever the extension is enabled."})]})]}),d.ambientLoop&&t.jsx(Q,{label:"Ambient volume",value:d.ambientVolume,min:0,max:100,step:5,disabled:y,onChange:e=>u(t=>({...t,ambientVolume:e})),onCommit:e=>f({ambientVolume:e})}),t.jsx(Q,{label:"Fade duration (ms)",value:d.fadeTimeMs,min:0,max:2e3,step:50,disabled:y,onChange:e=>u(t=>({...t,fadeTimeMs:e})),onCommit:e=>f({fadeTimeMs:e})})]}):t.jsx("p",{className:"text-xs text-text-tertiary",children:"Naming, intensity ranges, allowed actions, ambient loop, fade timing."})]})]})},StatusComponent:({extension:n,onConnect:i})=>{const s=C(),[o,l]=e.useState(!1),[c,d]=e.useState("");e.useEffect(()=>{V(n.resolvedSettings)},[n.resolvedSettings]);const u=s.isPlaying?`${s.currentAction??"Playing"} ${Math.round(s.currentIntensity)}%`:s.ambientActive?"Ambient loop":"Ready",m=s.lastError?s.lastError:s.isPlaying?`${s.currentType??"pleasure"} at ${Math.round(100*s.currentVolume)}% volume`:s.ambientActive?`base.mp3 at ${s.settings.ambientVolume}%`:"No audio playing.";return t.jsxs("div",{className:"space-y-3 min-w-[260px]",children:[t.jsxs("div",{children:[t.jsxs("div",{className:"text-sm font-semibold text-text-primary flex items-center gap-2",children:[t.jsx(r,{className:"w-4 h-4"}),"Audio Estim â ",u]}),t.jsx("div",{className:"text-xs text-text-tertiary mt-0.5",children:m})]}),t.jsxs("div",{className:"flex gap-2 flex-wrap",children:[s.settings.ambientLoop&&!s.ambientActive&&t.jsx("button",{type:"button",className:"flex-1 inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md bg-orchid-600 hover:bg-orchid-500 disabled:opacity-50 text-white text-sm font-medium transition-colors",disabled:o,onClick:async()=>
1{l(!0),d("");try{await _()}catch(e){d(e instanceof Error?e.message:String(e))}finally{l(!1)}},children:"Start ambient"}),t.jsxs("button",{type:"button",className:"flex-1 inline-flex items-center justify-center gap-1.5 px-3 py-2 rounded-md border border-rose-500/30 bg-rose-900/30 hover:bg-rose-900/50 disabled:opacity-50 text-sm text-rose-100 transition-colors",disabled:o||!s.isPlaying&&!s.ambientActive,onClick:async()=>{l(!0),d(""),D(),await R(),l(!1)},children:[t.jsx(a,{className:"w-4 h-4"}),"Stop"]}),i&&t.jsx("button",{type:"button",className:"px-3 py-2 rounded-md border border-orchid-500/20 bg-bg-deep hover:bg-bg-card text-sm text-text-primary transition-colors",onClick:()=>i(),children:"Settings"})]}),c&&t.jsx("div",{className:"text-xs text-rose-300",children:c})]})},settings:{fields:{engagementMode:{id:"engagementMode",type:"select",scope:"persona",allowPersonaOverride:!0,title:"AI engagement",description:"How aggressively the AI drives the audio each turn.",default:b.engagementMode,options:[{label:"Always stimulate when responding",value:"always"},{label:"Stimulate as needed throughout the scene",value:"organic"},{label:"Custom instructions",value:"custom"}]},masterVolume:{id:"masterVolume",type:"slider",scope:"global",allowPersonaOverride:!1,title:"Master volume",description:"Final cap on output volume (combined with per-type min/max).",default:b.masterVolume,min:0,max:100,step:5},displayName:{id:"displayName",type:"text",scope:"global",allowPersonaOverride:!1,title:"Toy name shown to the AI",description:"What the AI calls this device. Generic noun is best.",default:b.displayName,placeholder:"e-stim"},toolName:{id:"toolName",type:"text",scope:"global",allowPersonaOverride:!1,title:"Tool name",description:"Function name the AI calls. Lowercase + underscores only.",default:b.toolName,placeholder:"stimulate_genitals"},pleasureMinIntensity:{id:"pleasureMinIntensity",type:"slider",scope:"global",allowPersonaOverride:!1,title:"Pleasure minimum intensity",description:"Floor for pleasure volume. AI value 0 maps here.",default:b.pleasureMinIntensity,min:0,max:100,step:5},pleasureMaxIntensity:{id:"pleasureMaxIntensity",type:"slider",scope:"global",allowPersonaOverride:!1,title:"Pleasure maximum intensity",description:"Ceiling for pleasure volume. AI value 100 maps here.",default:b.pleasureMaxIntensity,min:0,max:100,step:5},enablePain:{id:"enablePain",type:"toggle",scope:"global",allowPersonaOverride:!1,title:"Allow pain samples",description:"Disable to remove pain (shock/whip) from the AI tool entirely.",default:b.enablePain},painMinIntensity:{id:"painMinIntensity",type:"slider",scope:"global",allowPersonaOverride:!1,title:"Pain minimum intensity",description:"Floor for pain volume. AI value 0 maps here.",default:b.painMinIntensity,min:0,max:100,step:5},painMaxIntensity:{id:"painMaxIntensity",type:"slider",scope:"global",allowPersonaOverride:!1,title:"Pain maximum intensity",description:"Safety cap. AI value 100 maps here; pain volume never exceeds this.",default:b.painMaxIntensity,min:0,max:100,step:5},ambientLoop:{id:"ambientLoop",type:"toggle",scope:"global",allowPersonaOverride:!1,title:"Ambient base loop",description:"Play base.mp3 quietly underneath while the extension is active.",default:b.ambientLoop},ambientVolume:{id:"ambientVolume",type:"slider",scope:"global",allowPersonaOverride:!1,title:"Ambient volume",description:"Volume of the ambient loop when enabled.",default:b.ambientVolume,min:0,max:100,step:5},fadeTimeMs:{id:"fadeTimeMs",type:"number",scope:"global",allowPersonaOverride:!1,title:"Fade duration (ms)",description:"Length of the fade in/out for each sample.",default:b.fadeTimeMs,min:0,max:5e3,step:50},customGuidance:{id:"customGuidance",type:"textarea",scope:"persona",allowPersonaOverride:!0,title:"Custom AI guidance",description:"Used when engagement = Custom. Replaces the default guidance.",default:"",rows:4,placeholder:"e.g. Stay on tease until the user begs; never use shock above 30."},enabledPleasureActions:{id:"enabledPleasureActions",type:"textarea",scope:"global",allowPersonaOverride:!1,title:"Enabled pleasure actions",description:"Pleasure action names the AI is allowed to use.",default:b.enabledPleasureActions},enabledPainActions:{id:"enabledPainActions",type:"textarea",scope:"global",allowPersonaOverride:!1,title:"Enabled pain actions",description:"Pain action names the AI is allowed to use.",default:b.enabledPainActions},outputDeviceId:{id:"outputDeviceId",type:"text",scope:"global",allowPersonaOverride:!1,title:"Audio output device",description:"Selected audio output device (empty = system default).",default:b.outputDeviceId}},mainLayout:["engagementMode","masterVolume","displayName","toolName","pleasureMinIntensity","pleasureMaxIntensity","enablePain","painMinIntensity","painMaxIntensity","ambientLoop","ambientVolume","fadeTimeMs","customGuidance"],quickLayout:["engagementMode","masterVolume"]}},useExtension:()=>{const t=C(),[n,a]=e.useState(y.enabled);e.useEffect(()=>{$(n)},[n]);const i=function(e){if(e.lastError)return{color:"red",label:"Audio blocked",description:e.lastError};if(e.isPlaying)return{color:"green",label:`${e.currentAction??"playing"} ${Math.round(e.currentIntensity)}%`,description:`${e.currentType??"pleasure"} â volume ${Math.round(100*e.currentVolume)}%`};if(e.ambientActive)return{color:"green",label:"Ambient",description:"Base loop running."};const t=0===e.settings.enabledPleasureActions.length,n=!e.settings.enablePain||0===e.settings.enabledPainActions.length;return t&&n?{color:"yellow",label:"No actions enabled",description:"Pick at least one action in Settings."}:{color:"white",label:"Ready",description:"No audio playing."}}(t);return{isInstalled:!0,isAvailable:"undefined"!=typeof Audio,isEnabled:n,isActive:t.isPlaying||t.ambientActive,statusIndicator:i,attention:t.lastError?{ping:!0,label:"Needs attention"}:void 0,globalSettings:{masterVolume:t.settings.masterVolume,displayName:t.
1settings.displayName,toolName:t.settings.toolName,pleasureMinIntensity:t.settings.pleasureMinIntensity,pleasureMaxIntensity:t.settings.pleasureMaxIntensity,painMinIntensity:t.settings.painMinIntensity,painMaxIntensity:t.settings.painMaxIntensity,enablePain:t.settings.enablePain,enabledPleasureActions:t.settings.enabledPleasureActions,enabledPainActions:t.settings.enabledPainActions,ambientLoop:t.settings.ambientLoop,ambientVolume:t.settings.ambientVolume,fadeTimeMs:t.settings.fadeTimeMs,outputDeviceId:t.settings.outputDeviceId},personaSettings:{engagementMode:t.settings.engagementMode,customGuidance:t.settings.customGuidance},resolvedSettings:{...t.settings},promptState:X(t),enable:async()=>{a(!0),$(!0),T().ambientLoop&&await _()},disable:async()=>{a(!1),$(!1),D(),await R()},updateGlobalSettings:async e=>{const t=T();V(e);T().ambientLoop&&!t.ambientLoop&&n&&await _()},updatePersonaSettings:async e=>{V(e)},updateSettings:async e=>{V(e)}}},getTools:()=>[ee(),te()],executeTool:async(e,t,n)=>{const a=t||{};if(e===T().toolName){const e=await L({action:String(a.action??"").toLowerCase(),intensity:Number(a.intensity??0),speed:null!=a.speed?Number(a.speed):void 0,durationMs:null!=a.durationMs?Number(a.durationMs):void 0});if(!e.ok)throw new Error(e.error||"Audio estim play failed.");return e}if(e===Z)return D(),{ok:!0};throw new Error(`Unknown Audio Estim tool "${e}".`)},getPromptContributions:e=>function(){const e=T(),t=k(),n=e.displayName||"e-stim",a=[...e.enabledPleasureActions,...e.enablePain?e.enabledPainActions:[]],i=[`You have access to a ${n} that plays sensation audio (pleasure: ${e.enabledPleasureActions.join(", ")}${e.enablePain?`; pain: ${e.enabledPainActions.join(", ")}`:""}).`,`Pick an action from: ${a.join(", ")}.`,"Speed (0â10) picks the rhythm of the sample. Intensity (0â100) picks the strength.",`The sample loops automatically â once started it keeps going at the chosen intensity until you call ${e.toolName} again or call the stop tool.`,"Use `stop` to silence it immediately."].join(" "),s=[];if("custom"===e.engagementMode){const t=(e.customGuidance||"").trim();return t?s.push({placement:"tool_guidance",priority:30,text:t}):s.push({placement:"tool_guidance",priority:30,text:`Custom engagement mode is selected but no instructions were entered â use the ${n} sparingly until the user provides guidance.`}),s}return"always"===e.engagementMode?s.push({placement:"tool_guidance",priority:40,text:[i,`On every turn you MUST call ${e.toolName} (or stop it if stillness fits the moment).`].join(" ")}):s.push({placement:"tool_guidance",priority:30,text:[i,`Call ${e.toolName} when the sensation should change; let it ride otherwise.`].join(" ")}),t.isPlaying?s.push({placement:"persona_post",priority:25,text:[`# ${n} state`,`Playing: ${t.currentAction} (${t.currentType}) | Intensity: ${Math.round(t.currentIntensity)}%`].join("\n")}):t.ambientActive&&s.push({placement:"persona_post",priority:25,text:`# ${n} state\nAmbient loop running at ${e.ambientVolume}%.`}),s}(),onEnable:async()=>{$(!0),T().ambientLoop&&await _()},onDisable:async()=>{$(!1),D(),await R()},onEmergencyToyStop:async()=>{D(),await R()}};export{ne as audioEstimExtension};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.