diff --git a/svg-feedback/index.html b/svg-feedback/index.html index 476b889..a0902cb 100644 --- a/svg-feedback/index.html +++ b/svg-feedback/index.html @@ -27,8 +27,11 @@

LLM SVG Art Evolution

- + + + + @@ -75,11 +78,12 @@

History

stop: document.getElementById('stop'), speedSlider: document.getElementById('speed-slider'), frameCounter: document.getElementById('frame-counter'), - history: document.getElementById('history') + history: document.getElementById('history'), + initialSvg: document.getElementById('initial-svg') }; // Load saved presets from localStorage - const savedPresets = JSON.parse(localStorage.getItem('savedPresets') || '{}'); + const savedPresets = JSON.parse(localStorage.getItem('savedPresets2') || '{}'); // Base presets combined with saved presets const presets = { @@ -164,10 +168,11 @@

History

let isRunning = false; let frames = []; let frameIndex = 0; - let currentSeed; - let initialSeed; + let currentSeed = 42; + let initialSeed = 42; let lastFrameTime = 0; let animationFrame = null; + let currentState = null; function createEmptyCanvas() { elements.preview.innerHTML = ` @@ -181,8 +186,22 @@

History

} function extractSvgContent(text) { - const svgMatch = text.match(/```(?:svg|xml)\n([\s\S]*?)\n```/); - return svgMatch ? svgMatch[1].trim() : null; + // Try to match SVG with language specifier + let svgMatch = text.match(/```(?:svg|xml)\n([\s\S]*?)\n```/); + if (svgMatch) return svgMatch[1].trim(); + + // Try to match SVG without language specifier + svgMatch = text.match(/```([\s\S]*?)```/); + if (svgMatch && svgMatch[1].trim().startsWith('History } lastFrameTime = performance.now(); - frameIndex = 0; function animate(currentTime) { const frameDelay = parseInt(elements.speedSlider.value); const elapsed = currentTime - lastFrameTime; if (elapsed >= frameDelay && frames.length > 0) { - updateFrame(frameIndex); frameIndex = (frameIndex + 1) % frames.length; + updateFrame(frameIndex); lastFrameTime = currentTime; } @@ -294,6 +312,8 @@

History

throw new Error('Incomplete SVG content'); } + console.log(`Response character count: ${text.length}`); + return svgContent; } catch (error) { @@ -313,7 +333,6 @@

History

const basePrompt = elements.basePrompt.value || preset.prompt; try { - const currentState = frames.length > 0 ? frames[frames.length - 1] : null; const evolutionPrompt = currentState ? `Evolve this SVG art while maintaining some consistency with the previous state. ${basePrompt}` : basePrompt; @@ -321,6 +340,7 @@

History

const svgContent = await generateText(evolutionPrompt, currentState); if (svgContent) { + currentState = svgContent; frames.push(svgContent); // Add to history @@ -333,20 +353,18 @@

History

`; elements.history.appendChild(historyItem); - // If this is the first frame, start the animation + // Update frame index to show the latest frame + frameIndex = frames.length - 1; + updateFrame(frameIndex); + + // Start animation if it's not already running if (frames.length === 1) { startPreviewAnimation(); } // Keep evolving - if (isRunning) { - setTimeout(evolve, 1500); - } - } else { - console.error('Failed to generate SVG content'); if (isRunning) { currentSeed++; - elements.seed.value = currentSeed; setTimeout(evolve, 1500); } } @@ -354,77 +372,115 @@

History

console.error('Evolution error:', error); if (isRunning) { currentSeed++; - elements.seed.value = currentSeed; setTimeout(evolve, 1500); } } } - function startEvolution() { + async function startEvolution() { + if (isRunning) return; + isRunning = true; elements.start.disabled = true; elements.stop.disabled = false; + frames = []; frameIndex = 0; - initialSeed = parseInt(elements.seed.value); + currentState = null; + + // Reset seed to initial value currentSeed = initialSeed; - elements.frameCounter.textContent = 'Frame 0/0'; - evolve(); + + if (elements.initialSvg.value?.trim?.()) { + const prompt = elements.basePrompt.value; + const initialSvg = elements.initialSvg.value.trim() + `\n\nPrompt: ${prompt}`; + try { + const svgContent = extractSvgContent(initialSvg); + if (svgContent) { + currentState = svgContent; + frames.push(svgContent); + updateFrame(frameIndex); + } + } catch (error) { + console.error('Invalid initial SVG:', error); + } + } + + await evolve(); } function stopEvolution() { isRunning = false; elements.start.disabled = false; elements.stop.disabled = true; - currentSeed = initialSeed; - elements.seed.value = initialSeed; if (animationFrame) { cancelAnimationFrame(animationFrame); - animationFrame = null; } + // Reset seed to initial value + currentSeed = initialSeed; } - function getFirstFourWords(str) { - return str.split(/\s+/).slice(0, 4).join(' ').toLowerCase().replace(/[^\w\s-]/g, ''); + function getFirstThreeWords(str) { + return str.trim().split(/\s+/).slice(0, 3).join(' '); } function saveCurrentPreset() { - const prompt = elements.basePrompt.value; - const temperature = parseFloat(elements.temperature.value); - const key = getFirstFourWords(prompt); + const defaultName = getFirstThreeWords(elements.basePrompt.value); + const name = prompt('Enter a name for this preset:', defaultName); + if (!name) return; - const savedPresets = JSON.parse(localStorage.getItem('savedPresets') || '{}'); - savedPresets[key] = { - prompt, - temperature, - name: key + const preset = { + name, + prompt: elements.basePrompt.value, + temperature: elements.temperature.value, + initialSvg: elements.initialSvg.value, + model: elements.modelSelect.value, + seed: currentSeed }; - localStorage.setItem('savedPresets', JSON.stringify(savedPresets)); + const savedPresets = JSON.parse(localStorage.getItem('savedPresets2') || '{}'); + savedPresets[name] = preset; + + localStorage.setItem('savedPresets2', JSON.stringify(savedPresets)); // Add to select if not exists - if (!elements.presetSelect.querySelector(`option[value="${key}"]`)) { + if (!elements.presetSelect.querySelector(`option[value="${name}"]`)) { const option = document.createElement('option'); - option.value = key; - option.textContent = key; + option.value = name; + option.textContent = name; elements.presetSelect.appendChild(option); } - elements.presetSelect.value = key; + elements.presetSelect.value = name; } + elements.presetSelect.addEventListener('change', () => { + const preset = { ...presets[elements.presetSelect.value] }; + console.log('Loading preset:', preset); + elements.basePrompt.value = preset.prompt; + elements.temperature.value = preset.temperature; + elements.temperatureValue.textContent = preset.temperature; + if (preset.initialSvg) { + elements.initialSvg.value = preset.initialSvg; + } + if (preset.model) { + elements.modelSelect.value = preset.model; + } + if (preset.seed) { + currentSeed = preset.seed; + initialSeed = preset.seed; + } else { + currentSeed = 42; + initialSeed = 42; + } + }); + // Event Listeners elements.start.addEventListener('click', startEvolution); elements.stop.addEventListener('click', stopEvolution); elements.temperature.addEventListener('input', (e) => { elements.temperatureValue.textContent = e.target.value; }); - elements.presetSelect.addEventListener('change', (e) => { - const preset = presets[e.target.value]; - elements.basePrompt.value = preset.prompt; - elements.temperature.value = preset.temperature; - elements.temperatureValue.textContent = preset.temperature; - }); // Initialize document.addEventListener('DOMContentLoaded', () => { @@ -438,6 +494,9 @@

History

createEmptyCanvas(); elements.presetSelect.value = 'digital-nature'; elements.basePrompt.value = presets['digital-nature'].prompt; + if (presets['digital-nature'].initialSvg) { + elements.initialSvg.value = presets['digital-nature'].initialSvg; + } }); diff --git a/svg-feedback/styles.css b/svg-feedback/styles.css index e850b59..43e2cf4 100644 --- a/svg-feedback/styles.css +++ b/svg-feedback/styles.css @@ -96,11 +96,31 @@ button:disabled { cursor: not-allowed; } +#preview { + width: 100%; + aspect-ratio: 1; + border: 1px solid #ccc; + margin-bottom: 1rem; + overflow: hidden; +} + #history { display: flex; flex-wrap: wrap; - gap: 5px; - margin-top: 5px; + gap: 10px; + margin-top: 20px; + overflow: hidden; +} + +.history-item { + border: 1px solid #ccc; + padding: 5px; + cursor: pointer; + overflow: hidden; +} + +.history-item > div { + overflow: hidden; } .history-item { @@ -215,3 +235,19 @@ input[type="range"] { font-family: monospace; margin-left: 10px; } + +textarea { + width: 100%; + margin-bottom: 1rem; + padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; + font-family: monospace; +} + +#initial-svg { + font-size: 0.9em; + background-color: #1a1a1a; + border-color: #333; + color: #aaa; +}