tangled
alpha
login
or
join now
dunkirk.sh
/
plastic
2
fork
atom
self modifying website
2
fork
atom
overview
issues
pulls
pipelines
feat: add tool calls
dunkirk.sh
9 months ago
99b77929
3cfc384e
verified
This commit was signed with the committer's
known signature
.
dunkirk.sh
SSH Key Fingerprint:
SHA256:DqcG0RXYExE26KiWo3VxJnsxswN1QNfTBvB+bdSpk80=
+126
-15
1 changed file
expand all
collapse all
unified
split
index.html
+126
-15
index.html
···
300
300
</div>
301
301
302
302
<script>
303
303
+
// Tool call system for AI to interact with the page
304
304
+
window.toolCallbacks = {
305
305
+
replaceElement: function(selector, newHTML) {
306
306
+
const element = document.querySelector(selector);
307
307
+
if (element) {
308
308
+
element.outerHTML = newHTML;
309
309
+
return { success: true, message: `Replaced element: ${selector}` };
310
310
+
}
311
311
+
return { success: false, message: `Element not found: ${selector}` };
312
312
+
},
313
313
+
314
314
+
updateElement: function(selector, newContent) {
315
315
+
const element = document.querySelector(selector);
316
316
+
if (element) {
317
317
+
element.innerHTML = newContent;
318
318
+
return { success: true, message: `Updated element: ${selector}` };
319
319
+
}
320
320
+
return { success: false, message: `Element not found: ${selector}` };
321
321
+
},
322
322
+
323
323
+
addElement: function(parentSelector, newHTML, position = 'beforeend') {
324
324
+
const parent = document.querySelector(parentSelector);
325
325
+
if (parent) {
326
326
+
parent.insertAdjacentHTML(position, newHTML);
327
327
+
return { success: true, message: `Added element to: ${parentSelector}` };
328
328
+
}
329
329
+
return { success: false, message: `Parent not found: ${parentSelector}` };
330
330
+
},
331
331
+
332
332
+
removeElement: function(selector) {
333
333
+
const element = document.querySelector(selector);
334
334
+
if (element) {
335
335
+
element.remove();
336
336
+
return { success: true, message: `Removed element: ${selector}` };
337
337
+
}
338
338
+
return { success: false, message: `Element not found: ${selector}` };
339
339
+
},
340
340
+
341
341
+
updateStyle: function(selector, styleObj) {
342
342
+
const element = document.querySelector(selector);
343
343
+
if (element) {
344
344
+
Object.assign(element.style, styleObj);
345
345
+
return { success: true, message: `Updated styles for: ${selector}` };
346
346
+
}
347
347
+
return { success: false, message: `Element not found: ${selector}` };
348
348
+
},
349
349
+
350
350
+
executeJS: function(code) {
351
351
+
try {
352
352
+
const result = eval(code);
353
353
+
return { success: true, message: 'JavaScript executed', result: result };
354
354
+
} catch (error) {
355
355
+
return { success: false, message: `JS Error: ${error.message}` };
356
356
+
}
357
357
+
}
358
358
+
};
359
359
+
360
360
+
function executeToolCall(toolCall) {
361
361
+
const { function: func, arguments: args } = toolCall;
362
362
+
console.log('Executing tool call:', func, args);
363
363
+
364
364
+
if (window.toolCallbacks[func]) {
365
365
+
// Handle parameter name variations
366
366
+
if (func === 'updateStyle' && args.styleObj) {
367
367
+
// Convert styleObj to the expected parameter name
368
368
+
return window.toolCallbacks[func](args.selector, args.styleObj);
369
369
+
} else {
370
370
+
// Use all argument values in order
371
371
+
return window.toolCallbacks[func](...Object.values(args));
372
372
+
}
373
373
+
}
374
374
+
return { success: false, message: `Unknown tool: ${func}` };
375
375
+
}
376
376
+
303
377
async function generateAndExecute() {
304
378
const userPrompt = document.getElementById("codeEditor").value;
305
379
const statusDiv = document.getElementById("statusDisplay");
···
325
399
messages: [
326
400
{
327
401
role: "user",
328
328
-
content: `Here is the current HTML page:\n\n${currentPageHTML}\n\nUser request: "${userPrompt}"\n\nGenerate HTML code that fits the DOS/retro terminal aesthetic for this request. Use flat colors like #00ff41 (green), #ffff00 (yellow), #00ffff (cyan), #ffffff (white), #c0c0c0 (gray), #000080 (blue), and #ff0000 (red). Use monospace fonts and simple borders. Make it look like it belongs in a 1990s DOS program. Only return the HTML code to add, no explanations.`,
402
402
+
content: `Here is the current HTML page:\n\n${currentPageHTML}\n\nUser request: "${userPrompt}"\n\nIMPORTANT: You must respond with ONLY one of these two formats:\n\nFORMAT 1 - Tool calls (for precise modifications):\n{"tool_calls": [{"function": "functionName", "arguments": {"param": "value"}}]}\n\nFORMAT 2 - Raw HTML (to append to page):\n<div>Your HTML content here</div>\n\nDO NOT include any explanatory text, markdown formatting, or additional commentary. Respond with ONLY the JSON or HTML.\n\nAvailable tools:\n1. replaceElement(selector, newHTML) - Replace an element\n2. updateElement(selector, newContent) - Update element content \n3. addElement(parentSelector, newHTML, position) - Add element\n4. removeElement(selector) - Remove an element\n5. updateStyle(selector, styleObj) - Update CSS styles\n6. executeJS(code) - Run JavaScript code\n\nUse DOS/retro aesthetic with flat colors: #000000 (black), #ffffff (white), #c0c0c0 (gray), #000080 (blue), #ff0000 (red). Use monospace fonts.`,
329
403
},
330
404
],
331
405
}),
···
341
415
statusDiv.textContent = "AI PROCESSING...";
342
416
343
417
const data = await response.json();
344
344
-
console.log("API Response:", data); // Debug log
418
418
+
console.log("API Response:", data);
345
419
346
346
-
let generatedCode;
420
420
+
let generatedContent;
347
421
if (
348
422
data.choices &&
349
423
data.choices[0] &&
350
424
data.choices[0].message
351
425
) {
352
352
-
generatedCode = data.choices[0].message.content;
426
426
+
generatedContent = data.choices[0].message.content;
353
427
} else if (data.content) {
354
354
-
generatedCode = data.content;
428
428
+
generatedContent = data.content;
355
429
} else if (data.response) {
356
356
-
generatedCode = data.response;
430
430
+
generatedContent = data.response;
357
431
} else if (typeof data === "string") {
358
358
-
generatedCode = data;
432
432
+
generatedContent = data;
359
433
} else {
360
434
throw new Error("Unexpected API response format");
361
435
}
362
436
363
363
-
// Clean up the code (remove markdown formatting if present)
364
364
-
let cleanCode = generatedCode
365
365
-
.replace(/```html\n?/g, "")
437
437
+
statusDiv.textContent = "EXECUTING COMMANDS...";
438
438
+
439
439
+
// Clean up response and extract JSON if present
440
440
+
let cleanResponse = generatedContent.trim();
441
441
+
442
442
+
// Remove markdown formatting
443
443
+
cleanResponse = cleanResponse
444
444
+
.replace(/```json\n?/g, "")
366
445
.replace(/```\n?/g, "");
367
446
368
368
-
statusDiv.textContent = "INJECTING CODE...";
447
447
+
// Try to extract JSON from mixed content
448
448
+
const jsonMatch = cleanResponse.match(/\{[\s\S]*"tool_calls"[\s\S]*\}/);
449
449
+
if (jsonMatch) {
450
450
+
cleanResponse = jsonMatch[0];
451
451
+
}
369
452
370
370
-
// Insert the generated code
371
371
-
document.querySelector(".content").innerHTML += cleanCode;
372
372
-
document.getElementById("codeEditor").value = "";
453
453
+
console.log('Cleaned response:', cleanResponse);
454
454
+
455
455
+
// Check if response contains tool calls
456
456
+
try {
457
457
+
const toolResponse = JSON.parse(cleanResponse);
458
458
+
if (toolResponse.tool_calls && Array.isArray(toolResponse.tool_calls)) {
459
459
+
// Execute tool calls
460
460
+
const results = [];
461
461
+
for (const toolCall of toolResponse.tool_calls) {
462
462
+
const result = executeToolCall(toolCall);
463
463
+
results.push(result);
464
464
+
console.log('Tool call result:', result);
465
465
+
}
466
466
+
statusDiv.textContent = `EXECUTED ${results.length} COMMANDS`;
467
467
+
} else {
468
468
+
throw new Error("Invalid tool call format");
469
469
+
}
470
470
+
} catch (jsonError) {
471
471
+
console.log('JSON parse error:', jsonError);
472
472
+
console.log('Raw response:', generatedContent);
473
473
+
console.log('Attempting to parse as HTML...');
474
474
+
475
475
+
// Not JSON, treat as HTML code
476
476
+
let cleanCode = generatedContent
477
477
+
.replace(/```html\n?/g, "")
478
478
+
.replace(/```\n?/g, "");
479
479
+
480
480
+
statusDiv.textContent = "INJECTING CODE...";
481
481
+
document.querySelector(".content").innerHTML += cleanCode;
482
482
+
statusDiv.textContent = "CODE EXECUTION SUCCESSFUL";
483
483
+
}
373
484
374
374
-
statusDiv.textContent = "CODE EXECUTION SUCCESSFUL";
485
485
+
document.getElementById("codeEditor").value = "";
375
486
376
487
// Clear status after 3 seconds
377
488
setTimeout(() => {