סקריפט לניווט, קיפול והרחבת קטעי קוד ב-ChatGPT ו-AI Studio
-
בתור אחד שעובד די הרבה עם בכתיבת קוד עם הבינה מלאכותית (כלומר, היא כותבת בשבילי...) עצבן אותי מאוד כל פעם שהייתי מקבל קטע קוד ארוך הייתי צריך לגלול את כולו כדי להגיע לסוף. אז בס"ד פניתי (איך לא?
) לעזרת הבינה והיא יצרה לי סקריפט לטמפרמונקי שמוסיף כפתורי קיפול והרחבת בלוקי הקוד וגם אפשרות גלילה לסוף/תחילת הקוד. (יצוין, שהבעיה קיימת רק ב-ChatGPT ו-AI Studio שכן בגרוק כבר קיימים כפתורים כאלו ובקלוד הקוד נפתח בקנבס צדדי כך שזה מיותר.)
אז מה יש לנו?
-
כפתורים שימושיים לכל בלוק קוד:
- מעל כל קטע קוד יופיעו שני כפתורים:
- כפתור "קפל": לחיצה עליו תעלים את הקוד (כדי לחסוך מקום במסך), ולחיצה נוספת תחזיר אותו ותשנה את הכפתור ל"הרחב".
- כפתור חץ למטה (↓): לחיצה עליו תקפיץ אתכם ישר לסוף של אותו קטע קוד.
- מתחת לכל קטע קוד יופיע כפתור נוסף:
- כפתור חץ למעלה (↑): לחיצה עליו תקפיץ אתכם חזרה להתחלה של אותו קטע קוד.
- מעל כל קטע קוד יופיעו שני כפתורים:
-
קיצורי דרך במקלדת (עם Ctrl):
- נמאס לכם להשתמש בעכבר לכל דבר? סתם שתדעו אפשר גם עם המקלדת...
Ctrl
+ חץ ימינה אוCtrl
+ חץ שמאלה: יקפל או ירחיב את בלוק הקוד שהכי קרוב למרכז המסך שלכם, או את האחרון שקיפלתם/הרחבתם.Ctrl
+ חץ למטה:- אם אתם בתחילת בלוק קוד, זה יקפיץ אתכם לסוף שלו.
- אם אתם כבר בסוף בלוק, זה יקפיץ אתכם לסוף של הבלוק הבא אחריו (אם יש).
Ctrl
+ חץ למעלה:- אם אתם בסוף בלוק קוד, זה יקפיץ אתכם להתחלה שלו.
- אם אתם כבר בהתחלה של בלוק, זה יקפיץ אתכם להתחלה של הבלוק הקודם (אם יש).
- נמאס לכם להשתמש בעכבר לכל דבר? סתם שתדעו אפשר גם עם המקלדת...
-
עובד גם כשנוסף קוד חדש:
- הסקריפט חכם מספיק כדי לזהות מתי ChatGPT או AI Studio מוסיפים קטעי קוד חדשים לשיחה, והוא ידאג להוסיף את הכפתורים והיכולות האלה גם אליהם באופן אוטומטי.
בשורה התחתונה:
הסקריפט הזה פשוט מוסיף כמה כפתורים קטנים ונוחים כדי להתמודד טוב יותר עם קטעי קוד ארוכים, לקפל אותם כשצריך, ולנווט ביניהם במהירות עם העכבר או המקלדת. לא מבין איך הסתדרתי בלעדיו עד היום...// ==UserScript== // @name Collapsible Code Blocks with RTL, Dynamic Tooltips & Keyboard Shortcuts // @namespace http://tampermonkey.net/ // @version 1.9.6 // @description Adds RTL code tools with smart tooltips, keyboard shortcuts (Ctrl+Arrows for navigation/toggle), styled UI and scroll features for ChatGPT and AI Studio. Further refined navigation with multiple collapsed blocks. // @author לאצי&AI // @match https://chatgpt.com/* // @match https://aistudio.google.com/* // @grant none // @icon https://img.icons8.com/ios-filled/50/programming.png // ==/UserScript== (function () { 'use strict'; // ... (CSS and functions: enhanceCodeBlocks, getAllEnhancedBlocksInDOM, findClosestBlock, updateNavigationStateIfNeeded - remain the same as 1.9.4 / 1.9.5) ... const style = document.createElement('style'); style.textContent = ` .code-toolbar-top, .code-toolbar-bottom { display: flex; gap: 6px; justify-content: flex-start; direction: rtl; text-align: right; margin: 6px 0; } .code-btn { background-color: #444; color: #fff; border: none; padding: 4px 8px; font-size: 12px; border-radius: 5px; cursor: pointer; transition: background-color 0.2s ease; position: relative; } .code-btn:hover { background-color: #666; } .code-btn::after { content: attr(data-tooltip); position: absolute; bottom: 100%; right: 0; background-color: #222; color: #fff; padding: 4px 6px; border-radius: 4px; white-space: nowrap; font-size: 11px; opacity: 0; pointer-events: none; transition: opacity 0.2s ease; margin-bottom: 6px; z-index: 9999; text-align: right; } .tooltip-below::after { bottom: auto; top: 100%; margin-bottom: 0; margin-top: 6px; } .code-btn:hover::after { opacity: 1; } `; document.head.appendChild(style); const enhancedBlockRegistry = []; let currentNavigationState = { blockInfo: null, targetPoint: 'top' }; let lastInteractedBlockForToggle = null; let lastManuallyCollapsedBlock = null; function enhanceCodeBlocks() { const blocks = document.querySelectorAll('pre'); blocks.forEach((block) => { if (block.dataset.enhanced) return; const toggleBtn = document.createElement('button'); toggleBtn.textContent = 'קפל'; toggleBtn.className = 'code-btn tooltip-below'; toggleBtn.setAttribute('data-tooltip', 'הצג או הסתר את בלוק הקוד'); const toBottomBtn = document.createElement('button'); toBottomBtn.textContent = '↓'; toBottomBtn.className = 'code-btn tooltip-below'; toBottomBtn.setAttribute('data-tooltip', 'גלול לסוף בלוק הקוד'); const toTopBtn = document.createElement('button'); toTopBtn.textContent = '↑'; toTopBtn.className = 'code-btn'; toTopBtn.setAttribute('data-tooltip', 'גלול לתחילת בלוק הקוד'); const toolbarTop = document.createElement('div'); toolbarTop.className = 'code-toolbar-top'; toolbarTop.appendChild(toggleBtn); toolbarTop.appendChild(toBottomBtn); const toolbarBottom = document.createElement('div'); toolbarBottom.className = 'code-toolbar-bottom'; toolbarBottom.appendChild(toTopBtn); const blockInfo = { preElement: block, toolbarTopElement: toolbarTop, toolbarBottomElement: toolbarBottom, toggleButtonElement: toggleBtn, id: `enhanced-block-${enhancedBlockRegistry.length}` }; toggleBtn.addEventListener('click', () => { const visible = block.style.display !== 'none'; block.style.display = visible ? 'none' : 'block'; toggleBtn.textContent = visible ? 'הרחב' : 'קפל'; if (!visible) { // Block was just expanded if (lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.id === blockInfo.id) { lastManuallyCollapsedBlock = null; } } // Note: lastManuallyCollapsedBlock is primarily set via keydown for intent. }); toBottomBtn.addEventListener('click', () => { toolbarBottom.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); toTopBtn.addEventListener('click', () => { toolbarTop.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); if (block.parentNode) { block.parentNode.insertBefore(toolbarTop, block); block.parentNode.insertBefore(toolbarBottom, block.nextSibling); } enhancedBlockRegistry.push(blockInfo); block.dataset.enhanced = "true"; block.dataset.blockId = blockInfo.id; }); } function getAllEnhancedBlocksInDOM() { // Ensure this list is always up-to-date with the DOM order // This might need re-evaluation if blocks can be reordered in the DOM by other means const livePreElements = Array.from(document.querySelectorAll('pre[data-enhanced="true"]')); return livePreElements.map(pre => { const foundInfo = enhancedBlockRegistry.find(info => info.id === pre.dataset.blockId); return foundInfo; // Should always find it if registry is in sync }).filter(Boolean); // Filter out any undefined if a pre was removed weirdly } function findClosestBlock(blocksToSearch, includeCollapsed = false) { if (!blocksToSearch || blocksToSearch.length === 0) return null; let closestBlockInfo = null; let minDistance = Infinity; const viewportCenterY = window.innerHeight / 2; blocksToSearch.forEach(blockInfo => { if(!blockInfo) return; // Safety check const preElement = blockInfo.preElement; if (!includeCollapsed && preElement.style.display === 'none') return; const elementForLocation = preElement.style.display === 'none' ? blockInfo.toolbarTopElement : preElement; if (!elementForLocation || !document.body.contains(elementForLocation)) return; const rect = elementForLocation.getBoundingClientRect(); if (rect.bottom > 0 && rect.top < window.innerHeight) { const elementCenterY = rect.top + rect.height / 2; const distance = Math.abs(elementCenterY - viewportCenterY); if (distance < minDistance) { minDistance = distance; closestBlockInfo = blockInfo; } } }); return closestBlockInfo; } function updateNavigationStateIfNeeded(purpose = 'navigation') { const allDomBlocks = getAllEnhancedBlocksInDOM(); // This now reflects current DOM order if (allDomBlocks.length === 0) { currentNavigationState.blockInfo = null; if (lastInteractedBlockForToggle && !document.body.contains(lastInteractedBlockForToggle.preElement)) lastInteractedBlockForToggle = null; if (lastManuallyCollapsedBlock && !document.body.contains(lastManuallyCollapsedBlock.preElement)) lastManuallyCollapsedBlock = null; return { allBlocks: [], visibleBlocks: [] }; } let blockToFocus = null; let visibleBlocks = allDomBlocks.filter(b => b.preElement.style.display !== 'none'); if (purpose === 'toggle') { if (lastInteractedBlockForToggle && document.body.contains(lastInteractedBlockForToggle.preElement)) { blockToFocus = allDomBlocks.find(b => b.id === lastInteractedBlockForToggle.id); } if (!blockToFocus) { blockToFocus = findClosestBlock(allDomBlocks, true); } } else { let currentNavBlockId = currentNavigationState.blockInfo?.id; const currentBlockFromAll = currentNavBlockId ? allDomBlocks.find(b => b.id === currentNavBlockId) : null; if (currentBlockFromAll && currentBlockFromAll.preElement.style.display !== 'none') { blockToFocus = currentBlockFromAll; } else if (lastManuallyCollapsedBlock && document.body.contains(lastManuallyCollapsedBlock.preElement) && lastManuallyCollapsedBlock.preElement.style.display === 'none' && currentBlockFromAll && currentBlockFromAll.id === lastManuallyCollapsedBlock.id) { blockToFocus = currentBlockFromAll; } else { blockToFocus = findClosestBlock(visibleBlocks, false); } if (!blockToFocus && visibleBlocks.length > 0) { blockToFocus = visibleBlocks[0]; } else if (!blockToFocus && lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.preElement.style.display === 'none') { blockToFocus = allDomBlocks.find(b => b.id === lastManuallyCollapsedBlock.id); } if (blockToFocus && blockToFocus.preElement.style.display !== 'none' && (!currentNavigationState.blockInfo || blockToFocus.id !== currentNavigationState.blockInfo.id) ) { const rect = blockToFocus.preElement.getBoundingClientRect(); currentNavigationState.targetPoint = (rect.height > 0 && Math.abs(rect.top - window.innerHeight / 2) < Math.abs(rect.bottom - window.innerHeight / 2)) ? 'top' : 'bottom'; } else if (!blockToFocus) { currentNavigationState.blockInfo = null; } } currentNavigationState.blockInfo = blockToFocus; return { allBlocks: allDomBlocks, visibleBlocks: visibleBlocks }; } document.addEventListener('keydown', function (event) { if (!event.ctrlKey) return; const { allBlocks, visibleBlocks } = updateNavigationStateIfNeeded(event.key === 'ArrowLeft' || event.key === 'ArrowRight' ? 'toggle' : 'navigation'); let currentBlockForNav = currentNavigationState.blockInfo; switch (event.key) { case 'ArrowLeft': case 'ArrowRight': event.preventDefault(); if (currentBlockForNav) { currentBlockForNav.toggleButtonElement.click(); lastInteractedBlockForToggle = currentBlockForNav; if (currentBlockForNav.preElement.style.display === 'none') { lastManuallyCollapsedBlock = currentBlockForNav; } else { if (lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.id === currentBlockForNav.id) { lastManuallyCollapsedBlock = null; } } } break; case 'ArrowDown': event.preventDefault(); if (!currentBlockForNav) { currentBlockForNav = visibleBlocks.length > 0 ? visibleBlocks[0] : (lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.preElement.style.display === 'none' ? allBlocks.find(b => b.id === lastManuallyCollapsedBlock.id) : null); if (!currentBlockForNav) break; currentNavigationState.blockInfo = currentBlockForNav; currentNavigationState.targetPoint = (currentBlockForNav.preElement.style.display === 'none') ? 'top' : 'top'; } if (currentBlockForNav.preElement.style.display !== 'none' && currentNavigationState.targetPoint === 'top') { currentBlockForNav.toolbarBottomElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentNavigationState.targetPoint = 'bottom'; } else { const currentBlockIndexInFullList = allBlocks.findIndex(b => b.id === currentBlockForNav.id); let nextTargetBlock = null; for (let i = currentBlockIndexInFullList + 1; i < allBlocks.length; i++) { const potentialTarget = allBlocks[i]; if (lastManuallyCollapsedBlock && potentialTarget.id === lastManuallyCollapsedBlock.id && potentialTarget.preElement.style.display === 'none') { nextTargetBlock = potentialTarget; break; } if (potentialTarget.preElement.style.display !== 'none') { nextTargetBlock = potentialTarget; break; } } if (nextTargetBlock) { currentNavigationState.blockInfo = nextTargetBlock; const targetElementForScroll = nextTargetBlock.preElement.style.display === 'none' ? nextTargetBlock.toolbarTopElement : nextTargetBlock.toolbarBottomElement; const scrollTargetPoint = nextTargetBlock.preElement.style.display === 'none' ? 'top' : 'bottom'; targetElementForScroll.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentNavigationState.targetPoint = scrollTargetPoint; if (nextTargetBlock.preElement.style.display === 'none') { lastInteractedBlockForToggle = nextTargetBlock; } else { lastInteractedBlockForToggle = null; } } } break; case 'ArrowUp': event.preventDefault(); if (!currentBlockForNav) { currentBlockForNav = visibleBlocks.length > 0 ? visibleBlocks[visibleBlocks.length - 1] : (lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.preElement.style.display === 'none' ? allBlocks.find(b => b.id === lastManuallyCollapsedBlock.id) : null); if (!currentBlockForNav) break; currentNavigationState.blockInfo = currentBlockForNav; currentNavigationState.targetPoint = (currentBlockForNav.preElement.style.display === 'none') ? 'bottom' : 'bottom'; } if (currentBlockForNav.preElement.style.display !== 'none' && currentNavigationState.targetPoint === 'bottom') { currentBlockForNav.toolbarTopElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentNavigationState.targetPoint = 'top'; } else { const currentBlockIndexInFullList = allBlocks.findIndex(b => b.id === currentBlockForNav.id); let prevTargetBlock = null; for (let i = currentBlockIndexInFullList - 1; i >= 0; i--) { const potentialTarget = allBlocks[i]; if (lastManuallyCollapsedBlock && potentialTarget.id === lastManuallyCollapsedBlock.id && potentialTarget.preElement.style.display === 'none') { prevTargetBlock = potentialTarget; break; } if (potentialTarget.preElement.style.display !== 'none') { prevTargetBlock = potentialTarget; break; } } if (prevTargetBlock) { currentNavigationState.blockInfo = prevTargetBlock; prevTargetBlock.toolbarTopElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentNavigationState.targetPoint = 'top'; if (prevTargetBlock.preElement.style.display === 'none') { lastInteractedBlockForToggle = prevTargetBlock; } else { lastInteractedBlockForToggle = null; } } } break; } }); const observer = new MutationObserver(() => { enhanceCodeBlocks(); // Potentially re-validate currentNavigationState if DOM changes significantly // For now, rely on keydown to re-evaluate. }); observer.observe(document.body, { childList: true, subtree: true }); enhanceCodeBlocks(); updateNavigationStateIfNeeded('navigation'); })();
קיים באג קטן בניווט בין קטעי הקוד באמצעות מקשי הקיצור, כשחלק מהבלוקים מקופלים הוא עלול לדלג עליהם.
כמו"כ אשמח אם מישהו יוכל לשפר את הקוד כך שהכפתורים יהיו דביקים ויופיעו כל הזמן לאורך כל הגלילה בבלוק הקוד הנוכחי, כך שיהיו זמינים כל הזמן. כמובן שבמקרה כזה כל הכפתורים צריכים להופיע בשורה אחת ולא בשני שורות נפרדות כמו עכשיו. -
-
בתור אחד שעובד די הרבה עם בכתיבת קוד עם הבינה מלאכותית (כלומר, היא כותבת בשבילי...) עצבן אותי מאוד כל פעם שהייתי מקבל קטע קוד ארוך הייתי צריך לגלול את כולו כדי להגיע לסוף. אז בס"ד פניתי (איך לא?
) לעזרת הבינה והיא יצרה לי סקריפט לטמפרמונקי שמוסיף כפתורי קיפול והרחבת בלוקי הקוד וגם אפשרות גלילה לסוף/תחילת הקוד. (יצוין, שהבעיה קיימת רק ב-ChatGPT ו-AI Studio שכן בגרוק כבר קיימים כפתורים כאלו ובקלוד הקוד נפתח בקנבס צדדי כך שזה מיותר.)
אז מה יש לנו?
-
כפתורים שימושיים לכל בלוק קוד:
- מעל כל קטע קוד יופיעו שני כפתורים:
- כפתור "קפל": לחיצה עליו תעלים את הקוד (כדי לחסוך מקום במסך), ולחיצה נוספת תחזיר אותו ותשנה את הכפתור ל"הרחב".
- כפתור חץ למטה (↓): לחיצה עליו תקפיץ אתכם ישר לסוף של אותו קטע קוד.
- מתחת לכל קטע קוד יופיע כפתור נוסף:
- כפתור חץ למעלה (↑): לחיצה עליו תקפיץ אתכם חזרה להתחלה של אותו קטע קוד.
- מעל כל קטע קוד יופיעו שני כפתורים:
-
קיצורי דרך במקלדת (עם Ctrl):
- נמאס לכם להשתמש בעכבר לכל דבר? סתם שתדעו אפשר גם עם המקלדת...
Ctrl
+ חץ ימינה אוCtrl
+ חץ שמאלה: יקפל או ירחיב את בלוק הקוד שהכי קרוב למרכז המסך שלכם, או את האחרון שקיפלתם/הרחבתם.Ctrl
+ חץ למטה:- אם אתם בתחילת בלוק קוד, זה יקפיץ אתכם לסוף שלו.
- אם אתם כבר בסוף בלוק, זה יקפיץ אתכם לסוף של הבלוק הבא אחריו (אם יש).
Ctrl
+ חץ למעלה:- אם אתם בסוף בלוק קוד, זה יקפיץ אתכם להתחלה שלו.
- אם אתם כבר בהתחלה של בלוק, זה יקפיץ אתכם להתחלה של הבלוק הקודם (אם יש).
- נמאס לכם להשתמש בעכבר לכל דבר? סתם שתדעו אפשר גם עם המקלדת...
-
עובד גם כשנוסף קוד חדש:
- הסקריפט חכם מספיק כדי לזהות מתי ChatGPT או AI Studio מוסיפים קטעי קוד חדשים לשיחה, והוא ידאג להוסיף את הכפתורים והיכולות האלה גם אליהם באופן אוטומטי.
בשורה התחתונה:
הסקריפט הזה פשוט מוסיף כמה כפתורים קטנים ונוחים כדי להתמודד טוב יותר עם קטעי קוד ארוכים, לקפל אותם כשצריך, ולנווט ביניהם במהירות עם העכבר או המקלדת. לא מבין איך הסתדרתי בלעדיו עד היום...// ==UserScript== // @name Collapsible Code Blocks with RTL, Dynamic Tooltips & Keyboard Shortcuts // @namespace http://tampermonkey.net/ // @version 1.9.6 // @description Adds RTL code tools with smart tooltips, keyboard shortcuts (Ctrl+Arrows for navigation/toggle), styled UI and scroll features for ChatGPT and AI Studio. Further refined navigation with multiple collapsed blocks. // @author לאצי&AI // @match https://chatgpt.com/* // @match https://aistudio.google.com/* // @grant none // @icon https://img.icons8.com/ios-filled/50/programming.png // ==/UserScript== (function () { 'use strict'; // ... (CSS and functions: enhanceCodeBlocks, getAllEnhancedBlocksInDOM, findClosestBlock, updateNavigationStateIfNeeded - remain the same as 1.9.4 / 1.9.5) ... const style = document.createElement('style'); style.textContent = ` .code-toolbar-top, .code-toolbar-bottom { display: flex; gap: 6px; justify-content: flex-start; direction: rtl; text-align: right; margin: 6px 0; } .code-btn { background-color: #444; color: #fff; border: none; padding: 4px 8px; font-size: 12px; border-radius: 5px; cursor: pointer; transition: background-color 0.2s ease; position: relative; } .code-btn:hover { background-color: #666; } .code-btn::after { content: attr(data-tooltip); position: absolute; bottom: 100%; right: 0; background-color: #222; color: #fff; padding: 4px 6px; border-radius: 4px; white-space: nowrap; font-size: 11px; opacity: 0; pointer-events: none; transition: opacity 0.2s ease; margin-bottom: 6px; z-index: 9999; text-align: right; } .tooltip-below::after { bottom: auto; top: 100%; margin-bottom: 0; margin-top: 6px; } .code-btn:hover::after { opacity: 1; } `; document.head.appendChild(style); const enhancedBlockRegistry = []; let currentNavigationState = { blockInfo: null, targetPoint: 'top' }; let lastInteractedBlockForToggle = null; let lastManuallyCollapsedBlock = null; function enhanceCodeBlocks() { const blocks = document.querySelectorAll('pre'); blocks.forEach((block) => { if (block.dataset.enhanced) return; const toggleBtn = document.createElement('button'); toggleBtn.textContent = 'קפל'; toggleBtn.className = 'code-btn tooltip-below'; toggleBtn.setAttribute('data-tooltip', 'הצג או הסתר את בלוק הקוד'); const toBottomBtn = document.createElement('button'); toBottomBtn.textContent = '↓'; toBottomBtn.className = 'code-btn tooltip-below'; toBottomBtn.setAttribute('data-tooltip', 'גלול לסוף בלוק הקוד'); const toTopBtn = document.createElement('button'); toTopBtn.textContent = '↑'; toTopBtn.className = 'code-btn'; toTopBtn.setAttribute('data-tooltip', 'גלול לתחילת בלוק הקוד'); const toolbarTop = document.createElement('div'); toolbarTop.className = 'code-toolbar-top'; toolbarTop.appendChild(toggleBtn); toolbarTop.appendChild(toBottomBtn); const toolbarBottom = document.createElement('div'); toolbarBottom.className = 'code-toolbar-bottom'; toolbarBottom.appendChild(toTopBtn); const blockInfo = { preElement: block, toolbarTopElement: toolbarTop, toolbarBottomElement: toolbarBottom, toggleButtonElement: toggleBtn, id: `enhanced-block-${enhancedBlockRegistry.length}` }; toggleBtn.addEventListener('click', () => { const visible = block.style.display !== 'none'; block.style.display = visible ? 'none' : 'block'; toggleBtn.textContent = visible ? 'הרחב' : 'קפל'; if (!visible) { // Block was just expanded if (lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.id === blockInfo.id) { lastManuallyCollapsedBlock = null; } } // Note: lastManuallyCollapsedBlock is primarily set via keydown for intent. }); toBottomBtn.addEventListener('click', () => { toolbarBottom.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); toTopBtn.addEventListener('click', () => { toolbarTop.scrollIntoView({ behavior: 'smooth', block: 'center' }); }); if (block.parentNode) { block.parentNode.insertBefore(toolbarTop, block); block.parentNode.insertBefore(toolbarBottom, block.nextSibling); } enhancedBlockRegistry.push(blockInfo); block.dataset.enhanced = "true"; block.dataset.blockId = blockInfo.id; }); } function getAllEnhancedBlocksInDOM() { // Ensure this list is always up-to-date with the DOM order // This might need re-evaluation if blocks can be reordered in the DOM by other means const livePreElements = Array.from(document.querySelectorAll('pre[data-enhanced="true"]')); return livePreElements.map(pre => { const foundInfo = enhancedBlockRegistry.find(info => info.id === pre.dataset.blockId); return foundInfo; // Should always find it if registry is in sync }).filter(Boolean); // Filter out any undefined if a pre was removed weirdly } function findClosestBlock(blocksToSearch, includeCollapsed = false) { if (!blocksToSearch || blocksToSearch.length === 0) return null; let closestBlockInfo = null; let minDistance = Infinity; const viewportCenterY = window.innerHeight / 2; blocksToSearch.forEach(blockInfo => { if(!blockInfo) return; // Safety check const preElement = blockInfo.preElement; if (!includeCollapsed && preElement.style.display === 'none') return; const elementForLocation = preElement.style.display === 'none' ? blockInfo.toolbarTopElement : preElement; if (!elementForLocation || !document.body.contains(elementForLocation)) return; const rect = elementForLocation.getBoundingClientRect(); if (rect.bottom > 0 && rect.top < window.innerHeight) { const elementCenterY = rect.top + rect.height / 2; const distance = Math.abs(elementCenterY - viewportCenterY); if (distance < minDistance) { minDistance = distance; closestBlockInfo = blockInfo; } } }); return closestBlockInfo; } function updateNavigationStateIfNeeded(purpose = 'navigation') { const allDomBlocks = getAllEnhancedBlocksInDOM(); // This now reflects current DOM order if (allDomBlocks.length === 0) { currentNavigationState.blockInfo = null; if (lastInteractedBlockForToggle && !document.body.contains(lastInteractedBlockForToggle.preElement)) lastInteractedBlockForToggle = null; if (lastManuallyCollapsedBlock && !document.body.contains(lastManuallyCollapsedBlock.preElement)) lastManuallyCollapsedBlock = null; return { allBlocks: [], visibleBlocks: [] }; } let blockToFocus = null; let visibleBlocks = allDomBlocks.filter(b => b.preElement.style.display !== 'none'); if (purpose === 'toggle') { if (lastInteractedBlockForToggle && document.body.contains(lastInteractedBlockForToggle.preElement)) { blockToFocus = allDomBlocks.find(b => b.id === lastInteractedBlockForToggle.id); } if (!blockToFocus) { blockToFocus = findClosestBlock(allDomBlocks, true); } } else { let currentNavBlockId = currentNavigationState.blockInfo?.id; const currentBlockFromAll = currentNavBlockId ? allDomBlocks.find(b => b.id === currentNavBlockId) : null; if (currentBlockFromAll && currentBlockFromAll.preElement.style.display !== 'none') { blockToFocus = currentBlockFromAll; } else if (lastManuallyCollapsedBlock && document.body.contains(lastManuallyCollapsedBlock.preElement) && lastManuallyCollapsedBlock.preElement.style.display === 'none' && currentBlockFromAll && currentBlockFromAll.id === lastManuallyCollapsedBlock.id) { blockToFocus = currentBlockFromAll; } else { blockToFocus = findClosestBlock(visibleBlocks, false); } if (!blockToFocus && visibleBlocks.length > 0) { blockToFocus = visibleBlocks[0]; } else if (!blockToFocus && lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.preElement.style.display === 'none') { blockToFocus = allDomBlocks.find(b => b.id === lastManuallyCollapsedBlock.id); } if (blockToFocus && blockToFocus.preElement.style.display !== 'none' && (!currentNavigationState.blockInfo || blockToFocus.id !== currentNavigationState.blockInfo.id) ) { const rect = blockToFocus.preElement.getBoundingClientRect(); currentNavigationState.targetPoint = (rect.height > 0 && Math.abs(rect.top - window.innerHeight / 2) < Math.abs(rect.bottom - window.innerHeight / 2)) ? 'top' : 'bottom'; } else if (!blockToFocus) { currentNavigationState.blockInfo = null; } } currentNavigationState.blockInfo = blockToFocus; return { allBlocks: allDomBlocks, visibleBlocks: visibleBlocks }; } document.addEventListener('keydown', function (event) { if (!event.ctrlKey) return; const { allBlocks, visibleBlocks } = updateNavigationStateIfNeeded(event.key === 'ArrowLeft' || event.key === 'ArrowRight' ? 'toggle' : 'navigation'); let currentBlockForNav = currentNavigationState.blockInfo; switch (event.key) { case 'ArrowLeft': case 'ArrowRight': event.preventDefault(); if (currentBlockForNav) { currentBlockForNav.toggleButtonElement.click(); lastInteractedBlockForToggle = currentBlockForNav; if (currentBlockForNav.preElement.style.display === 'none') { lastManuallyCollapsedBlock = currentBlockForNav; } else { if (lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.id === currentBlockForNav.id) { lastManuallyCollapsedBlock = null; } } } break; case 'ArrowDown': event.preventDefault(); if (!currentBlockForNav) { currentBlockForNav = visibleBlocks.length > 0 ? visibleBlocks[0] : (lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.preElement.style.display === 'none' ? allBlocks.find(b => b.id === lastManuallyCollapsedBlock.id) : null); if (!currentBlockForNav) break; currentNavigationState.blockInfo = currentBlockForNav; currentNavigationState.targetPoint = (currentBlockForNav.preElement.style.display === 'none') ? 'top' : 'top'; } if (currentBlockForNav.preElement.style.display !== 'none' && currentNavigationState.targetPoint === 'top') { currentBlockForNav.toolbarBottomElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentNavigationState.targetPoint = 'bottom'; } else { const currentBlockIndexInFullList = allBlocks.findIndex(b => b.id === currentBlockForNav.id); let nextTargetBlock = null; for (let i = currentBlockIndexInFullList + 1; i < allBlocks.length; i++) { const potentialTarget = allBlocks[i]; if (lastManuallyCollapsedBlock && potentialTarget.id === lastManuallyCollapsedBlock.id && potentialTarget.preElement.style.display === 'none') { nextTargetBlock = potentialTarget; break; } if (potentialTarget.preElement.style.display !== 'none') { nextTargetBlock = potentialTarget; break; } } if (nextTargetBlock) { currentNavigationState.blockInfo = nextTargetBlock; const targetElementForScroll = nextTargetBlock.preElement.style.display === 'none' ? nextTargetBlock.toolbarTopElement : nextTargetBlock.toolbarBottomElement; const scrollTargetPoint = nextTargetBlock.preElement.style.display === 'none' ? 'top' : 'bottom'; targetElementForScroll.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentNavigationState.targetPoint = scrollTargetPoint; if (nextTargetBlock.preElement.style.display === 'none') { lastInteractedBlockForToggle = nextTargetBlock; } else { lastInteractedBlockForToggle = null; } } } break; case 'ArrowUp': event.preventDefault(); if (!currentBlockForNav) { currentBlockForNav = visibleBlocks.length > 0 ? visibleBlocks[visibleBlocks.length - 1] : (lastManuallyCollapsedBlock && lastManuallyCollapsedBlock.preElement.style.display === 'none' ? allBlocks.find(b => b.id === lastManuallyCollapsedBlock.id) : null); if (!currentBlockForNav) break; currentNavigationState.blockInfo = currentBlockForNav; currentNavigationState.targetPoint = (currentBlockForNav.preElement.style.display === 'none') ? 'bottom' : 'bottom'; } if (currentBlockForNav.preElement.style.display !== 'none' && currentNavigationState.targetPoint === 'bottom') { currentBlockForNav.toolbarTopElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentNavigationState.targetPoint = 'top'; } else { const currentBlockIndexInFullList = allBlocks.findIndex(b => b.id === currentBlockForNav.id); let prevTargetBlock = null; for (let i = currentBlockIndexInFullList - 1; i >= 0; i--) { const potentialTarget = allBlocks[i]; if (lastManuallyCollapsedBlock && potentialTarget.id === lastManuallyCollapsedBlock.id && potentialTarget.preElement.style.display === 'none') { prevTargetBlock = potentialTarget; break; } if (potentialTarget.preElement.style.display !== 'none') { prevTargetBlock = potentialTarget; break; } } if (prevTargetBlock) { currentNavigationState.blockInfo = prevTargetBlock; prevTargetBlock.toolbarTopElement.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentNavigationState.targetPoint = 'top'; if (prevTargetBlock.preElement.style.display === 'none') { lastInteractedBlockForToggle = prevTargetBlock; } else { lastInteractedBlockForToggle = null; } } } break; } }); const observer = new MutationObserver(() => { enhanceCodeBlocks(); // Potentially re-validate currentNavigationState if DOM changes significantly // For now, rely on keydown to re-evaluate. }); observer.observe(document.body, { childList: true, subtree: true }); enhanceCodeBlocks(); updateNavigationStateIfNeeded('navigation'); })();
קיים באג קטן בניווט בין קטעי הקוד באמצעות מקשי הקיצור, כשחלק מהבלוקים מקופלים הוא עלול לדלג עליהם.
כמו"כ אשמח אם מישהו יוכל לשפר את הקוד כך שהכפתורים יהיו דביקים ויופיעו כל הזמן לאורך כל הגלילה בבלוק הקוד הנוכחי, כך שיהיו זמינים כל הזמן. כמובן שבמקרה כזה כל הכפתורים צריכים להופיע בשורה אחת ולא בשני שורות נפרדות כמו עכשיו.@לאצי גם ב GPT יש אפשרות לקנבס
-
-
@לאצי גם ב GPT יש אפשרות לקנבס
@יום-חדש-מתחיל ידוע לי, אבל אני לא אוהב להשתמש אתו כי הוא מחליף לי כל קוד בקוד הקודם. לדוג' אם אני צריך שני קודים שונים אז את הקוד השני הוא כותב ע"ג אותו קנבס של הקוד הראשון ומוחק את הראשון. מה שקצת מסבך את העניינים.
-
@יום-חדש-מתחיל ידוע לי, אבל אני לא אוהב להשתמש אתו כי הוא מחליף לי כל קוד בקוד הקודם. לדוג' אם אני צריך שני קודים שונים אז את הקוד השני הוא כותב ע"ג אותו קנבס של הקוד הראשון ומוחק את הראשון. מה שקצת מסבך את העניינים.
@לאצי כתב בסקריפט לניווט, קיפול והרחבת קטעי קוד ב-ChatGPT ו-AI Studio:
@יום-חדש-מתחיל ידוע לי, אבל אני לא אוהב להשתמש אתו כי הוא מחליף לי כל קוד בקוד הקודם. לדוג' אם אני צריך שני קודים שונים אז את הקוד השני הוא כותב ע"ג אותו קנבס של הקוד הראשון ומוחק את הראשון. מה שקצת מסבך את העניינים.
יש אפשרות לחזור לגירסאות קודמות.
כמובן זה עדיין מסבך מאוד, רק אני מקווה שאתה מודע לאפשרות הזו.
-
@לאצי כתב בסקריפט לניווט, קיפול והרחבת קטעי קוד ב-ChatGPT ו-AI Studio:
@יום-חדש-מתחיל ידוע לי, אבל אני לא אוהב להשתמש אתו כי הוא מחליף לי כל קוד בקוד הקודם. לדוג' אם אני צריך שני קודים שונים אז את הקוד השני הוא כותב ע"ג אותו קנבס של הקוד הראשון ומוחק את הראשון. מה שקצת מסבך את העניינים.
יש אפשרות לחזור לגירסאות קודמות.
כמובן זה עדיין מסבך מאוד, רק אני מקווה שאתה מודע לאפשרות הזו.
@ע-ה-דכו-ע כן, מודע. אבל כדבריך;
@ע-ה-דכו-ע כתב בסקריפט לניווט, קיפול והרחבת קטעי קוד ב-ChatGPT ו-AI Studio:
זה עדיין מסבך מאוד