Sudokupad copy cells

Add a button to sudokupad.app allowing to copy the current cell selection to clipboard

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name        Sudokupad copy cells
// @namespace   Violentmonkey Scripts
// @match       https://sudokupad.app/*
// @grant       GM_setClipboard
// @version     1.1
// @author      Giuseppe Stelluto
// @license     GPL3
// @description Add a button to sudokupad.app allowing to copy the current cell selection to clipboard
// @require https://cdn.jsdelivr.net/npm/@violentmonkey/dom@2
// ==/UserScript==

function copyCells() {
  let grid = Framework.app.grid.cells;

  let allCells = grid.reduce((acc, row2) => acc.concat(row2));
  let highlightedCells = allCells.filter((cell) => cell.highlighted);
  let highlightedRows = highlightedCells.map((cell) => cell.row);
  let highlightedCols = highlightedCells.map((cell) => cell.col);
  // check if the selection lives in multiple rows/cols and is therefore not copyable
  if (highlightedRows.some((row) => row !== highlightedRows[0])
      && highlightedCols.some((col) => col !== highlightedCols[0])) {
    Framework.showAlert("The selection is invalid or empty: only select digits from one row or column.");
  }
  let digits = highlightedCells.map((cell) => cell.value);
  if (digits.length > 0) {
    GM_setClipboard(digits.join(''));
    Framework.showAlert(digits.join('') + " copied to clipboard!")
  }
}

function main() {
  const controlsSmall = document.querySelector('#controls .controls-app.controls-small');

  let button = document.createElement('button');

  const iconWrapper = document.createElement('div');
  iconWrapper.className = 'icon';
  const icon = document.createElement('span');
  icon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>content-copy</title><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" /></svg>'
  iconWrapper.appendChild(icon);
  button.appendChild(iconWrapper);

  // button.innerText = "C"
  button.addEventListener('click', (e) => copyCells());
  button.addEventListener('touchend', (e) => copyCells());
  controlsSmall.appendChild(button);
}

const disconnect = VM.observe(document.body, () => {
  // Find the target node
  const controls = document.querySelector('#controls');

  if (controls) {
    try {
      main()
    } catch {
      console.log("some error")
    } finally {
      return true;
    }
  }
});