Changeset 3496250
- Timestamp:
- 04/01/2026 07:21:18 AM (17 hours ago)
- Location:
- livedraft-search-replace
- Files:
-
- 10 added
- 3 edited
-
tags/1.6.3 (added)
-
tags/1.6.3/languages (added)
-
tags/1.6.3/languages/livedraft-search-replace-ja.mo (added)
-
tags/1.6.3/languages/livedraft-search-replace-ja.po (added)
-
tags/1.6.3/languages/livedraft-search-replace.pot (added)
-
tags/1.6.3/livedraft-search-replace.js (added)
-
tags/1.6.3/livedraft-search-replace.php (added)
-
tags/1.6.3/readme.txt (added)
-
tags/1.6.3/screenshot-1.png (added)
-
tags/1.6.3/screenshot-2.png (added)
-
trunk/livedraft-search-replace.js (modified) (10 diffs)
-
trunk/livedraft-search-replace.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
livedraft-search-replace/trunk/livedraft-search-replace.js
r3495312 r3496250 9 9 * @copyright 2026 Kasuga 10 10 * @license GPL-2.0-or-later 11 * @version 2.1.1 11 * @version 2.1.2 12 * 12 13 */ 13 14 … … 43 44 } 44 45 return window; 46 }; 47 48 // Return the root writing-flow element where block content lives. 49 // Shared by performScan, the click handler, and the autoPopulate listener. 50 const getEditorElement = () => { 51 const d = getEditorDocument(); 52 return ( 53 d.querySelector('.block-editor-writing-flow') || 54 d.querySelector('.editor-writing-flow') || 55 d.querySelector('.is-root-container') || 56 d.querySelector('.block-editor-block-list__layout') || 57 d.body 58 ); 45 59 }; 46 60 … … 131 145 }, []); 132 146 133 // Auto-populate: update the search field when text is selected in the editor. 134 // Listens on both the top-level doc and the iframe doc. 147 // Auto-populate: update the search field only on drag-selection. 135 148 useEffect(() => { 136 149 if (!autoPopulate) return; 150 151 const editorDoc = getEditorDocument(); 152 const editorEl = getEditorElement(); 153 154 let isDragging = false; 155 156 const handleMouseDown = () => { 157 isDragging = false; 158 }; 159 160 const handleMouseMove = () => { 161 isDragging = true; 162 }; 163 164 const handleMouseUp = () => { 165 setTimeout(() => { isDragging = false; }, 0); 166 }; 167 137 168 const handleSelectionChange = () => { 169 if (!isDragging) return; 138 170 if (isInternalUpdating.current) return; 139 171 const activeEl = document.activeElement; 140 172 if (activeEl && (activeEl.tagName === 'INPUT' || activeEl.tagName === 'TEXTAREA')) return; 141 173 142 const selection = window.getSelection(); 143 if (selection && selection.rangeCount > 0) { 144 const text = selection.toString(); 145 if (text && text.length > 0) { 146 setSearch(text); 147 lastSearchedRef.current = text; 148 } 149 } 174 // Read selected text; for iframe-based themes (e.g. Astra) use the iframe's window. 175 let text = ''; 150 176 try { 151 177 const editorWin = getEditorWindow(); 152 if (editorWin !== window) { 153 const iframeSel = editorWin.getSelection(); 154 if (iframeSel && iframeSel.rangeCount > 0) { 155 const text = iframeSel.toString(); 156 if (text && text.length > 0) { 157 setSearch(text); 158 lastSearchedRef.current = text; 159 } 160 } 161 } 178 const sel = editorWin.getSelection(); 179 if (sel) text = sel.toString(); 162 180 } catch(e) {} 163 }; 164 181 if (!text) { 182 const sel = window.getSelection(); 183 if (sel) text = sel.toString(); 184 } 185 186 if (!text) return; 187 188 setSearch(text); 189 lastSearchedRef.current = text; 190 }; 191 192 if (editorEl) { 193 editorEl.addEventListener('mousedown', handleMouseDown); 194 editorEl.addEventListener('mousemove', handleMouseMove); 195 editorEl.addEventListener('mouseup', handleMouseUp); 196 } 165 197 document.addEventListener('selectionchange', handleSelectionChange); 166 198 try { 167 const editorDoc = getEditorDocument();168 199 if (editorDoc !== document) { 169 200 editorDoc.addEventListener('selectionchange', handleSelectionChange); … … 172 203 173 204 return () => { 205 if (editorEl) { 206 editorEl.removeEventListener('mousedown', handleMouseDown); 207 editorEl.removeEventListener('mousemove', handleMouseMove); 208 editorEl.removeEventListener('mouseup', handleMouseUp); 209 } 174 210 document.removeEventListener('selectionchange', handleSelectionChange); 175 211 try { 176 const editorDoc = getEditorDocument();177 212 if (editorDoc !== document) { 178 213 editorDoc.removeEventListener('selectionchange', handleSelectionChange); … … 204 239 } 205 240 206 const editor = 207 editorDoc.querySelector('.block-editor-writing-flow') || 208 editorDoc.querySelector('.editor-writing-flow') || 209 editorDoc.querySelector('.is-root-container') || 210 editorDoc.querySelector('.block-editor-block-list__layout') || 211 editorDoc.body; 241 const editor = getEditorElement(); 212 242 213 243 if (!editor) { ticking.current = false; return; } … … 363 393 364 394 // Replace current or all matches. Writes to block attributes to integrate with undo/redo. 365 //366 // HTML entity handling:367 // Standard mode: flexible pattern matching both raw chars and entity equivalents.368 // Regex mode: decode entities before matching so the regex sees plain text.369 395 const handleReplace = useCallback((all = false, specificMatch = null) => { 370 396 if (editorMode !== 'visual' || matchesRef.current.length === 0) return; … … 463 489 } else { 464 490 performUpdate(targetMatch.clientId, targetMatch); 465 try {466 const editorWin = getEditorWindow();467 if (editorWin.getSelection) editorWin.getSelection().removeAllRanges();468 } catch(e) {}469 if (window.getSelection) window.getSelection().removeAllRanges();470 491 } 471 492 472 493 setTimeout(() => { 473 try {474 const editorWin = getEditorWindow();475 if (editorWin.getSelection) editorWin.getSelection().removeAllRanges();476 } catch(e) {}477 if (window.getSelection) window.getSelection().removeAllRanges();478 479 494 if (all) { 480 495 setCurrent(0); … … 506 521 useEffect(() => { 507 522 const editorDoc = getEditorDocument(); 508 const editorEl = 509 editorDoc.querySelector('.block-editor-writing-flow') || 510 editorDoc.querySelector('.editor-writing-flow') || 511 editorDoc.querySelector('.is-root-container') || 512 editorDoc.querySelector('.block-editor-block-list__layout'); 523 const editorEl = getEditorElement(); 513 524 if (!editorEl) return; 514 515 const handleMouseDown = (e) => {516 if (e.detail >= 2) {517 e.preventDefault();518 }519 };520 525 521 526 const handleEditorAction = (e) => { … … 550 555 } 551 556 }; 552 553 editorEl.addEventListener('mousedown', handleMouseDown);554 557 editorEl.addEventListener('click', handleEditorAction); 555 558 return () => { 556 editorEl.removeEventListener('mousedown', handleMouseDown);557 559 editorEl.removeEventListener('click', handleEditorAction); 558 560 }; … … 560 562 561 563 // Re-scan on every block change (including Undo/Redo and after Replace). 562 useEffect(() => {564 useEffect(() => { 563 565 if (isInternalUpdating.current) return; 564 566 -
livedraft-search-replace/trunk/livedraft-search-replace.php
r3495312 r3496250 3 3 * Plugin Name: LiveDraft Search & Replace 4 4 * Description: A high-performance, real-time Search and Replace tool for the Block Editor sidebar. 5 * Version: 1.6. 25 * Version: 1.6.3 6 6 * Author: Kasuga 7 7 * License: GPLv2 or later -
livedraft-search-replace/trunk/readme.txt
r3495312 r3496250 6 6 Tested up to: 6.9 7 7 Requires PHP: 7.4 8 Stable tag: 1.6. 28 Stable tag: 1.6.3 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 101 101 == Changelog == 102 102 103 = 1.6.3 = 104 * Fixed: The fix in version 1.6.2 was insufficient, so an additional fix was applied. 105 103 106 = 1.6.2 = 104 107 * Fixed: Prevented double-click from triggering browser word-selection, which was causing the Auto-populate feature to overwrite the search field before replacement.
Note: See TracChangeset
for help on using the changeset viewer.