Skip to content

Commit 0c26041

Browse files
minor fixes (#96)
* Simplify LLM feedback code while maintaining core functionality - Streamlined UI with cleaner layout and better visual hierarchy - Reduced CSS by 70% using a simpler container-based approach - Simplified JavaScript from ~400 lines to ~130 lines - Improved state management and evolution logic - Maintained all core functionality including art generation, preview, and history Closes #91 Mentat precommits passed. Log: https://mentat.ai/log/ccb24099-4857-47b7-b5d7-d856a21c9041 * Update .gitignore with additional common patterns Added patterns for: - Yarn PnP files - Additional build output directory (out/) - General debug logs - OS-specific files (DS_Store, Thumbs.db) Mentat precommits passed. Log: https://mentat.ai/log/75248827-ef9b-4de6-9167-557d758749fb * Make .gitignore more comprehensive for all app types - Removed wildcard prefixes to work recursively at any depth - Added patterns for various build tools and frameworks - Added web-specific patterns for compiled assets - Added testing and temporary file patterns - Improved organization and categorization Mentat precommits passed. Log: https://mentat.ai/log/9478638d-b20e-4b73-a8ec-7dc7c7cfd202 * Update .gitignore to better handle subfolder apps - Added `**/` prefix to match files at any directory depth - Added patterns for Yarn 2+ cache files - Included vanilla HTML/JS specific patterns - Added more build output and test directories - Improved handling of OS-specific files at all levels Mentat precommits passed. Log: https://mentat.ai/log/4786f47d-56c8-44b6-bb53-1e2e7696062a * Revert "Make .gitignore more comprehensive for all app types" This reverts commit a424654. * enhance visual poetry UI and generation enhance visual poetry UI and generation - Update presets to focus on visual poetry styles (Apollinaire, Mallarmé, etc) - Improve monospace text display with better styling and dimensions - Add seed parameter for reproducible generations - Remove separate current art window in favor of preview - Enhance generation logic with better state management - Improve error handling and logging * Tweaks more prompts * add model selector * more robust fetching logic * small tweaks * improvements --------- Co-authored-by: MentatBot <160964065+MentatBot@users.noreply.github.com>
1 parent 2e8786e commit 0c26041

File tree

1 file changed

+126
-42
lines changed

1 file changed

+126
-42
lines changed

llm-feedback/index.html

Lines changed: 126 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,34 @@
7676
display: block;
7777
margin: 8px 0;
7878
}
79+
#speed-slider {
80+
-webkit-appearance: none;
81+
background: #ddd;
82+
height: 2px;
83+
direction: rtl;
84+
}
85+
86+
#speed-slider::-webkit-slider-thumb {
87+
-webkit-appearance: none;
88+
width: 15px;
89+
height: 15px;
90+
border-radius: 50%;
91+
background: #666;
92+
cursor: pointer;
93+
}
94+
95+
#speed-slider::-moz-range-thumb {
96+
width: 15px;
97+
height: 15px;
98+
border-radius: 50%;
99+
background: #666;
100+
cursor: pointer;
101+
border: none;
102+
}
103+
104+
#speed-slider::-moz-range-progress {
105+
background-color: transparent;
106+
}
79107
</style>
80108
</head>
81109
<body>
@@ -99,27 +127,34 @@ <h1>LLM ASCII Art Evolution</h1>
99127
<label for="base-prompt">Animation Prompt:</label>
100128
<textarea id="base-prompt" rows="4"></textarea>
101129

