Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Universal Code Splitter for AI by _XPK_ - Fork for DuckDuckGo IA
- // @description Split code blocks for AI with draggable and minimizable panel
- // @namespace tm-code-splitter
- // @version 1.2
- // @author Pascal
- // @match https://duck.ai/chat*
- // @run-at document-start
- // @grant GM_setClipboard
- // @grant GM_getValue
- // @grant GM_setValue
- // @license MIT
- // ==/UserScript==
- // === Fork of GM "Diviseur de Code Universel pour IA" by _XPK_ ===
- // === https://greasyfork.org/fr/scripts/561642-universal-code-splitter-for-ai ===
- (function () {
- 'use strict';
- const COLORS = {
- bg: '#1e1e1e',
- border: '#333',
- accent: '#0e639c',
- success: '#1e3a1e',
- text: '#d4d4d4',
- blue: '#4fc1ff',
- orange: '#ce9178'
- };
- let isVisible = true;
- let isMinimized = GM_getValue('panelMinimized', false);
- const panel = document.createElement('div');
- function formatSize(bytes) {
- return bytes < 1024
- ? `${bytes} B`
- : `${(bytes / 1024).toFixed(1)} KB`;
- }
- function estimateTokens(text) {
- return Math.ceil(
- new TextEncoder().encode(text).length / 3.2
- );
- }
- function cleanOldMarkers(text) {
- return text
- .replace(
- /\[PART \d+ OF \d+\][\s\S]*?--- START PART \d+ ---\r?\n/g,
- ''
- )
- .replace(/\r?\n--- END PART \d+ ---/g, '');
- }
- async function copyToClipboard(text, index, total) {
- const header =
- `[PART ${index + 1} OF ${total}]\n` +
- `[NOTE: This is part of a larger file. Please confirm with "OK" only. ` +
- `Do not analyze until Part ${total} is received.]\n` +
- `--- START PART ${index + 1} ---\n`;
- const footer = `\n--- END PART ${index + 1} ---`;
- const fullText = header + text + footer;
- try {
- await navigator.clipboard.writeText(fullText);
- } catch (error) {
- GM_setClipboard(fullText);
- }
- }
- function saveCurrentPosition() {
- const rect = panel.getBoundingClientRect();
- GM_setValue('previousPanelX', rect.left);
- GM_setValue('previousPanelY', rect.top);
- }
- function restorePreviousPosition() {
- const savedX = GM_getValue('previousPanelX', null);
- const savedY = GM_getValue('previousPanelY', null);
- panel.style.right = 'auto';
- panel.style.bottom = 'auto';
- if (savedX !== null && savedY !== null) {
- const rect = panel.getBoundingClientRect();
- const safeX = Math.max(
- 0,
- Math.min(savedX, window.innerWidth - rect.width)
- );
- const safeY = Math.max(
- 0,
- Math.min(savedY, window.innerHeight - rect.height)
- );
- panel.style.left = `${safeX}px`;
- panel.style.top = `${safeY}px`;
- } else {
- panel.style.left = 'auto';
- panel.style.top = '15px';
- panel.style.right = '15px';
- }
- }
- function moveToMinimizedPosition() {
- panel.style.left = 'auto';
- panel.style.top = 'auto';
- panel.style.right = '15px';
- panel.style.bottom = '15px';
- }
- function setMinimized(minimized) {
- const content = panel.querySelector('#sc-content');
- const minimizeButton = panel.querySelector('#sc-minimize');
- if (!content || !minimizeButton) return;
- if (minimized) {
- // Save the maximized position before moving to bottom-right.
- saveCurrentPosition();
- isMinimized = true;
- content.style.display = 'none';
- minimizeButton.textContent = '+';
- minimizeButton.title = 'Restore';
- panel.style.width = '180px';
- moveToMinimizedPosition();
- } else {
- isMinimized = false;
- content.style.display = 'block';
- minimizeButton.textContent = '−';
- minimizeButton.title = 'Minimize';
- panel.style.width = '300px';
- restorePreviousPosition();
- }
- GM_setValue('panelMinimized', isMinimized);
- }
- function makeDraggable(element) {
- const header = element.querySelector('#sc-header');
- if (!header) return;
- let isDragging = false;
- let offsetX = 0;
- let offsetY = 0;
- const savedX = GM_getValue('previousPanelX', null);
- const savedY = GM_getValue('previousPanelY', null);
- if (isMinimized) {
- moveToMinimizedPosition();
- } else if (savedX !== null && savedY !== null) {
- element.style.left = `${savedX}px`;
- element.style.top = `${savedY}px`;
- element.style.right = 'auto';
- element.style.bottom = 'auto';
- } else {
- element.style.left = 'auto';
- element.style.top = '15px';
- element.style.right = '15px';
- element.style.bottom = 'auto';
- }
- header.addEventListener('mousedown', (event) => {
- if (event.target.closest('button')) return;
- const rect = element.getBoundingClientRect();
- isDragging = true;
- offsetX = event.clientX - rect.left;
- offsetY = event.clientY - rect.top;
- element.style.right = 'auto';
- element.style.bottom = 'auto';
- element.classList.add('dragging');
- event.preventDefault();
- });
- document.addEventListener('mousemove', (event) => {
- if (!isDragging) return;
- const rect = element.getBoundingClientRect();
- let newX = event.clientX - offsetX;
- let newY = event.clientY - offsetY;
- newX = Math.max(
- 0,
- Math.min(newX, window.innerWidth - rect.width)
- );
- newY = Math.max(
- 0,
- Math.min(newY, window.innerHeight - rect.height)
- );
- element.style.left = `${newX}px`;
- element.style.top = `${newY}px`;
- });
- document.addEventListener('mouseup', () => {
- if (!isDragging) return;
- isDragging = false;
- element.classList.remove('dragging');
- const rect = element.getBoundingClientRect();
- // Save the current position only while maximized.
- // The minimized bottom-right position should not overwrite it.
- if (!isMinimized) {
- GM_setValue('previousPanelX', rect.left);
- GM_setValue('previousPanelY', rect.top);
- }
- });
- }
- function initUI() {
- if (document.getElementById('sc-panel-root')) return;
- panel.id = 'sc-panel-root';
- Object.assign(panel.style, {
- position: 'fixed',
- left: 'auto',
- top: '15px',
- right: '15px',
- bottom: 'auto',
- zIndex: '2147483647',
- background: COLORS.bg,
- color: COLORS.text,
- padding: '15px',
- borderRadius: '10px',
- fontFamily: 'Segoe UI, sans-serif',
- fontSize: '13px',
- width: '300px',
- boxSizing: 'border-box',
- boxShadow: '0 8px 24px rgba(0, 0, 0, 0.5)',
- border: `1px solid ${COLORS.border}`,
- display: isVisible ? 'block' : 'none',
- userSelect: 'none'
- });
- panel.innerHTML = `
- <div id="sc-header" style="
- display:flex;
- justify-content:space-between;
- align-items:center;
- margin-bottom:12px;
- border-bottom:1px solid ${COLORS.border};
- padding-bottom:8px;
- cursor:move;
- ">
- <span style="
- color:${COLORS.blue};
- font-weight:bold;
- ">
- Code Splitter v1.2
- </span>
- <div style="
- display:flex;
- align-items:center;
- gap:8px;
- ">
- <span style="
- font-size:10px;
- color:#666;
- ">
- Alt+S
- </span>
- <button id="sc-minimize" title="Minimize" style="
- background:none;
- color:#aaa;
- border:none;
- cursor:pointer;
- font-size:17px;
- line-height:1;
- padding:0 3px;
- ">−</button>
- </div>
- </div>
- <div id="sc-content">
- <div id="sc-info" style="
- background:#252526;
- padding:10px;
- border-radius:4px;
- margin-bottom:12px;
- font-size:11px;
- display:none;
- border-left:3px solid ${COLORS.blue};
- ">
- <div style="
- display:flex;
- justify-content:space-between;
- ">
- <span>Total Size:</span>
- <b id="info-size">-</b>
- </div>
- <div style="
- display:flex;
- justify-content:space-between;
- ">
- <span>Est. Tokens:</span>
- <b id="info-tokens">-</b>
- </div>
- </div>
- <div style="margin-bottom:10px;">
- <label style="
- display:flex;
- justify-content:space-between;
- margin-bottom:5px;
- font-size:11px;
- ">
- <span>Character Limit:</span>
- <span id="limit-display" style="
- color:${COLORS.orange};
- ">
- 16,000
- </span>
- </label>
- <input
- id="sc-limit"
- type="range"
- min="1000"
- max="100000"
- step="1000"
- value="16000"
- style="
- width:100%;
- cursor:pointer;
- "
- >
- </div>
- <button id="sc-read" style="
- width:100%;
- background:${COLORS.accent};
- color:white;
- border:none;
- padding:10px;
- border-radius:4px;
- cursor:pointer;
- font-weight:bold;
- ">
- Read Clipboard
- </button>
- <div id="sc-buttons" style="
- display:grid;
- grid-template-columns:repeat(4, 1fr);
- gap:6px;
- margin-top:10px;
- max-height:180px;
- overflow-y:auto;
- "></div>
- <div id="sc-status" style="
- margin-top:12px;
- font-size:11px;
- color:#888;
- text-align:center;
- ">
- Ready for input.
- </div>
- </div>
- `;
- document.body.appendChild(panel);
- const btnRead = panel.querySelector('#sc-read');
- const btnMinimize = panel.querySelector('#sc-minimize');
- const limitSlider = panel.querySelector('#sc-limit');
- const limitDisplay = panel.querySelector('#limit-display');
- limitSlider.addEventListener('input', (event) => {
- limitDisplay.textContent =
- Number(event.target.value).toLocaleString();
- });
- makeDraggable(panel);
- setMinimized(isMinimized);
- btnMinimize.addEventListener('click', (event) => {
- event.stopPropagation();
- setMinimized(!isMinimized);
- });
- limitSlider.addEventListener('input', (event) => {
- limitDisplay.textContent =
- Number(event.target.value).toLocaleString();
- });
- btnRead.addEventListener('click', async () => {
- btnRead.disabled = true;
- btnRead.style.opacity = '0.5';
- const status = panel.querySelector('#sc-status');
- const container = panel.querySelector('#sc-buttons');
- const info = panel.querySelector('#sc-info');
- const infoSize = panel.querySelector('#info-size');
- const infoTokens = panel.querySelector('#info-tokens');
- try {
- let text = await navigator.clipboard.readText();
- if (!text || text.trim() === '') {
- throw new Error('Clipboard is empty');
- }
- text = cleanOldMarkers(text);
- const encoder = new TextEncoder();
- const totalBytes = encoder.encode(text).length;
- info.style.display = 'block';
- infoSize.textContent = formatSize(totalBytes);
- infoTokens.textContent =
- estimateTokens(text).toLocaleString();
- const maxCharacters = parseInt(
- limitSlider.value,
- 10
- );
- const lines = text.split(/\r?\n/);
- const parts = [];
- let currentChunk = [];
- let currentCharacters = 0;
- for (const line of lines) {
- const lineCharacters = line.length + 1;
- if (
- currentChunk.length > 0 &&
- currentCharacters + lineCharacters > maxCharacters
- ) {
- parts.push(currentChunk.join('\n'));
- currentChunk = [];
- currentCharacters = 0;
- }
- currentChunk.push(line);
- currentCharacters += lineCharacters;
- }
- if (currentChunk.length > 0) {
- parts.push(currentChunk.join('\n'));
- }
- container.innerHTML = '';
- parts.forEach((content, index) => {
- const button = document.createElement('button');
- button.textContent = index + 1;
- Object.assign(button.style, {
- background: '#333',
- color: '#ccc',
- border: '1px solid #444',
- padding: '8px 0',
- borderRadius: '4px',
- cursor: 'pointer'
- });
- button.addEventListener('click', async () => {
- await copyToClipboard(
- content,
- index,
- parts.length
- );
- container
- .querySelectorAll('button')
- .forEach((item) => {
- item.style.background = '#333';
- item.style.borderColor = '#444';
- });
- button.style.background = COLORS.success;
- button.style.borderColor = '#4ec9b0';
- status.textContent =
- `Part ${index + 1}/${parts.length} copied.`;
- });
- container.appendChild(button);
- });
- status.textContent =
- `Generated ${parts.length} part${
- parts.length === 1 ? '' : 's'
- }.`;
- } catch (error) {
- status.textContent = `Error: ${error.message}`;
- } finally {
- btnRead.disabled = false;
- btnRead.style.opacity = '1';
- }
- });
- }
- window.addEventListener('keydown', (event) => {
- if (
- event.altKey &&
- event.key.toLowerCase() === 's' &&
- !event.target.matches(
- 'input, textarea, [contenteditable="true"]'
- )
- ) {
- event.preventDefault();
- isVisible = !isVisible;
- panel.style.display = isVisible ? 'block' : 'none';
- }
- });
- const start = setInterval(() => {
- if (document.body) {
- clearInterval(start);
- initUI();
- }
- }, 200);
- })();
Advertisement