Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,10 @@ <h3>XYZ Editor</h3>
</div>
<div class="selection-hint">Click atoms in the 3D structure or the rows below to select them.</div>
<div id="xyz-content"></div>
<textarea id="xyz-content-edit" class="xyz-editor"></textarea>
<div id="xyz-edit-wrap">
<div id="xyz-edit-backdrop"><div id="xyz-edit-highlights"></div></div>
<textarea id="xyz-content-edit" class="xyz-editor"></textarea>
</div>
<div class="action-buttons">
<button onclick="updateStructure()" title="Update molecular structure with current XYZ coordinates">Update Structure</button>
<button onclick="optimizeStructure()" title="Optimize molecular geometry using built-in force field">Optimize Structure</button>
Expand Down
10 changes: 10 additions & 0 deletions js/selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
59 changes: 53 additions & 6 deletions js/xyz-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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';
Expand All @@ -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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

// 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) ? `<span class="xyz-edit-hl">${safe}</span>` : 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 {
Expand Down