Scratch_vlm_v1 / templates /index4.html
WebashalarForML's picture
Upload 175 files
a522962 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Scratch Game JSON Generator</title>
<style>
body { font-family: Arial, sans-serif; margin: 2rem; background: #f9f9f9; }
.asset-row, .sound-row {
margin-bottom: .5em;
display: flex;
align-items: center;
gap: 0.5em; /* Add some space between elements */
}
.asset-row input, .asset-row select,
.sound-row input, .sound-row select {
margin-right: 0; /* Reset margin-right for better gap control */
}
#backdrops-container .asset-row,
#sprites-container .asset-row {
flex-direction: column; /* Stack name/file and its sounds */
align-items: flex-start;
border: 1px solid #ddd;
padding: 1em;
margin-bottom: 1em;
border-radius: 5px;
}
.asset-main-row { /* New class for the main part of asset row */
display: flex;
align-items: center;
width: 100%;
gap: 0.5em;
}
.asset-sounds-container { /* Reusable for both backdrops and sprites */
margin-top: 0.5em;
padding-left: 1em;
border-left: 2px solid #eee;
}
button { margin-top: 1rem; padding: .5rem 1rem; font-size: 1rem; cursor: pointer; }
button.remove {
background: #f44336;
color: white;
border: none;
border-radius: 3px;
padding: 0.3em 0.6em;
font-size: 0.8em;
cursor: pointer;
margin-left: 0.5em;
}
button.add-sound {
background: #4CAF50;
color: white;
border: none;
border-radius: 3px;
padding: 0.3em 0.6em;
font-size: 0.8em;
cursor: pointer;
margin-top: 0.5em;
}
pre { background: #222; color: #eee; padding: 1rem; overflow-x: auto; max-height: 60vh; }
</style>
</head>
<body>
<h1>Scratch Game JSON Generator</h1>
<label for="gameDesc">Enter game description:</label><br />
<textarea id="gameDesc" rows="4" placeholder="e.g. jumping cat game over obstacle"></textarea><br />
<h2>Backdrops</h2>
<div id="backdrops-container"></div>
<button id="add-backdrop">+ Add Backdrop</button>
<h2>Sprites</h2>
<div id="sprites-container"></div>
<button id="add-sprite">+ Add Sprite</button><br/>
<button id="generateBtn">Generate Scratch JSON</button>
<h2>Output JSON:</h2>
<pre id="output">Waiting for input...</pre>
<script>
let availableBackdrops = [];
let availableSprites = [];
let availableSounds = []; // New array for available sounds
// Fetch lists of files from server
async function loadAssetLists() {
const resp = await fetch('/list_assets');
const { backdrops, sprites, sounds } = await resp.json();
availableBackdrops = backdrops;
availableSprites = sprites;
availableSounds = sounds;
}
// Helper to create asset (backdrop/sprite) rows
function makeAssetRow(containerId, available, type) {
const container = document.getElementById(containerId);
const row = document.createElement('div');
row.className = 'asset-row';
row.classList.add(`${type.toLowerCase()}-item`); // Add type-specific class
let innerHTML = `
<div class="asset-main-row">
<input type="text" placeholder="${type} name" class="asset-name"/>
<select class="asset-select">
${available.map(a => `<option value="${a}">${a}</option>`).join('')}
</select>
<button class="remove">×</button>
</div>
<div class="asset-sounds-container">
<h3>Sounds for this ${type}:</h3>
<div class="asset-individual-sounds"></div>
<button type="button" class="add-sound">+ Add Sound</button>
</div>
`;
row.innerHTML = innerHTML;
// Attach event listener for adding sounds to this specific asset row
row.querySelector('.add-sound').onclick = () => {
makeSoundRow(row.querySelector('.asset-individual-sounds'), availableSounds, `${type} Sound`);
};
row.querySelector('.remove').onclick = () => row.remove();
container.append(row);
}
// Helper to create sound rows
function makeSoundRow(container, available, type) {
const row = document.createElement('div');
row.className = 'sound-row';
row.innerHTML = `
<input type="text" placeholder="${type} name" class="sound-name"/>
<select class="sound-select">
${available.map(s => `<option value="${s}">${s}</option>`).join('')}
</select>
<button class="remove">×</button>
`;
row.querySelector('.remove').onclick = () => row.remove();
container.append(row);
}
document.getElementById('add-backdrop').onclick = () => {
makeAssetRow('backdrops-container', availableBackdrops, 'Backdrop');
};
// REMOVED: add-stage-sound button listener
// document.getElementById('add-stage-sound').onclick = () => {
// makeSoundRow('stage-sounds-container', availableSounds, 'Stage Sound');
// };
document.getElementById('add-sprite').onclick = () => {
makeAssetRow('sprites-container', availableSprites, 'Sprite');
};
document.getElementById('generateBtn').addEventListener('click', async () => {
const desc = document.getElementById('gameDesc').value.trim();
if (!desc) return alert('Please enter a game description.');
const collectAssetAndSounds = (containerId, type) => {
const collectedData = [];
const soundsPayload = {}; // Keyed by asset name (backdrop or sprite)
Array.from(document.getElementById(containerId).querySelectorAll(`.${type.toLowerCase()}-item`)).forEach(itemRow => {
const itemNameInput = itemRow.querySelector('.asset-main-row .asset-name');
const itemFilenameSelect = itemRow.querySelector('.asset-main-row .asset-select');
const itemName = itemNameInput.value.trim();
const itemFilename = itemFilenameSelect.value;
if (itemName && itemFilename) {
collectedData.push({
name: itemName,
filename: itemFilename
});
const individualSoundsContainer = itemRow.querySelector('.asset-individual-sounds');
if (individualSoundsContainer) {
// Associate sounds with the asset's name
soundsPayload[itemName] = Array.from(individualSoundsContainer.querySelectorAll('.sound-row'))
.map(row => ({
name: row.querySelector('.sound-name').value.trim(),
filename: row.querySelector('.sound-select').value
}))
.filter(s => s.name && s.filename);
}
}
});
return { assets: collectedData, sounds: soundsPayload };
};
const backdropsData = collectAssetAndSounds('backdrops-container', 'Backdrop');
const spritesData = collectAssetAndSounds('sprites-container', 'Sprite');
// The stage is special. If you want a global "stage" sound that isn't tied to a specific backdrop,
// you'd need a separate input for it. For now, we're assuming sounds are tied to backdrops.
// If there's only one backdrop, its sounds effectively become "stage sounds".
const payload = {
description: desc,
backdrops: backdropsData.assets,
sprites: spritesData.assets,
// Pass backdrop sounds and sprite sounds separately for the backend
backdrop_sounds: backdropsData.sounds, // New: Sounds keyed by backdrop name
sprite_sounds: spritesData.sounds
};
const output = document.getElementById('output');
output.textContent = 'Generating...';
const resp = await fetch('/generate_game', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!resp.ok) {
const err = await resp.json();
output.textContent = 'Error: ' + (err.error || resp.statusText);
return;
}
const data = await resp.json();
output.textContent = JSON.stringify(data, null, 2);
});
// initial load
loadAssetLists();
</script>
</body>
</html>