if(zone)awaitexecute(store,{type:'worldTrigger',kind:'zone-change',zone,playerName:String(body.payload.name??'').slice(0,40),detail:'Authoritative ALE human-player zone event'},config);
constrefresh=useCallback(async()=>{try{constr=awaitfetch('/api/state');constd:any=awaitr.json();if(!r.ok)thrownewError(d.error);setState(d);setError('');}catch(e){setError(einstanceofError?e.message:'Could not load realm');}},[]);
register({name:'azerothai_read_realm',description:'Read current bot identities, recipes, director settings and recent events.',inputSchema:{type:'object',properties:{},additionalProperties:false},annotations:{readOnlyHint:true},execute:async()=>{constr=awaitfetch('/api/state');if(!r.ok)thrownewError('Realm unavailable');returnr.json();}});
register({name:'azerothai_create_bot',description:'Create an offline bot identity; does not spawn it in game.',inputSchema:{type:'object',properties:{name:{type:'string'},role:{type:'string'},race:{type:'string'},recipeId:{type:'string'},goal:{type:'string'}},required:['name','role','race','recipeId','goal'],additionalProperties:false},annotations:{readOnlyHint:false},execute:async(input:any)=>act({...input,type:'createBot'})});return()=>controller.abort();},[act]);
<divclassName="page-heading"><div><divclassName="eyebrow">{page==='GM Console'?'THE WORLD IS YOUR SANDBOX':'AZEROTHAI CONTROL PLANE'}</div><h1>{page==='Dashboard'?'Your realm, alive.':page}</h1><pclassName="subtitle">{({'Dashboard':'The people, personalities, and stories that make your world.','World Monitor':'Watch Azeroth breathe: population, activity, systems, and the lives unfolding right now.','Realm Director':'Shape the adventure around you. Let your characters bring it to life.','World & Housing':'Homes, routines, simulation bands, and believable population flow.','Bots':'Give every character a purpose, then let their story unfold.','Bot Recipes':'Same model. Different personalities. A world of possibilities.','Memory & Bonds':'A shared history makes the next encounter matter.','Event Stream':'Follow the decisions and changes happening across your realm.','GM Console':'Pull up a chair. Put on the blue robe. Run your world.','Server & Models':'Connect the game engine and the minds behind your characters.'}asany)[page]}</p></div><divclassName="actions">{page==='Bot Recipes'?<ButtononClick={()=>{setRecipe(null);setRecipeOpen(true)}}><Plussize={16}/>Createrecipe</Button>:<><Buttonvariant="outline"disabled={busy}onClick={()=>voidrefresh()}><RefreshCwsize={15}/>Refresh</Button><ButtononClick={()=>setCreateOpen(true)}><Plussize={16}/>Createbot</Button></>}</div></div>
{page==='Dashboard'&&<><divclassName="stats">{[[Users,'Active bots',state.bots.filter(b=>b.status==='online').length,'Online in the simulation'],[BookOpen,'Bot recipes',state.recipes.length,'Distinct ways to experience the world'],[Brain,'Memories',state.bots.reduce((n,b)=>n+b.memories.length,0),'Stories carried into future decisions'],[Network,'Relationships',state.bots.reduce((n,b)=>n+b.relationships.length,0),'Directed bonds between characters']].map(([Icon,label,value,note]:any)=><divclassName="panel stat"key={label}><divclassName="stat-label">{label}<Icon/></div><divclassName="stat-value">{value}</div><small>{label==='Active bots'&&state.settings.connector!=='simulation'?'Acknowledged by the game bridge':note}</small></div>)}</div><sectionclassName="panel director-strip"><divclassName="director-content"><CompassclassName="director-icon"/><div><divclassName="eyebrow">YOURNEXTADVENTURE</div><h2>{state.director.experience}</h2><p>{state.director.zone}·{state.director.brief}</p></div></div><Buttonvariant="outline"onClick={()=>nav('Realm Director')}>Shapetheexperience<ArrowRightsize={15}/></Button></section><divclassName="dashboard-grid"><Paneltitle="Your characters"icon={Users}action={<Buttonvariant="ghost"size="sm"onClick={()=>nav('Bots')}>Managebots<ArrowRightsize={14}/></Button>}>{botTable(state.bots.slice(0,5))}</Panel><Paneltitle="Realm pulse"icon={Radio}><divclassName="panel-body">{Array.from(newSet(state.bots.filter(b=>b.status==='online').map(b=>b.zone))).map(zone=>{constn=state.bots.filter(b=>b.status==='online'&&b.zone===zone).length;return<divclassName="zone-row"key={zone}><spanclassName="zone-label">{zone}</span><spanclassName="bar"><istyle={{width:`${n/Math.max(1,state.bots.filter(b=>b.status==='online').length)*100}%`}}/></span><span>{n}</span></div>})}<pclassName="fineprint">Botlocationsfromtheselectedconnector.Simulationlocationsareillustrative.</p><divclassName="engine-box"style={{marginTop:25,marginBottom:0}}><Brainsize={23}/><div><h3>{state.settings.provider==='deterministic'?'Personality simulator':state.settings.model}</h3><p>{state.settings.provider==='deterministic'?'No model required. Try a decision from any online bot.':'Local model selected. Each decision includes identity and memories.'}</p></div></div></div></Panel><Paneltitle="Recent activity"icon={Activity}action={<Buttonvariant="ghost"size="sm"onClick={()=>nav('Event Stream')}>Viewstream<ArrowRightsize={14}/></Button>}>{<Eventsevents={state.events.slice(-5)}/>}</Panel><Paneltitle="The Game Master’s chair"icon={Shield}><divclassName="panel-body"><pclassName="list-muted">Setthescene.Announceagathering.Moveacharactertothenextchapteroftheirstory.</p><divclassName="gm-preview"><span><GM>AzerothAI</span><p>Welcometoyourworld.</p><code>.announceAnadventureawaits...</code></div><Buttonvariant="outline"onClick={()=>nav('GM Console')}>OpenGMconsole<Terminalsize={15}/></Button></div></Panel></div></>}
functionBotProfile({bot:b,state,act,busy,perform,confirm}:any){const[memory,setMemory]=useState(''),[importance,setImportance]=useState(7),[target,setTarget]=useState(state.bots.find((x:Bot)=>x.id!==b.id)?.id??''),[affinity,setAffinity]=useState(40),[note,setNote]=useState(''),[recipeId,setRecipeId]=useState(b.recipeId);constactions=b.status==='online'?['pause','despawn']:b.status==='paused'?['resume','despawn']:b.status==='offline'?['spawn','retire']:[];return<><divclassName="detail-header"><Avatarbot={b}/><div><h2>{b.name}</h2><pclassName="subtitle">Level{b.level}{b.race}{b.role}</p></div><spanclassName={`tag ${b.status}`}style={{marginLeft:'auto'}}>{b.status}</span></div><divclassName="actions">{actions.map(a=><Buttonkey={a}size="sm"variant={a==='spawn'?'default':'outline'}disabled={busy}onClick={()=>a==='retire'?confirm({title:`Retire ${b.name}?`,description:'The identity and memories remain in your realm, but this bot cannot spawn again in this prototype.',action:{type:'lifecycle',botId:b.id,action:a}}):perform({type:'lifecycle',botId:b.id,action:a})}>{a==='pause'?<Pausesize={14}/>:a==='spawn'||a==='resume'?<Playsize={14}/>:null}{a.charAt(0).toUpperCase()+a.slice(1)}</Button>)}<Buttonsize="sm"disabled={busy||b.status!=='online'}onClick={()=>perform({type:'think',botId:b.id})}><Brainsize={15}/>{busy?'Working…':'Run decision'}</Button></div><TabsdefaultValue="identity"className="mt-6"><TabsListclassName="w-full"><TabsTriggervalue="identity">Identity</TabsTrigger><TabsTriggervalue="memory">Memory</TabsTrigger><TabsTriggervalue="bonds">Relationships</TabsTrigger></TabsList><TabsContentvalue="identity"><divclassName="detail-block"><h3>Currentgoal</h3><p>{b.goal}</p><pstyle={{marginTop:12,display:'flex',gap:7,alignItems:'center'}}><MapPinsize={15}/>{b.zone}</p></div><divclassName="detail-block"><h3>{b.personality.name}<small>v{b.recipeVersion}</small></h3><p>{b.personality.description}</p><divclassName="actions"style={{marginTop:12}}>{b.personality.tags.map((t:string)=><spanclassName="tag"key={t}>{t}</span>)}</div><divclassName="field"style={{marginTop:20}}><Choicelabel="Apply recipe"value={recipeId}onChange={setRecipeId}items={state.recipes.map((r:Recipe)=>({value:r.id,label:`${r.name} · v${r.version}`}))}/><Buttonvariant="outline"disabled={busy}onClick={()=>perform({type:'applyRecipe',botId:b.id,recipeId})}>Applycurrentrecipeversion</Button></div></div><divclassName="detail-block"><h3>Latestdecision</h3><p>{b.lastDecision?`${b.lastDecision.action}: ${b.lastDecision.reason}`:'No decisions yet. Run a decision while this bot is online.'}</p><small>{b.decisions}decisionsacknowledged</small></div></TabsContent><TabsContentvalue="memory"><formclassName="detail-block"onSubmit={asynce=>{e.preventDefault();try{awaitact({type:'memory',botId:b.id,text:memory,importance});setMemory('');}catch{}}}><labelclassName="field">Addamemory<textarearequiredvalue={memory}maxLength={1000}onChange={e=>setMemory(e.target.value)}placeholder="What should this character remember?"/></label><divclassName="range-row"><divclassName="range-label">Importance<span>{importance}/10</span></div><Slideraria-label="Memory importance"min={1}max={10}value={[importance]}onValueChange={v=>setImportance(v[0])}/></div><Buttondisabled={busy}>Savememory</Button></form>{[...b.memories].reverse().map((m:any)=><divclassName="memory"key={m.id}><pclassName="list-muted">{m.text}</p><small>{m.source}·Importance{m.importance}/10·{newDate(m.at).toLocaleDateString()}</small></div>)}</TabsContent><TabsContentvalue="bonds"><pclassName="fineprint">Bondsaredirectional.Onecharacter’strustdoesnotimplytheother’s.</p><formclassName="detail-block"onSubmit={asynce=>{e.preventDefault();try{awaitact({type:'relationship',botId:b.id,targetId:target,affinity,note});setNote('');}catch{}}}><divclassName="field"><label>Othercharacter</label><Choicelabel="Other character"value={target}onChange={setTarget}items={state.bots.filter((x:Bot)=>x.id!==b.id).map((x:Bot)=>({value:x.id,label:x.name}))}/></div><divclassName="range-row"><divclassName="range-label">Affinity<span>{affinity>0?'+':''}{affinity}</span></div><Slideraria-label="Affinity"min={-100}max={100}value={[affinity]}onValueChange={v=>setAffinity(v[0])}/></div><labelclassName="field">Whythisbondexists<textarearequiredmaxLength={300}value={note}onChange={e=>setNote(e.target.value)}/></label><ButtonclassName="mt-4"disabled={busy||!target}>Saverelationship</Button></form>{b.relationships.map((r:any)=><divclassName="memory"key={r.targetId}><h3>{state.bots.find((x:Bot)=>x.id===r.targetId)?.name}<spanclassName="tag">{r.affinity>0?'+':''}{r.affinity}</span></h3><pclassName="list-muted">{r.note}</p></div>)}</TabsContent></Tabs></>}
<Paneltitle="What is happening right now"icon={Radio}className="span-all"><div>{worldEvents.length?worldEvents.map((e:RealmEvent)=><divclassName="event-row"key={e.id}><spanclassName="event-time">{newDate(e.at).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit'})}</span><spanclassName={`event-kind ${e.kind}`}>{e.kind}</span><span>{e.message}</span></div>):<divclassName="empty-note">Therealmeventfeedisquiet.</div>}</div></Panel>
<Paneltitle="Authoritative ALE pulse"icon={Clock3}className="span-all"><div>{aleEvents.length?aleEvents.slice(0,12).map((e:any)=><divclassName="event-row"key={e.id}><spanclassName="event-time">{newDate(e.at).toLocaleTimeString([],{hour:'2-digit',minute:'2-digit',second:'2-digit'})}</span><spanclassName="event-kind system">ALE</span><span>{e.kind}{e.name?` · ${e.name}`:''}{e.zone?` · ${e.zone}`:''}{e.isBot?' · Playerbot':''}</span></div>):<divclassName="empty-note">NoauthoritativeALEworldeventshavearrivedyet.Whentheplayablerealmisonline,login,combat,zone,quest,weather,andotherservereventswillappearhere.</div>}</div></Panel>
return<><divclassName="stats">{[[House,'Homes',state.homes.length,'Persistent places residents return to'],[Users,'Foreground',foreground,`Max ${state.settings.population.foregroundMax}`],[Activity,'Background',background,`Max ${state.settings.population.backgroundMax}`],[Pause,'Dormant',dormant,'Sleeping or cheaply simulated']].map(([Icon,label,value,note]:any)=><divclassName="panel stat"key={label}><divclassName="stat-label">{label}<Icon/></div><divclassName="stat-value">{value}</div><small>{note}</small></div>)}</div><divclassName="section-grid"><Paneltitle="World Population Director"icon={Compass}><formclassName="panel-body"onSubmit={asynce=>{e.preventDefault();try{awaitact({type:'worldTrigger',kind,zone,playerName,detail});toast.success('World trigger evaluated');}catch{}}}><divclassName="form-grid"><labelclassName="field">Trigger<Choicelabel="World trigger"value={kind}onChange={setKind}items={options(['zone-change','interaction','time','manual'])}/></label><labelclassName="field">Zone<Inputrequiredvalue={zone}onChange={e=>setZone(e.target.value)}/></label><labelclassName="field">Playername<Inputvalue={playerName}onChange={e=>setPlayerName(e.target.value)}/></label><labelclassName="field full">Context<textareamaxLength={500}value={detail}onChange={e=>setDetail(e.target.value)}placeholder="What happened in the world?"/></label></div><divclassName="form-footer"><Buttondisabled={busy}><Sparklessize={16}/>Evaluatepopulation</Button></div><pclassName="fineprint">AzerothAIprefersexistingresidentswithabelievablereasontobehere.Itdoesnotcreatedisposableidentitiestofillazone.</p></form></Panel><Paneltitle="Latest population plan"icon={Activity}><divclassName="panel-body">{plan?<><divclassName="health-row"><span>Trigger</span><strong>{plan.trigger.kind}·{plan.trigger.zone}</strong></div><divclassName="health-row"><span>Zonetarget</span><strong>{plan.target}</strong></div><divclassName="health-row"><span>Alreadyactive</span><strong>{plan.activeBefore}</strong></div><divclassName="health-row"><span>Requested</span><strong>{plan.requested}</strong></div><divclassName="health-row"><span>Capacityavailable</span><strong>{plan.capacity}</strong></div><divclassName="health-row"><span>Residentsselected</span><strong>{plan.selectedBotIds.length}</strong></div>{plan.selectedBotIds.length>0&&<pclassName="list-muted"style={{marginTop:16}}>{plan.selectedBotIds.map((id:string)=>state.bots.find((b:Bot)=>b.id===id)?.name).filter(Boolean).join(', ')}</p>}{plan.rejectedReasons.map((x:string)=><pclassName="fineprint"key={x}>{x}</p>)}</>:<divclassName="empty-note">Nopopulationtriggerhasbeenevaluatedyet.</div>}</div></Panel></div><Paneltitle="Housing registry"icon={House}className="mt-5"><TableclassName="data-table"><TableHeader><TableRow><TableHead>Home</TableHead><TableHead>Zone</TableHead><TableHead>Type</TableHead><TableHead>Residents</TableHead><TableHead>Safety</TableHead></TableRow></TableHeader><TableBody>{state.homes.map((h:any)=><TableRowkey={h.id}><TableCell><strong>{h.name}</strong><smallstyle={{display:'block'}}>{h.description}</small></TableCell><TableCell>{h.zone}</TableCell><TableCell>{h.type}</TableCell><TableCell>{h.residentIds.map((id:string)=>state.bots.find((b:Bot)=>b.id===id)?.name).filter(Boolean).join(', ')||'Vacant'}<small>({h.residentIds.length}/{h.capacity})</small></TableCell><TableCell>{h.safety}/100</TableCell></TableRow>)}</TableBody></Table></Panel></>
}
functionDirector({state,act,busy}:any){const[d,setD]=useState(state.director);return<divclassName="section-grid"><Paneltitle="Set the scene"icon={Compass}><formclassName="panel-body"onSubmit={asynce=>{e.preventDefault();try{awaitact({type:'director',...d});toast.success('Direction saved and bot goals updated');}catch{}}}><divclassName="form-grid"><labelclassName="field full">Whatkindofsessiondoyouwant?<Choicelabel="Experience"value={d.experience}onChange={experience=>setD({...d,experience})}items={options(['Adventuring companions','A lively town','Exploration and discovery','Rivalries and tension'])}/></label><labelclassName="field">Yourcharactername<Inputvalue={d.playerName}maxLength={40}onChange={e=>setD({...d,playerName:e.target.value})}placeholder="Optional"/></label><labelclassName="field">Focuszone<Inputrequiredvalue={d.zone}maxLength={100}onChange={e=>setD({...d,zone:e.target.value})}/></label><labelclassName="field full">Yourdirection<textarearequiredmaxLength={500}value={d.brief}onChange={e=>setD({...d,brief:e.target.value})}style={{minHeight:140}}/></label></div><divclassName="form-footer"><Buttondisabled={busy}><Sparklessize={16}/>Applyrealmdirection</Button></div><pclassName="fineprint">Updatesthegoalsofallnon-retiredbots.Runindividualdecisionsfromabotprofiletoactonthisdirection.</p></form></Panel><Paneltitle="You play. The world responds."icon={Shield}><divclassName="panel-body"><divclassName="engine-box"><Userssize={24}/><div><h3>Characters,notbackgroundnoise</h3><p>Ahealerwhoremembersyou.Atraderwithanagenda.Afamiliarrivalontheroad.</p></div></div><divclassName="engine-box"><Compasssize={24}/><div><h3>Direction,notascriptedoutcome</h3><p>Yourbriefgoesintobotgoals.Personality,memory,andavailableactionsinfluencetheirnextchoice.</p></div></div><divclassName="engine-box"><Shieldsize={24}/><div><h3>Thegamestaysthegame</h3><p>Thegameconnectortranslatesintent.AzerothCoreresolvesmovement,combat,loot,andprogression.</p></div></div><divclassName="notice">LivePlayerbotscontinueplayingunderAzerothCoreevenwithnohumanonline.AzerothAIperiodicallyaddspersonality-,memory-,andgoal-drivenhigh-levelintentionsforauthoritativelyobservedresidents.</div></div></Panel></div>}
<Paneltitle="Server connector"icon={Server}><divclassName="panel-body"><divclassName="field"><label>Gameconnector</label><Choicelabel="Game connector"value={s.connector}onChange={connector=>setS({...s,connector})}items={[{value:'simulation',label:'Simulation · Ready to use'},{value:'azerothcore',label:'AzerothCore · HTTP bridge'}]}/></div><divclassName="health-row"><span>Gamebridgeconfigured</span><strong>{capabilities.bridgeConfigured?'Yes':'Not yet'}</strong></div><divclassName="health-row"><span>ALEcontrolplane</span><strong>{capabilities.aleConfigured?'Configured':'Not yet'}</strong></div><divclassName="health-row"><span>GMSOAPconfigured</span><strong>{capabilities.soapConfigured?'Yes':'Not yet'}</strong></div><divclassName="health-row"><span>Mechanicsauthority</span><strong>AzerothCore</strong></div><ButtonclassName="mt-5"variant="outline"disabled={busy}onClick={async()=>{try{constr=awaitact({type:'health'});setHealth(r.health.detail);}catch{setHealth('Connection test failed. Check server configuration.')}}}><RefreshCwsize={15}/>Testsavedconnector</Button>{health&&<pclassName="status-message"role="status">{health}</p>}</div></Panel>
<Paneltitle="AI brain router"icon={Brain}><divclassName="panel-body"><divclassName="field"><label>Decisionprovider</label><Choicelabel="Decision provider"value={s.provider}onChange={provider=>setS({...s,provider})}items={[{value:'deterministic',label:'Personality simulator · No model needed'},{value:'ollama',label:'Ollama · Local inference'},{value:'openai-compatible',label:'OpenAI-compatible endpoint'}]}/></div><divclassName="choice-row"style={{justifyContent:'space-between',marginTop:20}}><labelhtmlFor="brain-router">Tier-awarebrainrouter</label><Switchid="brain-router"checked={brains.enabled}onCheckedChange={(enabled:boolean)=>setBrains({enabled})}/></div><labelclassName="field"style={{marginTop:20}}>Residentmodel<Inputvalue={brains.residentModel}onChange={e=>setBrains({residentModel:e.target.value})}/></label><labelclassName="field"style={{marginTop:20}}>Hero/Zakumodel<Inputvalue={brains.heroModel}onChange={e=>setBrains({heroModel:e.target.value})}/></label><divclassName="choice-row"style={{justifyContent:'space-between',marginTop:20}}><labelhtmlFor="background-llm">UseLLMforbackgroundresidents</label><Switchid="background-llm"checked={brains.backgroundUseLLM}onCheckedChange={(backgroundUseLLM:boolean)=>setBrains({backgroundUseLLM})}/></div><divclassName="form-grid"style={{marginTop:20}}><labelclassName="field">Residentmemories<Inputtype="number"min={1}max={50}value={brains.residentMemoryLimit}onChange={e=>setBrains({residentMemoryLimit:Number(e.target.value)})}/></label><labelclassName="field">Heromemories<Inputtype="number"min={1}max={100}value={brains.heroMemoryLimit}onChange={e=>setBrains({heroMemoryLimit:Number(e.target.value)})}/></label><labelclassName="field">Residenttokenbudget<Inputtype="number"min={64}max={2048}value={brains.residentMaxTokens}onChange={e=>setBrains({residentMaxTokens:Number(e.target.value)})}/></label><labelclassName="field">Herotokenbudget<Inputtype="number"min={64}max={4096}value={brains.heroMaxTokens}onChange={e=>setBrains({heroMaxTokens:Number(e.target.value)})}/></label></div><divclassName="health-row"><span>Localmodelendpointconfigured</span><strong>{capabilities.llmConfigured?'Yes':'Not yet'}</strong></div><pclassName="fineprint">Default: Qwen3.54BforresidentsandZaku.Backgroundresidentsstayondeterministicutilitylogicunlessenabledhere,savingCPUforforegroundconversationsandimportantdecisions.</p><divclassName="form-footer"><Buttondisabled={busy}onClick={async()=>{try{awaitact({type:'settings',...s,model:brains.residentModel});toast.success('Brain router saved');}catch{}}}>Savebrainconfiguration</Button></div></div></Panel>
<Paneltitle="A modular control plane"icon={Network}className="span-all"><divclassName="panel-body section-grid"><div><h3>AzerothAI</h3><pclassName="list-muted">Identity→memoryretrieval→brainrouting→high-levelintent</p></div><div><h3>AzerothCore+ALE</h3><pclassName="list-muted">Movement→combat→inventory→worldrules→authoritativeevents</p></div></div></Panel>
</div>
}
functionGM({state,act,busy,capabilities,setConfirm}:any){const[command,setCommand]=useState('.server info'),[live,setLive]=useState(false),[history,setHistory]=useState<string[]>([]),[index,setIndex]=useState(-1);constgmEvents=state.events.filter((e:RealmEvent)=>e.kind==='gm'||e.kind==='error').slice(-30);constshortcuts=[['Server info','.server info'],['World announcement','.announce Travelers, gather at the gates of Stormwind!'],['Teleport character','.teleport name Yourname Stormwind'],['Character info','.pinfo Yourname'],['Message of the day','.server motd']];return<divclassName="dashboard-grid"><Paneltitle="Worldserver console"icon={Terminal}action={<spanclassName="sim-badge">{live?'LIVE COMMANDS':'SIMULATED COMMANDS'}</span>}><divclassName="console-output"role="log"aria-label="GM command history"><divclassName="console-welcome"><GM>AZEROTHAI<br/><span>Realmoperationsterminal·Commandchannelready</span></div><pclassName="console-muted">{live?'Commands will execute on your configured worldserver.':'Simulation mode. No connection to a real worldserver.'}</p>{gmEvents.length?gmEvents.map((e:RealmEvent)=><divclassName={`console-entry ${e.kind==='error'?'console-error':''}`}key={e.id}><small>{newDate(e.at).toLocaleTimeString()}</small><pre>{e.message}</pre></div>):<pclassName="console-muted">Type.serverinfotogetstarted.</p>}</div><formclassName="console-input"onSubmit={asynce=>{e.preventDefault();if(!command.trim())return;setHistory(h=>[command,...h].slice(0,30));setIndex(-1);if(live){setConfirm({title:'Execute on the live worldserver?',description:command,action:{type:'gm',command,live:true,confirmed:true}});return;}try{awaitact({type:'gm',command,live:false});setCommand('');}catch{}}}><span>></span><Inputaria-label="GM command"value={command}placeholder=".announce Your adventure begins here"maxLength={300}onChange={e=>setCommand(e.target.value)}onKeyDown={e=>{if(e.key==='ArrowUp'){e.preventDefault();constn=Math.min(index+1,history.length-1);setIndex(n);if(n>=0)setCommand(history[n]);}if(e.key==='ArrowDown'){e.preventDefault();constn=Math.max(-1,index-1);setIndex(n);setCommand(n>=0?history[n]:'');}}}/><Buttondisabled={busy||!command.trim()}aria-label="Run GM command"><Sendsize={16}/></Button></form></Panel><div><Paneltitle="GM toolbox"icon={Shield}><divclassName="panel-body"><divclassName="choice-row"style={{justifyContent:'space-between',marginBottom:20}}><labelhtmlFor="gm-live">Useliveworldserver</label><Switchid="gm-live"checked={live}onCheckedChange={setLive}disabled={!capabilities.soapConfigured}/></div>{shortcuts.map(([label,cmd])=><buttonclassName="gm-shortcut"key={label}onClick={()=>setCommand(cmd)}><span>{label}</span><code>{cmd.split(' ').slice(0,label==='Teleport character'?2:1).join(' ')}</code></button>)}<pclassName="fineprint">LivecommandsuseAzerothCoreSOAPandrequireconfirmation.Supportedcommandsarelimitedtothistoolbox;botmodelscannotissuethem.</p></div></Panel><Paneltitle="The blue-robed GM"icon={ScrollText}className="mt-5"><divclassName="panel-body"><pclassName="list-muted">In-gamecommandssuchas<code>.gmon</code>,<code>.gmvisibleoff</code>,and<code>.summon</code>needyourlogged-inGMcharacter.Thiswebconsolehasnocharacterpositionorselectedtarget.</p><pclassName="fineprint">Thefirstprototypeprovidesremoteannouncements,namedteleports,andserver/playerinformation.MoreGMpowerscanbeaddedexplicitly.</p></div></Panel></div></div>}
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.