Spaces:
Running
Running
I have my own files for classes and lore I want to be able to upload
Browse files- index.html +22 -4
- script.js +150 -2
- style.css +8 -1
index.html
CHANGED
|
@@ -54,7 +54,6 @@
|
|
| 54 |
</div>
|
| 55 |
</div>
|
| 56 |
</section>
|
| 57 |
-
|
| 58 |
<!-- Lore Creation -->
|
| 59 |
<section class="mb-12 bg-white rounded-xl shadow-lg p-6">
|
| 60 |
<h2 class="text-2xl font-bold text-primary-500 mb-4">
|
|
@@ -62,7 +61,17 @@
|
|
| 62 |
Craft Your Lore
|
| 63 |
</h2>
|
| 64 |
<div class="space-y-4">
|
| 65 |
-
<div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
<label class="block text-gray-700 mb-2">World Name</label>
|
| 67 |
<input type="text" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
|
| 68 |
</div>
|
|
@@ -76,15 +85,24 @@
|
|
| 76 |
</div>
|
| 77 |
</div>
|
| 78 |
</section>
|
| 79 |
-
|
| 80 |
<!-- Character Classes -->
|
| 81 |
<section class="mb-12 bg-white rounded-xl shadow-lg p-6">
|
| 82 |
<h2 class="text-2xl font-bold text-primary-500 mb-4">
|
| 83 |
<i data-feather="users" class="inline mr-2"></i>
|
| 84 |
Design Character Classes
|
| 85 |
</h2>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
<div id="classes-container">
|
| 87 |
-
|
| 88 |
</div>
|
| 89 |
<button id="add-class-btn" class="mt-4 flex items-center text-secondary-500 font-medium hover:text-secondary-600">
|
| 90 |
<i data-feather="plus" class="mr-2"></i> Add New Class
|
|
|
|
| 54 |
</div>
|
| 55 |
</div>
|
| 56 |
</section>
|
|
|
|
| 57 |
<!-- Lore Creation -->
|
| 58 |
<section class="mb-12 bg-white rounded-xl shadow-lg p-6">
|
| 59 |
<h2 class="text-2xl font-bold text-primary-500 mb-4">
|
|
|
|
| 61 |
Craft Your Lore
|
| 62 |
</h2>
|
| 63 |
<div class="space-y-4">
|
| 64 |
+
<div class="flex items-center space-x-4 mb-4">
|
| 65 |
+
<label class="relative cursor-pointer bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg transition">
|
| 66 |
+
<span class="flex items-center">
|
| 67 |
+
<i data-feather="upload" class="mr-2 w-4 h-4"></i>
|
| 68 |
+
Upload Lore File
|
| 69 |
+
</span>
|
| 70 |
+
<input type="file" id="lore-upload" accept=".json,.txt,.md" class="hidden">
|
| 71 |
+
</label>
|
| 72 |
+
<span class="text-sm text-gray-500">or create manually below</span>
|
| 73 |
+
</div>
|
| 74 |
+
<div>
|
| 75 |
<label class="block text-gray-700 mb-2">World Name</label>
|
| 76 |
<input type="text" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-primary-500">
|
| 77 |
</div>
|
|
|
|
| 85 |
</div>
|
| 86 |
</div>
|
| 87 |
</section>
|
|
|
|
| 88 |
<!-- Character Classes -->
|
| 89 |
<section class="mb-12 bg-white rounded-xl shadow-lg p-6">
|
| 90 |
<h2 class="text-2xl font-bold text-primary-500 mb-4">
|
| 91 |
<i data-feather="users" class="inline mr-2"></i>
|
| 92 |
Design Character Classes
|
| 93 |
</h2>
|
| 94 |
+
<div class="flex items-center space-x-4 mb-4">
|
| 95 |
+
<label class="relative cursor-pointer bg-gray-100 hover:bg-gray-200 px-4 py-2 rounded-lg transition">
|
| 96 |
+
<span class="flex items-center">
|
| 97 |
+
<i data-feather="upload" class="mr-2 w-4 h-4"></i>
|
| 98 |
+
Upload Classes File
|
| 99 |
+
</span>
|
| 100 |
+
<input type="file" id="classes-upload" accept=".json,.txt,.md" class="hidden">
|
| 101 |
+
</label>
|
| 102 |
+
<span class="text-sm text-gray-500">or create manually below</span>
|
| 103 |
+
</div>
|
| 104 |
<div id="classes-container">
|
| 105 |
+
<!-- Classes will be added here -->
|
| 106 |
</div>
|
| 107 |
<button id="add-class-btn" class="mt-4 flex items-center text-secondary-500 font-medium hover:text-secondary-600">
|
| 108 |
<i data-feather="plus" class="mr-2"></i> Add New Class
|
script.js
CHANGED
|
@@ -1,8 +1,156 @@
|
|
|
|
|
| 1 |
document.addEventListener('DOMContentLoaded', function() {
|
| 2 |
// Initialize class counter
|
| 3 |
let classCount = 0;
|
| 4 |
-
|
| 5 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
document.getElementById('add-class-btn').addEventListener('click', function() {
|
| 7 |
classCount++;
|
| 8 |
const container = document.getElementById('classes-container');
|
|
|
|
| 1 |
+
|
| 2 |
document.addEventListener('DOMContentLoaded', function() {
|
| 3 |
// Initialize class counter
|
| 4 |
let classCount = 0;
|
| 5 |
+
|
| 6 |
+
// Handle lore file upload
|
| 7 |
+
document.getElementById('lore-upload').addEventListener('change', function(e) {
|
| 8 |
+
const file = e.target.files[0];
|
| 9 |
+
if (!file) return;
|
| 10 |
+
|
| 11 |
+
const reader = new FileReader();
|
| 12 |
+
reader.onload = function(e) {
|
| 13 |
+
try {
|
| 14 |
+
const loreData = JSON.parse(e.target.result);
|
| 15 |
+
// Populate lore fields with uploaded data
|
| 16 |
+
if (loreData.worldName) {
|
| 17 |
+
document.querySelector('input[type="text"]').value = loreData.worldName;
|
| 18 |
+
}
|
| 19 |
+
if (loreData.backgroundStory) {
|
| 20 |
+
document.querySelector('textarea').value = loreData.backgroundStory;
|
| 21 |
+
}
|
| 22 |
+
if (loreData.majorFactions) {
|
| 23 |
+
document.querySelectorAll('input[type="text"]')[1].value = loreData.majorFactions;
|
| 24 |
+
}
|
| 25 |
+
} catch (error) {
|
| 26 |
+
alert('Error parsing lore file. Please check the format.');
|
| 27 |
+
}
|
| 28 |
+
};
|
| 29 |
+
reader.readAsText(file);
|
| 30 |
+
});
|
| 31 |
+
|
| 32 |
+
// Handle classes file upload
|
| 33 |
+
document.getElementById('classes-upload').addEventListener('change', function(e) {
|
| 34 |
+
const file = e.target.files[0];
|
| 35 |
+
if (!file) return;
|
| 36 |
+
|
| 37 |
+
const reader = new FileReader();
|
| 38 |
+
reader.onload = function(e) {
|
| 39 |
+
try {
|
| 40 |
+
const classesData = JSON.parse(e.target.result);
|
| 41 |
+
const container = document.getElementById('classes-container');
|
| 42 |
+
container.innerHTML = ''; // Clear existing classes
|
| 43 |
+
|
| 44 |
+
classesData.forEach((classData, index) => {
|
| 45 |
+
classCount++;
|
| 46 |
+
const classCard = document.createElement('div');
|
| 47 |
+
classCard.className = 'class-card bg-gray-50 p-4 rounded-lg border border-gray-200 mb-4';
|
| 48 |
+
classCard.innerHTML = `
|
| 49 |
+
<div class="flex justify-between items-start mb-2">
|
| 50 |
+
<h3 class="font-semibold text-lg">${classData.name || `New Class #${index+1}`}</h3>
|
| 51 |
+
<button class="text-red-500 hover:text-red-600">
|
| 52 |
+
<i data-feather="trash-2"></i>
|
| 53 |
+
</button>
|
| 54 |
+
</div>
|
| 55 |
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 56 |
+
<div>
|
| 57 |
+
<label class="block text-gray-700 mb-1 text-sm">Class Name</label>
|
| 58 |
+
<input type="text" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${classData.name || ''}">
|
| 59 |
+
</div>
|
| 60 |
+
<div>
|
| 61 |
+
<label class="block text-gray-700 mb-1 text-sm">Primary Attribute</label>
|
| 62 |
+
<select class="w-full px-3 py-1 border border-gray-300 rounded-md">
|
| 63 |
+
<option ${classData.attribute === 'Strength' ? 'selected' : ''}>Strength</option>
|
| 64 |
+
<option ${classData.attribute === 'Dexterity' ? 'selected' : ''}>Dexterity</option>
|
| 65 |
+
<option ${classData.attribute === 'Intelligence' ? 'selected' : ''}>Intelligence</option>
|
| 66 |
+
<option ${classData.attribute === 'Wisdom' ? 'selected' : ''}>Wisdom</option>
|
| 67 |
+
<option ${classData.attribute === 'Charisma' ? 'selected' : ''}>Charisma</option>
|
| 68 |
+
<option ${classData.attribute === 'Constitution' ? 'selected' : ''}>Constitution</option>
|
| 69 |
+
</select>
|
| 70 |
+
</div>
|
| 71 |
+
<div>
|
| 72 |
+
<label class="block text-gray-700 mb-1 text-sm">Hit Points</label>
|
| 73 |
+
<input type="text" class="w-full px-3 py-1 border border-gray-300 rounded-md" value="${classData.hitPoints || '1d8'}">
|
| 74 |
+
</div>
|
| 75 |
+
<div>
|
| 76 |
+
<label class="block text-gray-700 mb-1 text-sm">Armor Proficiency</label>
|
| 77 |
+
<select class="w-full px-3 py-1 border border-gray-300 rounded-md">
|
| 78 |
+
<option ${classData.armor === 'Light' ? 'selected' : ''}>Light</option>
|
| 79 |
+
<option ${classData.armor === 'Medium' ? 'selected' : ''}>Medium</option>
|
| 80 |
+
<option ${classData.armor === 'Heavy' ? 'selected' : ''}>Heavy</option>
|
| 81 |
+
<option ${classData.armor === 'None' ? 'selected' : ''}>None</option>
|
| 82 |
+
</select>
|
| 83 |
+
</div>
|
| 84 |
+
<div class="md:col-span-2">
|
| 85 |
+
<label class="block text-gray-700 mb-1 text-sm">Class Description</label>
|
| 86 |
+
<textarea rows="2" class="w-full px-3 py-1 border border-gray-300 rounded-md">${classData.description || ''}</textarea>
|
| 87 |
+
</div>
|
| 88 |
+
<div class="md:col-span-2">
|
| 89 |
+
<label class="block text-gray-700 mb-1 text-sm">Special Abilities</label>
|
| 90 |
+
<div class="abilities-container space-y-2">
|
| 91 |
+
${classData.abilities ? classData.abilities.map(ability => `
|
| 92 |
+
<div class="flex items-center">
|
| 93 |
+
<input type="text" class="flex-1 px-2 py-1 border border-gray-300 rounded-md mr-2" value="${ability}">
|
| 94 |
+
<button class="text-red-500 hover:text-red-600">
|
| 95 |
+
<i data-feather="x" class="w-4 h-4"></i>
|
| 96 |
+
</button>
|
| 97 |
+
</div>
|
| 98 |
+
`).join('') : ''}
|
| 99 |
+
</div>
|
| 100 |
+
<button class="mt-2 text-sm text-secondary-500 hover:text-secondary-600 flex items-center">
|
| 101 |
+
<i data-feather="plus" class="w-4 h-4 mr-1"></i> Add Ability
|
| 102 |
+
</button>
|
| 103 |
+
</div>
|
| 104 |
+
</div>
|
| 105 |
+
`;
|
| 106 |
+
|
| 107 |
+
container.appendChild(classCard);
|
| 108 |
+
feather.replace();
|
| 109 |
+
|
| 110 |
+
// Add event listener for delete button
|
| 111 |
+
classCard.querySelector('button').addEventListener('click', function() {
|
| 112 |
+
container.removeChild(classCard);
|
| 113 |
+
classCount--;
|
| 114 |
+
});
|
| 115 |
+
|
| 116 |
+
// Add event listeners for abilities
|
| 117 |
+
const abilitiesBtn = classCard.querySelector('.abilities-container').nextElementSibling;
|
| 118 |
+
abilitiesBtn.addEventListener('click', function() {
|
| 119 |
+
addAbilityField(this.previousElementSibling);
|
| 120 |
+
});
|
| 121 |
+
|
| 122 |
+
// Add event listeners for existing ability delete buttons
|
| 123 |
+
classCard.querySelectorAll('.abilities-container button').forEach(btn => {
|
| 124 |
+
btn.addEventListener('click', function() {
|
| 125 |
+
this.parentElement.remove();
|
| 126 |
+
});
|
| 127 |
+
});
|
| 128 |
+
});
|
| 129 |
+
} catch (error) {
|
| 130 |
+
alert('Error parsing classes file. Please check the format.');
|
| 131 |
+
}
|
| 132 |
+
};
|
| 133 |
+
reader.readAsText(file);
|
| 134 |
+
});
|
| 135 |
+
|
| 136 |
+
function addAbilityField(container) {
|
| 137 |
+
const abilityDiv = document.createElement('div');
|
| 138 |
+
abilityDiv.className = 'flex items-center';
|
| 139 |
+
abilityDiv.innerHTML = `
|
| 140 |
+
<input type="text" class="flex-1 px-2 py-1 border border-gray-300 rounded-md mr-2" placeholder="Ability name">
|
| 141 |
+
<button class="text-red-500 hover:text-red-600">
|
| 142 |
+
<i data-feather="x" class="w-4 h-4"></i>
|
| 143 |
+
</button>
|
| 144 |
+
`;
|
| 145 |
+
container.appendChild(abilityDiv);
|
| 146 |
+
feather.replace();
|
| 147 |
+
|
| 148 |
+
// Add event listener for delete ability button
|
| 149 |
+
abilityDiv.querySelector('button').addEventListener('click', function() {
|
| 150 |
+
container.removeChild(abilityDiv);
|
| 151 |
+
});
|
| 152 |
+
}
|
| 153 |
+
// Add class button functionality
|
| 154 |
document.getElementById('add-class-btn').addEventListener('click', function() {
|
| 155 |
classCount++;
|
| 156 |
const container = document.getElementById('classes-container');
|
style.css
CHANGED
|
@@ -30,12 +30,19 @@ button:hover {
|
|
| 30 |
::-webkit-scrollbar-thumb:hover {
|
| 31 |
background: #555;
|
| 32 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
/* Character class card styles */
|
| 35 |
.class-card {
|
| 36 |
transition: all 0.3s ease;
|
| 37 |
}
|
| 38 |
-
|
| 39 |
.class-card:hover {
|
| 40 |
transform: translateY(-5px);
|
| 41 |
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
|
|
| 30 |
::-webkit-scrollbar-thumb:hover {
|
| 31 |
background: #555;
|
| 32 |
}
|
| 33 |
+
/* Upload button styles */
|
| 34 |
+
input[type="file"] + span {
|
| 35 |
+
transition: all 0.2s ease;
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
input[type="file"]:hover + span {
|
| 39 |
+
background-color: #e5e7eb;
|
| 40 |
+
}
|
| 41 |
|
| 42 |
/* Character class card styles */
|
| 43 |
.class-card {
|
| 44 |
transition: all 0.3s ease;
|
| 45 |
}
|
|
|
|
| 46 |
.class-card:hover {
|
| 47 |
transform: translateY(-5px);
|
| 48 |
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|