mirror of
https://github.com/kolbytn/mindcraft.git
synced 2025-08-04 06:15:32 +02:00
Merge 3ee54b3cf0
into 2b6a1115db
This commit is contained in:
commit
8ec8118c17
6 changed files with 565 additions and 69 deletions
47
config.json
Normal file
47
config.json
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"minecraft_version": "1.21.1", // supports up to 1.21.1
|
||||||
|
"host": "127.0.0.1", // or "localhost", "your.ip.address.here"
|
||||||
|
"port": 55916,
|
||||||
|
"auth": "offline", // or "microsoft"
|
||||||
|
|
||||||
|
// the mindserver manages all agents and hosts the UI
|
||||||
|
"host_mindserver": true, // if true, the mindserver will be hosted on this machine. otherwise, specify a public IP address
|
||||||
|
"mindserver_host": "localhost",
|
||||||
|
"mindserver_port": 8080,
|
||||||
|
|
||||||
|
// the base profile is shared by all bots for default prompts/examples/modes
|
||||||
|
"base_profile": "./profiles/defaults/survival.json", // also see creative.json, god_mode.json
|
||||||
|
"profiles": [
|
||||||
|
"./andy.json",
|
||||||
|
// "./profiles/gpt.json",
|
||||||
|
// "./profiles/claude.json",
|
||||||
|
// "./profiles/gemini.json",
|
||||||
|
// "./profiles/llama.json",
|
||||||
|
// "./profiles/qwen.json",
|
||||||
|
// "./profiles/grok.json",
|
||||||
|
// "./profiles/mistral.json",
|
||||||
|
// "./profiles/deepseek.json",
|
||||||
|
|
||||||
|
// using more than 1 profile requires you to /msg each bot indivually
|
||||||
|
// individual profiles override values from the base profile
|
||||||
|
],
|
||||||
|
"load_memory": false, // load memory from previous session
|
||||||
|
"init_message": "Respond with hello world and your name", // sends to all on spawn
|
||||||
|
"only_chat_with": [], // users that the bots listen to and send general messages to. if empty it will chat publicly
|
||||||
|
"speak": false, // allows all bots to speak through system text-to-speech. works on windows, mac, on linux you need to `apt install espeak`
|
||||||
|
"language": "en", // translate to/from this language. Supports these language names: https://cloud.google.com/translate/docs/languages
|
||||||
|
"show_bot_views": false, // show bot's view in browser at localhost:3000, 3001...
|
||||||
|
|
||||||
|
"allow_insecure_coding": false, // allows newAction command and model can write/run code on your computer. enable at own risk
|
||||||
|
"allow_vision": false, // allows vision model to interpret screenshots as inputs
|
||||||
|
"blocked_actions" : [], // commands to disable and remove from docs. Ex: ["!setMode"]
|
||||||
|
"code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout
|
||||||
|
"relevant_docs_count": 5, // number of relevant code function docs to select for prompting. -1 for all
|
||||||
|
|
||||||
|
"max_messages": 15, // max number of messages to keep in context
|
||||||
|
"num_examples": 2, // number of examples to give to the model
|
||||||
|
"max_commands": -1, // max number of commands that can be used in consecutive responses. -1 for no limit
|
||||||
|
"verbose_commands": true, // show full command syntax
|
||||||
|
"narrate_behavior": true, // chat simple automatic actions ('Picking up item!')
|
||||||
|
"chat_bot_messages": true, // publicly chat messages to other bots
|
||||||
|
}
|
21
settings.js
21
settings.js
|
@ -1,3 +1,6 @@
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
"minecraft_version": "1.21.1", // supports up to 1.21.1
|
"minecraft_version": "1.21.1", // supports up to 1.21.1
|
||||||
"host": "127.0.0.1", // or "localhost", "your.ip.address.here"
|
"host": "127.0.0.1", // or "localhost", "your.ip.address.here"
|
||||||
|
@ -47,6 +50,24 @@ const settings = {
|
||||||
"log_all_prompts": false, // log ALL prompts to file
|
"log_all_prompts": false, // log ALL prompts to file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const configPath = path.resolve('./src/server/saved_settings.json'); // This is to save user-modified settings edited via the Mindserver.
|
||||||
|
if(fs.existsSync(configPath)) {
|
||||||
|
try {
|
||||||
|
const newSettings = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||||
|
Object.assign(settings, newSettings);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error reading config.json. Using default settings.", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(settings, null, 4))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update settings
|
||||||
|
export function updateSettings(newSettings) {
|
||||||
|
Object.assign(settings, newSettings);
|
||||||
|
fs.writeFileSync(configPath, JSON.stringify(settings, null, 4));
|
||||||
|
}
|
||||||
|
|
||||||
// these environment variables override certain settings
|
// these environment variables override certain settings
|
||||||
if (process.env.MINECRAFT_PORT) {
|
if (process.env.MINECRAFT_PORT) {
|
||||||
settings.port = process.env.MINECRAFT_PORT;
|
settings.port = process.env.MINECRAFT_PORT;
|
||||||
|
|
|
@ -3,6 +3,8 @@ import express from 'express';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
import settings, { updateSettings } from '../../settings.js';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
// Module-level variables
|
// Module-level variables
|
||||||
let io;
|
let io;
|
||||||
|
@ -128,6 +130,27 @@ export function createMindServer(port = 8080) {
|
||||||
console.error('Error: ', error);
|
console.error('Error: ', error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on('get-settings', (callback) => {
|
||||||
|
callback(settings);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('update-settings', (newSettings) => {
|
||||||
|
updateSettings(newSettings);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('get-profile-dir', (callback) => {
|
||||||
|
const profileDir = path.join(__dirname, '../../profiles/');
|
||||||
|
fs.readdir(profileDir, (err, files) => {
|
||||||
|
if (err) {
|
||||||
|
console.error("Could not list the profiles.", err);
|
||||||
|
callback([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileNames = files.filter(file => file.endsWith('.json')).map(file => './profiles/' + file);
|
||||||
|
callback(fileNames);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(port, 'localhost', () => {
|
server.listen(port, 'localhost', () => {
|
||||||
|
|
|
@ -1,68 +1,135 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Mindcraft</title>
|
<title>Mindcraft</title>
|
||||||
<script src="/socket.io/socket.io.js"></script>
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
<style>
|
<link rel="stylesheet" type="text/css" href="/styles.css">
|
||||||
body {
|
<meta viewport="width=device-width, initial-scale=1.0">
|
||||||
font-family: Arial, sans-serif;
|
<meta charset="UTF-8">
|
||||||
margin: 20px;
|
|
||||||
background: #1a1a1a;
|
|
||||||
color: #e0e0e0;
|
|
||||||
}
|
|
||||||
#agents {
|
|
||||||
background: #2d2d2d;
|
|
||||||
padding: 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
color: #ffffff;
|
|
||||||
}
|
|
||||||
.agent {
|
|
||||||
margin: 10px 0;
|
|
||||||
padding: 10px;
|
|
||||||
background: #363636;
|
|
||||||
border-radius: 4px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.restart-btn, .start-btn, .stop-btn {
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
padding: 5px 10px;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
.restart-btn {
|
|
||||||
background: #4CAF50;
|
|
||||||
}
|
|
||||||
.start-btn {
|
|
||||||
background: #2196F3;
|
|
||||||
}
|
|
||||||
.stop-btn {
|
|
||||||
background: #f44336;
|
|
||||||
}
|
|
||||||
.restart-btn:hover { background: #45a049; }
|
|
||||||
.start-btn:hover { background: #1976D2; }
|
|
||||||
.stop-btn:hover { background: #d32f2f; }
|
|
||||||
.status-icon {
|
|
||||||
font-size: 12px;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
.status-icon.online {
|
|
||||||
color: #4CAF50;
|
|
||||||
}
|
|
||||||
.status-icon.offline {
|
|
||||||
color: #f44336;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Mindcraft</h1>
|
<h1>Mindcraft</h1>
|
||||||
<div id="agents"></div>
|
<div id="agents" class="container"></div>
|
||||||
|
<div id="settings" class="container">
|
||||||
|
<h3>Edit Settings</h3>
|
||||||
|
<button onclick="toggleSettings()" id="settingsToggler">Show Settings</button>
|
||||||
|
<div id="settingsContainer" class="miniContainer" style="display:none;">
|
||||||
|
<br>
|
||||||
|
<em>Edits won't take effect until restarting Mindcraft.</em>
|
||||||
|
<br>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcVersion">Minecraft Version: </label>
|
||||||
|
<input id="mcVersion" type="text" placeholder="Up to 1.21.1">
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcHostSelector">Host: </label>
|
||||||
|
<input id="mcHostSelector" type="text" placeholder="localhost">
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcPort">Port: </label>
|
||||||
|
<input id="mcPort" type="number" placeholder="55916">
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcAuth">Authentication Method: </label>
|
||||||
|
<select id="mcAuth">
|
||||||
|
<option value="offline">Offline (requires cracked server)</option>
|
||||||
|
<option value="microsoft">Microsoft Account</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcBaseProfile">Base Profile: </label>
|
||||||
|
<select id="mcBaseProfile">
|
||||||
|
<option value="./profiles/defaults/survival.json">Survival</option>
|
||||||
|
<option value="./profiles/defaults/creative.json">Creative</option>
|
||||||
|
<option value="./profiles/defaults/god_mode.json">God Mode</option>
|
||||||
|
</select>
|
||||||
|
<h5>The base profile is shared by all bots for default prompts/examples/modes</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcProfileList">Profiles: </label>
|
||||||
|
<div id="mcProfileList"></div>
|
||||||
|
<button onclick="addProfileItem(`mcProfileList`, `./andy.json`)" class="blue-btn add-btn"><svg width="20" height="20" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="white"><rect x="10" y="2" width="4" height="20"/><rect x="2" y="10" width="20" height="4"/></g></svg>Add Item</button>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcLoadMemory">Load Memory?</label>
|
||||||
|
<input type="radio" name="mcLoadMemory" value="true">Yes</input>
|
||||||
|
<input type="radio" name="mcLoadMemory" value="false" checked>No</input>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcInitMessage">Initial Message</label>
|
||||||
|
<input id="mcInitMessage" type="text" placeholder="Respond with hello world and your name" style="min-width:40%;">
|
||||||
|
<h5>This message gets sent to all bots on spawn.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcOnlyChatWith">Only chat with: (leave empty to chat publicly)</label>
|
||||||
|
<div id="mcOnlyChatWith"></div>
|
||||||
|
<button onclick="addProfileItem(`mcOnlyChatWith`, `Bob123`)" class="blue-btn add-btn"><svg width="20" height="20" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="white"><rect x="10" y="2" width="4" height="20"/><rect x="2" y="10" width="20" height="4"/></g></svg>Add Item</button>
|
||||||
|
<h5>Users that the bots listen to and send general messages to. If empty, it will chat publicly.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcSpeak">Speak?</label>
|
||||||
|
<input type="radio" name="mcSpeak" value="true">Yes</input>
|
||||||
|
<input type="radio" name="mcSpeak" value="false" checked>No</input>
|
||||||
|
<h5>Allows all bots to speak through system text-to-speech.<br>This works on Windows and Mac, but on Linux you may need to run <code>sudo apt install espeak</code> first.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcLanguage">Language: </label>
|
||||||
|
<input id="mcLanguage" type="text" placeholder="en">
|
||||||
|
<h5>Translates chat into specified language. <a href="https://cloud.google.com/translate/docs/languages">Supported languages</a></h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcShowBotViews">Show bot views in browser?</label>
|
||||||
|
<input type="radio" name="mcShowBotViews" value="true">Yes</input>
|
||||||
|
<input type="radio" name="mcShowBotViews" value="false" checked>No</input>
|
||||||
|
<h5>Only supports 1.20.4 and previous versions.<br>These can be accessed at http://localhost:3000, 3001, 3002, etc. depending on which bot you want to view.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcAllowInsecureCoding">Allow Insecure Coding?</label>
|
||||||
|
<input type="radio" name="mcAllowInsecureCoding" value="true">Yes</input>
|
||||||
|
<input type="radio" name="mcAllowInsecureCoding" value="false" checked>No</input>
|
||||||
|
<h5>Allows newAction command and model can write/run code on your computer. Enable at your own risk (it's fine if nobody <b>untrustworthy</b> is playing with your bot).</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcAllowVision">Allow Vision?</label>
|
||||||
|
<input type="radio" name="mcAllowVision" value="true">Yes</input>
|
||||||
|
<input type="radio" name="mcAllowVision" value="false" checked>No</input>
|
||||||
|
<h5>Allows the vision model to interpret screenshots as inputs. Requires version 1.20.4 or less.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcBlockedActions">Blocked Actions: </label>
|
||||||
|
<div id="mcBlockedActions"></div>
|
||||||
|
<button onclick="addProfileItem(`mcBlockedActions`, `!setMode`)" class="blue-btn add-btn"><svg width="20" height="20" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><g fill="white"><rect x="10" y="2" width="4" height="20"/><rect x="2" y="10" width="20" height="4"/></g></svg>Add Item</button>
|
||||||
|
<h5>Commands to disable and remove from the docs.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcMessageHistory">Maxmimum Message History Length: </label>
|
||||||
|
<input id="mcMessageHistory" type="number" placeholder="15">
|
||||||
|
<h5>Maximum number of previous messages to keep in context. Increasing it may produce longer response times and higher cost if using a paid API, but with the benefit of increasing memory.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcVerboseCommands">Verbose Commands?</label>
|
||||||
|
<input type="radio" name="mcVerboseCommands" value="true" checked>Yes</input>
|
||||||
|
<input type="radio" name="mcVerboseCommands" value="false">No</input>
|
||||||
|
<h5>Show full command syntax.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcNarrateBehavior">Narrate Behavior?</label>
|
||||||
|
<input type="radio" name="mcNarrateBehavior" value="true" checked>Yes</input>
|
||||||
|
<input type="radio" name="mcNarrateBehavior" value="false">No</input>
|
||||||
|
<h5>Chat simple, automatic actions for example <code>Picking up item!</code></h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption">
|
||||||
|
<label for="mcChatBotMessages">Chat to other bots?</label>
|
||||||
|
<input type="radio" name="mcChatBotMessages" value="true" checked>Yes</input>
|
||||||
|
<input type="radio" name="mcChatBotMessages" value="false">No</input>
|
||||||
|
<h5>Publicly chat messages to other bots.</h5>
|
||||||
|
</div>
|
||||||
|
<div class="formOption" style="display:flex;">
|
||||||
|
<button class="green-btn add-btn" onclick="saveSettings()"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="24" fill="currentColor" class="bi bi-floppy" viewBox="0 0 16 16"><path d="M11 2H9v3h2z"/><path d="M1.5 0h11.586a1.5 1.5 0 0 1 1.06.44l1.415 1.414A1.5 1.5 0 0 1 16 2.914V14.5a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 14.5v-13A1.5 1.5 0 0 1 1.5 0M1 1.5v13a.5.5 0 0 0 .5.5H2v-4.5A1.5 1.5 0 0 1 3.5 9h9a1.5 1.5 0 0 1 1.5 1.5V15h.5a.5.5 0 0 0 .5-.5V2.914a.5.5 0 0 0-.146-.353l-1.415-1.415A.5.5 0 0 0 13.086 1H13v4.5A1.5 1.5 0 0 1 11.5 7h-7A1.5 1.5 0 0 1 3 5.5V1H1.5a.5.5 0 0 0-.5.5m3 4a.5.5 0 0 0 .5.5h7a.5.5 0 0 0 .5-.5V1H4zM3 15h10v-4.5a.5.5 0 0 0-.5-.5h-9a.5.5 0 0 0-.5.5z"/></svg>Save</button>
|
||||||
|
<button class="red-btn add-btn" onclick="getSettings()"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" class="bi bi-x" viewBox="0 0 16 16"><path fill="white" d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708"/></svg>Reset</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const socket = io();
|
const socket = io();
|
||||||
|
@ -78,17 +145,17 @@
|
||||||
</span>
|
</span>
|
||||||
<div>
|
<div>
|
||||||
${agent.in_game ? `
|
${agent.in_game ? `
|
||||||
<button class="stop-btn" onclick="stopAgent('${agent.name}')">Stop</button>
|
<button class="red-btn" onclick="stopAgent('${agent.name}')">Stop</button>
|
||||||
<button class="restart-btn" onclick="restartAgent('${agent.name}')">Restart</button>
|
<button class="green-btn" onclick="restartAgent('${agent.name}')">Restart</button>
|
||||||
<input type="text" id="messageInput" placeholder="Enter a message or command..."></input><button class="start-btn" onclick="sendMessage('${agent.name}', document.getElementById('messageInput').value)">Send</button>
|
<input type="text" id="messageInput" placeholder="Enter a message or command..."></input><button class="blue-btn" onclick="sendMessage('${agent.name}', document.getElementById('messageInput').value)">Send</button>
|
||||||
` : `
|
` : `
|
||||||
<button class="start-btn" onclick="startAgent('${agent.name}')">Start</button>
|
<button class="blue-btn" onclick="startAgent('${agent.name}')">Start</button>
|
||||||
`}
|
`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('') +
|
`).join('') +
|
||||||
`<button class="stop-btn" onclick="killAllAgents()">Stop All</button>
|
`<button class="red-btn" onclick="killAllAgents()">Stop All</button>
|
||||||
<button class="stop-btn" onclick="shutdown()">Shutdown</button>` :
|
<button class="red-btn" onclick="shutdown()">Shutdown</button>` :
|
||||||
'<div class="agent">No agents connected</div>';
|
'<div class="agent">No agents connected</div>';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -111,10 +178,208 @@
|
||||||
function shutdown() {
|
function shutdown() {
|
||||||
socket.emit('shutdown');
|
socket.emit('shutdown');
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMessage(agentName, message) {
|
function sendMessage(agentName, message) {
|
||||||
socket.emit('send-message', agentName, message)
|
socket.emit('send-message', agentName, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pathProfiles;
|
||||||
|
function getProfilesFromPath() {
|
||||||
|
socket.emit('get-profile-dir', (response) => {
|
||||||
|
pathProfiles = response;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSettings() {
|
||||||
|
document.getElementById("settingsContainer").style.display = document.getElementById("settingsContainer").style.display === "none" ? "block" : "none";
|
||||||
|
document.getElementById("settingsToggler").innerText = document.getElementById("settingsToggler").innerText === "Show Settings" ? "Hide Settings" : "Show Settings";
|
||||||
|
if (document.getElementById("settingsContainer").style.display = "block") {
|
||||||
|
getSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let settings;
|
||||||
|
function getSettings() {
|
||||||
|
socket.emit('get-settings', (response) => {
|
||||||
|
settings = response;
|
||||||
|
console.log(settings);
|
||||||
|
updateSettings(settings);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveSettings() {
|
||||||
|
const newSettings = getNewSettings();
|
||||||
|
socket.emit('update-settings', newSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateListItem(listId, listContent, enabled = true, clear = true) {
|
||||||
|
if (clear) {
|
||||||
|
document.getElementById(listId).innerHTML = "";
|
||||||
|
listContent.forEach(element => {
|
||||||
|
addProfileItem(listId, element, enabled);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const list = document.getElementById(listId);
|
||||||
|
const existingItemsDivs = document.querySelectorAll(`#${listId} .list-item`);
|
||||||
|
let existingItems = Array.from(existingItemsDivs)
|
||||||
|
.map(item => item.querySelector('input[type="text"]').value);
|
||||||
|
listContent.forEach(element => {
|
||||||
|
if (existingItems.includes(element)) {
|
||||||
|
const item = Array.from(list.querySelectorAll('.list-item'))
|
||||||
|
.find(item => item.querySelector('input[type="text"]').value === element);
|
||||||
|
if (item) {
|
||||||
|
const checkbox = item.querySelector('input[type="checkbox"]');
|
||||||
|
console.log(`Setting this to ${enabled}: `, checkbox);
|
||||||
|
console.log("Checkbox in DOM?", document.body.contains(checkbox));
|
||||||
|
console.log("Before: ", checkbox.checked);
|
||||||
|
checkbox.checked = enabled;
|
||||||
|
console.log("After: ", checkbox.checked);
|
||||||
|
const itemName = item.querySelector('.name');
|
||||||
|
if (checkbox.checked) {
|
||||||
|
itemName.classList.remove('not-selected');
|
||||||
|
} else {
|
||||||
|
itemName.classList.add('not-selected');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addProfileItem(listId, element, enabled);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addProfileItem(listId, element, enabled);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBooleanRadio(radioName, value) {
|
||||||
|
const trueRadio = document.querySelector(`input[name="${radioName}"][value="true"]`);
|
||||||
|
const falseRadio = document.querySelector(`input[name="${radioName}"][value="false"]`);
|
||||||
|
trueRadio.checked = value;
|
||||||
|
falseRadio.checked = !value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBooleanRadio(radioName) {
|
||||||
|
return document.querySelector(`input[name="${radioName}"][value="true"]`).checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSettings(settings) {
|
||||||
|
// string/integer fields
|
||||||
|
document.getElementById("mcVersion").value = settings["minecraft_version"];
|
||||||
|
document.getElementById("mcHostSelector").value = settings["host"];
|
||||||
|
document.getElementById("mcPort").value = settings["port"];
|
||||||
|
document.getElementById("mcAuth").value = settings["auth"];
|
||||||
|
document.getElementById("mcBaseProfile").value = settings["base_profile"];
|
||||||
|
document.getElementById("mcInitMessage").value = settings["init_message"];
|
||||||
|
document.getElementById("mcLanguage").value = settings["language"];
|
||||||
|
document.getElementById("mcMessageHistory").value = settings["max_messages"];
|
||||||
|
|
||||||
|
// boolean fields
|
||||||
|
updateBooleanRadio("mcLoadMemory", settings["load_memory"]);
|
||||||
|
updateBooleanRadio("mcSpeak", settings["speak"]);
|
||||||
|
updateBooleanRadio("mcShowBotViews", settings["show_bot_views"]);
|
||||||
|
updateBooleanRadio("mcAllowInsecureCoding", settings["allow_insecure_coding"]);
|
||||||
|
updateBooleanRadio("mcAllowVision", settings["allow_vision"]);
|
||||||
|
updateBooleanRadio("mcVerboseCommands", settings["verbose_commands"]);
|
||||||
|
updateBooleanRadio("mcNarrateBehavior", settings["narrate_behavior"]);
|
||||||
|
updateBooleanRadio("mcChatBotMessages", settings["chat_bot_messages"]);
|
||||||
|
|
||||||
|
// array/list fields
|
||||||
|
updateListItem("mcBlockedActions", settings["blocked_actions"]);
|
||||||
|
updateListItem("mcOnlyChatWith", settings["only_chat_with"]);
|
||||||
|
|
||||||
|
// handling profiles is special
|
||||||
|
updateListItem("mcProfileList", settings["profiles"]);
|
||||||
|
pathProfiles = undefined;
|
||||||
|
getProfilesFromPath();
|
||||||
|
const intervalId = setInterval(() => {
|
||||||
|
if (pathProfiles !== undefined) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
updateListItem("mcProfileList", pathProfiles, enabled=false);
|
||||||
|
updateListItem("mcProfileList", settings["profiles"], enabled=true, clear = false);
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNewSettings() {
|
||||||
|
let settings = {};
|
||||||
|
|
||||||
|
// string/integer fields
|
||||||
|
settings["minecraft_version"] = document.getElementById("mcVersion").value;
|
||||||
|
settings["host"] = document.getElementById("mcHostSelector").value;
|
||||||
|
settings["port"] = parseInt(document.getElementById("mcPort").value, 10); // Convert to integer using base 10
|
||||||
|
settings["auth"] = document.getElementById("mcAuth").value;
|
||||||
|
settings["base_profile"] = document.getElementById("mcBaseProfile").value;
|
||||||
|
settings["init_message"] = document.getElementById("mcInitMessage").value;
|
||||||
|
settings["language"] = document.getElementById("mcLanguage").value;
|
||||||
|
settings["max_messages"] = parseInt(document.getElementById("mcMessageHistory").value, 10); // Convert to integer using base 10
|
||||||
|
|
||||||
|
// boolean fields
|
||||||
|
settings["load_memory"] = getBooleanRadio("mcLoadMemory");
|
||||||
|
settings["speak"] = getBooleanRadio("mcSpeak");
|
||||||
|
settings["show_bot_views"] = getBooleanRadio("mcShowBotViews");
|
||||||
|
settings["allow_insecure_coding"] = getBooleanRadio("mcAllowInsecureCoding");
|
||||||
|
settings["allow_vision"] = getBooleanRadio("mcAllowVision");
|
||||||
|
settings["verbose_commands"] = getBooleanRadio("mcVerboseCommands");
|
||||||
|
settings["narrate_behavior"] = getBooleanRadio("mcNarrateBehavior");
|
||||||
|
settings["chat_bot_messages"] = getBooleanRadio("mcChatBotMessages");
|
||||||
|
|
||||||
|
// array/list fields
|
||||||
|
settings["profiles"] = getItemNames("mcProfileList");
|
||||||
|
settings["blocked_actions"] = getItemNames("mcBlockedActions");
|
||||||
|
settings["only_chat_with"] = getItemNames("mcOnlyChatWith");
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addProfileItem(listName, itemNameArg, enabled) {
|
||||||
|
const itemDiv = document.createElement('div');
|
||||||
|
itemDiv.className = 'list-item';
|
||||||
|
|
||||||
|
const itemName = document.createElement('input');
|
||||||
|
itemName.type = 'text';
|
||||||
|
itemName.className = 'name';
|
||||||
|
itemName.value = itemNameArg;
|
||||||
|
|
||||||
|
const itemCheckbox = document.createElement('input');
|
||||||
|
itemCheckbox.type = 'checkbox';
|
||||||
|
itemCheckbox.checked = enabled;
|
||||||
|
if (itemCheckbox.checked) {
|
||||||
|
itemName.classList.remove('not-selected');
|
||||||
|
} else {
|
||||||
|
itemName.classList.add('not-selected');
|
||||||
|
}
|
||||||
|
itemCheckbox.onchange = function() {
|
||||||
|
if (itemCheckbox.checked) {
|
||||||
|
itemName.classList.remove('not-selected');
|
||||||
|
} else {
|
||||||
|
itemName.classList.add('not-selected');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteButton = document.createElement('button');
|
||||||
|
deleteButton.innerHTML = `<svg width="24" height="24" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><g fill="white"><rect x="30" y="35" width="40" height="50" rx="5"></rect><rect x="25" y="30" width="50" height="5"></rect><rect x="40" y="20" width="20" height="10"></rect><line x1="40" y1="40" x2="40" y2="75" stroke="red" stroke-width="4"></line><line x1="50" y1="40" x2="50" y2="75" stroke="red" stroke-width="4"></line><line x1="60" y1="40" x2="60" y2="75" stroke="red" stroke-width="4"></line></g></svg>Delete`;
|
||||||
|
deleteButton.style.display = "flex";
|
||||||
|
deleteButton.style.flexDirection = "row";
|
||||||
|
deleteButton.style.alignItems = "center";
|
||||||
|
deleteButton.style.padding = "3px 10px";
|
||||||
|
deleteButton.className = 'red-btn';
|
||||||
|
deleteButton.onclick = function() {
|
||||||
|
itemDiv.remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
itemDiv.appendChild(itemCheckbox);
|
||||||
|
itemDiv.appendChild(itemName);
|
||||||
|
itemDiv.appendChild(deleteButton);
|
||||||
|
|
||||||
|
document.getElementById(listName).appendChild(itemDiv);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemNames(listName) {
|
||||||
|
const items = document.querySelectorAll(`#${listName} .list-item`);
|
||||||
|
const names = Array.from(items)
|
||||||
|
.filter(item => item.querySelector('input[type="checkbox"]').checked)
|
||||||
|
.map(item => item.querySelector('input[type="text"]').value);
|
||||||
|
return names;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
139
src/server/public/styles.css
Normal file
139
src/server/public/styles.css
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background: #2d2d2d;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||||
|
margin:20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.miniContainer {
|
||||||
|
background: linear-gradient(to bottom, #414141, #3f3f3f);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.formOption {
|
||||||
|
background: #2d2d2d4f;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius:4px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||||
|
margin:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"], input[type="number"], select {
|
||||||
|
background: #cfcfcf;
|
||||||
|
color: #000;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding:5px;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin-bottom:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background:#aaaaaa;
|
||||||
|
border-radius:4px;
|
||||||
|
padding:4px;
|
||||||
|
color:#000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item input[type="text"] {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item input[type="checkbox"] {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item .name.not-selected {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: #929292
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding: 10px;
|
||||||
|
background: #363636;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 5px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background: #5c5c5c
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 3px 10px;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn svg {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-btn {
|
||||||
|
background: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blue-btn {
|
||||||
|
background: #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.red-btn {
|
||||||
|
background: #f44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green-btn:hover { background: #45a049; }
|
||||||
|
.blue-btn:hover { background: #1976D2; }
|
||||||
|
.red-btn:hover { background: #d32f2f; }
|
||||||
|
|
||||||
|
.status-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-icon.online {
|
||||||
|
color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-icon.offline {
|
||||||
|
color: #f44336;
|
||||||
|
}
|
1
src/server/saved_settings.json
Normal file
1
src/server/saved_settings.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{}
|
Loading…
Add table
Reference in a new issue