130+
<label for="model-select">Model:</label>
131+
<select id="model-select"></select>
132+
133+
<div style="display: flex; align-items: center; gap: 20px; margin: 10px 0;">
134+
<div style="display: flex; align-items: center; gap: 10px;">
135+
<label for="temperature">Temperature:</label>
136+
<input type="range" id="temperature" min="0" max="2" step="0.1" value="0.8">
137+
<span id="temperature-value">0.8</span>
138+
</div>
139+
<div style="display: flex; align-items: center; gap: 10px;">
140+
<label for="seed">Seed:</label>
141+
<input type="number" id="seed" value="-1">
142+
</div>
143+
</div>
144+
145+
<div id="preview-window" class="mono"></div>
146+
147+
<div style="display: flex; align-items: center; gap: 10px; margin-top: 10px;">
148+
<label for="speed-slider">Speed:</label>
149+
<input type="range" id="speed-slider" min="100" max="2000" value="700" step="100" style="direction: rtl;">
150+
</div>
151+
102152
<div>
103153
<button id="startBtn">Start Evolution</button>
104154
<button id="stopBtn" disabled>Stop Evolution</button>
105155
</div>
106-
107-
<select id="model-select"></select>
108-
109-
<label for="temperature">Temperature:</label>
110-
<input type="range" id="temperature" min="0" max="2" step="0.1" value="0.8">
111-
112-
<label>
113-
Seed (optional):
114-
<input type="number" id="seed" placeholder="Random">
115-
</label>
116156
</div>
117157

118-
<div class="container">
119-
<h2>Preview</h2>
120-
<div id="preview-window" class="mono"></div>
121-
</div>
122-
123158
<div class="container">
124159
<h2>History</h2>
125160
<div id="history"></div>
@@ -180,7 +215,8 @@ <h2>History</h2>
180215
preset: document.getElementById('preset-select'),
181216
prompt: document.getElementById('base-prompt'),
182217
preview: document.getElementById('preview-window'),
183-
modelSelect: document.getElementById('model-select')
218+
modelSelect: document.getElementById('model-select'),
219+
speedSlider: document.getElementById('speed-slider')
184220
};
185221

186222
// Initialize model selector
@@ -202,6 +238,7 @@ <h2>History</h2>
202238
let isRunning = false;
203239
let frames = [];
204240
let frameIndex = 0;
241+
let animationInterval;
205242

206243
// Core functions
207244
function createEmptyCanvas() {
@@ -217,19 +254,20 @@ <h2>History</h2>
217254
}
218255

219256
function startPreviewAnimation() {
220-
// Update preview every 300ms
221-
setInterval(() => {
257+
frameIndex = 0;
258+
if (animationInterval) clearInterval(animationInterval);
259+
260+
animationInterval = setInterval(() => {
222261
if (frames.length > 0) {
223-
// console.log('Displaying frame:', frameIndex, frames.length);
224262
elements.preview.textContent = extractDisplayContent(frames[frameIndex]);
225263
frameIndex = (frameIndex + 1) % frames.length;
226264
}
227-
}, 700);
265+
}, parseInt(elements.speedSlider.value));
228266
}
229267

