⚒ BETA Wizardebop 3.00 ⚒

The Best Acellus.com Mod/Cheat Menu

  1. // ==UserScript==
  2. // @name ⚒ BETA Wizardebop 3.00 ⚒
  3. // @author Type Stuff
  4. // @description The Best Acellus.com Mod/Cheat Menu
  5. // @version 0.25
  6. // @match https://admin192a.acellus.com/student/*
  7. // @match https://admin192c.acellus.com/student/*
  8. // @match https://admin190c.acellus.com/student/*
  9. // @match https://admin190a.acellus.com/student/*
  10. // @run-at document-start
  11. // @grant none
  12. // @namespace https://greatest.deepsurf.us/users/1394549
  13. // @icon https://img.freepik.com/free-vector/halloween-witch-hat-isolated-illustration_18591-83719.jpg
  14. // @license Proprietary — see below
  15. // ==/UserScript==
  16.  
  17. /*
  18. © 2025 TypeStuff. All Rights Reserved.
  19.  
  20. You are free to:
  21. • Install and run this script.
  22. • Modify the code for your own, personal, non-commercial use.
  23.  
  24. You MAY NOT:
  25. • Redistribute, re-host, repost, or re-upload this script in any form.
  26. • Sell, license, or otherwise make available this script (original or modified).
  27. */
  28.  
  29. (function() {
  30. 'use strict';
  31.  
  32. /* -------------------
  33. 0) AI CHATBOT INTEGRATION
  34. ------------------- */
  35. function setupAIChatbot() {
  36. const aiTab = document.querySelector('.category-menu#ai');
  37. const container = document.createElement('div');
  38. container.id = 'ai-chatbot';
  39. container.style = 'padding:8px;';
  40. container.innerHTML = `
  41. <div id="chat-container" style="display:flex;flex-direction:column;height:300px;">
  42. <div id="chat-messages" style="flex:1;overflow:auto;border:1px solid #ccc;padding:8px;background:#fff;border-radius:4px;margin-bottom:8px;"></div>
  43. <div style="display:flex;align-items:center;">
  44. <select id="template-select" class="wm-input" style="margin-right:8px;">
  45. <option value="">No template</option>
  46. <option value="essay">Essay Writer</option>
  47. </select>
  48. <input id="chat-input" type="text" class="wm-input" placeholder="Type your message..." style="flex:1;margin-right:8px;"/>
  49. <button id="chat-send" class="wm-button">Send</button>
  50. </div>
  51. <div id="chat-error" style="color:red;font-size:12px;margin-top:4px;display:none;"></div>
  52. </div>
  53. `;
  54. aiTab.appendChild(container);
  55.  
  56. const AI_MAX = 15;
  57. const AI_PROMPTS_KEY = 'wizardebop-ai-prompts';
  58. const AI_TEMPLATE_KEY = 'wizardebop-ai-template';
  59.  
  60. function getPromptTimestamps(){
  61. let arr = [];
  62. try { arr = JSON.parse(localStorage.getItem(AI_PROMPTS_KEY) || '[]'); }
  63. catch { arr = []; }
  64. return Array.isArray(arr) ? arr : [];
  65. }
  66. function savePromptTimestamps(arr){
  67. localStorage.setItem(AI_PROMPTS_KEY, JSON.stringify(arr));
  68. }
  69. function recordPrompt(){
  70. const now = Date.now();
  71. const oneHour = 60*60*1000;
  72. let arr = getPromptTimestamps().filter(ts => now - ts < oneHour);
  73. arr.push(now);
  74. savePromptTimestamps(arr);
  75. return arr.length;
  76. }
  77. function canSendPrompt(){
  78. const now = Date.now();
  79. const oneHour = 60*60*1000;
  80. return getPromptTimestamps().filter(ts => now - ts < oneHour).length < AI_MAX;
  81. }
  82.  
  83. async function fetchAI(messages) {
  84. const res = await fetch('http://localhost:11434', {
  85. method: 'POST',
  86. headers: {
  87. 'Content-Type': 'application/json'
  88. },
  89. body: JSON.stringify({
  90. model: 'gemm3:1b',
  91. messages
  92. })
  93. });
  94.  
  95. if (!res.ok) {
  96. throw new Error(`AI request failed: ${res.status} ${res.statusText}`);
  97. }
  98.  
  99. const data = await res.json();
  100. return data.choices[0].message.content.trim();
  101. }
  102.  
  103. const tplSelect = document.getElementById('template-select');
  104. const inputEl = document.getElementById('chat-input');
  105. const sendBtn = document.getElementById('chat-send');
  106. const msgsDiv = document.getElementById('chat-messages');
  107. const errDiv = document.getElementById('chat-error');
  108.  
  109. tplSelect.value = localStorage.getItem(AI_TEMPLATE_KEY) || '';
  110.  
  111. sendBtn.addEventListener('click', async () => {
  112. errDiv.style.display = 'none';
  113. if (!canSendPrompt()) {
  114. errDiv.textContent = `Rate limit reached: ${AI_MAX} prompts per hour.`;
  115. errDiv.style.display = 'block';
  116. return;
  117. }
  118. const raw = inputEl.value.trim();
  119. if (!raw) return;
  120. const template = tplSelect.value;
  121. localStorage.setItem(AI_TEMPLATE_KEY, template);
  122.  
  123. let messages = [];
  124. if (template === 'essay') {
  125. messages.push({
  126. role: 'system',
  127. content: 'Make a detailed 200-word essay that gets a 100%, and grammar is perfect and punctuation is also perfect, about:'
  128. });
  129. }
  130. messages.push({ role: 'user', content: raw });
  131.  
  132. recordPrompt();
  133. if (!canSendPrompt()) sendBtn.disabled = true;
  134.  
  135. msgsDiv.innerHTML += `<div><strong>You:</strong> ${raw}</div>`;
  136. msgsDiv.scrollTop = msgsDiv.scrollHeight;
  137. inputEl.value = '';
  138.  
  139. try {
  140. const aiResp = await fetchAI(messages);
  141. msgsDiv.innerHTML += `<div><strong>AI:</strong> ${aiResp}</div>`;
  142. msgsDiv.scrollTop = msgsDiv.scrollHeight;
  143.  
  144. if (template === 'essay') {
  145. const summary = await fetchAI([
  146. { role: 'assistant', content: aiResp },
  147. { role: 'user', content: 'Describe what the above essay is about in one sentence.' }
  148. ]);
  149. msgsDiv.innerHTML += `<div><em>Summary:</em> ${summary}</div>`;
  150. msgsDiv.scrollTop = msgsDiv.scrollHeight;
  151. }
  152. } catch (err) {
  153. errDiv.textContent = 'Error: ' + err.message;
  154. errDiv.style.display = 'block';
  155. }
  156. });
  157. }
  158.  
  159. /* ------------------
  160. 1) SETTINGS & STATE
  161. ------------------ */
  162. const STORAGE = {
  163. wallpaper: 'wizardebop-wallpaper',
  164. unpause: 'wizardebop-unpause',
  165. pasteFast: 'wizardebop-paste',
  166. autoSpeed: 'wizardebop-autoSpeed',
  167. btnBg: 'wizardebop-btn-bg',
  168. btnHover: 'wizardebop-btn-hover',
  169. btnRadius: 'wizardebop-btn-radius',
  170. btnFont: 'wizardebop-btn-font',
  171. btnPad: 'wizardebop-btn-pad',
  172. animDur: 'wizardebop-anim-duration',
  173. password: 'wizardebop-password',
  174. keybind: 'wizardebop-keybind',
  175. notes: 'wizardebop-notes',
  176. todos: 'wizardebop-todos',
  177. showTimer: 'wizardebop-show-timer'
  178. };
  179.  
  180. let autoUnpause = localStorage.getItem(STORAGE.unpause) === 'true';
  181. let pasteFast = localStorage.getItem(STORAGE.pasteFast) === 'true';
  182. let autoSpeed = localStorage.getItem(STORAGE.autoSpeed) === 'true';
  183. let userPass = localStorage.getItem(STORAGE.password) || '';
  184. let toggleKey = localStorage.getItem(STORAGE.keybind) || 'F2';
  185. let savedNotes = localStorage.getItem(STORAGE.notes) || '';
  186. let todoList = JSON.parse(localStorage.getItem(STORAGE.todos) || '[]');
  187. let showTimerOnScreen = localStorage.getItem(STORAGE.showTimer) === 'true';
  188.  
  189. const BTN = {
  190. bg: localStorage.getItem(STORAGE.btnBg) || 'rgba(128,128,128,0.5)',
  191. hover: localStorage.getItem(STORAGE.btnHover) || 'rgba(255,255,255,0.3)',
  192. radius: localStorage.getItem(STORAGE.btnRadius) || '6px',
  193. font: localStorage.getItem(STORAGE.btnFont) || '14px',
  194. pad: localStorage.getItem(STORAGE.btnPad) || '6px 12px'
  195. };
  196. const ANIM = {
  197. gradDur: +localStorage.getItem(STORAGE.animDur) || 12
  198. };
  199.  
  200. let timerInterval = null,
  201. timerElapsed = 0;
  202. let pomoInterval = null,
  203. pomoRemaining = 25 * 60,
  204. pomoPhase = 'work',
  205. pomoCycleCount = 0;
  206.  
  207. function save(k,v){ localStorage.setItem(k,v); }
  208. function saveTodos(){ save(STORAGE.todos, JSON.stringify(todoList)); }
  209.  
  210. /* -------------------
  211. 2) AUTO-UNPAUSE
  212. ------------------- */
  213. function enableUnpause(){
  214. Object.defineProperty(document,'hidden',{get:()=>false});
  215. Object.defineProperty(document,'visibilityState',{get:()=> 'visible'});
  216. document.addEventListener('visibilitychange',e=>e.stopImmediatePropagation(),true);
  217. }
  218. if(autoUnpause) enableUnpause();
  219. new MutationObserver(()=>{ if(autoUnpause) enableUnpause(); })
  220. .observe(document,{childList:true,subtree:true});
  221.  
  222. /* -------------------
  223. 3) WALLPAPER HELPERS
  224. ------------------- */
  225. function loadWP(){
  226. try{ return JSON.parse(localStorage.getItem(STORAGE.wallpaper)); }
  227. catch{ return null; }
  228. }
  229. function saveWP(type,val){
  230. localStorage.setItem(STORAGE.wallpaper, JSON.stringify({type,val}));
  231. }
  232. function createWP(){
  233. let el = document.getElementById('global-wallpaper');
  234. if(!el){
  235. el = document.createElement('div');
  236. el.id = 'global-wallpaper';
  237. document.body.prepend(el);
  238. }
  239. return el;
  240. }
  241. function changeWP(type,val,saveFlag=true){
  242. const w = createWP();
  243. Object.assign(w.style,{
  244. position:'fixed', top:'0', left:'0',
  245. width:'100vw', height:'100vh',
  246. zIndex:'-1', pointerEvents:'none', animation:''
  247. });
  248. if(type==='gradient'){
  249. w.style.background = 'linear-gradient(135deg,rgba(255,182,193,0.6),rgba(176,224,230,0.6),rgba(221,160,221,0.6),rgba(255,228,181,0.6))';
  250. w.style.backgroundSize = '200% 200%';
  251. w.style.animation = `pastelGradient ${ANIM.gradDur}s ease infinite`;
  252. } else if(type.startsWith('ai_')){
  253. const topic = type.split('_')[1];
  254. w.style.background = `url('https://source.unsplash.com/1600x900/?ai-art,${topic}') no-repeat center/cover`;
  255. } else if(type==='custom'){
  256. w.style.background = val;
  257. } else if(type==='upload'){
  258. w.style.background = `url('${val}') no-repeat center/cover`;
  259. }
  260. if(saveFlag) saveWP(type,val);
  261. }
  262.  
  263. /* -------------------
  264. 4) ESSAY PASTER GUI
  265. ------------------- */
  266. function setupPaste(){
  267. let last = null;
  268. const overlay = document.createElement('div');
  269. overlay.id = 'pasteOverlay';
  270. overlay.style.cssText =
  271. 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);' +
  272. 'background:rgba(0,0,0,0.7);padding:20px;border-radius:8px;z-index:10003;display:none';
  273. overlay.innerHTML =
  274. `<div style="display:flex;flex-direction:column;gap:10px;">
  275. <h3 style="margin:0;color:#000">✎ Paste Anything</h3>
  276. <textarea id="pasteText"
  277. style="width:300px;height:100px;padding:8px;border-radius:4px;border:none;color:#000">
  278. </textarea>
  279. </div>
  280. <div style="display:flex;gap:8px;margin-top:10px;">
  281. <button id="pasteNow"
  282. style="padding:6px;border:none;border-radius:4px;cursor:pointer;background:${BTN.bg};transition:background 0.3s;">
  283. Paste
  284. </button>
  285. <button id="closePaste"
  286. style="padding:6px;border:none;border-radius:4px;cursor:pointer;background:${BTN.bg};transition:background 0.3s;">
  287. Close
  288. </button>
  289. <button id="speedButton"
  290. style="padding:6px;border:none;border-radius:4px;cursor:pointer;background:${BTN.bg};transition:background 0.3s;">
  291. ${autoSpeed ? '1.5× Auto ON' : '1.5× Now'}
  292. </button>
  293. </div>`;
  294. document.body.appendChild(overlay);
  295.  
  296. overlay.querySelector('#pasteNow').addEventListener('click', ()=>{
  297. const txt = overlay.querySelector('#pasteText').value;
  298. if(last){
  299. last.focus();
  300. let i=0;
  301. const ev = new Event('input',{bubbles:true});
  302. const iv = setInterval(()=>{
  303. if(last.isContentEditable) last.textContent += txt[i];
  304. else last.value += txt[i];
  305. last.dispatchEvent(ev);
  306. if(++i >= txt.length) clearInterval(iv);
  307. },5);
  308. }
  309. overlay.style.display = 'none';
  310. });
  311.  
  312. overlay.querySelector('#closePaste').addEventListener('click', ()=>{
  313. overlay.style.display = 'none';
  314. });
  315.  
  316. const speedBtn = overlay.querySelector('#speedButton');
  317. speedBtn.addEventListener('click', ()=>{
  318. document.querySelectorAll('video').forEach(v=> v.playbackRate = 1.5);
  319. });
  320. speedBtn.addEventListener('contextmenu', e=>{
  321. e.preventDefault();
  322. autoSpeed = !autoSpeed;
  323. save(STORAGE.autoSpeed, autoSpeed);
  324. speedBtn.textContent = autoSpeed ? '1.5× Auto ON' : '1.5× Now';
  325. if(autoSpeed) applyAutoSpeed();
  326. });
  327.  
  328. document.addEventListener('click', e=>{
  329. if(!pasteFast) return;
  330. const t = e.target;
  331. if(document.getElementById('settings-menu')?.contains(t)) return;
  332. if(overlay.contains(t)) return;
  333. if(t.isContentEditable || t.nodeName==='TEXTAREA' || (t.nodeName==='INPUT'&&t.type==='text')){
  334. last = t;
  335. overlay.style.display = 'block';
  336. }
  337. }, true);
  338. }
  339.  
  340. /* -------------------
  341. 5) STYLE INJECTION
  342. ------------------- */
  343. const styleEl = document.createElement('style');
  344. styleEl.textContent = `
  345. @keyframes pastelGradient {
  346. 0% { background-position:0% 0% }
  347. 50% { background-position:100% 100% }
  348. 100% { background-position:0% 0% }
  349. }
  350.  
  351. #settings-overlay {
  352. position:fixed;top:0;left:0;width:100vw;height:100vh;
  353. background:rgba(0,0,0,0.2);backdrop-filter:blur(4px);z-index:10000;
  354. display:none;
  355. }
  356. #settings-menu {
  357. position:fixed;top:50%;left:50%;
  358. transform:translate(-50%,-50%) scale(0.8);
  359. width:760px;height:420px;
  360. background:rgba(255,255,255,0.8);
  361. backdrop-filter:blur(6px);
  362. border-radius:12px;
  363. box-shadow:0 0 20px rgba(0,0,0,0.5);
  364. display:none;flex-direction:row;
  365. z-index:10001;opacity:0;
  366. transition:opacity 0.3s ease,transform 0.3s ease;
  367. }
  368. #settings-menu.visible {
  369. display:flex;opacity:1;
  370. transform:translate(-50%,-50%) scale(1);
  371. }
  372. #settings-button {
  373. position:fixed;bottom:20px;right:20px;
  374. background:rgba(128,128,128,0.5);
  375. color:#000;border:none;border-radius:6px;
  376. padding:6px;font-size:18px;cursor:pointer;
  377. z-index:10002;backdrop-filter:blur(4px);
  378. transition:transform 0.2s;
  379. }
  380. #settings-button:hover { transform:scale(1.1) }
  381. .settings-categories {
  382. width:20%;background:rgba(0,0,0,0.1);
  383. display:flex;flex-direction:column;
  384. padding:10px;overflow-y:auto;
  385. }
  386. .settings-categories button {
  387. background:rgba(255,255,255,0.5);
  388. color:#000;border:none;margin:4px 0;
  389. padding:6px;border-radius:6px;
  390. cursor:pointer;transition:background 0.3s,transform 0.2s;
  391. text-align:left;font-size:14px;
  392. }
  393. .settings-categories button:hover {
  394. background:rgba(200,200,200,0.6);
  395. transform:translateX(4px);
  396. }
  397. .settings-categories button.active {
  398. background:rgba(200,200,200,0.8);color:#000;
  399. }
  400. .settings-details {
  401. width:80%;padding:20px;overflow-y:auto;color:#000;
  402. position:relative;
  403. }
  404. .category-menu { display:none }
  405. .category-menu.active { display:block }
  406. .category-menu#timer {
  407. background:rgba(200,200,200,0.1);
  408. border-radius:6px; padding:10px;
  409. }
  410. .category-menu h3 { margin-top:0;font-size:18px }
  411. .category-menu h4 { margin:12px 0 4px;font-size:16px }
  412. .category-menu label { display:block;margin:8px 0;font-size:14px }
  413. .category-menu input.wm-input {
  414. width:90%;margin:4px 0;padding:4px;
  415. border-radius:4px;border:1px solid #ccc;
  416. }
  417. button.wm-button {
  418. margin:6px 4px;padding:${BTN.pad};
  419. background:${BTN.bg};border:none;
  420. border-radius:${BTN.radius};
  421. font-size:${BTN.font};cursor:pointer;
  422. transition:background 0.3s;
  423. }
  424. button.wm-button:hover { background:${BTN.hover} }
  425. #close-x {
  426. position:absolute;top:8px;right:8px;
  427. background:transparent;border:none;
  428. font-size:20px;cursor:pointer;
  429. }
  430. #screen-timer {
  431. position:absolute;top:100px;left:100px;
  432. background:rgba(0,0,0,0.7);color:#fff;
  433. padding:8px 12px;border-radius:4px;
  434. font-size:16px;cursor:move;
  435. z-index:10005;display:none;user-select:none;
  436. }
  437. `;
  438. document.head.appendChild(styleEl);
  439.  
  440. /* -------------------
  441. 6) CREATE ON-SCREEN TIMER
  442. -------------------
  443. */
  444. let screenTimerEl;
  445. function createScreenTimer(){
  446. if(screenTimerEl) return;
  447. screenTimerEl = document.createElement('div');
  448. screenTimerEl.id = 'screen-timer';
  449. screenTimerEl.textContent = '00:00:00';
  450. document.body.appendChild(screenTimerEl);
  451. let dragging=false, ox=0, oy=0;
  452. screenTimerEl.addEventListener('mousedown', e=>{
  453. dragging=true;
  454. ox = e.clientX - screenTimerEl.offsetLeft;
  455. oy = e.clientY - screenTimerEl.offsetTop;
  456. e.preventDefault();
  457. });
  458. document.addEventListener('mousemove', e=>{
  459. if(dragging){
  460. screenTimerEl.style.left = `${e.clientX - ox}px`;
  461. screenTimerEl.style.top = `${e.clientY - oy}px`;
  462. }
  463. });
  464. document.addEventListener('mouseup', ()=>dragging=false);
  465. screenTimerEl.style.display = showTimerOnScreen ? 'block' : 'none';
  466. }
  467.  
  468. /* -------------------
  469. 7) AUTO-SPEED LOGIC
  470. -------------------
  471. */
  472. function applyAutoSpeed(){
  473. const setAll = ()=> document.querySelectorAll('video').forEach(v=> v.playbackRate = 1.5);
  474. setAll();
  475. new MutationObserver(setAll)
  476. .observe(document.body, { childList: true, subtree: true });
  477. document.addEventListener('play', e=>{
  478. if(e.target.tagName === 'VIDEO')
  479. e.target.playbackRate = 1.5;
  480. }, true);
  481. }
  482.  
  483. /* -------------------
  484. 8) BUILD SETTINGS MENU
  485. -------------------
  486. */
  487. function buildMenu(){
  488. const overlay = document.createElement('div');
  489. overlay.id = 'settings-overlay';
  490. document.body.appendChild(overlay);
  491.  
  492. const btn = document.createElement('button');
  493. btn.id = 'settings-button';
  494. btn.textContent = '⚙️';
  495. document.body.appendChild(btn);
  496.  
  497. const menu = document.createElement('div');
  498. menu.id = 'settings-menu';
  499. menu.innerHTML =
  500. `<button id='close-x'>×</button>` +
  501. `<div class='settings-categories'>` +
  502. `<button class='cat-btn active' data-cat='general'>General</button>` +
  503. `<button class='cat-btn' data-cat='ai'>AI</button>` +
  504. `<button class='cat-btn' data-cat='wallpaper'>Wallpaper</button>` +
  505. `<button class='cat-btn' data-cat='buttons'>Buttons</button>` +
  506. `<button class='cat-btn' data-cat='animations'>Animations</button>` +
  507. `<button class='cat-btn' data-cat='notes'>Notes</button>` +
  508. `<button class='cat-btn' data-cat='todo'>To-Do</button>` +
  509. `<button class='cat-btn' data-cat='timer'>Timer</button>` +
  510. `<button class='cat-btn' data-cat='extras'>Extras</button>` +
  511. `</div>` +
  512. `<div class='settings-details'>` +
  513.  
  514. `<div class='category-menu active' id='general'>` +
  515. `<h3>General</h3>` +
  516. `<label><input type='checkbox' id='chk-unpause' ${autoUnpause?'checked':''}/> Auto-Unpause</label>` +
  517. `<label><input type='checkbox' id='chk-paste' ${pasteFast?'checked':''}/> Essay Paster</label>` +
  518. `<label><input type='checkbox' id='chk-speed' ${autoSpeed?'checked':''}/> Auto 1.5× Video</label>` +
  519. `<label>Password: <input type='password' id='menu-pass' class='wm-input' value='${userPass}'></label>` +
  520. `<label>Keybind: <button id='keybind-btn' class='wm-button'>${toggleKey===' ' ? 'Space' : toggleKey}</button></label>` +
  521. `<button id='apply-general' class='wm-button'>Apply</button>` +
  522. `</div>` +
  523.  
  524. `<div class='category-menu' id='ai'>` +
  525. `<h3>AI</h3>` +
  526. `<div id='ai-settings' style='padding:4px 0;color:#555;'></div>` +
  527. `</div>` +
  528.  
  529. `<div class='category-menu' id='wallpaper'>` +
  530. `<h3>Wallpaper</h3>` +
  531. `<button class='wm-button' data-wallpaper='gradient'>Gradient</button>` +
  532. `<button class='wm-button' data-wallpaper='ai_nature'>AI Nature</button>` +
  533. `<button class='wm-button' data-wallpaper='ai_cityscape'>AI Cityscape</button>` +
  534. `<button class='wm-button' data-wallpaper='ai_abstract'>AI Abstract</button>` +
  535. `<button class='wm-button' data-wallpaper='custom'>Custom Color</button>` +
  536. `<button class='wm-button' id='upload-wallpaper'>Upload</button>` +
  537. `<input type='file' id='upload-input' accept='image/*'>` +
  538. `</div>` +
  539.  
  540. `<div class='category-menu' id='buttons'>` +
  541. `<h3>Buttons</h3>` +
  542. `<label>BG:<br><input id='btn-bg' class='wm-input' value='${BTN.bg}'></label>` +
  543. `<label>Hover BG:<br><input id='btn-hover' class='wm-input' value='${BTN.hover}'></label>` +
  544. `<label>Radius:<br><input id='btn-radius' class='wm-input' value='${BTN.radius}'></label>` +
  545. `<label>Font Size:<br><input id='btn-font' class='wm-input' value='${BTN.font}'></label>` +
  546. `<label>Padding:<br><input id='btn-pad' class='wm-input' value='${BTN.pad}'></label>` +
  547. `<button class='wm-button' id='apply-buttons'>Apply</button>` +
  548. `</div>` +
  549.  
  550. `<div class='category-menu' id='animations'>` +
  551. `<h3>Animations</h3>` +
  552. `<label>Gradient Duration (s):<br><input type='number' id='anim-duration' class='wm-input' value='${ANIM.gradDur}' min='1'></label>` +
  553. `<button class='wm-button' id='apply-animations'>Apply</button>` +
  554. `</div>` +
  555.  
  556. `<div class='category-menu' id='notes'>` +
  557. `<h3>Notes</h3>` +
  558. `<textarea id='notes-text' style='width:100%;height:200px;padding:8px;border:1px solid #ccc;border-radius:4px;'>${savedNotes}</textarea>` +
  559. `<button id='save-notes' class='wm-button'>Save Notes</button>` +
  560. `</div>` +
  561.  
  562. `<div class='category-menu' id='todo'>` +
  563. `<h3>To-Do List</h3>` +
  564. `<input id='todo-input' placeholder='New task...'><button id='add-todo' class='wm-button'>Add</button>` +
  565. `<ul id='todo-list'></ul>` +
  566. `</div>` +
  567.  
  568. `<div class='category-menu' id='timer'>` +
  569. `<h3>Timer</h3>` +
  570. `<span id='timer-display-general'>00:00:00</span>` +
  571. `<label><input type='checkbox' id='chk-show-screen' ${showTimerOnScreen?'checked':''}/> Show timer on screen</label><br>` +
  572. `<button id='timer-toggle-general' class='wm-button'>Start</button>` +
  573. `<button id='timer-reset-general' class='wm-button'>Reset</button>` +
  574. `</div>` +
  575.  
  576. `<div class='category-menu' id='extras'><h3>Extras</h3><p>More coming soon…</p></div>` +
  577.  
  578. `</div>`;
  579. document.body.appendChild(menu);
  580.  
  581. menu.querySelectorAll('.cat-btn').forEach(b=>{
  582. b.addEventListener('click',()=>{
  583. menu.querySelectorAll('.cat-btn').forEach(x=>x.classList.remove('active'));
  584. menu.querySelectorAll('.category-menu').forEach(x=>x.classList.remove('active'));
  585. b.classList.add('active');
  586. menu.querySelector('#'+b.dataset.cat).classList.add('active');
  587. });
  588. });
  589.  
  590. const showM = ()=>{ overlay.style.display='block'; menu.classList.add('visible'); };
  591. const hideM = ()=>{ menu.classList.remove('visible'); overlay.style.display='none'; };
  592.  
  593. btn.addEventListener('click',()=>{
  594. if(userPass){
  595. const p = prompt('Enter password:');
  596. if(p !== userPass) return;
  597. }
  598. showM();
  599. });
  600. document.addEventListener('keydown', e=>{
  601. if(e.key === toggleKey){
  602. if(menu.classList.contains('visible')) hideM();
  603. else {
  604. if(userPass){
  605. const p = prompt('Enter password:');
  606. if(p !== userPass) return;
  607. }
  608. showM();
  609. }
  610. }
  611. });
  612. menu.querySelector('#close-x').addEventListener('click', hideM);
  613. overlay.addEventListener('click', hideM);
  614.  
  615. menu.querySelector('#apply-general').addEventListener('click', ()=>{
  616. autoUnpause = menu.querySelector('#chk-unpause').checked;
  617. pasteFast = menu.querySelector('#chk-paste').checked;
  618. autoSpeed = menu.querySelector('#chk-speed').checked;
  619. userPass = menu.querySelector('#menu-pass').value;
  620. save(STORAGE.unpause, autoUnpause);
  621. save(STORAGE.pasteFast, pasteFast);
  622. save(STORAGE.autoSpeed, autoSpeed);
  623. save(STORAGE.password, userPass);
  624. save(STORAGE.keybind, toggleKey);
  625. if(autoUnpause) enableUnpause();
  626. if(autoSpeed) applyAutoSpeed();
  627. hideM();
  628. });
  629.  
  630. const disp = menu.querySelector('#timer-display-general'),
  631. btnT = menu.querySelector('#timer-toggle-general'),
  632. btnR = menu.querySelector('#timer-reset-general'),
  633. chkS = menu.querySelector('#chk-show-screen');
  634. function updateTimer(){
  635. const h = Math.floor(timerElapsed/3600).toString().padStart(2,'0'),
  636. m = Math.floor((timerElapsed%3600)/60).toString().padStart(2,'0'),
  637. s = (timerElapsed%60).toString().padStart(2,'0');
  638. disp.textContent = `${h}:${m}:${s}`;
  639. if(showTimerOnScreen && screenTimerEl) screenTimerEl.textContent = disp.textContent;
  640. }
  641. btnT.addEventListener('click', ()=>{
  642. if(timerInterval){
  643. clearInterval(timerInterval);
  644. timerInterval = null;
  645. btnT.textContent = 'Start';
  646. } else {
  647. createScreenTimer();
  648. if(showTimerOnScreen) screenTimerEl.style.display = 'block';
  649. timerInterval = setInterval(()=>{
  650. timerElapsed++;
  651. updateTimer();
  652. }, 1000);
  653. btnT.textContent = 'Stop';
  654. }
  655. });
  656. btnR.addEventListener('click', ()=>{
  657. clearInterval(timerInterval);
  658. timerInterval = null;
  659. timerElapsed = 0;
  660. updateTimer();
  661. btnT.textContent = 'Start';
  662. });
  663. chkS.addEventListener('change', e=>{
  664. showTimerOnScreen = e.target.checked;
  665. save(STORAGE.showTimer, showTimerOnScreen);
  666. if(screenTimerEl) screenTimerEl.style.display = showTimerOnScreen ? 'block' : 'none';
  667. });
  668. }
  669.  
  670. /* -------------------
  671. 9) INIT
  672. ------------------- */
  673. function wireUpWallpaperButtons(){
  674. const uploadInput = document.querySelector('#upload-input');
  675. document.body.addEventListener('click', e => {
  676. const btn = e.target;
  677. if (btn.matches('#wallpaper button[data-wallpaper]')) {
  678. const type = btn.getAttribute('data-wallpaper');
  679. if (type === 'custom') {
  680. const val = prompt('Enter a CSS background (color, gradient, etc):');
  681. if (val) changeWP(type, val);
  682. } else {
  683. changeWP(type, null);
  684. }
  685. }
  686. if (btn.id === 'upload-wallpaper') {
  687. uploadInput.click();
  688. }
  689. });
  690. uploadInput.addEventListener('change', e => {
  691. const file = e.target.files[0];
  692. if (!file) return;
  693. const reader = new FileReader();
  694. reader.onload = () => changeWP('upload', reader.result);
  695. reader.readAsDataURL(file);
  696. });
  697. }
  698.  
  699. function init(){
  700. setupPaste();
  701. buildMenu();
  702. setupAIChatbot(); // ← initialize AI after menu exists
  703. createScreenTimer();
  704. wireUpWallpaperButtons();
  705. if(autoSpeed) applyAutoSpeed();
  706. }
  707. if(document.readyState==='loading')
  708. document.addEventListener('DOMContentLoaded', init);
  709. else
  710. init();
  711.  
  712. // Restore wallpaper
  713. const wp = loadWP();
  714. if(wp) window.addEventListener('DOMContentLoaded', ()=>changeWP(wp.type, wp.val, false));
  715.  
  716. })();