你仍沒有實現「當index為5時的Log時,恢復到第5個操作完成時的狀態」現在是「第5個操作完成前的狀態」。只需告知所要修改的地方。
代码: 全选
為了達成「點擊 Log 即回到該操作**完成後**狀態」以及「從中間操作時**截斷後續歷史**」的需求,我們需要對 `saveStateForUndo` 增加截斷邏輯,並統一 `handleUndo`、`handleRedo` 與 `jumpToLog` 的狀態應用邏輯。
以下是修改建議:
### 1. 統一狀態恢復邏輯 (建議新增一個輔助函式或直接替換)
為了確保所有跳轉行為一致,建議將恢復狀態的邏輯封裝。
```javascript
// Add or use this logic inside Undo/Redo/Jump to ensure consistency
function applyState(targetState) {
if (isSingleWordMode) {
charDefCodeMap = new Map();
for (let [k, v] of targetState.charDefCodeMap.entries()) {
charDefCodeMap.set(k, [...v]);
}
rebuildSingleWordFileContent();
processContent();
if (typeof runDomumorText === 'function') runDomumorText();
} else {
filecontent = targetState.filecontent;
processContent(JSON.stringify(targetState.lines));
}
}
```
### 2. 修改 `saveStateForUndo` (增加截斷邏輯)
當在歷史中間進行新操作時,必須刪除 `currentIndex` 之後的所有記錄。
```javascript
function saveStateForUndo(description = 'Manual Edit') {
// --- TRUNCATE LOGIC ---
// If we are not at the end of the stack, remove all "future" states
if (currentIndex < historyStack.length - 1) {
historyStack = historyStack.slice(0, currentIndex + 1);
}
let currentLines = mcc0string.split('\n').filter(l => l.trim() !== '');
let charDefMapCopy = new Map();
for (let [k, v] of charDefCodeMap.entries()) {
charDefMapCopy.set(k, [...v]);
}
historyStack.push({
lines: currentLines,
filecontent: filecontent,
charDefCodeMap: charDefMapCopy,
desc: description
});
// Update pointer to the newly pushed state
currentIndex = historyStack.length - 1;
renderActionLogs();
}
```
### 3. 修改 `handleUndo`, `handleRedo`, `jumpToLog`
確保指標移動後,直接套用該索引對應的狀態。
```javascript
function handleUndo() {
if (currentIndex <= 0) return;
let oldState = getSystemState();
currentIndex--; // Move pointer back
applyState(historyStack[currentIndex]); // Apply the state at new index
detectAndRenderChanges(oldState, getSystemState());
justMovedItem = null;
renderActionLogs();
}
function handleRedo() {
if (currentIndex >= historyStack.length - 1) return;
let oldState = getSystemState();
currentIndex++; // Move pointer forward
applyState(historyStack[currentIndex]); // Apply the state at new index
detectAndRenderChanges(oldState, getSystemState());
justMovedItem = null;
renderActionLogs();
}
function jumpToLog(index) {
if (index < 0 || index >= historyStack.length || index === currentIndex) return;
let oldState = getSystemState();
currentIndex = index; // Directly jump to the clicked state
applyState(historyStack[currentIndex]);
detectAndRenderChanges(oldState, getSystemState());
justMovedItem = null;
renderActionLogs();
}
```
### 4. 修改 `renderActionLogs` (優化顯示)
確保當前處於活躍狀態的操作被清楚標記。
```javascript
function renderActionLogs() {
const container = document.getElementById('actionLogContainer');
const list = document.getElementById('actionLogsList');
if (!container || !list) return;
if (historyStack.length === 0) {
container.style.display = 'none';
return;
}
container.style.display = 'block';
list.innerHTML = '';
historyStack.forEach((log, index) => {
let logDiv = document.createElement('div');
logDiv.className = 'log-item' + (index === currentIndex ? ' active-log' : '');
// Display order numbers to make it clear (Optional)
logDiv.textContent = `${index + 1}. ${log.desc}`;
logDiv.onclick = () => jumpToLog(index);
list.appendChild(logDiv);
// Auto-scroll to active log
if (index === currentIndex) {
setTimeout(() => logDiv.scrollIntoView({ block: 'nearest' }), 0);
}
});
}
```
### 修改重點點評:
1. **截斷邏輯 (Truncation)**:在 `saveStateForUndo` 中使用 `slice(0, currentIndex + 1)`。這保證了如果你撤銷(Undo)了 3 次然後做了一個新動作,那原本被撤銷的 3 個動作會從歷史中永久消失,並由新動作接替。
2. **已執行狀態定義**:
* 要實現「第 5 個操作已執行」,`historyStack[5]` 必須存儲該操作完成後的數據快照。
* **請確保在主邏輯中**:先修改變數(如 `charDefCodeMap`),**隨後**才調用 `saveStateForUndo()`。
3. **狀態同步**:`currentIndex` 始終指向當前頁面呈現的數據在 `historyStack` 中的索引位置。點擊 Log `i` 就讀取 `stack[i]`,這解決了「感覺是執行前狀態」的落差感。
4. **初始化建議**:建議在頁面加載或讀取文件完成時,主動調用一次 `saveStateForUndo('Initial State')`,這樣 index 0 就會是原始狀態。
代码: 全选
為了實現「點擊 Log 即進入該操作完成後的狀態」以及「中間操作時截斷後續歷史」的功能,請按照以下修改建議進行替換。主要邏輯變動在於 `saveStateForUndo` 的截斷處理,以及跳轉邏輯的統一。
### 1. 修改 `saveStateForUndo` 函式
在保存新狀態前,先根據當前 `currentIndex` 截斷(Truncate)掉「未來」的歷史記錄。
```javascript
function saveStateForUndo(description = 'Manual Edit') {
// Truncate the history stack if the pointer is not at the end
if (currentIndex < historyStack.length - 1) {
historyStack = historyStack.slice(0, currentIndex + 1);
}
let currentLines = mcc0string.split('\n').filter(l => l.trim() !== '');
let charDefMapCopy = new Map();
for (let [k, v] of charDefCodeMap.entries()) {
charDefMapCopy.set(k, [...v]);
}
historyStack.push({
lines: currentLines,
filecontent: filecontent,
charDefCodeMap: charDefMapCopy,
desc: description
});
// Always point to the latest state after saving
currentIndex = historyStack.length - 1;
renderActionLogs();
}
```
### 2. 統一提取 `applyState` 輔助函式(建議新增)
為了確保 `Undo`、`Redo` 和 `jumpToLog` 的行為一致(即:跳到該 index 就還原該 index 儲存的完成態),建議提取此邏輯。
```javascript
// Helper to restore state and refresh UI
function applyState(targetState) {
if (isSingleWordMode) {
charDefCodeMap = new Map();
for (let [k, v] of targetState.charDefCodeMap.entries()) {
charDefCodeMap.set(k, [...v]);
}
rebuildSingleWordFileContent();
processContent();
if (typeof runDomumorText === 'function') runDomumorText();
} else {
filecontent = targetState.filecontent;
processContent(JSON.stringify(targetState.lines));
}
}
```
### 3. 修改 `handleUndo`, `handleRedo` 與 `jumpToLog`
使用簡化的指標移動邏輯。注意:`currentIndex` 指向的就是「當前畫面呈現的狀態」。
```javascript
function handleUndo() {
if (currentIndex <= 0) return;
let oldState = getSystemState();
currentIndex--; // Move pointer back
applyState(historyStack[currentIndex]);
detectAndRenderChanges(oldState, getSystemState());
justMovedItem = null;
renderActionLogs();
}
function handleRedo() {
if (currentIndex >= historyStack.length - 1) return;
let oldState = getSystemState();
currentIndex++; // Move pointer forward
applyState(historyStack[currentIndex]);
detectAndRenderChanges(oldState, getSystemState());
justMovedItem = null;
renderActionLogs();
}
function jumpToLog(index) {
if (index < 0 || index >= historyStack.length || index === currentIndex) return;
let oldState = getSystemState();
currentIndex = index; // Jump directly to the selected state's index
applyState(historyStack[currentIndex]);
detectAndRenderChanges(oldState, getSystemState());
justMovedItem = null;
renderActionLogs();
}
```
### 4. 修改 `renderActionLogs` 中的高亮邏輯
確保索引與顯示內容一致,並在點擊時觸發正確跳轉。
```javascript
// Inside renderActionLogs looping through historyStack
historyStack.forEach((log, index) => {
let logDiv = document.createElement('div');
logDiv.className = 'log-item';
// Highlight exact match of currentIndex
if (index === currentIndex) {
logDiv.classList.add('active-log');
}
// State index + description
logDiv.textContent = `${index + 1}. ${log.desc}`;
logDiv.onclick = () => jumpToLog(index);
list.appendChild(logDiv);
});
```
### 關鍵改進說明:
1. **截斷未來 (Truncation)**:在 `saveStateForUndo` 中使用 `slice(0, currentIndex + 1)`。這確保了如果你撤銷到過去後進行了新操作,原本指標之後的「未來」操作會被刪除,重新開始一段歷史。
2. **狀態與索引對應 (Index Mapping)**:現在 `historyStack[index]` 代表的是執行完該描述 (desc) 動作後的數據快照。
* 當 `currentIndex` 為 5,畫面顯示的就是第 5 個 Log 的結果。
* `jumpToLog(index)` 直接將指標設為該索引並套用數據,保證「點到哪,畫面就到哪」。
3. **初始狀態建議**:為了讓使用者能退回到「最初始狀態」,建議在程式初始化(加載文件完成後)先手動呼叫一次 `saveStateForUndo('Initial State')`。這樣 index 0 就會是原始狀態。