tangled
alpha
login
or
join now
dunkirk.sh
/
plastic
2
fork
atom
self modifying website
2
fork
atom
overview
issues
pulls
pipelines
feat: remove some copy and add runtime and memeory
dunkirk.sh
9 months ago
71e5906f
ffacc84f
verified
This commit was signed with the committer's
known signature
.
dunkirk.sh
SSH Key Fingerprint:
SHA256:DqcG0RXYExE26KiWo3VxJnsxswN1QNfTBvB+bdSpk80=
+199
-113
1 changed file
expand all
collapse all
unified
split
index.html
+199
-113
index.html
···
188
188
</div>
189
189
190
190
<div class="content">
191
191
-
<p class="prompt">C:\PLASTIC> DIR</p>
192
192
-
193
193
-
<div class="file-listing">
194
194
-
<div class="file-line">
195
195
-
Volume in drive C is PLASTIC-SYS
196
196
-
</div>
197
197
-
<div class="file-line">Directory of C:\PLASTIC</div>
198
198
-
<div class="file-line"></div>
199
199
-
<div class="file-line">
200
200
-
<span class="dir">SYSTEM <DIR></span> 12-15-95
201
201
-
3:42p
202
202
-
</div>
203
203
-
<div class="file-line">
204
204
-
<span class="exe">PLASTIC EXE</span> 24,576 12-15-95
205
205
-
3:42p
206
206
-
</div>
207
207
-
<div class="file-line">
208
208
-
<span class="txt">README TXT</span> 1,024 12-15-95 3:42p
209
209
-
</div>
210
210
-
<div class="file-line">
211
211
-
<span class="txt">CONFIG SYS</span> 512 12-15-95 3:42p
212
212
-
</div>
213
213
-
<div class="file-line">4 File(s) 26,112 bytes</div>
214
214
-
<div class="file-line">524,288 bytes free</div>
215
215
-
</div>
216
216
-
217
191
<p class="prompt">C:\PLASTIC> PLASTIC.EXE</p>
218
192
219
193
<pre class="ascii-art">
···
272
246
<li>Supports EGA/VGA graphics modes</li>
273
247
</ul>
274
248
275
275
-
<h2>System Commands:</h2>
276
276
-
<p>
277
277
-
<span class="command">HELP</span>
278
278
-
<span class="command">DIR</span>
279
279
-
<span class="command">EDIT</span>
280
280
-
<span class="command">RUN</span>
281
281
-
<span class="command">EXIT</span>
282
282
-
</p>
283
283
-
284
249
<div class="separator">
285
250
════════════════════════════════════════════════════════════
286
251
</div>
287
252
288
288
-
<p>Runtime: 00:13:37 | Memory: 589K free | CPU: 80486DX-33</p>
289
289
-
<p class="error">
290
290
-
WARNING: Unauthorized modification may corrupt system files
253
253
+
<p>
254
254
+
Runtime: <span id="runtime">00:13:37</span> | Memory:
255
255
+
<span id="memory">589K free</span> | CPU: 80486DX-33
291
256
</p>
292
292
-
293
293
-
<p class="prompt">C:\PLASTIC> <span class="cursor">_</span></p>
294
257
</div>
295
258
296
259
<div class="status-bar">
297
260
<span>F1=Help F2=Save F3=Load F10=Menu</span>
298
298
-
<span>12:34 PM</span>
261
261
+
<span>12:00</span>
299
262
</div>
300
263
</div>
301
264
302
265
<script>
303
266
// Tool call system for AI to interact with the page
304
267
window.toolCallbacks = {
305
305
-
replaceElement: function(selector, newHTML) {
268
268
+
replaceElement: function (selector, newHTML) {
306
269
const element = document.querySelector(selector);
307
270
if (element) {
308
271
element.outerHTML = newHTML;
309
309
-
return { success: true, message: `Replaced element: ${selector}` };
272
272
+
return {
273
273
+
success: true,
274
274
+
message: `Replaced element: ${selector}`,
275
275
+
};
310
276
}
311
311
-
return { success: false, message: `Element not found: ${selector}` };
277
277
+
return {
278
278
+
success: false,
279
279
+
message: `Element not found: ${selector}`,
280
280
+
};
312
281
},
313
313
-
314
314
-
updateElement: function(selector, newContent) {
282
282
+
283
283
+
updateElement: function (selector, newContent) {
315
284
const element = document.querySelector(selector);
316
285
if (element) {
317
286
element.innerHTML = newContent;
318
318
-
return { success: true, message: `Updated element: ${selector}` };
287
287
+
return {
288
288
+
success: true,
289
289
+
message: `Updated element: ${selector}`,
290
290
+
};
319
291
}
320
320
-
return { success: false, message: `Element not found: ${selector}` };
292
292
+
return {
293
293
+
success: false,
294
294
+
message: `Element not found: ${selector}`,
295
295
+
};
321
296
},
322
322
-
323
323
-
addElement: function(parentSelector, newHTML, position = 'beforeend') {
297
297
+
298
298
+
addElement: function (
299
299
+
parentSelector,
300
300
+
newHTML,
301
301
+
position = "beforeend",
302
302
+
) {
324
303
const parent = document.querySelector(parentSelector);
325
304
if (parent) {
326
305
parent.insertAdjacentHTML(position, newHTML);
327
327
-
return { success: true, message: `Added element to: ${parentSelector}` };
306
306
+
return {
307
307
+
success: true,
308
308
+
message: `Added element to: ${parentSelector}`,
309
309
+
};
328
310
}
329
329
-
return { success: false, message: `Parent not found: ${parentSelector}` };
311
311
+
return {
312
312
+
success: false,
313
313
+
message: `Parent not found: ${parentSelector}`,
314
314
+
};
330
315
},
331
331
-
332
332
-
removeElement: function(selector) {
316
316
+
317
317
+
removeElement: function (selector) {
333
318
const element = document.querySelector(selector);
334
319
if (element) {
335
320
element.remove();
336
336
-
return { success: true, message: `Removed element: ${selector}` };
321
321
+
return {
322
322
+
success: true,
323
323
+
message: `Removed element: ${selector}`,
324
324
+
};
337
325
}
338
338
-
return { success: false, message: `Element not found: ${selector}` };
326
326
+
return {
327
327
+
success: false,
328
328
+
message: `Element not found: ${selector}`,
329
329
+
};
339
330
},
340
340
-
341
341
-
updateStyle: function(selector, styleObj) {
331
331
+
332
332
+
updateStyle: function (selector, styleObj) {
342
333
const element = document.querySelector(selector);
343
334
if (element) {
344
335
Object.assign(element.style, styleObj);
345
345
-
return { success: true, message: `Updated styles for: ${selector}` };
336
336
+
return {
337
337
+
success: true,
338
338
+
message: `Updated styles for: ${selector}`,
339
339
+
};
346
340
}
347
347
-
return { success: false, message: `Element not found: ${selector}` };
341
341
+
return {
342
342
+
success: false,
343
343
+
message: `Element not found: ${selector}`,
344
344
+
};
348
345
},
349
349
-
350
350
-
executeJS: function(code) {
346
346
+
347
347
+
executeJS: function (code) {
351
348
try {
352
349
const result = eval(code);
353
353
-
return { success: true, message: 'JavaScript executed', result: result };
350
350
+
return {
351
351
+
success: true,
352
352
+
message: "JavaScript executed",
353
353
+
result: result,
354
354
+
};
354
355
} catch (error) {
355
355
-
return { success: false, message: `JS Error: ${error.message}` };
356
356
+
return {
357
357
+
success: false,
358
358
+
message: `JS Error: ${error.message}`,
359
359
+
};
356
360
}
357
357
-
}
361
361
+
},
358
362
};
359
363
360
364
function executeToolCall(toolCall) {
361
365
const { function: func, arguments: args } = toolCall;
362
362
-
console.log('Executing tool call:', func, args);
363
363
-
366
366
+
console.log("Executing tool call:", func, args);
367
367
+
364
368
if (window.toolCallbacks[func]) {
365
369
try {
366
370
// Handle different function signatures
367
367
-
if (func === 'removeElement') {
371
371
+
if (func === "removeElement") {
368
372
// removeElement expects just a selector
369
369
-
const selector = args.selector || Object.keys(args)[0] || Object.values(args)[0];
373
373
+
const selector =
374
374
+
args.selector ||
375
375
+
Object.keys(args)[0] ||
376
376
+
Object.values(args)[0];
370
377
return window.toolCallbacks[func](selector);
371
371
-
} else if (func === 'updateStyle' && args.styleObj) {
372
372
-
return window.toolCallbacks[func](args.selector, args.styleObj);
373
373
-
} else if (func === 'executeJS') {
378
378
+
} else if (func === "updateStyle" && args.styleObj) {
379
379
+
return window.toolCallbacks[func](
380
380
+
args.selector,
381
381
+
args.styleObj,
382
382
+
);
383
383
+
} else if (func === "executeJS") {
374
384
return window.toolCallbacks[func](args.code);
375
375
-
} else if (func === 'updateElement') {
376
376
-
return window.toolCallbacks[func](args.selector, args.newContent);
377
377
-
} else if (func === 'replaceElement') {
378
378
-
return window.toolCallbacks[func](args.selector, args.newHTML);
379
379
-
} else if (func === 'addElement') {
380
380
-
return window.toolCallbacks[func](args.parentSelector, args.newHTML, args.position);
385
385
+
} else if (func === "updateElement") {
386
386
+
return window.toolCallbacks[func](
387
387
+
args.selector,
388
388
+
args.newContent,
389
389
+
);
390
390
+
} else if (func === "replaceElement") {
391
391
+
return window.toolCallbacks[func](
392
392
+
args.selector,
393
393
+
args.newHTML,
394
394
+
);
395
395
+
} else if (func === "addElement") {
396
396
+
return window.toolCallbacks[func](
397
397
+
args.parentSelector,
398
398
+
args.newHTML,
399
399
+
args.position,
400
400
+
);
381
401
} else {
382
402
// Fallback: use all argument values in order
383
383
-
return window.toolCallbacks[func](...Object.values(args));
403
403
+
return window.toolCallbacks[func](
404
404
+
...Object.values(args),
405
405
+
);
384
406
}
385
407
} catch (error) {
386
386
-
return { success: false, message: `Error executing ${func}: ${error.message}` };
408
408
+
return {
409
409
+
success: false,
410
410
+
message: `Error executing ${func}: ${error.message}`,
411
411
+
};
387
412
}
388
413
}
389
414
return { success: false, message: `Unknown tool: ${func}` };
···
453
478
454
479
// Clean up response and extract JSON if present
455
480
let cleanResponse = generatedContent.trim();
456
456
-
481
481
+
457
482
// Remove markdown formatting
458
483
cleanResponse = cleanResponse
459
484
.replace(/```json\n?/g, "")
460
485
.replace(/```\n?/g, "");
461
486
462
487
// Try to extract JSON from mixed content
463
463
-
const jsonMatch = cleanResponse.match(/\{[\s\S]*"tool_calls"[\s\S]*\}/);
488
488
+
const jsonMatch = cleanResponse.match(
489
489
+
/\{[\s\S]*"tool_calls"[\s\S]*\}/,
490
490
+
);
464
491
if (jsonMatch) {
465
492
cleanResponse = jsonMatch[0];
466
493
}
467
494
468
468
-
console.log('Cleaned response:', cleanResponse);
495
495
+
console.log("Cleaned response:", cleanResponse);
469
496
470
497
// Check if response contains tool calls
471
498
try {
472
499
const toolResponse = JSON.parse(cleanResponse);
473
473
-
if (toolResponse.tool_calls && Array.isArray(toolResponse.tool_calls)) {
500
500
+
if (
501
501
+
toolResponse.tool_calls &&
502
502
+
Array.isArray(toolResponse.tool_calls)
503
503
+
) {
474
504
// Execute tool calls
475
505
const results = [];
476
506
for (const toolCall of toolResponse.tool_calls) {
477
507
const result = executeToolCall(toolCall);
478
508
results.push(result);
479
479
-
console.log('Tool call result:', result);
509
509
+
console.log("Tool call result:", result);
480
510
}
481
511
statusDiv.textContent = `EXECUTED ${results.length} COMMANDS`;
482
482
-
512
512
+
483
513
// Send feedback to AI about tool results
484
484
-
setTimeout(() => sendToolFeedback(userPrompt, results), 100);
514
514
+
setTimeout(
515
515
+
() => sendToolFeedback(userPrompt, results),
516
516
+
100,
517
517
+
);
485
518
} else {
486
519
throw new Error("Invalid tool call format");
487
520
}
488
521
} catch (jsonError) {
489
489
-
console.log('JSON parse error:', jsonError);
490
490
-
console.log('Raw response:', generatedContent);
491
491
-
console.log('Attempting to parse as HTML...');
492
492
-
522
522
+
console.log("JSON parse error:", jsonError);
523
523
+
console.log("Raw response:", generatedContent);
524
524
+
console.log("Attempting to parse as HTML...");
525
525
+
493
526
// Not JSON, treat as HTML code
494
527
let cleanCode = generatedContent
495
528
.replace(/```html\n?/g, "")
496
529
.replace(/```\n?/g, "");
497
530
498
531
statusDiv.textContent = "INJECTING CODE...";
499
499
-
document.querySelector(".content").innerHTML += cleanCode;
532
532
+
document.querySelector(".content").innerHTML +=
533
533
+
cleanCode;
500
534
statusDiv.textContent = "CODE EXECUTION SUCCESSFUL";
501
535
}
502
536
···
519
553
520
554
async function sendToolFeedback(originalPrompt, toolResults) {
521
555
const statusDiv = document.getElementById("statusDisplay");
522
522
-
556
556
+
523
557
try {
524
558
statusDiv.textContent = "SENDING FEEDBACK TO AI...";
525
525
-
559
559
+
526
560
const currentPageHTML = document.documentElement.outerHTML;
527
527
-
const resultsText = toolResults.map(r =>
528
528
-
`${r.success ? '✓' : '✗'} ${r.message}${r.result ? ` (result: ${r.result})` : ''}`
529
529
-
).join('\n');
561
561
+
const resultsText = toolResults
562
562
+
.map(
563
563
+
(r) =>
564
564
+
`${r.success ? "✓" : "✗"} ${r.message}${r.result ? ` (result: ${r.result})` : ""}`,
565
565
+
)
566
566
+
.join("\n");
530
567
531
568
const response = await fetch(
532
569
"https://ai.hackclub.com/chat/completions",
···
547
584
);
548
585
549
586
if (!response.ok) {
550
550
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
587
587
+
throw new Error(
588
588
+
`HTTP ${response.status}: ${response.statusText}`,
589
589
+
);
551
590
}
552
591
553
592
const data = await response.json();
554
593
let followUpContent;
555
555
-
556
556
-
if (data.choices && data.choices[0] && data.choices[0].message) {
594
594
+
595
595
+
if (
596
596
+
data.choices &&
597
597
+
data.choices[0] &&
598
598
+
data.choices[0].message
599
599
+
) {
557
600
followUpContent = data.choices[0].message.content;
558
601
} else if (data.content) {
559
602
followUpContent = data.content;
···
564
607
}
565
608
566
609
followUpContent = followUpContent.trim();
567
567
-
console.log('Follow-up response:', followUpContent);
610
610
+
console.log("Follow-up response:", followUpContent);
568
611
569
612
if (followUpContent === "COMPLETE") {
570
613
statusDiv.textContent = "AI SATISFIED - TASK COMPLETE";
571
571
-
setTimeout(() => statusDiv.textContent = "", 3000);
614
614
+
setTimeout(() => (statusDiv.textContent = ""), 3000);
572
615
return;
573
616
}
574
617
575
618
// Process follow-up commands
576
619
statusDiv.textContent = "AI MAKING ADJUSTMENTS...";
577
577
-
620
620
+
578
621
// Try to parse as tool calls
579
622
try {
580
580
-
const jsonMatch = followUpContent.match(/\{[\s\S]*"tool_calls"[\s\S]*\}/);
623
623
+
const jsonMatch = followUpContent.match(
624
624
+
/\{[\s\S]*"tool_calls"[\s\S]*\}/,
625
625
+
);
581
626
if (jsonMatch) {
582
627
const toolResponse = JSON.parse(jsonMatch[0]);
583
583
-
if (toolResponse.tool_calls && Array.isArray(toolResponse.tool_calls)) {
628
628
+
if (
629
629
+
toolResponse.tool_calls &&
630
630
+
Array.isArray(toolResponse.tool_calls)
631
631
+
) {
584
632
const followUpResults = [];
585
633
for (const toolCall of toolResponse.tool_calls) {
586
634
const result = executeToolCall(toolCall);
587
635
followUpResults.push(result);
588
588
-
console.log('Follow-up tool result:', result);
636
636
+
console.log(
637
637
+
"Follow-up tool result:",
638
638
+
result,
639
639
+
);
589
640
}
590
641
statusDiv.textContent = `AI EXECUTED ${followUpResults.length} ADJUSTMENTS`;
591
642
}
···
594
645
let cleanCode = followUpContent
595
646
.replace(/```html\n?/g, "")
596
647
.replace(/```\n?/g, "");
597
597
-
document.querySelector(".content").innerHTML += cleanCode;
598
598
-
statusDiv.textContent = "AI ADDED FOLLOW-UP CONTENT";
648
648
+
document.querySelector(".content").innerHTML +=
649
649
+
cleanCode;
650
650
+
statusDiv.textContent =
651
651
+
"AI ADDED FOLLOW-UP CONTENT";
599
652
}
600
653
} catch (error) {
601
601
-
console.log('Follow-up parsing error:', error);
654
654
+
console.log("Follow-up parsing error:", error);
602
655
statusDiv.textContent = "AI FEEDBACK ERROR";
603
656
}
604
657
605
605
-
setTimeout(() => statusDiv.textContent = "", 4000);
606
606
-
658
658
+
setTimeout(() => (statusDiv.textContent = ""), 4000);
607
659
} catch (error) {
608
660
statusDiv.textContent = `FEEDBACK ERROR: ${error.message}`;
609
609
-
console.error('Feedback error:', error);
610
610
-
setTimeout(() => statusDiv.textContent = "", 3000);
661
661
+
console.error("Feedback error:", error);
662
662
+
setTimeout(() => (statusDiv.textContent = ""), 3000);
611
663
}
612
664
}
613
665
···
629
681
const timeStr = now.toLocaleTimeString([], {
630
682
hour: "2-digit",
631
683
minute: "2-digit",
684
684
+
hour12: false,
632
685
});
633
686
const statusBarTime = document.querySelector(
634
687
".status-bar span:last-child",
635
688
);
636
689
if (statusBarTime) {
637
690
statusBarTime.textContent = timeStr;
691
691
+
}
692
692
+
693
693
+
// Update memory in status bar based on actual tab memory usage
694
694
+
const memoryUsage = Math.round(
695
695
+
performance.memory
696
696
+
? performance.memory.usedJSHeapSize / 1024
697
697
+
: 0,
698
698
+
);
699
699
+
700
700
+
const memoryStr = `${589 - Math.min(Math.round(memoryUsage / 1024), 500)}K free`;
701
701
+
const memory = document.getElementById("memory");
702
702
+
if (memory) {
703
703
+
memory.textContent = memoryStr;
704
704
+
}
705
705
+
706
706
+
// Update runtime in status bar - based on how long the page has been open
707
707
+
const pageOpenTime = performance.timing
708
708
+
? performance.timing.navigationStart || Date.now()
709
709
+
: Date.now();
710
710
+
const timeOpen = Math.floor((Date.now() - pageOpenTime) / 1000);
711
711
+
const hours = Math.floor(timeOpen / 3600)
712
712
+
.toString()
713
713
+
.padStart(2, "0");
714
714
+
const minutes = Math.floor((timeOpen % 3600) / 60)
715
715
+
.toString()
716
716
+
.padStart(2, "0");
717
717
+
const seconds = Math.floor(timeOpen % 60)
718
718
+
.toString()
719
719
+
.padStart(2, "0");
720
720
+
const runtimeStr = `${hours}:${minutes}:${seconds}`;
721
721
+
const runtime = document.getElementById("runtime");
722
722
+
if (runtime) {
723
723
+
runtime.textContent = runtimeStr;
638
724
}
639
725
}, 1000);
640
726
</script>