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
</div>
301
302
<script>
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
303
async function generateAndExecute() {
304
const userPrompt = document.getElementById("codeEditor").value;
305
const statusDiv = document.getElementById("statusDisplay");
···
325
messages: [
326
{
327
role: "user",
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.`,
329
},
330
],
331
}),
···
341
statusDiv.textContent = "AI PROCESSING...";
342
343
const data = await response.json();
344
-
console.log("API Response:", data); // Debug log
345
346
-
let generatedCode;
347
if (
348
data.choices &&
349
data.choices[0] &&
350
data.choices[0].message
351
) {
352
-
generatedCode = data.choices[0].message.content;
353
} else if (data.content) {
354
-
generatedCode = data.content;
355
} else if (data.response) {
356
-
generatedCode = data.response;
357
} else if (typeof data === "string") {
358
-
generatedCode = data;
359
} else {
360
throw new Error("Unexpected API response format");
361
}
362
363
-
// Clean up the code (remove markdown formatting if present)
364
-
let cleanCode = generatedCode
365
-
.replace(/```html\n?/g, "")
0
0
0
0
0
366
.replace(/```\n?/g, "");
367
368
-
statusDiv.textContent = "INJECTING CODE...";
0
0
0
0
369
370
-
// Insert the generated code
371
-
document.querySelector(".content").innerHTML += cleanCode;
372
-
document.getElementById("codeEditor").value = "";
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
373
374
-
statusDiv.textContent = "CODE EXECUTION SUCCESSFUL";
375
376
// Clear status after 3 seconds
377
setTimeout(() => {
···
300
</div>
301
302
<script>
303
+
// Tool call system for AI to interact with the page
304
+
window.toolCallbacks = {
305
+
replaceElement: function(selector, newHTML) {
306
+
const element = document.querySelector(selector);
307
+
if (element) {
308
+
element.outerHTML = newHTML;
309
+
return { success: true, message: `Replaced element: ${selector}` };
310
+
}
311
+
return { success: false, message: `Element not found: ${selector}` };
312
+
},
313
+
314
+
updateElement: function(selector, newContent) {
315
+
const element = document.querySelector(selector);
316
+
if (element) {
317
+
element.innerHTML = newContent;
318
+
return { success: true, message: `Updated element: ${selector}` };
319
+
}
320
+
return { success: false, message: `Element not found: ${selector}` };
321
+
},
322
+
323
+
addElement: function(parentSelector, newHTML, position = 'beforeend') {
324
+
const parent = document.querySelector(parentSelector);
325
+
if (parent) {
326
+
parent.insertAdjacentHTML(position, newHTML);
327
+
return { success: true, message: `Added element to: ${parentSelector}` };
328
+
}
329
+
return { success: false, message: `Parent not found: ${parentSelector}` };
330
+
},
331
+
332
+
removeElement: function(selector) {
333
+
const element = document.querySelector(selector);
334
+
if (element) {
335
+
element.remove();
336
+
return { success: true, message: `Removed element: ${selector}` };
337
+
}
338
+
return { success: false, message: `Element not found: ${selector}` };
339
+
},
340
+
341
+
updateStyle: function(selector, styleObj) {
342
+
const element = document.querySelector(selector);
343
+
if (element) {
344
+
Object.assign(element.style, styleObj);
345
+
return { success: true, message: `Updated styles for: ${selector}` };
346
+
}
347
+
return { success: false, message: `Element not found: ${selector}` };
348
+
},
349
+
350
+
executeJS: function(code) {
351
+
try {
352
+
const result = eval(code);
353
+
return { success: true, message: 'JavaScript executed', result: result };
354
+
} catch (error) {
355
+
return { success: false, message: `JS Error: ${error.message}` };
356
+
}
357
+
}
358
+
};
359
+
360
+
function executeToolCall(toolCall) {
361
+
const { function: func, arguments: args } = toolCall;
362
+
console.log('Executing tool call:', func, args);
363
+
364
+
if (window.toolCallbacks[func]) {
365
+
// Handle parameter name variations
366
+
if (func === 'updateStyle' && args.styleObj) {
367
+
// Convert styleObj to the expected parameter name
368
+
return window.toolCallbacks[func](args.selector, args.styleObj);
369
+
} else {
370
+
// Use all argument values in order
371
+
return window.toolCallbacks[func](...Object.values(args));
372
+
}
373
+
}
374
+
return { success: false, message: `Unknown tool: ${func}` };
375
+
}
376
+
377
async function generateAndExecute() {
378
const userPrompt = document.getElementById("codeEditor").value;
379
const statusDiv = document.getElementById("statusDisplay");
···
399
messages: [
400
{
401
role: "user",
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.`,
403
},
404
],
405
}),
···
415
statusDiv.textContent = "AI PROCESSING...";
416
417
const data = await response.json();
418
+
console.log("API Response:", data);
419
420
+
let generatedContent;
421
if (
422
data.choices &&
423
data.choices[0] &&
424
data.choices[0].message
425
) {
426
+
generatedContent = data.choices[0].message.content;
427
} else if (data.content) {
428
+
generatedContent = data.content;
429
} else if (data.response) {
430
+
generatedContent = data.response;
431
} else if (typeof data === "string") {
432
+
generatedContent = data;
433
} else {
434
throw new Error("Unexpected API response format");
435
}
436
437
+
statusDiv.textContent = "EXECUTING COMMANDS...";
438
+
439
+
// Clean up response and extract JSON if present
440
+
let cleanResponse = generatedContent.trim();
441
+
442
+
// Remove markdown formatting
443
+
cleanResponse = cleanResponse
444
+
.replace(/```json\n?/g, "")
445
.replace(/```\n?/g, "");
446
447
+
// Try to extract JSON from mixed content
448
+
const jsonMatch = cleanResponse.match(/\{[\s\S]*"tool_calls"[\s\S]*\}/);
449
+
if (jsonMatch) {
450
+
cleanResponse = jsonMatch[0];
451
+
}
452
453
+
console.log('Cleaned response:', cleanResponse);
454
+
455
+
// Check if response contains tool calls
456
+
try {
457
+
const toolResponse = JSON.parse(cleanResponse);
458
+
if (toolResponse.tool_calls && Array.isArray(toolResponse.tool_calls)) {
459
+
// Execute tool calls
460
+
const results = [];
461
+
for (const toolCall of toolResponse.tool_calls) {
462
+
const result = executeToolCall(toolCall);
463
+
results.push(result);
464
+
console.log('Tool call result:', result);
465
+
}
466
+
statusDiv.textContent = `EXECUTED ${results.length} COMMANDS`;
467
+
} else {
468
+
throw new Error("Invalid tool call format");
469
+
}
470
+
} catch (jsonError) {
471
+
console.log('JSON parse error:', jsonError);
472
+
console.log('Raw response:', generatedContent);
473
+
console.log('Attempting to parse as HTML...');
474
+
475
+
// Not JSON, treat as HTML code
476
+
let cleanCode = generatedContent
477
+
.replace(/```html\n?/g, "")
478
+
.replace(/```\n?/g, "");
479
+
480
+
statusDiv.textContent = "INJECTING CODE...";
481
+
document.querySelector(".content").innerHTML += cleanCode;
482
+
statusDiv.textContent = "CODE EXECUTION SUCCESSFUL";
483
+
}
484
485
+
document.getElementById("codeEditor").value = "";
486
487
// Clear status after 3 seconds
488
setTimeout(() => {