您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
크랙용 보조메모장 및 매크로
// ==UserScript== // @name 크랙보조메모장(메보) // @namespace https://crack.wrtn.ai // @version 1.2 // @description 크랙용 보조메모장 및 매크로 // @author 바보륍부이 // @license MIT // @match https://crack.wrtn.ai/* // @grant none // ==/UserScript== (function () { 'use strict'; const storageKey = 'wrtn_memo_list_v2'; const panelStateKey = 'wrtn_memo_panel_state_v2'; const selectionKey = 'wrtn_memo_selected_index_v2'; const randomHistory = new Map(); let panel = null; let content, control; let memoList = []; let selectedIndex = Number(localStorage.getItem(selectionKey) || '-1'); let isMinimized = false; const defaultHeight = '550px'; const save = () => localStorage.setItem(storageKey, JSON.stringify(memoList)); const savePanelState = () => { const state = { top: panel.style.top, left: panel.style.left, width: panel.style.width, height: panel.style.height, minimized: isMinimized, expandedHeight: (!isMinimized ? panel.style.height : localStorage.getItem(panelStateKey + '_expandedHeight') || defaultHeight) }; localStorage.setItem(panelStateKey, JSON.stringify(state)); localStorage.setItem(panelStateKey + '_expandedHeight', state.expandedHeight); localStorage.setItem(selectionKey, selectedIndex); }; const getProcessedBody = (body, key = '') => { const lines = body.trim().split('\n'); const mode = lines[0]; if ((mode === '랜덤' || mode === '와일드카드') && lines.length > 1) { const options = lines.slice(1); let last = randomHistory.get(key); let choice; const filtered = options.filter(o => o !== last); if (filtered.length === 0) { choice = options[Math.floor(Math.random() * options.length)]; } else { choice = filtered[Math.floor(Math.random() * filtered.length)]; } randomHistory.set(key, choice); return { text: choice, replace: mode === '와일드카드' }; } else { return { text: body, replace: false }; } }; const savedState = JSON.parse(localStorage.getItem(panelStateKey) || '{}'); isMinimized = savedState.minimized || false; memoList = JSON.parse(localStorage.getItem(storageKey) || '[]'); panel = document.createElement('div'); panel.style.position = 'fixed'; panel.style.top = savedState.top || '100px'; panel.style.left = savedState.left || '10px'; panel.style.width = savedState.width || '400px'; panel.style.height = isMinimized ? '36px' : (savedState.expandedHeight || defaultHeight); panel.style.background = '#1a1a1a'; panel.style.color = '#fff'; panel.style.fontFamily = 'sans-serif'; panel.style.fontSize = '14px'; panel.style.zIndex = '9999'; panel.style.borderRadius = '10px'; panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.4)'; panel.style.display = 'flex'; panel.style.flexDirection = 'column'; panel.style.overflow = 'hidden'; panel.style.resize = 'both'; const header = document.createElement('div'); header.textContent = '📋 메모 리스트 (Ctrl+Enter로 본문 입력)'; header.style = 'padding: 8px; background: #333; font-weight: bold; cursor: move; user-select: none'; panel.appendChild(header); content = document.createElement('div'); content.style = 'flex: 1; overflow-y: auto; padding: 10px; background: #222;'; panel.appendChild(content); control = document.createElement('div'); control.style = 'display: flex; flex-direction: column; gap: 6px; padding: 10px; background: #111;'; const titleInput = document.createElement('input'); titleInput.placeholder = '제목'; titleInput.style = 'padding: 6px; border-radius: 4px; border: none;'; control.appendChild(titleInput); const bodyInput = document.createElement('textarea'); bodyInput.placeholder = '본문'; bodyInput.rows = 3; bodyInput.style = 'padding: 6px; border-radius: 4px; resize: vertical; border: none; min-height: 60px;'; control.appendChild(bodyInput); const addBtn = document.createElement('button'); addBtn.textContent = '추가'; addBtn.style = 'padding: 6px 10px; background: #4caf50; color: white; border: none; border-radius: 4px;'; control.appendChild(addBtn); panel.appendChild(control); const toggleMinimize = () => { isMinimized = !isMinimized; content.style.display = isMinimized ? 'none' : 'block'; control.style.display = isMinimized ? 'none' : 'flex'; panel.style.height = isMinimized ? '36px' : localStorage.getItem(panelStateKey + '_expandedHeight') || defaultHeight; savePanelState(); }; header.addEventListener('dblclick', toggleMinimize); addBtn.addEventListener('click', () => { const title = titleInput.value.trim(); const body = bodyInput.value.trim(); if (!title && !body) return; memoList.push({ title, body }); titleInput.value = ''; bodyInput.value = ''; save(); renderList(); }); window.addEventListener('keydown', (e) => { if (e.key === 'Enter' && e.ctrlKey && selectedIndex >= 0) { const textarea = document.querySelector('textarea[placeholder*="메시지 보내기"]'); if (!textarea) return alert("입력창을 찾을 수 없습니다."); const memo = memoList[selectedIndex]; const { text, replace } = getProcessedBody(memo.body, 'key_' + selectedIndex); const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set; setter?.call(textarea, replace ? text : textarea.value + '\n' + text); textarea.dispatchEvent(new Event('input', { bubbles: true })); } }); let offsetX, offsetY, dragging = false; header.addEventListener('mousedown', (e) => { dragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; document.body.style.userSelect = 'none'; }); document.addEventListener('mousemove', (e) => { if (!dragging) return; panel.style.left = `${e.clientX - offsetX}px`; panel.style.top = `${e.clientY - offsetY}px`; }); document.addEventListener('mouseup', () => { if (!dragging) return; dragging = false; document.body.style.userSelect = ''; savePanelState(); }); new ResizeObserver(savePanelState).observe(panel); document.body.appendChild(panel); const renderList = () => { content.innerHTML = ''; memoList.forEach((item, index) => { const row = document.createElement('div'); row.style = 'display: flex; flex-direction: column; gap: 4px; padding: 6px 0; border-bottom: 1px solid #333;'; const topRow = document.createElement('div'); topRow.style = 'display: flex; align-items: center; gap: 6px;'; const radio = document.createElement('input'); radio.type = 'radio'; radio.name = 'memoSelect'; radio.checked = selectedIndex === index; radio.addEventListener('change', () => { selectedIndex = index; savePanelState(); }); topRow.appendChild(radio); const title = document.createElement('span'); title.textContent = item.title; title.style = 'flex: 1; font-weight: bold; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;'; topRow.appendChild(title); const quickBtn = document.createElement('button'); quickBtn.textContent = '입력'; quickBtn.style = 'padding: 2px 6px; background: #2196f3; color: white; border: none; border-radius: 4px;'; quickBtn.addEventListener('click', () => { const textarea = document.querySelector('textarea[placeholder*="메시지 보내기"]'); if (!textarea) return alert("입력창을 찾을 수 없습니다."); const { text, replace } = getProcessedBody(item.body, 'key_' + index); const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set; setter?.call(textarea, replace ? text : textarea.value + '\n' + text); textarea.dispatchEvent(new Event('input', { bubbles: true })); }); topRow.appendChild(quickBtn); const editBtn = document.createElement('button'); editBtn.textContent = '✏️'; editBtn.style = 'padding: 2px 6px; background: #ff9800; color: white; border: none; border-radius: 4px;'; let isEditing = false; let textarea = null; editBtn.addEventListener('click', () => { if (isEditing) { item.body = textarea.value.trim(); isEditing = false; save(); renderList(); } else { isEditing = true; body.textContent = ''; textarea = document.createElement('textarea'); textarea.value = item.body; textarea.rows = item.body.split('\n').length; textarea.style = 'width: 100%; resize: vertical; padding: 4px; background: #111; color: white; border: 1px solid #555; border-radius: 4px;'; body.appendChild(textarea); textarea.focus(); } }); topRow.appendChild(editBtn); const del = document.createElement('button'); del.textContent = '❌'; del.style = 'padding: 2px 6px; background: #c62828; color: white; border: none; border-radius: 4px;'; del.addEventListener('click', () => { memoList.splice(index, 1); if (selectedIndex === index) selectedIndex = -1; else if (selectedIndex > index) selectedIndex--; save(); renderList(); savePanelState(); }); topRow.appendChild(del); const body = document.createElement('div'); body.textContent = (item.body || '').split('\n')[0]; body.style = 'color: #aaa; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;'; row.appendChild(topRow); row.appendChild(body); content.appendChild(row); }); }; renderList(); if (isMinimized) toggleMinimize(); })();