230268
async function generateText(prompt, currentState) {
231-
const formatInstructions = `- You create an output state from an input state according to the prompt. It will be visualized in a 40x20 grid.
232-
- Output exactly 20 lines, each exactly 40 characters wide
269+
const formatInstructions = `- You create an output state from an input state according to the prompt. It will be visualized in a 30x15 grid.
270+
- Output exactly 15 lines, each exactly 30 characters wide
233271
- You may output explanations but wrap the next state in triple backticks \`\`\` so we can parse it for display
234272
- If there is an input state, transform it gradually according to the prompt.
235273
- If there is no input state, use the prompt to come up with a starting state. Be creative
@@ -239,8 +277,6 @@ <h2>History</h2>
239277
${prompt}`;
240278

241279
try {
242-
console.log('Generation started');
243-
console.log('Current state:', currentState);
244280
console.log('Prompt:', prompt);
245281
console.log('System instructions:', formatInstructions);
246282

@@ -249,35 +285,75 @@ <h2>History</h2>
249285
const encodedPrompt = encodeURIComponent(currentState);
250286
const encodedSystem = encodeURIComponent(formatInstructions);
251287

252-
let seed = elements.seed.value ? parseInt(elements.seed.value) : Math.floor(Math.random() * 10);
288+
let seed = elements.seed.value && elements.seed.value !== '-1' ?
289+
parseInt(elements.seed.value) :
290+
Math.floor(Math.random() * 1000000);
253291
elements.seed.value = seed; // Update input with chosen seed
254292

255-
256-
257293
let response = null;
258294
console.log('Fetching...');
295+
296+
let retryCount = 0;
297+
const maxRetries = 3;
298+
const retryDelay = 15000; // 15 seconds delay between retries
299+
const timeoutDuration = 20000; // 20 seconds timeout
300+
259301
do {
260-
const url = `https://text.pollinations.ai/${encodedPrompt}?system=${encodedSystem}&seed=${seed}&model=${getSelectedModel()}`;
261-
console.log('Request URL:', url);
262-
263302
try {
264-
response = await fetch(url);
303+
const url = `https://text.pollinations.ai/${encodedPrompt}?system=${encodedSystem}&seed=${seed}&model=${getSelectedModel()}&temperature=${elements.temperature.value}`;
304+
console.log('Request URL:', url);
305+
306+
const controller = new AbortController();
307+
const timeout = setTimeout(() => {
308+
controller.abort();
309+
}, timeoutDuration);
310+
311+
try {
312+
response = await fetch(url, {
313+
signal: controller.signal
314+
});
315+
clearTimeout(timeout);
316+
} catch (error) {
317+
clearTimeout(timeout);
318+
if (error.name === 'AbortError') {
319+
throw new Error('Request timed out after ' + timeoutDuration/1000 + ' seconds');
320+
}
321+
throw error;
322+
}
323+
324+
if (!response.ok) {
325+
retryCount++;
326+
if (retryCount >= maxRetries) {
327+
console.error(`Failed after ${maxRetries} attempts. Stopping.`);
328+
elements.start.textContent = 'Start Evolution';
329+
return '';
330+
}
331+
console.log(`Attempt ${retryCount}/${maxRetries} failed. Waiting ${retryDelay/1000} seconds before retry...`);
332+
seed = seed + 1;
333+
await new Promise(resolve => setTimeout(resolve, retryDelay));
334+
continue;
335+
}
336+
337+
const text = await response.text();
338+
console.log('Raw response:', text);
339+
elements.start.textContent = 'Start Evolution';
340+
return text;
341+
265342
} catch (error) {
266343
console.error('Fetch error:', error);
344+
retryCount++;
345+
if (retryCount >= maxRetries) {
346+
console.error(`Failed after ${maxRetries} attempts. Stopping.`);
347+
elements.start.textContent = 'Start Evolution';
348+
return '';
349+
}
350+
console.log(`Attempt ${retryCount}/${maxRetries} failed. Waiting ${retryDelay/1000} seconds before retry...`);
351+
await new Promise(resolve => setTimeout(resolve, retryDelay));
267352
}
268-
if (!response.ok) {
269-
seed = seed + 1;
270-
await new Promise(resolve => setTimeout(resolve, 10000));
271-
}
272-
console.log('Trying fetch...', url);
273-
} while (!response.ok);
274-
console.log('Response received:', response.status);
275-
276-
const text = await response.text();
277-
console.log('Raw response:', text);
353+
} while (retryCount < maxRetries);
278354

279355
elements.start.textContent = 'Start Evolution';
280-
return text;
356+
return '';
281357
} catch (error) {
282358
console.error('Generation error:', error);
283359
elements.start.textContent = 'Start Evolution';
@@ -353,16 +429,24 @@ <h2>History</h2>
353429
// Event Listeners
354430
elements.start.addEventListener('click', startEvolution);
355431
elements.stop.addEventListener('click', stopEvolution);
432+
elements.temperature.addEventListener('input', (e) => {
433+
document.getElementById('temperature-value').textContent = e.target.value;
434+
});
356435
elements.preset.addEventListener('change', () => {
357436
const preset = presets[elements.preset.value];
358437
elements.prompt.value = preset.prompt;
359438
elements.temperature.value = preset.temperature;
439+
document.getElementById('temperature-value').textContent = preset.temperature;
360440
});
361441

362442
elements.prompt.addEventListener('input', () => {
363443
// Don't clear frames on prompt change anymore
364444
});
365445

446+
elements.speedSlider.addEventListener('change', () => {
447+
if (frames.length > 0) startPreviewAnimation();
448+
});
449+
366450
// Initialize
367451
elements.preset.value = 'calligram';
368452
elements.prompt.value = presets.calligram.prompt;

0 commit comments

Comments
 (0)