// ==UserScript==
// @name Pterodactyl Egg Search
// @namespace https://github.com/lauridskern/pterodactyl-egg-search
// @version 1.0
// @description Adds a searchable dropdown for egg selection in Pterodactyl's egg import modal
// @author Laurids Kern
// @match https://*/admin/nests*
// @match http://*/admin/nests*
// @grant GM_addStyle
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const GITHUB_API_URL = 'https://api.github.com/repos/parkervcp/eggs/git/trees/master?recursive=1';
GM_addStyle(`
.egg-combobox-container {
display: flex;
flex-direction: column;
width: 100%;
margin-bottom: 15px;
}
.egg-combobox-input-wrapper {
position: relative;
display: flex;
width: 100%;
}
.egg-combobox-input {
flex: 1;
width: 100%;
padding-right: 30px;
}
.egg-combobox-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
max-height: 200px;
overflow-y: auto;
background-color: #b5bcc1;
border: 1px solid #ccc;
border-top: none;
z-index: 1000;
display: none;
}
.egg-combobox-option {
padding: 5px 10px;
cursor: pointer;
color: #444;
}
.egg-combobox-option:hover {
background-color: #a0a7ac;
}
.egg-combobox-arrow {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 4px solid transparent;
border-top: 5px solid #888;
pointer-events: none;
}
.egg-combobox-error {
color: #721c24;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
padding: 10px;
margin-top: 10px;
border-radius: 4px;
}
`);
function formatEggName(name) {
return name.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}
async function fetchEggJSONs() {
try {
const response = await fetch(GITHUB_API_URL);
if (!response.ok) {
throw new Error(`GitHub API responded with status: ${response.status}`);
}
const data = await response.json();
const jsonFiles = data.tree.filter(item => item.path.endsWith('.json') && item.path.includes('/egg-'));
const eggs = await Promise.all(jsonFiles.map(async file => {
try {
const contentResponse = await fetch(`https://raw.githubusercontent.com/parkervcp/eggs/master/${file.path}`);
if (!contentResponse.ok) {
throw new Error(`Failed to fetch egg JSON: ${contentResponse.status}`);
}
const json = await contentResponse.text();
const name = file.path.split('/').pop().replace('.json', '').replace('egg-', '');
return {
name: formatEggName(name),
json: json
};
} catch (error) {
console.error(`Error fetching egg JSON for ${file.path}:`, error);
return null;
}
}));
return eggs.filter(egg => egg !== null);
} catch (error) {
console.error(`Error fetching egg JSONs: ${error.message}`);
return [];
}
}
function createComboBox(eggs) {
const container = document.createElement('div');
container.className = 'form-group egg-combobox-container';
container.innerHTML = `
`;
const input = container.querySelector('#eggCombobox');
const dropdown = container.querySelector('.egg-combobox-dropdown');
function updateDropdown() {
const filter = input.value.toLowerCase();
const filteredEggs = eggs.filter(egg => egg.name.toLowerCase().includes(filter));
dropdown.innerHTML = filteredEggs.map(egg => `${egg.name}
`).join('');
dropdown.style.display = filteredEggs.length > 0 ? 'block' : 'none';
}
input.addEventListener('input', updateDropdown);
input.addEventListener('focus', updateDropdown);
input.addEventListener('blur', () => {
setTimeout(() => { dropdown.style.display = 'none'; }, 200);
});
dropdown.addEventListener('mousedown', (e) => {
if (e.target.classList.contains('egg-combobox-option')) {
input.value = e.target.textContent;
dropdown.style.display = 'none';
const selectedEgg = eggs.find(egg => egg.name === input.value);
if (selectedEgg) {
const fileInput = document.querySelector('input[type="file"]');
if (fileInput) {
const blob = new Blob([selectedEgg.json], {type: 'application/json'});
const file = new File([blob], `${selectedEgg.name.replace(/ /g, '-').toLowerCase()}.json`, {type: 'application/json'});
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
fileInput.files = dataTransfer.files;
}
}
}
});
return container;
}
function injectComboBox(eggs) {
const modal = document.querySelector('.modal-content');
if (!modal) return;
const modalTitle = modal.querySelector('.modal-title');
if (!modalTitle || !modalTitle.textContent.trim().includes('Import an Egg')) return;
const modalBody = modal.querySelector('.modal-body');
if (!modalBody) return;
const existingComboBox = modalBody.querySelector('.egg-combobox-container');
if (existingComboBox) existingComboBox.remove();
const comboBox = createComboBox(eggs);
modalBody.insertBefore(comboBox, modalBody.firstChild);
}
async function main() {
try {
const eggs = await fetchEggJSONs();
if (eggs.length === 0) {
throw new Error('No eggs found or error fetching eggs');
}
// Check for modal every second
const intervalId = setInterval(() => {
const modal = document.querySelector('.modal-content');
if (modal) {
injectComboBox(eggs);
clearInterval(intervalId);
}
}, 1000);
} catch (error) {
console.error('Failed to initialize Pterodactyl Egg Dropdown:', error);
const modal = document.querySelector('.modal-content .modal-body');
if (modal) {
const errorElement = document.createElement('div');
errorElement.className = 'egg-combobox-error';
errorElement.textContent = 'Failed to load egg list. Please try refreshing the page or check the console for more details.';
modal.insertBefore(errorElement, modal.firstChild);
}
}
}
// Run the script when the page is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', main);
} else {
main();
}
})();