보조바퀴(크랙 기억력 보조)

크랙 기억력 보조용 메모장

// ==UserScript==
// @name         보조바퀴(크랙 기억력 보조)
// @namespace    https://crack.wrtn.ai
// @version      1.9
// @description  크랙 기억력 보조용 메모장
// @author       바보륍부이
// @license      MIT
// @match        https://crack.wrtn.ai/u/*/c/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  let textKey = `wrtn_custom_text_${location.pathname}`;
  const panelKey = 'wrtn_custom_panel';

  const savedPanelState = JSON.parse(localStorage.getItem(panelKey) || '{}');
  let isMinimized = savedPanelState.minimized || false;

  const panel = document.createElement('div');
  panel.style.position = 'fixed';
  panel.style.top = savedPanelState.top || '100px';
  panel.style.left = savedPanelState.left || '10px';
  panel.style.width = savedPanelState.width || '600px';
  panel.style.height = savedPanelState.height || '500px';
  panel.style.backgroundColor = '#1a1a1a';
  panel.style.padding = '0';
  panel.style.borderRadius = '10px';
  panel.style.zIndex = '9999';
  panel.style.color = '#fff';
  panel.style.fontFamily = 'sans-serif';
  panel.style.boxShadow = '0 4px 10px rgba(0,0,0,0.3)';
  panel.style.resize = 'both';
  panel.style.overflow = 'hidden';
  panel.style.display = 'flex';
  panel.style.flexDirection = 'column';

  const dragHeader = document.createElement('div');
  dragHeader.style.backgroundColor = '#333';
  dragHeader.style.padding = '8px 10px';
  dragHeader.style.borderRadius = '10px 10px 0 0';
  dragHeader.style.cursor = 'move';
  dragHeader.style.userSelect = 'none';
  dragHeader.style.fontSize = '12px';
  dragHeader.style.fontWeight = 'bold';
  dragHeader.style.borderBottom = '1px solid #444';
  dragHeader.style.flexShrink = '0';
  dragHeader.textContent = '📝 보조바퀴-캐챗 기억력 보조 메모장 (Ctrl+엔터로 빠른 입력 가능)';

  const helpIcon = document.createElement('div');
  helpIcon.textContent = '❓';
  helpIcon.title = '요약은 캐즘 등 다른 프로그램을 활용해주세요\n직접 메모해도 됩니다\n다른 방으로 이동하는 등 URL이 바뀌면 갱신 버튼을 눌러주세요\n최상단을 더블클릭하면 최소화 됩니다';
  helpIcon.style.position = 'absolute';
  helpIcon.style.top = '5px';
  helpIcon.style.right = '10px';
  helpIcon.style.cursor = 'help';
  helpIcon.style.fontSize = '14px';
  dragHeader.appendChild(helpIcon);

  const content = document.createElement('div');
  content.style.padding = '10px';
  content.style.flex = '1';
  content.style.overflow = 'auto';
  content.style.display = 'flex';
  content.style.flexDirection = 'column';

  const input = document.createElement('textarea');
  input.rows = 50;
  input.placeholder = '추가할 내용 입력';
  input.style.width = '100%';
  input.style.flex = '1';
  input.style.padding = '8px';
  input.style.borderRadius = '5px';
  input.style.border = '1px solid #444';
  input.style.backgroundColor = '#2a2a2a';
  input.style.color = '#fff';
  input.style.resize = 'none';
  input.style.minHeight = '200px';

  const controlRow = document.createElement('div');
  controlRow.style.display = 'flex';
  controlRow.style.alignItems = 'center';
  controlRow.style.gap = '8px';
  controlRow.style.padding = '10px';
  controlRow.style.backgroundColor = '#222';
  controlRow.style.borderTop = '1px solid #444';
  controlRow.style.borderRadius = '0 0 10px 10px';
  controlRow.style.flexShrink = '0';

  const button = document.createElement('button');
  button.textContent = '텍스트 추가';
  button.style.flex = '1';
  button.style.padding = '8px';
  button.style.border = 'none';
  button.style.borderRadius = '5px';
  button.style.backgroundColor = '#4caf50';
  button.style.color = '#fff';
  button.style.cursor = 'pointer';

  const refreshButton = document.createElement('button');
  refreshButton.textContent = '🔄 갱신';
  refreshButton.style.flex = '1';
  refreshButton.style.padding = '8px';
  refreshButton.style.border = 'none';
  refreshButton.style.borderRadius = '5px';
  refreshButton.style.backgroundColor = '#555';
  refreshButton.style.color = '#fff';
  refreshButton.style.cursor = 'pointer';

  const countDisplay = document.createElement('div');
  countDisplay.style.marginLeft = 'auto';
  countDisplay.style.fontSize = '12px';

  const loadTextFromStorage = () => {
    textKey = `wrtn_custom_text_${location.pathname}`;
    const savedText = localStorage.getItem(textKey);
    input.value = savedText || '';
    countDisplay.textContent = `총 ${input.value.length}글자`;
  };

  loadTextFromStorage();

  refreshButton.addEventListener('click', loadTextFromStorage);

  const toggleMinimize = () => {
    isMinimized = !isMinimized;
    if (isMinimized) {
      content.style.display = 'none';
      controlRow.style.display = 'none';
      panel.style.height = 'auto';
      dragHeader.style.borderRadius = '10px';
      dragHeader.style.borderBottom = 'none';
    } else {
      content.style.display = 'flex';
      controlRow.style.display = 'flex';
      panel.style.height = savedPanelState.height || '500px';
      dragHeader.style.borderRadius = '10px 10px 0 0';
      dragHeader.style.borderBottom = '1px solid #444';
    }
    const currentState = JSON.parse(localStorage.getItem(panelKey) || '{}');
    currentState.minimized = isMinimized;
    localStorage.setItem(panelKey, JSON.stringify(currentState));
  };

  if (isMinimized) toggleMinimize();
  dragHeader.addEventListener('dblclick', toggleMinimize);

  const appendToTextarea = () => {
    const newText = input.value.trim();
    if (!newText) return;
    const textarea = document.querySelector('textarea[placeholder*="메시지 보내기"]');
    if (!textarea) {
      alert("textarea를 찾을 수 없습니다.");
      return;
    }
    const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value')?.set;
    setter?.call(textarea, textarea.value + '\n' + newText);
    textarea.dispatchEvent(new Event('input', { bubbles: true }));
    localStorage.setItem(textKey, input.value);
  };

  window.addEventListener('keydown', (e) => {
    if (e.key === 'Enter' && e.ctrlKey) appendToTextarea();
  });

  button.addEventListener('click', appendToTextarea);
  input.addEventListener('input', () => {
    localStorage.setItem(textKey, input.value);
    countDisplay.textContent = `총 ${input.value.length}글자`;
  });

  let offsetX, offsetY, dragging = false;
  dragHeader.addEventListener('mousedown', (e) => {
    dragging = true;
    offsetX = e.clientX - panel.offsetLeft;
    offsetY = e.clientY - panel.offsetTop;
    document.body.style.userSelect = 'none';
    e.preventDefault();
  });

  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 = '';
    const currentState = JSON.parse(localStorage.getItem(panelKey) || '{}');
    currentState.top = panel.style.top;
    currentState.left = panel.style.left;
    currentState.width = panel.style.width;
    currentState.height = panel.style.height;
    localStorage.setItem(panelKey, JSON.stringify(currentState));
  });

  new ResizeObserver(() => {
    const rect = panel.getBoundingClientRect();
    const currentState = JSON.parse(localStorage.getItem(panelKey) || '{}');
    currentState.top = `${rect.top}px`;
    currentState.left = `${rect.left}px`;
    currentState.width = `${rect.width}px`;
    currentState.height = `${rect.height}px`;
    localStorage.setItem(panelKey, JSON.stringify(currentState));
  }).observe(panel);

  controlRow.appendChild(button);
  controlRow.appendChild(refreshButton);
  controlRow.appendChild(countDisplay);
  content.appendChild(input);
  panel.appendChild(dragHeader);
  panel.appendChild(content);
  panel.appendChild(controlRow);
  document.body.appendChild(panel);
})();