diff --git a/css/styles.css b/css/styles.css index eaafb42..8adb14f 100644 --- a/css/styles.css +++ b/css/styles.css @@ -161,11 +161,56 @@ .xyz-content-edit { display: none; } - textarea.xyz-editor { + /* XYZ edit textarea + highlight overlay. The backdrop sits behind a + transparent-background textarea and tints the lines of selected + atoms; both share identical metrics so the text lines up exactly. */ + #xyz-edit-wrap { + position: relative; + margin: 10px 0; + } + #xyz-content-edit.xyz-editor, + #xyz-edit-backdrop { width: 100%; - min-height: 200px; + box-sizing: border-box; + margin: 0; + padding: 6px; + border: 1px solid #ccc; font-family: monospace; - margin: 10px 0; + font-size: 13px; + line-height: 1.4; + white-space: pre; + letter-spacing: normal; + tab-size: 4; + } + #xyz-content-edit.xyz-editor { + position: relative; + z-index: 1; + display: block; + min-height: 200px; + resize: vertical; + overflow: auto; + background: transparent; + color: #000; + } + #xyz-edit-backdrop { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 0; + overflow: hidden; + pointer-events: none; + border-color: transparent; + background: #fff; + color: transparent; + } + #xyz-edit-highlights { + min-height: 200px; + } + #xyz-edit-highlights .xyz-edit-hl { + background: #fff3a0; + border-radius: 2px; } .mode-buttons { margin: 10px 0; diff --git a/index.html b/index.html index 0269834..98ca1b7 100644 --- a/index.html +++ b/index.html @@ -160,7 +160,10 @@

XYZ Editor

Click atoms in the 3D structure or the rows below to select them.
- +
+
+ +
diff --git a/js/selection.js b/js/selection.js index 7ae084a..f654210 100644 --- a/js/selection.js +++ b/js/selection.js @@ -10,6 +10,16 @@ function updateSelectionUI() { const n = selectedAtoms.size; countEl.textContent = n > 0 ? `${n} selected` : ''; } + refreshEditHighlightsIfVisible(); +} + +// When the XYZ editor's text edit mode is open, keep its highlight overlay in +// sync with the current selection (e.g. atoms picked in the 3D structure). +function refreshEditHighlightsIfVisible() { + const wrap = document.getElementById('xyz-edit-wrap'); + if (wrap && wrap.style.display !== 'none' && typeof renderXYZEditHighlights === 'function') { + renderXYZEditHighlights(); + } } // Turn the 3D halo on/off for a single atom (atomIndex is 0-based). diff --git a/js/xyz-editor.js b/js/xyz-editor.js index 1d1d1bf..f6f7dd4 100644 --- a/js/xyz-editor.js +++ b/js/xyz-editor.js @@ -106,7 +106,9 @@ function showXYZEditor() { // Update textarea content const editArea = document.getElementById('xyz-content-edit'); editArea.value = lastXYZData; - editArea.style.display = 'none'; // Ensure we start in selection mode + initXYZEditHighlights(); + renderXYZEditHighlights(); + document.getElementById('xyz-edit-wrap').style.display = 'none'; // Ensure we start in selection mode xyzContent.style.display = 'block'; document.getElementById('toggle-edit-mode').textContent = 'Switch to Edit Mode'; @@ -126,16 +128,16 @@ function showXYZEditor() { function toggleEditMode() { const selectionView = document.getElementById('xyz-content'); - const editView = document.getElementById('xyz-content-edit'); + const editView = document.getElementById('xyz-edit-wrap'); const editButton = document.getElementById('toggle-edit-mode'); - + if (selectionView.style.display !== 'none') { - // Switch to edit mode + // Switch to edit mode. Keep the current selection so the chosen atoms + // stay highlighted in the textarea, making them easy to edit. selectionView.style.display = 'none'; editView.style.display = 'block'; editButton.textContent = 'Switch to Selection Mode'; - // Clear any existing selections (keeps state Set, rows and halos in sync) - clearAtomSelection(); + renderXYZEditHighlights(); } else { // Switch to selection mode selectionView.style.display = 'block'; @@ -146,6 +148,51 @@ function toggleEditMode() { } } +// Escape text so user-entered atom names/coordinates can't inject markup into +// the highlight overlay. +function escapeHtmlForHighlight(s) { + return s.replace(/&/g, '&').replace(//g, '>'); +} + +// Re-draw the edit-mode highlight overlay: tint the textarea lines that belong +// to currently selected atoms. Atom i (0-based) lives on text line i + 2 +// (line 0 = atom count, line 1 = comment). +function renderXYZEditHighlights() { + const textarea = document.getElementById('xyz-content-edit'); + const backdrop = document.getElementById('xyz-edit-backdrop'); + const highlights = document.getElementById('xyz-edit-highlights'); + if (!textarea || !backdrop || !highlights) return; + + const selectedLines = new Set(Array.from(selectedAtoms).map(i => i + 2)); + const lines = textarea.value.split('\n'); + highlights.innerHTML = lines.map((line, idx) => { + const safe = escapeHtmlForHighlight(line); + return selectedLines.has(idx) ? `${safe}` : safe; + }).join('\n'); + + // Keep the overlay scroll-aligned with the textarea. + backdrop.scrollTop = textarea.scrollTop; + backdrop.scrollLeft = textarea.scrollLeft; +} + +// Attach the textarea listeners that keep the highlight overlay in sync. Runs +// once; guarded by a data flag so repeated showXYZEditor() calls don't stack +// listeners. +function initXYZEditHighlights() { + const textarea = document.getElementById('xyz-content-edit'); + if (!textarea || textarea.dataset.hlInit) return; + textarea.dataset.hlInit = '1'; + + const backdrop = document.getElementById('xyz-edit-backdrop'); + textarea.addEventListener('input', renderXYZEditHighlights); + textarea.addEventListener('scroll', function() { + if (backdrop) { + backdrop.scrollTop = textarea.scrollTop; + backdrop.scrollLeft = textarea.scrollLeft; + } + }); +} + function updateStructure() { const editView = document.getElementById('xyz-content-edit'); try {