Enhance wildcard handling and tooltip functionality in WildcardManager; add clear button to text editor modal
This commit is contained in:
@@ -479,11 +479,8 @@ class TextLoaderOperations:
|
||||
content = payload.get('content', '')
|
||||
wildcards_selections = payload.get('wildcards_selections', '')
|
||||
|
||||
if not content.strip():
|
||||
return self.error_response("Empty files are not allowed")
|
||||
|
||||
# Parse wildcards from the new content
|
||||
wildcards = self.node.parse_wildcards(content)
|
||||
wildcards = self.node.parse_wildcards(content) if content.strip() else []
|
||||
|
||||
# Apply existing selections if any
|
||||
try:
|
||||
@@ -508,7 +505,8 @@ class TextLoaderOperations:
|
||||
if wildcard.get('selected'):
|
||||
selections[wildcard['index']] = {
|
||||
'selected': wildcard['selected'],
|
||||
'original': wildcard['original']
|
||||
'original': wildcard['original'],
|
||||
'position': wildcard.get('position', 0) # NEW: Include position
|
||||
}
|
||||
# NEW: Preserve mark if it exists
|
||||
if wildcard.get('mark'):
|
||||
@@ -526,7 +524,9 @@ class TextLoaderOperations:
|
||||
wildcard_index = str(payload.get('wildcard_index', ''))
|
||||
selected_value = payload.get('selected_value', '')
|
||||
original_wildcard = payload.get('original_wildcard', '')
|
||||
mark_value = payload.get('mark_value') # NEW: Handle mark value
|
||||
mark_value = payload.get('mark_value')
|
||||
wildcard_position = payload.get('wildcard_position', 0) # NEW: Get position
|
||||
wildcard_content = payload.get('wildcard_content', '') # NEW: Get content
|
||||
wildcards_selections = payload.get('wildcards_selections', '')
|
||||
|
||||
try:
|
||||
@@ -534,19 +534,30 @@ class TextLoaderOperations:
|
||||
except json.JSONDecodeError:
|
||||
selections = {}
|
||||
|
||||
if selected_value:
|
||||
selections[wildcard_index] = {
|
||||
'selected': selected_value,
|
||||
'original': original_wildcard
|
||||
}
|
||||
# NEW: Handle mark value
|
||||
if mark_value is not None:
|
||||
if mark_value: # Non-empty mark
|
||||
selections[wildcard_index]['mark'] = mark_value
|
||||
elif 'mark' in selections[wildcard_index]:
|
||||
# Remove mark if empty value provided
|
||||
del selections[wildcard_index]['mark']
|
||||
else:
|
||||
# FIXED: Preserve existing data when updating
|
||||
if wildcard_index not in selections:
|
||||
selections[wildcard_index] = {}
|
||||
|
||||
# Update selection data if provided
|
||||
if selected_value is not None: # Allow empty string
|
||||
selections[wildcard_index]['selected'] = selected_value
|
||||
selections[wildcard_index]['original'] = original_wildcard
|
||||
selections[wildcard_index]['position'] = wildcard_position
|
||||
|
||||
# Handle mark value separately to preserve existing selection data
|
||||
if mark_value is not None:
|
||||
if mark_value: # Non-empty mark
|
||||
selections[wildcard_index]['mark'] = mark_value
|
||||
selections[wildcard_index]['mark_content'] = wildcard_content
|
||||
selections[wildcard_index]['mark_position'] = wildcard_position
|
||||
else:
|
||||
# Remove mark if empty value provided
|
||||
selections[wildcard_index].pop('mark', None)
|
||||
selections[wildcard_index].pop('mark_content', None)
|
||||
selections[wildcard_index].pop('mark_position', None)
|
||||
|
||||
# Only remove the entire entry if it has no useful data
|
||||
if (not selections[wildcard_index].get('selected') and not selections[wildcard_index].get('mark') and wildcard_index in selections):
|
||||
selections.pop(wildcard_index, None)
|
||||
|
||||
return self.success_response({
|
||||
@@ -554,6 +565,23 @@ class TextLoaderOperations:
|
||||
"selections_json": json.dumps(selections, ensure_ascii=False)
|
||||
})
|
||||
|
||||
def _cleanup_orphaned_marks(self, selections: Dict):
|
||||
"""Remove mark data for selections that no longer have a selected value"""
|
||||
keys_to_clean = []
|
||||
for key, selection_data in selections.items():
|
||||
if isinstance(selection_data, dict):
|
||||
if not selection_data.get('selected') and selection_data.get('mark'):
|
||||
# Mark exists but no selection - clean it up
|
||||
keys_to_clean.append(key)
|
||||
|
||||
for key in keys_to_clean:
|
||||
if 'mark' in selections[key]:
|
||||
del selections[key]['mark']
|
||||
if 'mark_content' in selections[key]:
|
||||
del selections[key]['mark_content']
|
||||
if 'mark_position' in selections[key]:
|
||||
del selections[key]['mark_position']
|
||||
|
||||
def _clear_wildcard_branch(self, selections: Dict, wildcard_index: str):
|
||||
keys_to_remove = [key for key in selections.keys() if key == wildcard_index or key.startswith(wildcard_index + '.')]
|
||||
for key in keys_to_remove:
|
||||
|
||||
@@ -101,8 +101,25 @@ class WildcardManager {
|
||||
if (!markIcon) return;
|
||||
|
||||
const selections = JSON.parse(this.operations.originalSelections || '{}');
|
||||
const pendingMark = this.operations.pendingSelections[wildcard.index]?.mark;
|
||||
const savedMark = selections[wildcard.index]?.mark ?? '';
|
||||
const wildcardId = this._createWildcardIdentifier(wildcard);
|
||||
|
||||
// Look for mark by wildcard identifier first, then fallback to index
|
||||
let savedMark = '';
|
||||
let pendingMark = this.operations.pendingSelections[wildcard.index]?.mark;
|
||||
|
||||
// First try to find by identifier in all selections
|
||||
for (const [selectionIndex, selectionData] of Object.entries(selections)) {
|
||||
const selectionId = this._getWildcardIdentifierFromSelections(selections, selectionIndex);
|
||||
if (selectionId === wildcardId && selectionData.mark) {
|
||||
savedMark = selectionData.mark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to direct index lookup
|
||||
if (!savedMark && selections[wildcard.index]?.mark) {
|
||||
savedMark = selections[wildcard.index].mark;
|
||||
}
|
||||
|
||||
this._setMarkIconState(markIcon, pendingMark, savedMark);
|
||||
}
|
||||
@@ -172,8 +189,25 @@ class WildcardManager {
|
||||
const dropdown = this._createCustomDropdown(wildcard);
|
||||
|
||||
const selections = JSON.parse(this.operations.originalSelections || '{}');
|
||||
const pendingMark = this.operations.pendingSelections[wildcard.index]?.mark;
|
||||
const savedMark = selections[wildcard.index]?.mark ?? '';
|
||||
const wildcardId = this._createWildcardIdentifier(wildcard);
|
||||
|
||||
// Look for mark by identifier first
|
||||
let savedMark = '';
|
||||
let pendingMark = this.operations.pendingSelections[wildcard.index]?.mark;
|
||||
|
||||
// Search for mark by identifier
|
||||
for (const [selectionIndex, selectionData] of Object.entries(selections)) {
|
||||
const selectionId = this._getWildcardIdentifierFromSelections(selections, selectionIndex);
|
||||
if (selectionId === wildcardId && selectionData.mark) {
|
||||
savedMark = selectionData.mark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to direct index
|
||||
if (!savedMark && selections[wildcard.index]?.mark) {
|
||||
savedMark = selections[wildcard.index].mark;
|
||||
}
|
||||
|
||||
const markIcon = document.createElement('span');
|
||||
markIcon.className = 'wildcard-mark-icon';
|
||||
@@ -184,26 +218,20 @@ class WildcardManager {
|
||||
markIcon.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
// Check if there's already a tooltip for THIS specific wildcard
|
||||
const existingTooltip = document.querySelector('.wildcard-mark-tooltip');
|
||||
const isThisWildcard = existingTooltip &&
|
||||
existingTooltip.dataset.wildcardIndex === wildcard.index.toString();
|
||||
|
||||
if (existingTooltip) {
|
||||
if (isThisWildcard) {
|
||||
// Close if it's the same wildcard
|
||||
console.log('Closing tooltip for same wildcard');
|
||||
this._closeActiveOverlays();
|
||||
return;
|
||||
} else {
|
||||
// Close existing and open new one for different wildcard
|
||||
console.log('Closing tooltip for different wildcard, opening new one');
|
||||
this._closeActiveOverlays();
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Showing new tooltip');
|
||||
this.showMarkTooltip(container, wildcard, pendingMark);
|
||||
this.showMarkTooltip(container, wildcard, pendingMark || savedMark);
|
||||
};
|
||||
|
||||
row.appendChild(dropdown);
|
||||
@@ -229,13 +257,9 @@ class WildcardManager {
|
||||
}
|
||||
|
||||
showMarkTooltip(container, wildcard, markValue = '') {
|
||||
console.log('showMarkTooltip called, activeOverlay before:', this.activeOverlay);
|
||||
|
||||
// Check if tooltip is already showing for this wildcard - improved check
|
||||
if (this.activeOverlay &&
|
||||
this.activeOverlay.classList.contains('wildcard-mark-tooltip') &&
|
||||
this.activeOverlay.parentNode) {
|
||||
console.log('Found existing tooltip, closing');
|
||||
this._closeActiveOverlays();
|
||||
return;
|
||||
}
|
||||
@@ -250,13 +274,25 @@ class WildcardManager {
|
||||
tooltip.className = 'wildcard-mark-tooltip';
|
||||
tooltip.dataset.wildcardIndex = wildcard.index;
|
||||
|
||||
// Set activeOverlay IMMEDIATELY after creating tooltip
|
||||
this.activeOverlay = tooltip;
|
||||
console.log('Set activeOverlay to:', this.activeOverlay);
|
||||
|
||||
const selections = JSON.parse(this.operations.originalSelections || '{}');
|
||||
const wildcardId = this._createWildcardIdentifier(wildcard);
|
||||
|
||||
// Find mark by identifier or index
|
||||
let savedMark = '';
|
||||
for (const [selectionIndex, selectionData] of Object.entries(selections)) {
|
||||
const selectionId = this._getWildcardIdentifierFromSelections(selections, selectionIndex);
|
||||
if (selectionId === wildcardId && selectionData.mark) {
|
||||
savedMark = selectionData.mark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!savedMark) {
|
||||
savedMark = selections[wildcard.index]?.mark ?? '';
|
||||
}
|
||||
|
||||
const pendingMark = this.operations.pendingSelections[wildcard.index]?.mark;
|
||||
const savedMark = selections[wildcard.index]?.mark ?? '';
|
||||
|
||||
const input = document.createElement('input');
|
||||
input.type = 'text';
|
||||
@@ -277,10 +313,11 @@ class WildcardManager {
|
||||
|
||||
const addMark = () => {
|
||||
let val = input.value.replace(/[^a-zA-Z]/g, '').toUpperCase();
|
||||
this.operations.updatePendingMark(wildcard.index, val);
|
||||
|
||||
// MODIFIED: Store wildcard position and content for stable identification
|
||||
this.operations.updatePendingMark(wildcard.index, val, wildcard);
|
||||
|
||||
this.activeOverlay = null;
|
||||
|
||||
input.blur();
|
||||
tooltip.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
tooltip.remove();
|
||||
@@ -288,17 +325,31 @@ class WildcardManager {
|
||||
if (markIcon) {
|
||||
const selections = JSON.parse(this.operations.originalSelections || '{}');
|
||||
const pendingMark = this.operations.pendingSelections[wildcard.index]?.mark;
|
||||
const savedMark = selections[wildcard.index]?.mark ?? '';
|
||||
|
||||
// Find saved mark by identifier
|
||||
let savedMark = '';
|
||||
const wildcardId = this._createWildcardIdentifier(wildcard);
|
||||
for (const [selectionIndex, selectionData] of Object.entries(selections)) {
|
||||
const selectionId = this._getWildcardIdentifierFromSelections(selections, selectionIndex);
|
||||
if (selectionId === wildcardId && selectionData.mark) {
|
||||
savedMark = selectionData.mark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!savedMark) {
|
||||
savedMark = selections[wildcard.index]?.mark ?? '';
|
||||
}
|
||||
|
||||
this._setMarkIconState(markIcon, pendingMark, savedMark);
|
||||
}
|
||||
}, 200);
|
||||
};
|
||||
|
||||
const deleteMark = () => {
|
||||
this.operations.updatePendingMark(wildcard.index, '');
|
||||
|
||||
this.operations.updatePendingMark(wildcard.index, '', wildcard);
|
||||
|
||||
this.activeOverlay = null;
|
||||
|
||||
input.blur();
|
||||
tooltip.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
tooltip.remove();
|
||||
@@ -306,7 +357,21 @@ class WildcardManager {
|
||||
if (markIcon) {
|
||||
const selections = JSON.parse(this.operations.originalSelections || '{}');
|
||||
const pendingMark = this.operations.pendingSelections[wildcard.index]?.mark;
|
||||
const savedMark = selections[wildcard.index]?.mark ?? '';
|
||||
|
||||
// Find saved mark by identifier
|
||||
let savedMark = '';
|
||||
const wildcardId = this._createWildcardIdentifier(wildcard);
|
||||
for (const [selectionIndex, selectionData] of Object.entries(selections)) {
|
||||
const selectionId = this._getWildcardIdentifierFromSelections(selections, selectionIndex);
|
||||
if (selectionId === wildcardId && selectionData.mark) {
|
||||
savedMark = selectionData.mark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!savedMark) {
|
||||
savedMark = selections[wildcard.index]?.mark ?? '';
|
||||
}
|
||||
|
||||
this._setMarkIconState(markIcon, pendingMark, savedMark);
|
||||
}
|
||||
}, 200);
|
||||
@@ -325,14 +390,13 @@ class WildcardManager {
|
||||
const icon = container.querySelector('.wildcard-mark-icon');
|
||||
const modal = document.querySelector('.text-editor-modal');
|
||||
|
||||
// Append to modal to avoid clipping by sidebar overflow
|
||||
modal.appendChild(tooltip);
|
||||
|
||||
// Add scroll listener to hide tooltip when scrolling
|
||||
const sidebarContent = document.querySelector('.wildcard-dropdowns');
|
||||
const handleScroll = () => {
|
||||
if (tooltip && tooltip.parentNode) {
|
||||
this.activeOverlay = null;
|
||||
input.blur();
|
||||
tooltip.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
tooltip.remove();
|
||||
@@ -349,7 +413,6 @@ class WildcardManager {
|
||||
const iconRect = icon.getBoundingClientRect();
|
||||
const modalRect = modal.getBoundingClientRect();
|
||||
|
||||
// Position relative to modal
|
||||
tooltip.style.position = 'absolute';
|
||||
tooltip.style.left = `${iconRect.right - modalRect.left - tooltip.offsetWidth}px`;
|
||||
tooltip.style.top = `${iconRect.top - modalRect.top - tooltip.offsetHeight - 4}px`;
|
||||
@@ -357,12 +420,16 @@ class WildcardManager {
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
tooltip.classList.add('show');
|
||||
requestAnimationFrame(() => {
|
||||
input.focus();
|
||||
});
|
||||
});
|
||||
}, 0);
|
||||
|
||||
const handleClickOutside = (event) => {
|
||||
if (!tooltip.contains(event.target) && event.target !== icon) {
|
||||
this.activeOverlay = null;
|
||||
input.blur();
|
||||
tooltip.classList.remove('show');
|
||||
setTimeout(() => {
|
||||
tooltip.remove();
|
||||
@@ -447,10 +514,6 @@ class WildcardManager {
|
||||
return container;
|
||||
}
|
||||
|
||||
_toggleCustomDropdown(container) {
|
||||
return container;
|
||||
}
|
||||
|
||||
_toggleCustomDropdown(container) {
|
||||
const isOpen = container.classList.contains('open');
|
||||
|
||||
@@ -539,7 +602,8 @@ class WildcardManager {
|
||||
|
||||
async handleSelectionChange(wildcard, selectedValue, selectedIndex) {
|
||||
try {
|
||||
this.operations.updatePendingSelection(wildcard.index, selectedValue, wildcard.original);
|
||||
// MODIFIED: Include position data
|
||||
this.operations.updatePendingSelection(wildcard.index, selectedValue, wildcard.original, wildcard.position);
|
||||
|
||||
this.updateChildrenVisibility(wildcard, selectedIndex, false);
|
||||
} catch (error) {
|
||||
@@ -747,9 +811,14 @@ class WildcardManager {
|
||||
try {
|
||||
const pendingMarks = {};
|
||||
Object.keys(this.operations.pendingSelections).forEach(wildcardIndex => {
|
||||
const markValue = this.operations.pendingSelections[wildcardIndex]?.mark;
|
||||
const selectionData = this.operations.pendingSelections[wildcardIndex];
|
||||
const markValue = selectionData?.mark;
|
||||
if (markValue !== undefined) {
|
||||
pendingMarks[wildcardIndex] = markValue;
|
||||
pendingMarks[wildcardIndex] = {
|
||||
mark: markValue,
|
||||
original: selectionData.original,
|
||||
position: selectionData.position
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -762,7 +831,7 @@ class WildcardManager {
|
||||
const section = container.querySelector(`[data-wildcard-index="${wildcardIndex}"]`);
|
||||
const markIcon = section?.querySelector('.wildcard-mark-icon');
|
||||
if (markIcon) {
|
||||
const savedMark = pendingMarks[wildcardIndex];
|
||||
const savedMark = pendingMarks[wildcardIndex].mark;
|
||||
this._setMarkIconState(markIcon, undefined, savedMark);
|
||||
}
|
||||
});
|
||||
@@ -773,26 +842,62 @@ class WildcardManager {
|
||||
}
|
||||
|
||||
async _savePendingSelections() {
|
||||
// Group updates: first do selections, then do marks to ensure marks are preserved
|
||||
const selectionUpdates = [];
|
||||
const markUpdates = [];
|
||||
|
||||
for (const [wildcardIndex, selectionData] of Object.entries(this.operations.pendingSelections)) {
|
||||
const updateData = {
|
||||
wildcard_index: wildcardIndex,
|
||||
selected_value: selectionData.selected,
|
||||
original_wildcard: selectionData.original,
|
||||
wildcards_selections: this.textLoaderInstance.getHiddenWidgetValue("wildcards_selections")
|
||||
};
|
||||
|
||||
// UPDATED: Ensure mark value is sent correctly
|
||||
if (selectionData.mark !== undefined) {
|
||||
updateData.mark_value = selectionData.mark;
|
||||
// If there's a selection change, add it to selection updates
|
||||
if (selectionData.selected !== undefined) {
|
||||
selectionUpdates.push({
|
||||
wildcard_index: wildcardIndex,
|
||||
selected_value: selectionData.selected,
|
||||
original_wildcard: selectionData.original,
|
||||
wildcard_position: selectionData.position,
|
||||
wildcard_content: selectionData.original
|
||||
});
|
||||
}
|
||||
|
||||
// If there's a mark change, add it to mark updates
|
||||
if (selectionData.mark !== undefined) {
|
||||
markUpdates.push({
|
||||
wildcard_index: wildcardIndex,
|
||||
selected_value: selectionData.selected || '',
|
||||
original_wildcard: selectionData.original,
|
||||
mark_value: selectionData.mark,
|
||||
wildcard_position: selectionData.position,
|
||||
wildcard_content: selectionData.original
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let latestSelectionsJson = this.textLoaderInstance.getHiddenWidgetValue("wildcards_selections");
|
||||
|
||||
// Process selection updates first
|
||||
for (const updateData of selectionUpdates) {
|
||||
updateData.wildcards_selections = latestSelectionsJson;
|
||||
|
||||
const response = await fetchSend(this.constants.MESSAGE_ROUTE, this.node.id, "update_wildcard_selection", updateData);
|
||||
|
||||
if (response?.status === "success" && response.selections_json) {
|
||||
this.textLoaderInstance.updateHiddenWidget("wildcards_selections", response.selections_json);
|
||||
this.operations.originalSelections = response.selections_json;
|
||||
latestSelectionsJson = response.selections_json;
|
||||
}
|
||||
}
|
||||
|
||||
// Process mark updates second, ensuring we preserve all existing marks
|
||||
for (const updateData of markUpdates) {
|
||||
updateData.wildcards_selections = latestSelectionsJson;
|
||||
|
||||
const response = await fetchSend(this.constants.MESSAGE_ROUTE, this.node.id, "update_wildcard_selection", updateData);
|
||||
|
||||
if (response?.status === "success" && response.selections_json) {
|
||||
latestSelectionsJson = response.selections_json;
|
||||
}
|
||||
}
|
||||
|
||||
// Update with the final selections JSON that contains all changes
|
||||
this.textLoaderInstance.updateHiddenWidget("wildcards_selections", latestSelectionsJson);
|
||||
this.operations.originalSelections = latestSelectionsJson;
|
||||
}
|
||||
|
||||
_flashSavedLabels(container) {
|
||||
@@ -834,6 +939,24 @@ class WildcardManager {
|
||||
_updateWildcardStructure(wildcards) {
|
||||
this.lastWildcardStructure = this._extractWildcardStructure(wildcards);
|
||||
}
|
||||
|
||||
// NEW: Create stable wildcard identifier
|
||||
_createWildcardIdentifier(wildcard) {
|
||||
// Combine original content and position for a stable identifier
|
||||
const content = wildcard.original || '';
|
||||
const position = wildcard.position || 0;
|
||||
// Create a hash-like identifier that's stable regardless of index changes
|
||||
return `${content}@${position}`;
|
||||
}
|
||||
|
||||
// NEW: Get wildcard identifier from selections
|
||||
_getWildcardIdentifierFromSelections(selections, wildcardIndex) {
|
||||
const selection = selections[wildcardIndex];
|
||||
if (selection && selection.original !== undefined && selection.position !== undefined) {
|
||||
return `${selection.original}@${selection.position}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class Operations {
|
||||
@@ -856,7 +979,7 @@ class Operations {
|
||||
this.hasUnsavedTextChanges = currentText !== this.originalText;
|
||||
}
|
||||
|
||||
updatePendingSelection(wildcardIndex, selectedValue, originalWildcard) {
|
||||
updatePendingSelection(wildcardIndex, selectedValue, originalWildcard, wildcardPosition) {
|
||||
let existingMark;
|
||||
const pendingMark = this.pendingSelections[wildcardIndex]?.mark;
|
||||
|
||||
@@ -865,7 +988,23 @@ class Operations {
|
||||
} else {
|
||||
try {
|
||||
const selections = JSON.parse(this.originalSelections || '{}');
|
||||
existingMark = selections[wildcardIndex]?.mark;
|
||||
|
||||
// NEW: Search by identifier first
|
||||
const wildcardId = `${originalWildcard}@${wildcardPosition}`;
|
||||
for (const [selectionIndex, selectionData] of Object.entries(selections)) {
|
||||
const selectionId = selectionData.original && selectionData.position !== undefined
|
||||
? `${selectionData.original}@${selectionData.position}`
|
||||
: null;
|
||||
if (selectionId === wildcardId && selectionData.mark) {
|
||||
existingMark = selectionData.mark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to direct index lookup
|
||||
if (existingMark === undefined) {
|
||||
existingMark = selections[wildcardIndex]?.mark;
|
||||
}
|
||||
} catch (e) {
|
||||
existingMark = undefined;
|
||||
}
|
||||
@@ -877,6 +1016,7 @@ class Operations {
|
||||
|
||||
this.pendingSelections[wildcardIndex].selected = selectedValue;
|
||||
this.pendingSelections[wildcardIndex].original = originalWildcard;
|
||||
this.pendingSelections[wildcardIndex].position = wildcardPosition; // NEW: Store position
|
||||
|
||||
if (existingMark !== undefined && existingMark !== '') {
|
||||
this.pendingSelections[wildcardIndex].mark = existingMark;
|
||||
@@ -885,24 +1025,51 @@ class Operations {
|
||||
this.hasUnsavedSelectionChanges = true;
|
||||
}
|
||||
|
||||
updatePendingMark(wildcardIndex, markValue) {
|
||||
// MODIFIED: Update to include wildcard metadata
|
||||
updatePendingMark(wildcardIndex, markValue, wildcard = null) {
|
||||
let originalMark = '';
|
||||
let selections = {};
|
||||
try {
|
||||
selections = JSON.parse(this.originalSelections || '{}');
|
||||
originalMark = selections[wildcardIndex]?.mark || '';
|
||||
|
||||
// NEW: Search by identifier if wildcard provided
|
||||
if (wildcard) {
|
||||
const wildcardId = `${wildcard.original}@${wildcard.position}`;
|
||||
for (const [selectionIndex, selectionData] of Object.entries(selections)) {
|
||||
const selectionId = selectionData.original && selectionData.position !== undefined
|
||||
? `${selectionData.original}@${selectionData.position}`
|
||||
: null;
|
||||
if (selectionId === wildcardId && selectionData.mark) {
|
||||
originalMark = selectionData.mark;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to direct lookup
|
||||
if (!originalMark) {
|
||||
originalMark = selections[wildcardIndex]?.mark || '';
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (!this.pendingSelections[wildcardIndex]) {
|
||||
const savedSelection = selections[wildcardIndex]?.selected || '';
|
||||
const savedOriginal = selections[wildcardIndex]?.original || '';
|
||||
const savedOriginal = wildcard?.original || selections[wildcardIndex]?.original || '';
|
||||
const savedPosition = wildcard?.position ?? selections[wildcardIndex]?.position;
|
||||
|
||||
this.pendingSelections[wildcardIndex] = {
|
||||
selected: savedSelection,
|
||||
original: savedOriginal
|
||||
original: savedOriginal,
|
||||
position: savedPosition // NEW: Store position
|
||||
};
|
||||
}
|
||||
|
||||
// NEW: Update position if wildcard provided
|
||||
if (wildcard) {
|
||||
this.pendingSelections[wildcardIndex].original = wildcard.original;
|
||||
this.pendingSelections[wildcardIndex].position = wildcard.position;
|
||||
}
|
||||
|
||||
this.pendingSelections[wildcardIndex].mark = markValue;
|
||||
|
||||
this.hasUnsavedSelectionChanges = (markValue !== originalMark) ||
|
||||
@@ -939,7 +1106,19 @@ class Operations {
|
||||
currentSelections = {};
|
||||
}
|
||||
|
||||
Object.assign(currentSelections, this.pendingSelections);
|
||||
// FIXED: Merge each pending selection individually to preserve existing marks
|
||||
for (const [wildcardIndex, pendingData] of Object.entries(this.pendingSelections)) {
|
||||
if (!currentSelections[wildcardIndex]) {
|
||||
currentSelections[wildcardIndex] = {};
|
||||
}
|
||||
|
||||
// Merge the pending data with existing data
|
||||
currentSelections[wildcardIndex] = {
|
||||
...currentSelections[wildcardIndex],
|
||||
...pendingData
|
||||
};
|
||||
}
|
||||
|
||||
this.textLoaderInstance.updateHiddenWidget("wildcards_selections", JSON.stringify(currentSelections));
|
||||
|
||||
this.originalSelections = JSON.stringify(currentSelections);
|
||||
@@ -1056,6 +1235,10 @@ class ButtonManager {
|
||||
return this.elements.createButton('text-editor-save-button', 'Save', () => this.handleSave());
|
||||
}
|
||||
|
||||
createClearButton() {
|
||||
return this.elements.createButton('text-editor-clear-button', 'Clear', () => this.handleClear());
|
||||
}
|
||||
|
||||
createResetButton() {
|
||||
return this.elements.createButton('wildcard-reset-button', 'Reset', () => this.handleReset());
|
||||
}
|
||||
@@ -1067,11 +1250,6 @@ class ButtonManager {
|
||||
async handleSave() {
|
||||
const textarea = this.elements.elements.textarea;
|
||||
const saveButton = document.querySelector('.text-editor-save-button');
|
||||
|
||||
if (!textarea.value.trim()) {
|
||||
alert('Empty content is not allowed');
|
||||
return;
|
||||
}
|
||||
|
||||
this.setButtonState(saveButton, 'Saving...', true);
|
||||
|
||||
@@ -1098,6 +1276,43 @@ class ButtonManager {
|
||||
}
|
||||
}
|
||||
|
||||
async handleClear() {
|
||||
if (window.confirm('Are you sure you want to clear all text content? This will remove all text and wildcards.')) {
|
||||
const clearButton = document.querySelector('.text-editor-clear-button');
|
||||
this.setButtonState(clearButton, 'Clearing...', true);
|
||||
|
||||
try {
|
||||
// Clear the textarea
|
||||
const textarea = this.elements.elements.textarea;
|
||||
textarea.value = '';
|
||||
|
||||
// Save the empty content
|
||||
await this.operations.saveContent('');
|
||||
|
||||
// Update UI to show no wildcards
|
||||
this.wildcardManager.createWildcardUI(this.elements.elements.wildcardDropdowns, []);
|
||||
this.elements.autoExpandSidebar(0);
|
||||
|
||||
// Show notification
|
||||
this.wildcardManager.showNotification(
|
||||
this.elements.elements.wildcardDropdowns,
|
||||
'Text content cleared'
|
||||
);
|
||||
|
||||
this.setButtonState(clearButton, 'Cleared!', true);
|
||||
|
||||
setTimeout(() => {
|
||||
this.setButtonState(clearButton, 'Clear', false);
|
||||
}, 1000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error clearing content:', error);
|
||||
alert('Error clearing content');
|
||||
this.setButtonState(clearButton, 'Clear', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleSaveSelections() {
|
||||
try {
|
||||
await this.wildcardManager.saveSelections(this.elements.elements.wildcardDropdowns);
|
||||
@@ -1204,8 +1419,18 @@ export function createTextEditorModal(node, textContent, constants, textLoaderIn
|
||||
wildcardControls.insertBefore(sidebarButtonsContainer, elements.elements.wildcardDropdowns);
|
||||
elements.elements.sidebarContent.appendChild(wildcardControls);
|
||||
|
||||
// MODIFIED: Create main button container with both Clear and Save buttons
|
||||
const mainButtonsContainer = document.createElement('div');
|
||||
Object.assign(mainButtonsContainer.style, {
|
||||
display: 'flex',
|
||||
gap: '10px'
|
||||
});
|
||||
|
||||
mainButtonsContainer.appendChild(buttonManager.createClearButton());
|
||||
mainButtonsContainer.appendChild(buttonManager.createSaveButton());
|
||||
|
||||
buttonContainer.appendChild(document.createElement('div'));
|
||||
buttonContainer.appendChild(buttonManager.createSaveButton());
|
||||
buttonContainer.appendChild(mainButtonsContainer);
|
||||
|
||||
mainSection.appendChild(buttonContainer);
|
||||
contentDiv.appendChild(mainSection);
|
||||
|
||||
Reference in New Issue
Block a user