decembre

GM - Fork - Universal Code Splitter for AI by _XPK_ - Fork for DuckDuckGo IA

Aug 27th, 2026
84
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Universal Code Splitter for AI by _XPK_ - Fork for DuckDuckGo IA
  3. // @description  Split code blocks for AI with draggable and minimizable panel
  4. // @namespace    tm-code-splitter
  5. // @version      1.2
  6. // @author       Pascal
  7. // @match        https://duck.ai/chat*
  8. // @run-at       document-start
  9. // @grant        GM_setClipboard
  10. // @grant        GM_getValue
  11. // @grant        GM_setValue
  12. // @license      MIT
  13. // ==/UserScript==
  14.  
  15. // === Fork of GM "Diviseur de Code Universel pour IA" by _XPK_ ===
  16. // === https://greasyfork.org/fr/scripts/561642-universal-code-splitter-for-ai ===
  17.  
  18.  
  19. (function () {
  20.     'use strict';
  21.  
  22.     const COLORS = {
  23.         bg: '#1e1e1e',
  24.         border: '#333',
  25.         accent: '#0e639c',
  26.         success: '#1e3a1e',
  27.         text: '#d4d4d4',
  28.         blue: '#4fc1ff',
  29.         orange: '#ce9178'
  30.     };
  31.  
  32.     let isVisible = true;
  33.     let isMinimized = GM_getValue('panelMinimized', false);
  34.  
  35.     const panel = document.createElement('div');
  36.  
  37.     function formatSize(bytes) {
  38.         return bytes < 1024
  39.             ? `${bytes} B`
  40.             : `${(bytes / 1024).toFixed(1)} KB`;
  41.     }
  42.  
  43.     function estimateTokens(text) {
  44.         return Math.ceil(
  45.             new TextEncoder().encode(text).length / 3.2
  46.         );
  47.     }
  48.  
  49.     function cleanOldMarkers(text) {
  50.         return text
  51.             .replace(
  52.                 /\[PART \d+ OF \d+\][\s\S]*?--- START PART \d+ ---\r?\n/g,
  53.                 ''
  54.             )
  55.             .replace(/\r?\n--- END PART \d+ ---/g, '');
  56.     }
  57.  
  58.     async function copyToClipboard(text, index, total) {
  59.         const header =
  60.             `[PART ${index + 1} OF ${total}]\n` +
  61.             `[NOTE: This is part of a larger file. Please confirm with "OK" only. ` +
  62.             `Do not analyze until Part ${total} is received.]\n` +
  63.             `--- START PART ${index + 1} ---\n`;
  64.  
  65.         const footer = `\n--- END PART ${index + 1} ---`;
  66.         const fullText = header + text + footer;
  67.  
  68.         try {
  69.             await navigator.clipboard.writeText(fullText);
  70.         } catch (error) {
  71.             GM_setClipboard(fullText);
  72.         }
  73.     }
  74.  
  75.     function saveCurrentPosition() {
  76.         const rect = panel.getBoundingClientRect();
  77.  
  78.         GM_setValue('previousPanelX', rect.left);
  79.         GM_setValue('previousPanelY', rect.top);
  80.     }
  81.  
  82.     function restorePreviousPosition() {
  83.         const savedX = GM_getValue('previousPanelX', null);
  84.         const savedY = GM_getValue('previousPanelY', null);
  85.  
  86.         panel.style.right = 'auto';
  87.         panel.style.bottom = 'auto';
  88.  
  89.         if (savedX !== null && savedY !== null) {
  90.             const rect = panel.getBoundingClientRect();
  91.  
  92.             const safeX = Math.max(
  93.                 0,
  94.                 Math.min(savedX, window.innerWidth - rect.width)
  95.             );
  96.  
  97.             const safeY = Math.max(
  98.                 0,
  99.                 Math.min(savedY, window.innerHeight - rect.height)
  100.             );
  101.  
  102.             panel.style.left = `${safeX}px`;
  103.             panel.style.top = `${safeY}px`;
  104.         } else {
  105.             panel.style.left = 'auto';
  106.             panel.style.top = '15px';
  107.             panel.style.right = '15px';
  108.         }
  109.     }
  110.  
  111.     function moveToMinimizedPosition() {
  112.         panel.style.left = 'auto';
  113.         panel.style.top = 'auto';
  114.         panel.style.right = '15px';
  115.         panel.style.bottom = '15px';
  116.     }
  117.  
  118.     function setMinimized(minimized) {
  119.         const content = panel.querySelector('#sc-content');
  120.         const minimizeButton = panel.querySelector('#sc-minimize');
  121.  
  122.         if (!content || !minimizeButton) return;
  123.  
  124.         if (minimized) {
  125.             // Save the maximized position before moving to bottom-right.
  126.             saveCurrentPosition();
  127.  
  128.             isMinimized = true;
  129.             content.style.display = 'none';
  130.             minimizeButton.textContent = '+';
  131.             minimizeButton.title = 'Restore';
  132.  
  133.             panel.style.width = '180px';
  134.             moveToMinimizedPosition();
  135.         } else {
  136.             isMinimized = false;
  137.             content.style.display = 'block';
  138.             minimizeButton.textContent = '−';
  139.             minimizeButton.title = 'Minimize';
  140.  
  141.             panel.style.width = '300px';
  142.             restorePreviousPosition();
  143.         }
  144.  
  145.         GM_setValue('panelMinimized', isMinimized);
  146.     }
  147.  
  148.     function makeDraggable(element) {
  149.         const header = element.querySelector('#sc-header');
  150.  
  151.         if (!header) return;
  152.  
  153.         let isDragging = false;
  154.         let offsetX = 0;
  155.         let offsetY = 0;
  156.  
  157.         const savedX = GM_getValue('previousPanelX', null);
  158.         const savedY = GM_getValue('previousPanelY', null);
  159.  
  160.         if (isMinimized) {
  161.             moveToMinimizedPosition();
  162.         } else if (savedX !== null && savedY !== null) {
  163.             element.style.left = `${savedX}px`;
  164.             element.style.top = `${savedY}px`;
  165.             element.style.right = 'auto';
  166.             element.style.bottom = 'auto';
  167.         } else {
  168.             element.style.left = 'auto';
  169.             element.style.top = '15px';
  170.             element.style.right = '15px';
  171.             element.style.bottom = 'auto';
  172.         }
  173.  
  174.         header.addEventListener('mousedown', (event) => {
  175.             if (event.target.closest('button')) return;
  176.  
  177.             const rect = element.getBoundingClientRect();
  178.  
  179.             isDragging = true;
  180.             offsetX = event.clientX - rect.left;
  181.             offsetY = event.clientY - rect.top;
  182.  
  183.             element.style.right = 'auto';
  184.             element.style.bottom = 'auto';
  185.  
  186.             element.classList.add('dragging');
  187.  
  188.             event.preventDefault();
  189.         });
  190.  
  191.         document.addEventListener('mousemove', (event) => {
  192.             if (!isDragging) return;
  193.  
  194.             const rect = element.getBoundingClientRect();
  195.  
  196.             let newX = event.clientX - offsetX;
  197.             let newY = event.clientY - offsetY;
  198.  
  199.             newX = Math.max(
  200.                 0,
  201.                 Math.min(newX, window.innerWidth - rect.width)
  202.             );
  203.  
  204.             newY = Math.max(
  205.                 0,
  206.                 Math.min(newY, window.innerHeight - rect.height)
  207.             );
  208.  
  209.             element.style.left = `${newX}px`;
  210.             element.style.top = `${newY}px`;
  211.         });
  212.  
  213.         document.addEventListener('mouseup', () => {
  214.             if (!isDragging) return;
  215.  
  216.             isDragging = false;
  217.             element.classList.remove('dragging');
  218.  
  219.             const rect = element.getBoundingClientRect();
  220.  
  221.             // Save the current position only while maximized.
  222.             // The minimized bottom-right position should not overwrite it.
  223.             if (!isMinimized) {
  224.                 GM_setValue('previousPanelX', rect.left);
  225.                 GM_setValue('previousPanelY', rect.top);
  226.             }
  227.         });
  228.     }
  229.  
  230.     function initUI() {
  231.         if (document.getElementById('sc-panel-root')) return;
  232.  
  233.         panel.id = 'sc-panel-root';
  234.  
  235.         Object.assign(panel.style, {
  236.             position: 'fixed',
  237.             left: 'auto',
  238.             top: '15px',
  239.             right: '15px',
  240.             bottom: 'auto',
  241.             zIndex: '2147483647',
  242.             background: COLORS.bg,
  243.             color: COLORS.text,
  244.             padding: '15px',
  245.             borderRadius: '10px',
  246.             fontFamily: 'Segoe UI, sans-serif',
  247.             fontSize: '13px',
  248.             width: '300px',
  249.             boxSizing: 'border-box',
  250.             boxShadow: '0 8px 24px rgba(0, 0, 0, 0.5)',
  251.             border: `1px solid ${COLORS.border}`,
  252.             display: isVisible ? 'block' : 'none',
  253.             userSelect: 'none'
  254.         });
  255.  
  256.         panel.innerHTML = `
  257.             <div id="sc-header" style="
  258.                display:flex;
  259.                justify-content:space-between;
  260.                align-items:center;
  261.                margin-bottom:12px;
  262.                border-bottom:1px solid ${COLORS.border};
  263.                padding-bottom:8px;
  264.                cursor:move;
  265.            ">
  266.                 <span style="
  267.                    color:${COLORS.blue};
  268.                    font-weight:bold;
  269.                ">
  270.                     Code Splitter v1.2
  271.                 </span>
  272.  
  273.                 <div style="
  274.                    display:flex;
  275.                    align-items:center;
  276.                    gap:8px;
  277.                ">
  278.                     <span style="
  279.                        font-size:10px;
  280.                        color:#666;
  281.                    ">
  282.                         Alt+S
  283.                     </span>
  284.  
  285.                     <button id="sc-minimize" title="Minimize" style="
  286.                        background:none;
  287.                        color:#aaa;
  288.                        border:none;
  289.                        cursor:pointer;
  290.                        font-size:17px;
  291.                        line-height:1;
  292.                        padding:0 3px;
  293.                    ">−</button>
  294.                 </div>
  295.             </div>
  296.  
  297.             <div id="sc-content">
  298.                 <div id="sc-info" style="
  299.                    background:#252526;
  300.                    padding:10px;
  301.                    border-radius:4px;
  302.                    margin-bottom:12px;
  303.                    font-size:11px;
  304.                    display:none;
  305.                    border-left:3px solid ${COLORS.blue};
  306.                ">
  307.                     <div style="
  308.                        display:flex;
  309.                        justify-content:space-between;
  310.                    ">
  311.                         <span>Total Size:</span>
  312.                         <b id="info-size">-</b>
  313.                     </div>
  314.  
  315.                     <div style="
  316.                        display:flex;
  317.                        justify-content:space-between;
  318.                    ">
  319.                         <span>Est. Tokens:</span>
  320.                         <b id="info-tokens">-</b>
  321.                     </div>
  322.                 </div>
  323.  
  324.                 <div style="margin-bottom:10px;">
  325.     <label style="
  326.        display:flex;
  327.        justify-content:space-between;
  328.        margin-bottom:5px;
  329.        font-size:11px;
  330.    ">
  331.         <span>Character Limit:</span>
  332.  
  333.         <span id="limit-display" style="
  334.            color:${COLORS.orange};
  335.        ">
  336.             16,000
  337.         </span>
  338.     </label>
  339.  
  340.     <input
  341.         id="sc-limit"
  342.         type="range"
  343.         min="1000"
  344.         max="100000"
  345.         step="1000"
  346.         value="16000"
  347.         style="
  348.            width:100%;
  349.            cursor:pointer;
  350.        "
  351.     >
  352. </div>
  353.  
  354.                 <button id="sc-read" style="
  355.                    width:100%;
  356.                    background:${COLORS.accent};
  357.                    color:white;
  358.                    border:none;
  359.                    padding:10px;
  360.                    border-radius:4px;
  361.                    cursor:pointer;
  362.                    font-weight:bold;
  363.                ">
  364.                     Read Clipboard
  365.                 </button>
  366.  
  367.                 <div id="sc-buttons" style="
  368.                    display:grid;
  369.                    grid-template-columns:repeat(4, 1fr);
  370.                    gap:6px;
  371.                    margin-top:10px;
  372.                    max-height:180px;
  373.                    overflow-y:auto;
  374.                "></div>
  375.  
  376.                 <div id="sc-status" style="
  377.                    margin-top:12px;
  378.                    font-size:11px;
  379.                    color:#888;
  380.                    text-align:center;
  381.                ">
  382.                     Ready for input.
  383.                 </div>
  384.             </div>
  385.         `;
  386.  
  387.         document.body.appendChild(panel);
  388.  
  389.         const btnRead = panel.querySelector('#sc-read');
  390.         const btnMinimize = panel.querySelector('#sc-minimize');
  391.         const limitSlider = panel.querySelector('#sc-limit');
  392.         const limitDisplay = panel.querySelector('#limit-display');
  393.  
  394. limitSlider.addEventListener('input', (event) => {
  395.     limitDisplay.textContent =
  396.         Number(event.target.value).toLocaleString();
  397. });
  398.  
  399.         makeDraggable(panel);
  400.         setMinimized(isMinimized);
  401.  
  402.         btnMinimize.addEventListener('click', (event) => {
  403.             event.stopPropagation();
  404.             setMinimized(!isMinimized);
  405.         });
  406.  
  407.         limitSlider.addEventListener('input', (event) => {
  408.             limitDisplay.textContent =
  409.                 Number(event.target.value).toLocaleString();
  410.         });
  411.  
  412.         btnRead.addEventListener('click', async () => {
  413.             btnRead.disabled = true;
  414.             btnRead.style.opacity = '0.5';
  415.  
  416.             const status = panel.querySelector('#sc-status');
  417.             const container = panel.querySelector('#sc-buttons');
  418.             const info = panel.querySelector('#sc-info');
  419.             const infoSize = panel.querySelector('#info-size');
  420.             const infoTokens = panel.querySelector('#info-tokens');
  421.  
  422.             try {
  423.                 let text = await navigator.clipboard.readText();
  424.  
  425.                 if (!text || text.trim() === '') {
  426.                     throw new Error('Clipboard is empty');
  427.                 }
  428.  
  429.                 text = cleanOldMarkers(text);
  430.  
  431.                 const encoder = new TextEncoder();
  432.                 const totalBytes = encoder.encode(text).length;
  433.  
  434.                 info.style.display = 'block';
  435.                 infoSize.textContent = formatSize(totalBytes);
  436.                 infoTokens.textContent =
  437.                     estimateTokens(text).toLocaleString();
  438.  
  439.                 const maxCharacters = parseInt(
  440.                     limitSlider.value,
  441.                     10
  442.                 );
  443.  
  444.                 const lines = text.split(/\r?\n/);
  445.                 const parts = [];
  446.  
  447.                 let currentChunk = [];
  448.                 let currentCharacters = 0;
  449.  
  450.                 for (const line of lines) {
  451.                     const lineCharacters = line.length + 1;
  452.  
  453.                     if (
  454.                         currentChunk.length > 0 &&
  455.                         currentCharacters + lineCharacters > maxCharacters
  456.                     ) {
  457.                         parts.push(currentChunk.join('\n'));
  458.                         currentChunk = [];
  459.                         currentCharacters = 0;
  460.                     }
  461.  
  462.                     currentChunk.push(line);
  463.                     currentCharacters += lineCharacters;
  464.                 }
  465.  
  466.                 if (currentChunk.length > 0) {
  467.                     parts.push(currentChunk.join('\n'));
  468.                 }
  469.  
  470.                 container.innerHTML = '';
  471.  
  472.                 parts.forEach((content, index) => {
  473.                     const button = document.createElement('button');
  474.  
  475.                     button.textContent = index + 1;
  476.  
  477.                     Object.assign(button.style, {
  478.                         background: '#333',
  479.                         color: '#ccc',
  480.                         border: '1px solid #444',
  481.                         padding: '8px 0',
  482.                         borderRadius: '4px',
  483.                         cursor: 'pointer'
  484.                     });
  485.  
  486.                     button.addEventListener('click', async () => {
  487.                         await copyToClipboard(
  488.                             content,
  489.                             index,
  490.                             parts.length
  491.                         );
  492.  
  493.                         container
  494.                             .querySelectorAll('button')
  495.                             .forEach((item) => {
  496.                                 item.style.background = '#333';
  497.                                 item.style.borderColor = '#444';
  498.                             });
  499.  
  500.                         button.style.background = COLORS.success;
  501.                         button.style.borderColor = '#4ec9b0';
  502.  
  503.                         status.textContent =
  504.                             `Part ${index + 1}/${parts.length} copied.`;
  505.                     });
  506.  
  507.                     container.appendChild(button);
  508.                 });
  509.  
  510.                 status.textContent =
  511.                     `Generated ${parts.length} part${
  512.                         parts.length === 1 ? '' : 's'
  513.                     }.`;
  514.             } catch (error) {
  515.                 status.textContent = `Error: ${error.message}`;
  516.             } finally {
  517.                 btnRead.disabled = false;
  518.                 btnRead.style.opacity = '1';
  519.             }
  520.         });
  521.     }
  522.  
  523.     window.addEventListener('keydown', (event) => {
  524.         if (
  525.             event.altKey &&
  526.             event.key.toLowerCase() === 's' &&
  527.             !event.target.matches(
  528.                 'input, textarea, [contenteditable="true"]'
  529.             )
  530.         ) {
  531.             event.preventDefault();
  532.  
  533.             isVisible = !isVisible;
  534.             panel.style.display = isVisible ? 'block' : 'none';
  535.         }
  536.     });
  537.  
  538.     const start = setInterval(() => {
  539.         if (document.body) {
  540.             clearInterval(start);
  541.             initUI();
  542.         }
  543.     }, 200);
  544. })();
  545.  
Advertisement
Comments
  • User was banned
Add Comment
Please, Sign In to add comment