-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapp.js
More file actions
481 lines (406 loc) Β· 15.7 KB
/
Copy pathapp.js
File metadata and controls
481 lines (406 loc) Β· 15.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
/**
* grug-group420 Portal
* grugbot420 Playground
*
* Supports:
* - Standalone mode (embedded grugbot)
* - Server mode (connects to local Bun server via /api/cmd)
*/
// ===== Server Connection =====
const API_BASE = window.location.origin;
let serverMode = false;
// Check if running on local server
async function checkServer() {
try {
const res = await fetch(`${API_BASE}/api/health`, {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
if (res.ok) {
const data = await res.json();
if (data.status === 'ok' && data.bot === 'grugbot420') {
serverMode = true;
console.log('π Connected to grugbot420 server v' + data.version);
updateServerStatus(true, data.version);
return true;
}
}
} catch (e) {
// Server not available, use embedded mode
}
serverMode = false;
console.log('π¦ Running in standalone mode (embedded grugbot420)');
updateServerStatus(false);
return false;
}
// Update UI to show connection status
function updateServerStatus(connected, version) {
const header = document.querySelector('.output-header span:last-of-type');
if (header && !header.classList.contains('clear-btn')) {
const indicator = connected ? 'π’' : 'π‘';
const mode = connected ? `server v${version}` : 'standalone';
header.textContent = `grugbot420 ${mode} ${indicator}`;
}
}
// Send command to server
async function serverCommand(cmd) {
try {
const res = await fetch(`${API_BASE}/api/cmd`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ cmd })
});
if (res.ok) {
const data = await res.json();
return data.result;
}
} catch (e) {
console.error('Server error, falling back to embedded:', e);
serverMode = false;
updateServerStatus(false);
}
return null;
}
// ===== grugbot420 Embedded Core =====
const grugbot = {
version: '4.2.0',
mood: 'ready to ship',
commands: {
help: {
desc: 'Show commands',
run: () => `π€ grugbot420 Commands:
help Show this help
wisdom Get grug wisdom
ship Ship some code
think What grug think?
debug Debug like grug
complexity Check complexity
joke Tell grug joke
stack Show tech stack
coffee Coffee status
manifesto The grug way
about About grugbot420
clear Clear output
Type command and press Enter!`
},
wisdom: {
desc: 'Grug wisdom',
run: () => {
const w = [
"𦴠\"Complexity very very bad. Simple good.\"",
"𦴠\"Say no to complexity. Say yes to ship.\"",
"𦴠\"Best code is no code. Second best is simple code.\"",
"𦴠\"Clever code is enemy. Future grug not understand.\"",
"𦴠\"Type system good. Help grug catch bug.\"",
"𦴠\"Test good, but simple code better than many test.\"",
"𦴠\"Copy paste not always bad. Abstraction can be worse.\"",
"𦴠\"Senior grug mass delete code. Junior grug add code.\"",
"𦴠\"When bug appear, first ask: is this too complex?\"",
"𦴠\"Premature optimization root of mass diff evil.\"",
"𦴠\"Refactor when pain, not when bored.\"",
"𦴠\"Naming hard. But good name worth many comment.\"",
"𦴠\"If function need many argument, maybe do too much.\"",
"𦴠\"Grug not mass diff. Grug small diff many time.\""
];
return w[Math.floor(Math.random() * w.length)];
}
},
ship: {
desc: 'Ship code',
run: () => {
const m = [
"π SHIPPED! Code in production. Grug happy.",
"π Deploy complete. Time for club sandwich.",
"π git push origin main... Success!",
"π Ship now, fix later. This is the way.",
"π Code shipped! Zero downtime. Grug proud.",
"π Pipeline green. Production updated. Grug rest.",
"π Deployed! Users already using. Ship fast win."
];
return m[Math.floor(Math.random() * m.length)];
}
},
think: {
desc: 'Grug thoughts',
run: () => {
const t = [
"π§ *grug brain processing*\n\n...no wait, grug no think. Grug DO.",
"π§ Grug think about simple solution.\n\nAnswer: delete code.",
"π§ Grug ponder existence...\n\nConclusion: ship or no ship. Only question.",
"π§ Brain hurt from complexity.\n\nMust simplify. Must ship.",
"π§ Grug think:\n\nif (works) { ship(); } else { console.log('here'); }",
"π§ Deep thought...\n\n\"Maybe we no need microservices. Maybe monolith fine.\""
];
return t[Math.floor(Math.random() * t.length)];
}
},
debug: {
desc: 'Debug protocol',
run: () => `π Grug Debug Protocolβ’ v4.20:
Step 1: console.log("here 1")
Step 2: console.log("here 2")
Step 3: console.log("why this no work")
Step 4: console.log(JSON.stringify(thing, null, 2))
Step 5: Take break. Drink water.
Step 6: Re-read error message (actually read this time)
Step 7: Find typo in variable name
Step 8: Fixed! Ship it! π
Debugging time: 2 hours
Time to find bug: 30 seconds
Time staring at screen: 1 hour 29 minutes 30 seconds`
},
complexity: {
desc: 'Check complexity',
run: () => {
const level = Math.floor(Math.random() * 100);
let status, emoji, advice;
if (level < 25) {
status = "EXCELLENT"; emoji = "π’";
advice = "Ship it! Code so simple even grug understand.";
} else if (level < 50) {
status = "GOOD"; emoji = "π‘";
advice = "Acceptable. Maybe delete few line?";
} else if (level < 75) {
status = "WARNING"; emoji = "π ";
advice = "Getting complex. Time to refactor before ship.";
} else {
status = "CRITICAL"; emoji = "π΄";
advice = "TOO COMPLEX! Grug brain hurt. Start over maybe.";
}
return `π Complexity Analysis
${emoji} Level: ${level}/100
${'β'.repeat(Math.floor(level/10))}${'β'.repeat(10 - Math.floor(level/10))}
Status: ${status}
Advice: ${advice}`;
}
},
joke: {
desc: 'Grug humor',
run: () => {
const j = [
"Why did grug cross road?\n\nTo mass delete code on other side. π¦΄",
"How many grug to change lightbulb?\n\nOne. But first ask: need lightbulb?",
"Grug walk into bar.\nBarkeeper: \"Why long face?\"\nGrug: \"Just review PR. 47 files changed.\"",
"What grug favorite band?\n\nThe Delete Keys πΈ",
"Knock knock.\nWho there?\nJavaScript.\nJavaScript who?\nJavaScript undefined. Cannot read property 'who'. π
",
"How grug count?\n\n0, 1, many, too many, refactor time",
"Why grug hate meetings?\n\nMeeting no compile. Meeting no ship."
];
return j[Math.floor(Math.random() * j.length)];
}
},
stack: {
desc: 'Tech stack',
run: () => `π grug-group420 Tech Stack:
Language: JavaScript (simple, run everywhere)
Runtime: Bun.js (fast, simple)
Framework: None (framework come, framework go)
Database: JSON file (fancy database later maybe)
Bundler: None (script tag work fine)
CSS: Vanilla (no need sass)
Testing: console.log + eyes
Deployment: git push
Monitoring: Twitter mentions
Total Dependencies: 0*
Lines of Config: 0*
Time to Build: 0ms*
*goals, not always reality`
},
coffee: {
desc: 'Coffee status',
run: () => {
const cups = Math.floor(Math.random() * 6) + 1;
const time = new Date().getHours();
let mood;
if (cups < 2) mood = "Need more coffee. Brain like molasses.";
else if (cups < 4) mood = "Good level. Can write code now.";
else if (cups < 5) mood = "Productive! Shipping at light speed!";
else mood = "MAXIMUM OVERDRIVE! REFACTORING EVERYTHING! π₯";
return `β Coffee Status Report
Cups Today: ${cups}
${'β'.repeat(cups)}
Current Mood: ${mood}
Time: ${time < 12 ? 'Morning - valid coffee time' : time < 17 ? 'Afternoon - questionable but ok' : 'Evening - grug sleep never'}`;
}
},
manifesto: {
desc: 'The grug way',
run: () => `π The grug-group420 Manifesto
1. Complexity is the enemy
2. Ship early, ship often
3. Delete more than you add
4. No framework is the best framework
5. If it works, it ships
6. Simple code > clever code
7. console.log is valid debugging
8. Meetings could have been a push
9. Zero dependencies is a feature
10. The best PR is a deletion PR
Remember: grug brain small.
Keep code small too.
𦴠grug-group420 | Est. 2026`
},
about: {
desc: 'About grugbot',
run: () => `π€ grugbot420 v${grugbot.version}
A simple bot for simple developers.
Built by grug-group420.
Mode: ${serverMode ? 'π’ Server (Bun.js)' : 'π‘ Standalone (embedded)'}
Philosophy: grugbrain.dev
Source: github.com/grug-group420/grugbot420
License: MIT (do what want)
Features:
β’ Zero dependencies
β’ Works offline
β’ Bun.js server optional
β’ No build step
β’ Copy paste friendly
Current mood: ${grugbot.mood}
𦴠Complexity is the enemy.`
},
server: {
desc: 'Server status',
run: () => serverMode
? `π’ Connected to grugbot420 server
Running locally via Bun.js
API: ${API_BASE}/api/cmd
Commands execute on server and return here.
Full CLI power in your browser!`
: `οΏ½οΏ½ Running in standalone mode
No local server detected.
Using embedded grugbot420.
To enable server mode:
cd grugbot420
bun run server/index.ts
Then refresh this page.`
},
clear: {
desc: 'Clear output',
run: () => '__CLEAR__'
}
},
process(input) {
const cmd = input.toLowerCase().trim();
if (!cmd) return "π€ Type 'help' to see commands.";
if (this.commands[cmd]) return this.commands[cmd].run();
// Check for partial matches
const matches = Object.keys(this.commands).filter(c => c.startsWith(cmd));
if (matches.length === 1) return this.commands[matches[0]].run();
if (matches.length > 1) return `β Did you mean: ${matches.join(', ')}?`;
return `β Unknown: "${cmd}"\n\nType 'help' for commands.`;
}
};
// ===== Typing Animation =====
const phrases = ['simple tools', 'boring technology', 'code that ships', 'zero dependencies', 'the grug way'];
let phraseIndex = 0, charIndex = 0, isDeleting = false;
function typeEffect() {
const el = document.querySelector('.typed-text');
if (!el) return;
const current = phrases[phraseIndex];
if (isDeleting) {
el.textContent = current.substring(0, charIndex - 1);
charIndex--;
} else {
el.textContent = current.substring(0, charIndex + 1);
charIndex++;
}
let delay = isDeleting ? 50 : 100;
if (!isDeleting && charIndex === current.length) {
delay = 2000;
isDeleting = true;
} else if (isDeleting && charIndex === 0) {
isDeleting = false;
phraseIndex = (phraseIndex + 1) % phrases.length;
delay = 500;
}
setTimeout(typeEffect, delay);
}
// ===== DOM Ready =====
document.addEventListener('DOMContentLoaded', async () => {
// Check for server connection
await checkServer();
// Start typing effect
typeEffect();
// Playground elements
const input = document.getElementById('user-input');
const runBtn = document.getElementById('run-btn');
const clearBtn = document.getElementById('clear-btn');
const output = document.getElementById('output');
const quickCmds = document.querySelectorAll('.quick-cmd');
if (!input || !output) return;
async function runCommand(cmd) {
const command = cmd || input.value.trim();
if (!command) return;
let result;
// Try server first if connected
if (serverMode && command !== 'clear' && command !== 'server') {
result = await serverCommand(command);
}
// Fall back to embedded
if (!result) {
result = grugbot.process(command);
}
if (result === '__CLEAR__') {
output.innerHTML = '<p class="welcome-msg">π€ Output cleared.</p>';
} else {
const cmdEl = document.createElement('p');
cmdEl.style.color = '#58a6ff';
cmdEl.style.marginTop = '1rem';
cmdEl.textContent = `$ ${command}`;
const resultEl = document.createElement('p');
resultEl.style.whiteSpace = 'pre-wrap';
resultEl.style.marginBottom = '0.5rem';
resultEl.textContent = result;
output.appendChild(cmdEl);
output.appendChild(resultEl);
output.scrollTop = output.scrollHeight;
}
input.value = '';
input.focus();
}
// Event listeners
runBtn?.addEventListener('click', () => runCommand());
clearBtn?.addEventListener('click', () => {
output.innerHTML = '<p class="welcome-msg">π€ Output cleared.</p>';
});
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') runCommand();
});
quickCmds.forEach(btn => {
btn.addEventListener('click', () => {
runCommand(btn.dataset.cmd);
});
});
// Smooth scroll
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
});
// Console easter egg
console.log(`
𦴠grug-group420 Portal
Type grugbot.process('help') for commands.
Or visit the playground above!
Server mode: Run 'bun run server/index.ts' in grugbot420 folder.
Remember: complexity bad. simple good. ship code.
`);
// Copy buttons for install section
document.querySelectorAll('.copy-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const text = btn.dataset.copy;
try {
await navigator.clipboard.writeText(text);
btn.textContent = 'β
';
setTimeout(() => btn.textContent = 'π', 2000);
} catch (e) {
btn.textContent = 'β';
setTimeout(() => btn.textContent = 'π', 2000);
}
});
});