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