分享和討論倉頡輸入法相關的資訊和内容
版面规则
公正客觀講理,杜絶廢話連篇
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

gemini thinking

代码: 全选

為了解決你的需求,我們需要在函式的一開始**紀錄進入函式前的 `isSingleWordMode` 狀態**(例如使用 `wasSingleWordMode` 變數)。

在原本的程式碼中,只要判定為單字模式,就會無條件執行 `activeEditKeys.clear()`,這會導致之前產生的編輯行(edit-row)全部被清空。為了讓它和詞語模式一樣可以「保留並累加」編輯行,我們只需要改為:**只有在「從詞語模式切換到單字模式」時才清空,如果是「連續輸入單字」則不清空 `activeEditKeys**`。

以下是修改後的完整 `handleEditSubmit` 函式:

```javascript
function handleEditSubmit() {
  let val = document.getElementById('codeInput').value.trim();
  if (!val) return;

  // 【新增】記錄執行前的單字模式狀態,用來判斷是否為「再次執行單字模式」
  let wasSingleWordMode = isSingleWordMode;

  // Validation profile for the input string
  let isEnglishCode = /^[a-zA-Z]+$/.test(val);
  let isZCode = isEnglishCode && val[0].toLowerCase() === 'z';
  let isSingleWordInput = (!isEnglishCode && val.length === 1) || (isEnglishCode && !isZCode);
  let isPhraseInput = (!isEnglishCode && val.length > 1) || isZCode;

  // Check input constraints if the editor interface is already operational
  if (document.getElementById('editArea').style.display === 'block') {
    if (isSingleWordMode && isPhraseInput) {
      alert('Constraint Error: Single Word mode active. Input cannot contain phrases or z-starting codes.');
      return;
    }
    if (!isSingleWordMode && isSingleWordInput) {
      alert('Constraint Error: Phrase mode active. Input cannot contain single words or non-z codes.');
      return;
    }
  }

  document.getElementById('codeInput').value = '';
  selectedEditItem = null;
  justMovedItem = null;
  pendingAction = null;
  updateActionButtonsUI();

  // Detect Single Word Mode (English without 'z' prefix, or a single Chinese char)
  isSingleWordMode = false;
  singleWordActiveCode = '';
  currentTargetWord = '';

  if (isEnglishCode && val[0].toLowerCase() !== 'z') {
    isSingleWordMode = true;
    singleWordActiveCode = val.toLowerCase().substring(0, 4);
  } else if (!isEnglishCode && val.length === 1) {
    isSingleWordMode = true;
    currentTargetWord = val; // Set the target word to highlight the Chinese char
    // Reverse lookup code from charDefCodeMap
    for (let [code, chars] of charDefCodeMap) {
      if (chars.includes(val)) {
        singleWordActiveCode = code;
        break;
      }
    }
  }

  if (isSingleWordMode) {
    // 【修改】如果原本就已經是單字模式,就不清空 activeEditKeys,以保留當前的 edit-row
    if (!wasSingleWordMode) {
      activeEditKeys.clear(); // 只有從詞語模式切換過來時才清空
    }
    
    for (let i = 1; i <= singleWordActiveCode.length; i++) {
      activeEditKeys.add(singleWordActiveCode.substring(0, i));
    }
    
    // 【修改】比照詞語模式,若已有歷史快照則不重複覆蓋,確保 Undo 機制正常
    if (!originalSnapshotState || originalSnapshotState.size === 0) {
      originalSnapshotState = getSystemState();
    }
  } else {
    // Original Phrase Logic
    if (isEnglishCode) {
      if (val[0].toLowerCase() !== 'z') val = 'z' + val;
      val = val.substring(0, 4).toLowerCase();
      currentEditCode = val;
      currentTargetWord = '';
    } else {
      currentTargetWord = val;
      // Check input pattern profile
      if (/^[a-zA-Z]+$/.test(val)) {
        if (val[0].toLowerCase() !== 'z') val = 'z' + val;
        val = val.substring(0, 4).toLowerCase();
        currentEditCode = val;
        currentTargetWord = '';
      } else {
        currentTargetWord = val;
        let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
        if (!lines.includes(val)) {
          // CRITICAL FIX: Capture baseline state *before* pushing the new word
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
          saveStateForUndo(`Added new word: ${val}`);
          lines.push(val);
          processContent(JSON.stringify(lines));
        }
        let code = 'z';
        let c1 = ccharfirst.get(val[0]) || '';
        if (val.length === 1) {
          code += c1;
        } else if (val.length === 2) {
          let c2 = ccharfirst.get(val[1]) || '';
          let c3 = ccharsecond.get(val[1]) || 'x';
          code += c1 + c2 + c3;
        } else {
          let c2 = ccharfirst.get(val[1]) || '';
          let c3 = ccharfirst.get(val[2]) || '';
          code += c1 + c2 + c3;
        }
        currentEditCode = code.substring(0, 4);
      }
    }
    if (currentEditCode.length >= 1) activeEditKeys.add('z');
    if (currentEditCode.length >= 2) activeEditKeys.add(currentEditCode.substring(0, 2));
    if (currentEditCode.length >= 3) activeEditKeys.add(currentEditCode.substring(0, 3));
    if (currentEditCode.length >= 4) activeEditKeys.add(currentEditCode);
    // Standard initialization fallback block
    if (!originalSnapshotState || originalSnapshotState.size === 0) {
      originalSnapshotState = getSystemState();
    }
  }

  // Keep input area visible allowing repeated inputs
  document.getElementById('inputArea').style.display = 'block';
  document.getElementById('editArea').style.display = 'block';

  // Toggle action buttons visibility based on single word vs phrase mode
  if (isSingleWordMode) {
    document.getElementById('btnMoveEnd').style.display = 'none';
    document.getElementById('btnDelete').style.display = 'none';
  } else {
    document.getElementById('btnMoveEnd').style.display = 'inline-block';
    document.getElementById('btnDelete').style.display = 'inline-block';
  }

  document.getElementById('inputArea').style.display = 'block';
  document.getElementById('editArea').style.display = 'block';
  renderEditArea();
}

```

### 主要修改點說明:

1. **狀態攔截**:在函式頂端加入 `let wasSingleWordMode = isSingleWordMode;`,用來捕捉本次輸入前的模式。
2. **條件式清空**:將 `if (isSingleWordMode)` 區塊內的 `activeEditKeys.clear();` 包裹在 `if (!wasSingleWordMode)` 判斷中。這樣一來,若前一次已經是單字模式,連續輸入新單字時,舊有的編碼按鈕與 row 就不會被洗掉,而是會像詞語模式一樣疊加保留。
3. **快照保護**:同步將 `originalSnapshotState = getSystemState();` 改為安全守衛寫法 `if (!originalSnapshotState ...)`,避免連續輸入時把最初的備份狀態給覆蓋掉了。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

把所有注釋裡的中文全部翻譯成英文,同時,不存在「詞語模式跳到單字模式」的情況,當處於一種模式時,不允許跳到另一種模式。

代码: 全选

Here is the updated `handleEditSubmit` function. All comments have been translated into English, and the logic handles mode persistence correctly.

Since mode switching is strictly disallowed once active, the check `if (!wasSingleWordMode)` will only evaluate to `true` during the very first initialization of the single-word mode session (when `editArea` is not yet open), ensuring a clean slate. On all subsequent sequential inputs within the same mode, it will safely skip clearing the keys, preserving your edit rows just like phrase mode does.

```javascript
function handleEditSubmit() {
  let val = document.getElementById('codeInput').value.trim();
  if (!val) return;

  // Record the single word mode state before execution to check for repeated inputs
  let wasSingleWordMode = isSingleWordMode;

  // Validation profile for the input string
  let isEnglishCode = /^[a-zA-Z]+$/.test(val);
  let isZCode = isEnglishCode && val[0].toLowerCase() === 'z';
  let isSingleWordInput = (!isEnglishCode && val.length === 1) || (isEnglishCode && !isZCode);
  let isPhraseInput = (!isEnglishCode && val.length > 1) || isZCode;

  // Check input constraints if the editor interface is already operational
  if (document.getElementById('editArea').style.display === 'block') {
    if (isSingleWordMode && isPhraseInput) {
      alert('Constraint Error: Single Word mode active. Input cannot contain phrases or z-starting codes.');
      return;
    }
    if (!isSingleWordMode && isSingleWordInput) {
      alert('Constraint Error: Phrase mode active. Input cannot contain single words or non-z codes.');
      return;
    }
  }

  document.getElementById('codeInput').value = '';
  selectedEditItem = null;
  justMovedItem = null;
  pendingAction = null;
  updateActionButtonsUI();

  // Detect Single Word Mode (English without 'z' prefix, or a single Chinese char)
  isSingleWordMode = false;
  singleWordActiveCode = '';
  currentTargetWord = '';

  if (isEnglishCode && val[0].toLowerCase() !== 'z') {
    isSingleWordMode = true;
    singleWordActiveCode = val.toLowerCase().substring(0, 4);
  } else if (!isEnglishCode && val.length === 1) {
    isSingleWordMode = true;
    currentTargetWord = val; // Set the target word to highlight the Chinese char
    // Reverse lookup code from charDefCodeMap
    for (let [code, chars] of charDefCodeMap) {
      if (chars.includes(val)) {
        singleWordActiveCode = code;
        break;
      }
    }
  }

  if (isSingleWordMode) {
    // If already in single word mode, do not clear activeEditKeys to retain current edit rows
    if (!wasSingleWordMode) {
      activeEditKeys.clear(); // Only clear during initial session setup
    }
    
    for (let i = 1; i <= singleWordActiveCode.length; i++) {
      activeEditKeys.add(singleWordActiveCode.substring(0, i));
    }
    
    // Do not overwrite if an original snapshot state already exists, ensuring Undo mechanism works correctly
    if (!originalSnapshotState || originalSnapshotState.size === 0) {
      originalSnapshotState = getSystemState();
    }
  } else {
    // Original Phrase Logic
    if (isEnglishCode) {
      if (val[0].toLowerCase() !== 'z') val = 'z' + val;
      val = val.substring(0, 4).toLowerCase();
      currentEditCode = val;
      currentTargetWord = '';
    } else {
      currentTargetWord = val;
      // Check input pattern profile
      if (/^[a-zA-Z]+$/.test(val)) {
        if (val[0].toLowerCase() !== 'z') val = 'z' + val;
        val = val.substring(0, 4).toLowerCase();
        currentEditCode = val;
        currentTargetWord = '';
      } else {
        currentTargetWord = val;
        let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
        if (!lines.includes(val)) {
          // CRITICAL FIX: Capture baseline state *before* pushing the new word
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
          saveStateForUndo(`Added new word: ${val}`);
          lines.push(val);
          processContent(JSON.stringify(lines));
        }
        let code = 'z';
        let c1 = ccharfirst.get(val[0]) || '';
        if (val.length === 1) {
          code += c1;
        } else if (val.length === 2) {
          let c2 = ccharfirst.get(val[1]) || '';
          let c3 = ccharsecond.get(val[1]) || 'x';
          code += c1 + c2 + c3;
        } else {
          let c2 = ccharfirst.get(val[1]) || '';
          let c3 = ccharfirst.get(val[2]) || '';
          code += c1 + c2 + c3;
        }
        currentEditCode = code.substring(0, 4);
      }
    }
    if (currentEditCode.length >= 1) activeEditKeys.add('z');
    if (currentEditCode.length >= 2) activeEditKeys.add(currentEditCode.substring(0, 2));
    if (currentEditCode.length >= 3) activeEditKeys.add(currentEditCode.substring(0, 3));
    if (currentEditCode.length >= 4) activeEditKeys.add(currentEditCode);
    // Standard initialization fallback block
    if (!originalSnapshotState || originalSnapshotState.size === 0) {
      originalSnapshotState = getSystemState();
    }
  }

  // Keep input area visible allowing repeated inputs
  document.getElementById('inputArea').style.display = 'block';
  document.getElementById('editArea').style.display = 'block';

  // Toggle action buttons visibility based on single word vs phrase mode
  if (isSingleWordMode) {
    document.getElementById('btnMoveEnd').style.display = 'none';
    document.getElementById('btnDelete').style.display = 'none';
  } else {
    document.getElementById('btnMoveEnd').style.display = 'inline-block';
    document.getElementById('btnDelete').style.display = 'inline-block';
  }

  document.getElementById('inputArea').style.display = 'block';
  document.getElementById('editArea').style.display = 'block';
  renderEditArea();
}

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

domchar5763.html

在單字模式下,undo,redo,點擊log-item,都無效。查找原因並修改代碼。

需要明確給出所要修改的地方以及要替換的代碼,不用給出所有的代碼。所有的注釋都要使用英文。

代码: 全选

```
      function handleEditSubmit() {
        let val = document.getElementById('codeInput').value.trim();
        if (!val) return;

        // Record the single word mode state before execution to check for repeated inputs
        let wasSingleWordMode = isSingleWordMode;

        // Validation profile for the input string
        let isEnglishCode = /^[a-zA-Z]+$/.test(val);
        let isZCode = isEnglishCode && val[0].toLowerCase() === 'z';
        let isSingleWordInput = (!isEnglishCode && val.length === 1) || (isEnglishCode && !isZCode);
        let isPhraseInput = (!isEnglishCode && val.length > 1) || isZCode;

        // Check input constraints if the editor interface is already operational
        if (document.getElementById('editArea').style.display === 'block') {
          if (isSingleWordMode && isPhraseInput) {
            alert('Constraint Error: Single Word mode active. Input cannot contain phrases or z-starting codes.');
            return;
          }
          if (!isSingleWordMode && isSingleWordInput) {
            alert('Constraint Error: Phrase mode active. Input cannot contain single words or non-z codes.');
            return;
          }
        }

        document.getElementById('codeInput').value = '';
        selectedEditItem = null;
        justMovedItem = null;
        pendingAction = null;
        updateActionButtonsUI();

        // Detect Single Word Mode (English without 'z' prefix, or a single Chinese char)
        isSingleWordMode = false;
        singleWordActiveCode = '';
        currentTargetWord = '';

        if (isEnglishCode && val[0].toLowerCase() !== 'z') {
          isSingleWordMode = true;
          singleWordActiveCode = val.toLowerCase().substring(0, 4);
        } else if (!isEnglishCode && val.length === 1) {
          isSingleWordMode = true;
          currentTargetWord = val; // Set the target word to highlight the Chinese char
          // Reverse lookup code from charDefCodeMap
          for (let [code, chars] of charDefCodeMap) {
            if (chars.includes(val)) {
              singleWordActiveCode = code;
              break;
            }
          }
        }

        if (isSingleWordMode) {
          // If already in single word mode, do not clear activeEditKeys to retain current edit rows
          if (!wasSingleWordMode) {
            activeEditKeys.clear(); // Only clear during initial session setup
          }

          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          // Do not overwrite if an original snapshot state already exists, ensuring Undo mechanism works correctly
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        } else {
          // Original Phrase Logic
          if (isEnglishCode) {
            if (val[0].toLowerCase() !== 'z') val = 'z' + val;
            val = val.substring(0, 4).toLowerCase();
            currentEditCode = val;
            currentTargetWord = '';
          } else {
            currentTargetWord = val;
            // Check input pattern profile
            if (/^[a-zA-Z]+$/.test(val)) {
              if (val[0].toLowerCase() !== 'z') val = 'z' + val;
              val = val.substring(0, 4).toLowerCase();
              currentEditCode = val;
              currentTargetWord = '';
            } else {
              currentTargetWord = val;
              let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
              if (!lines.includes(val)) {
                // CRITICAL FIX: Capture baseline state *before* pushing the new word
                if (!originalSnapshotState || originalSnapshotState.size === 0) {
                  originalSnapshotState = getSystemState();
                }
                saveStateForUndo(`Added new word: ${val}`);
                lines.push(val);
                processContent(JSON.stringify(lines));
              }
              let code = 'z';
              let c1 = ccharfirst.get(val[0]) || '';
              if (val.length === 1) {
                code += c1;
              } else if (val.length === 2) {
                let c2 = ccharfirst.get(val[1]) || '';
                let c3 = ccharsecond.get(val[1]) || 'x';
                code += c1 + c2 + c3;
              } else {
                let c2 = ccharfirst.get(val[1]) || '';
                let c3 = ccharfirst.get(val[2]) || '';
                code += c1 + c2 + c3;
              }
              currentEditCode = code.substring(0, 4);
            }
          }
          if (currentEditCode.length >= 1) activeEditKeys.add('z');
          if (currentEditCode.length >= 2) activeEditKeys.add(currentEditCode.substring(0, 2));
          if (currentEditCode.length >= 3) activeEditKeys.add(currentEditCode.substring(0, 3));
          if (currentEditCode.length >= 4) activeEditKeys.add(currentEditCode);
          // Standard initialization fallback block
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        }

        // Keep input area visible allowing repeated inputs
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'block';

        // Toggle action buttons visibility based on single word vs phrase mode
        if (isSingleWordMode) {
          document.getElementById('btnMoveEnd').style.display = 'none';
          document.getElementById('btnDelete').style.display = 'none';
        } else {
          document.getElementById('btnMoveEnd').style.display = 'inline-block';
          document.getElementById('btnDelete').style.display = 'inline-block';
        }

        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'block';
        renderEditArea();
      }

      // Helper to restore state and refresh UI
      function applyState(targetState) {
        if (isSingleWordMode) {
          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        } else {
          filecontent = targetState.filecontent;
          processContent(JSON.stringify(targetState.lines));
        }
      }

      function handleUndo() {
        if (currentIndex < 0) return; // Allow undoing down to the initial state

        let oldState = getSystemState();
        currentIndex--; // Move pointer back

        let targetState;
        if (currentIndex === -1) {
          // Apply the BEFORE state of the first action (Initial State)
          targetState = historyStack[0];
        } else {
          // Apply the AFTER state of the action we stepped back to
          targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];
        }

        applyState(targetState);

        detectAndRenderChanges(oldState, getSystemState());
        justMovedItem = null;
        renderActionLogs();
      }

      function handleRedo() {
        if (currentIndex >= historyStack.length - 1) return;

        let oldState = getSystemState();
        currentIndex++; // Move pointer forward

        // Apply the AFTER state of the redone action
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        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

        // Apply the AFTER state of the selected action log
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        detectAndRenderChanges(oldState, getSystemState());
        justMovedItem = null;
        renderActionLogs();
      }

      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]);
        }

        // Push the 'before' state
        historyStack.push({
          lines: currentLines,
          filecontent: filecontent,
          charDefCodeMap: charDefMapCopy,
          desc: description,
          afterState: null // Will be populated after the current action completes
        });

        // Always point to the latest state after saving
        currentIndex = historyStack.length - 1;
        renderActionLogs();

        // Asynchronously capture the state AFTER the synchronous action finishes
        let capturedIndex = currentIndex;
        setTimeout(() => {
          if (historyStack[capturedIndex]) {
            let afterLines = mcc0string.split('\n').filter(l => l.trim() !== '');
            let afterCharDefMapCopy = new Map();
            for (let [k, v] of charDefCodeMap.entries()) {
              afterCharDefMapCopy.set(k, [...v]);
            }
            historyStack[capturedIndex].afterState = {
              lines: afterLines,
              filecontent: filecontent,
              charDefCodeMap: afterCharDefMapCopy
            };
          }
        }, 0);
      }
```

在單字模式下,undo,redo,點擊log-item,都無效。查找原因並修改代碼。

需要明確給出所要修改的地方以及要替換的代碼,不用給出所有的代碼。所有的注釋都要使用英文。
附件
domchar5763.html.7z
(14.67 KiB) 已下载 11 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

gemini thinking

代码: 全选

### 原因分析

在單字模式(`isSingleWordMode === true`)下,`undo`、`redo` 和點擊 `log-item` 失效的核心原因在於 **`applyState` 函數的邏輯缺失**:

1. **未復原數據狀態**:當 `isSingleWordMode` 為 true 時,`applyState` 只更新了 `activeEditKeys`,卻完全跳過了實際數據(`filecontent` 與記錄單字編碼映射的 `charDefCodeMap`)的復原。
2. **未刷新 UI 渲染**:單字模式下的修改直接作用於字元映射,狀態回溯後必須重新呼叫 `renderEditArea()`,否則畫面上的編輯列表不會同步更新。

---

### 修改方案

請將原本的 `applyState` 函數替換為以下代碼。此修改確保了在單字模式下能正確還原 `filecontent`、深拷貝(Deep Copy)還原 `charDefCodeMap`,並即時刷新編輯區界面。

#### 尋找並替換 `applyState` 函數:

```javascript
      // Helper to restore state and refresh UI
      function applyState(targetState) {
        if (isSingleWordMode) {
          // Restore core data state for single word mode
          filecontent = targetState.filecontent;
          if (targetState.charDefCodeMap) {
            charDefCodeMap = new Map();
            for (let [k, v] of targetState.charDefCodeMap.entries()) {
              charDefCodeMap.set(k, [...v]);
            }
          }

          // Re-populate active edit keys configuration
          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }

          // Crucial: Refresh the edit workspace UI to reflect the restored state
          renderEditArea();
        } else {
          // Original Phrase Logic
          filecontent = targetState.filecontent;
          processContent(JSON.stringify(targetState.lines));
        }
      }

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

domchar5764.html

我認為問題已解決,就是花了太多時間。
附件
domchar5764.html.7z
(14.76 KiB) 已下载 10 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

當前的代碼,如果一開始輸入的是一個新詞,則log中會出現一個item,點擊undo時會回到沒加新詞的狀態。如果一開始輸入的是一個已有的詞,則log中不會出現item。

現在要改成,如果一開始輸入的是一個新詞,則log中會出現一個item,點擊undo時不會回到沒加新詞的狀態,也就是說historyStack至少存在一項,currentIndex最小只能是0而不能是-1。如果一開始輸入的是一個已有的詞,則log中會出現第一個item,它的內容是「begin with searching ...」。點擊undo時回到第一個item就不能再回退。注意只限於一開始時輸入已有的詞會在log中出現,即在editArea不存在時。

代码: 全选

```
      function handleEditSubmit() {
        let val = document.getElementById('codeInput').value.trim();
        if (!val) return;

        // Record the single word mode state before execution to check for repeated inputs
        let wasSingleWordMode = isSingleWordMode;

        // Validation profile for the input string
        let isEnglishCode = /^[a-zA-Z]+$/.test(val);
        let isZCode = isEnglishCode && val[0].toLowerCase() === 'z';
        let isSingleWordInput = (!isEnglishCode && val.length === 1) || (isEnglishCode && !isZCode);
        let isPhraseInput = (!isEnglishCode && val.length > 1) || isZCode;

        // Check input constraints if the editor interface is already operational
        if (document.getElementById('editArea').style.display === 'block') {
          if (isSingleWordMode && isPhraseInput) {
            alert('Constraint Error: Single Word mode active. Input cannot contain phrases or z-starting codes.');
            return;
          }
          if (!isSingleWordMode && isSingleWordInput) {
            alert('Constraint Error: Phrase mode active. Input cannot contain single words or non-z codes.');
            return;
          }
        }

        document.getElementById('codeInput').value = '';
        selectedEditItem = null;
        justMovedItem = null;
        pendingAction = null;
        updateActionButtonsUI();

        // Detect Single Word Mode (English without 'z' prefix, or a single Chinese char)
        isSingleWordMode = false;
        singleWordActiveCode = '';
        currentTargetWord = '';

        if (isEnglishCode && val[0].toLowerCase() !== 'z') {
          isSingleWordMode = true;
          singleWordActiveCode = val.toLowerCase().substring(0, 4);
        } else if (!isEnglishCode && val.length === 1) {
          isSingleWordMode = true;
          currentTargetWord = val; // Set the target word to highlight the Chinese char
          // Reverse lookup code from charDefCodeMap
          for (let [code, chars] of charDefCodeMap) {
            if (chars.includes(val)) {
              singleWordActiveCode = code;
              break;
            }
          }
        }

        if (isSingleWordMode) {
          // If already in single word mode, do not clear activeEditKeys to retain current edit rows
          if (!wasSingleWordMode) {
            activeEditKeys.clear(); // Only clear during initial session setup
          }

          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          // Do not overwrite if an original snapshot state already exists, ensuring Undo mechanism works correctly
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        } else {
          // Original Phrase Logic
          if (isEnglishCode) {
            if (val[0].toLowerCase() !== 'z') val = 'z' + val;
            val = val.substring(0, 4).toLowerCase();
            currentEditCode = val;
            currentTargetWord = '';
          } else {
            currentTargetWord = val;
            // Check input pattern profile
            if (/^[a-zA-Z]+$/.test(val)) {
              if (val[0].toLowerCase() !== 'z') val = 'z' + val;
              val = val.substring(0, 4).toLowerCase();
              currentEditCode = val;
              currentTargetWord = '';
            } else {
              currentTargetWord = val;
              let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
              if (!lines.includes(val)) {
                // CRITICAL FIX: Capture baseline state *before* pushing the new word
                if (!originalSnapshotState || originalSnapshotState.size === 0) {
                  originalSnapshotState = getSystemState();
                }
                saveStateForUndo(`Added new word: ${val}`);
                lines.push(val);
                processContent(JSON.stringify(lines));
              }
              let code = 'z';
              let c1 = ccharfirst.get(val[0]) || '';
              if (val.length === 1) {
                code += c1;
              } else if (val.length === 2) {
                let c2 = ccharfirst.get(val[1]) || '';
                let c3 = ccharsecond.get(val[1]) || 'x';
                code += c1 + c2 + c3;
              } else {
                let c2 = ccharfirst.get(val[1]) || '';
                let c3 = ccharfirst.get(val[2]) || '';
                code += c1 + c2 + c3;
              }
              currentEditCode = code.substring(0, 4);
            }
          }
          if (currentEditCode.length >= 1) activeEditKeys.add('z');
          if (currentEditCode.length >= 2) activeEditKeys.add(currentEditCode.substring(0, 2));
          if (currentEditCode.length >= 3) activeEditKeys.add(currentEditCode.substring(0, 3));
          if (currentEditCode.length >= 4) activeEditKeys.add(currentEditCode);
          // Standard initialization fallback block
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        }

        // Keep input area visible allowing repeated inputs
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'block';

        // Toggle action buttons visibility based on single word vs phrase mode
        if (isSingleWordMode) {
          document.getElementById('btnMoveEnd').style.display = 'none';
          document.getElementById('btnDelete').style.display = 'none';
        } else {
          document.getElementById('btnMoveEnd').style.display = 'inline-block';
          document.getElementById('btnDelete').style.display = 'inline-block';
        }

        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'block';
        renderEditArea();
      }

      // Helper to restore state and refresh UI
      function applyState(targetState) {
        if (isSingleWordMode) {
          // Restore core data state for single word mode
          filecontent = targetState.filecontent;
          if (targetState.charDefCodeMap) {
            charDefCodeMap = new Map();
            for (let [k, v] of targetState.charDefCodeMap.entries()) {
              charDefCodeMap.set(k, [...v]);
            }
          }

          // Re-populate active edit keys configuration
          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }

          // Crucial: Refresh the edit workspace UI to reflect the restored state
          renderEditArea();
        } else {
          // Original Phrase Logic
          filecontent = targetState.filecontent;
          processContent(JSON.stringify(targetState.lines));
        }
      }

      function handleUndo() {
        if (currentIndex < 0) return; // Allow undoing down to the initial state

        let oldState = getSystemState();
        currentIndex--; // Move pointer back

        let targetState;
        if (currentIndex === -1) {
          // Apply the BEFORE state of the first action (Initial State)
          targetState = historyStack[0];
        } else {
          // Apply the AFTER state of the action we stepped back to
          targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];
        }

        applyState(targetState);

        detectAndRenderChanges(oldState, getSystemState());
        justMovedItem = null;
        renderActionLogs();
      }

      function handleRedo() {
        if (currentIndex >= historyStack.length - 1) return;

        let oldState = getSystemState();
        currentIndex++; // Move pointer forward

        // Apply the AFTER state of the redone action
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        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

        // Apply the AFTER state of the selected action log
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        detectAndRenderChanges(oldState, getSystemState());
        justMovedItem = null;
        renderActionLogs();
      }

      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]);
        }

        // Push the 'before' state
        historyStack.push({
          lines: currentLines,
          filecontent: filecontent,
          charDefCodeMap: charDefMapCopy,
          desc: description,
          afterState: null // Will be populated after the current action completes
        });

        // Always point to the latest state after saving
        currentIndex = historyStack.length - 1;
        renderActionLogs();

        // Asynchronously capture the state AFTER the synchronous action finishes
        let capturedIndex = currentIndex;
        setTimeout(() => {
          if (historyStack[capturedIndex]) {
            let afterLines = mcc0string.split('\n').filter(l => l.trim() !== '');
            let afterCharDefMapCopy = new Map();
            for (let [k, v] of charDefCodeMap.entries()) {
              afterCharDefMapCopy.set(k, [...v]);
            }
            historyStack[capturedIndex].afterState = {
              lines: afterLines,
              filecontent: filecontent,
              charDefCodeMap: afterCharDefMapCopy
            };
          }
        }, 0);
      }
```

當前的代碼,如果一開始輸入的是一個新詞,則log中會出現一個item,點擊undo時會回到沒加新詞的狀態。如果一開始輸入的是一個已有的詞,則log中不會出現item。

現在要改成,如果一開始輸入的是一個新詞,則log中會出現一個item,點擊undo時不會回到沒加新詞的狀態,也就是說historyStack至少存在一項,currentIndex最小只能是0而不能是-1。如果一開始輸入的是一個已有的詞,則log中會出現第一個item,它的內容是「begin with searching ...」。點擊undo時回到第一個item就不能再回退。注意只限於一開始時輸入已有的詞會在log中出現,即在editArea不存在時。

所有的注釋都要使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

gemini 3.1pro

代码: 全选

Here is the modified code to meet your requirements.

I have adjusted the logic so that any initial input (when `editArea` is not yet visible) will register as the foundational action in the history log. If it's a new word, it logs as "Added new word: ..."; if it's an existing word, it logs as "begin with searching ...". I also updated the `handleUndo` function to establish `0` as the absolute minimum `currentIndex`, ensuring the user cannot back out of the very first state (the initial new word or the initial search). All comments are provided in English.

```javascript
      function handleEditSubmit() {
        let val = document.getElementById('codeInput').value.trim();
        if (!val) return;

        // Check if the editor interface is hidden, indicating this is the initial input
        let isInitialInput = document.getElementById('editArea').style.display !== 'block';

        // Record the single word mode state before execution to check for repeated inputs
        let wasSingleWordMode = isSingleWordMode;

        // Validation profile for the input string
        let isEnglishCode = /^[a-zA-Z]+$/.test(val);
        let isZCode = isEnglishCode && val[0].toLowerCase() === 'z';
        let isSingleWordInput = (!isEnglishCode && val.length === 1) || (isEnglishCode && !isZCode);
        let isPhraseInput = (!isEnglishCode && val.length > 1) || isZCode;

        // Check input constraints if the editor interface is already operational
        if (!isInitialInput) {
          if (isSingleWordMode && isPhraseInput) {
            alert('Constraint Error: Single Word mode active. Input cannot contain phrases or z-starting codes.');
            return;
          }
          if (!isSingleWordMode && isSingleWordInput) {
            alert('Constraint Error: Phrase mode active. Input cannot contain single words or non-z codes.');
            return;
          }
        }

        // Determine if a new word is about to be added to the dictionary
        let willAddNewWord = false;
        if (!isEnglishCode && val.length > 1) {
          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          if (!lines.includes(val)) {
            willAddNewWord = true;
          }
        }

        // If this is the initial input and it's an existing word/code, log the starting action
        if (isInitialInput && !willAddNewWord) {
          saveStateForUndo(`begin with searching ${val}`);
        }

        document.getElementById('codeInput').value = '';
        selectedEditItem = null;
        justMovedItem = null;
        pendingAction = null;
        updateActionButtonsUI();

        // Detect Single Word Mode (English without 'z' prefix, or a single Chinese char)
        isSingleWordMode = false;
        singleWordActiveCode = '';
        currentTargetWord = '';

        if (isEnglishCode && val[0].toLowerCase() !== 'z') {
          isSingleWordMode = true;
          singleWordActiveCode = val.toLowerCase().substring(0, 4);
        } else if (!isEnglishCode && val.length === 1) {
          isSingleWordMode = true;
          currentTargetWord = val; // Set the target word to highlight the Chinese char
          // Reverse lookup code from charDefCodeMap
          for (let [code, chars] of charDefCodeMap) {
            if (chars.includes(val)) {
              singleWordActiveCode = code;
              break;
            }
          }
        }

        if (isSingleWordMode) {
          // If already in single word mode, do not clear activeEditKeys to retain current edit rows
          if (!wasSingleWordMode) {
            activeEditKeys.clear(); // Only clear during initial session setup
          }

          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          // Do not overwrite if an original snapshot state already exists, ensuring Undo mechanism works correctly
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        } else {
          // Original Phrase Logic
          if (isEnglishCode) {
            if (val[0].toLowerCase() !== 'z') val = 'z' + val;
            val = val.substring(0, 4).toLowerCase();
            currentEditCode = val;
            currentTargetWord = '';
          } else {
            currentTargetWord = val;
            // Check input pattern profile
            if (/^[a-zA-Z]+$/.test(val)) {
              if (val[0].toLowerCase() !== 'z') val = 'z' + val;
              val = val.substring(0, 4).toLowerCase();
              currentEditCode = val;
              currentTargetWord = '';
            } else {
              currentTargetWord = val;
              let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
              if (!lines.includes(val)) {
                // CRITICAL FIX: Capture baseline state *before* pushing the new word
                if (!originalSnapshotState || originalSnapshotState.size === 0) {
                  originalSnapshotState = getSystemState();
                }
                saveStateForUndo(`Added new word: ${val}`);
                lines.push(val);
                processContent(JSON.stringify(lines));
              }
              let code = 'z';
              let c1 = ccharfirst.get(val[0]) || '';
              if (val.length === 1) {
                code += c1;
              } else if (val.length === 2) {
                let c2 = ccharfirst.get(val[1]) || '';
                let c3 = ccharsecond.get(val[1]) || 'x';
                code += c1 + c2 + c3;
              } else {
                let c2 = ccharfirst.get(val[1]) || '';
                let c3 = ccharfirst.get(val[2]) || '';
                code += c1 + c2 + c3;
              }
              currentEditCode = code.substring(0, 4);
            }
          }
          if (currentEditCode.length >= 1) activeEditKeys.add('z');
          if (currentEditCode.length >= 2) activeEditKeys.add(currentEditCode.substring(0, 2));
          if (currentEditCode.length >= 3) activeEditKeys.add(currentEditCode.substring(0, 3));
          if (currentEditCode.length >= 4) activeEditKeys.add(currentEditCode);
          // Standard initialization fallback block
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        }

        // Keep input area visible allowing repeated inputs
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'block';

        // Toggle action buttons visibility based on single word vs phrase mode
        if (isSingleWordMode) {
          document.getElementById('btnMoveEnd').style.display = 'none';
          document.getElementById('btnDelete').style.display = 'none';
        } else {
          document.getElementById('btnMoveEnd').style.display = 'inline-block';
          document.getElementById('btnDelete').style.display = 'inline-block';
        }

        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'block';
        renderEditArea();
      }

      // Helper to restore state and refresh UI
      function applyState(targetState) {
        if (isSingleWordMode) {
          // Restore core data state for single word mode
          filecontent = targetState.filecontent;
          if (targetState.charDefCodeMap) {
            charDefCodeMap = new Map();
            for (let [k, v] of targetState.charDefCodeMap.entries()) {
              charDefCodeMap.set(k, [...v]);
            }
          }

          // Re-populate active edit keys configuration
          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }

          // Crucial: Refresh the edit workspace UI to reflect the restored state
          renderEditArea();
        } else {
          // Original Phrase Logic
          filecontent = targetState.filecontent;
          processContent(JSON.stringify(targetState.lines));
        }
      }

      function handleUndo() {
        // Prevent undoing past the initial logged action (index 0).
        // The user can no longer revert to the unlogged state (-1).
        if (currentIndex <= 0) return;

        let oldState = getSystemState();
        currentIndex--; // Move pointer back

        // Apply the AFTER state of the action we stepped back to
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        detectAndRenderChanges(oldState, getSystemState());
        justMovedItem = null;
        renderActionLogs();
      }

      function handleRedo() {
        if (currentIndex >= historyStack.length - 1) return;

        let oldState = getSystemState();
        currentIndex++; // Move pointer forward

        // Apply the AFTER state of the redone action
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        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

        // Apply the AFTER state of the selected action log
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        detectAndRenderChanges(oldState, getSystemState());
        justMovedItem = null;
        renderActionLogs();
      }

      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]);
        }

        // Push the 'before' state
        historyStack.push({
          lines: currentLines,
          filecontent: filecontent,
          charDefCodeMap: charDefMapCopy,
          desc: description,
          afterState: null // Will be populated after the current action completes
        });

        // Always point to the latest state after saving
        currentIndex = historyStack.length - 1;
        renderActionLogs();

        // Asynchronously capture the state AFTER the synchronous action finishes
        let capturedIndex = currentIndex;
        setTimeout(() => {
          if (historyStack[capturedIndex]) {
            let afterLines = mcc0string.split('\n').filter(l => l.trim() !== '');
            let afterCharDefMapCopy = new Map();
            for (let [k, v] of charDefCodeMap.entries()) {
              afterCharDefMapCopy.set(k, [...v]);
            }
            historyStack[capturedIndex].afterState = {
              lines: afterLines,
              filecontent: filecontent,
              charDefCodeMap: afterCharDefMapCopy
            };
          }
        }, 0);
      }

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

當我在單字模式中作了改動,點擊cancel或finish之後,如果再次進入詞語模式,對比欄全部變為空,這是不對的,程式應在上一次cancal或finish後,清空對比欄的數據,不能影響到下一次的進程。

代码: 全选

```javascript
      function handleEditSubmit() {
        let val = document.getElementById('codeInput').value.trim();
        if (!val) return;

        // Check if the editor interface is hidden, indicating this is the initial input
        let isInitialInput = document.getElementById('editArea').style.display !== 'block';

        // Record the single word mode state before execution to check for repeated inputs
        let wasSingleWordMode = isSingleWordMode;

        // Validation profile for the input string
        let isEnglishCode = /^[a-zA-Z]+$/.test(val);
        let isZCode = isEnglishCode && val[0].toLowerCase() === 'z';
        let isSingleWordInput = (!isEnglishCode && val.length === 1) || (isEnglishCode && !isZCode);
        let isPhraseInput = (!isEnglishCode && val.length > 1) || isZCode;

        // Check input constraints if the editor interface is already operational
        if (!isInitialInput) {
          if (isSingleWordMode && isPhraseInput) {
            alert('Constraint Error: Single Word mode active. Input cannot contain phrases or z-starting codes.');
            return;
          }
          if (!isSingleWordMode && isSingleWordInput) {
            alert('Constraint Error: Phrase mode active. Input cannot contain single words or non-z codes.');
            return;
          }
        }

        // Determine if a new word is about to be added to the dictionary
        let willAddNewWord = false;
        if (!isEnglishCode && val.length > 1) {
          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          if (!lines.includes(val)) {
            willAddNewWord = true;
          }
        }

        // If this is the initial input and it's an existing word/code, log the starting action
        if (isInitialInput && !willAddNewWord) {
          saveStateForUndo(`begin with searching ${val}`);
        }

        document.getElementById('codeInput').value = '';
        selectedEditItem = null;
        justMovedItem = null;
        pendingAction = null;
        updateActionButtonsUI();

        // Detect Single Word Mode (English without 'z' prefix, or a single Chinese char)
        isSingleWordMode = false;
        singleWordActiveCode = '';
        currentTargetWord = '';

        if (isEnglishCode && val[0].toLowerCase() !== 'z') {
          isSingleWordMode = true;
          singleWordActiveCode = val.toLowerCase().substring(0, 4);
        } else if (!isEnglishCode && val.length === 1) {
          isSingleWordMode = true;
          currentTargetWord = val; // Set the target word to highlight the Chinese char
          // Reverse lookup code from charDefCodeMap
          for (let [code, chars] of charDefCodeMap) {
            if (chars.includes(val)) {
              singleWordActiveCode = code;
              break;
            }
          }
        }

        if (isSingleWordMode) {
          // If already in single word mode, do not clear activeEditKeys to retain current edit rows
          if (!wasSingleWordMode) {
            activeEditKeys.clear(); // Only clear during initial session setup
          }

          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          // Do not overwrite if an original snapshot state already exists, ensuring Undo mechanism works correctly
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        } else {
          // Original Phrase Logic
          if (isEnglishCode) {
            if (val[0].toLowerCase() !== 'z') val = 'z' + val;
            val = val.substring(0, 4).toLowerCase();
            currentEditCode = val;
            currentTargetWord = '';
          } else {
            currentTargetWord = val;
            // Check input pattern profile
            if (/^[a-zA-Z]+$/.test(val)) {
              if (val[0].toLowerCase() !== 'z') val = 'z' + val;
              val = val.substring(0, 4).toLowerCase();
              currentEditCode = val;
              currentTargetWord = '';
            } else {
              currentTargetWord = val;
              let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
              if (!lines.includes(val)) {
                // CRITICAL FIX: Capture baseline state *before* pushing the new word
                if (!originalSnapshotState || originalSnapshotState.size === 0) {
                  originalSnapshotState = getSystemState();
                }
                saveStateForUndo(`Added new word: ${val}`);
                lines.push(val);
                processContent(JSON.stringify(lines));
              }
              let code = 'z';
              let c1 = ccharfirst.get(val[0]) || '';
              if (val.length === 1) {
                code += c1;
              } else if (val.length === 2) {
                let c2 = ccharfirst.get(val[1]) || '';
                let c3 = ccharsecond.get(val[1]) || 'x';
                code += c1 + c2 + c3;
              } else {
                let c2 = ccharfirst.get(val[1]) || '';
                let c3 = ccharfirst.get(val[2]) || '';
                code += c1 + c2 + c3;
              }
              currentEditCode = code.substring(0, 4);
            }
          }
          if (currentEditCode.length >= 1) activeEditKeys.add('z');
          if (currentEditCode.length >= 2) activeEditKeys.add(currentEditCode.substring(0, 2));
          if (currentEditCode.length >= 3) activeEditKeys.add(currentEditCode.substring(0, 3));
          if (currentEditCode.length >= 4) activeEditKeys.add(currentEditCode);
          // Standard initialization fallback block
          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }
        }

        // Keep input area visible allowing repeated inputs
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'block';

        // Toggle action buttons visibility based on single word vs phrase mode
        if (isSingleWordMode) {
          document.getElementById('btnMoveEnd').style.display = 'none';
          document.getElementById('btnDelete').style.display = 'none';
        } else {
          document.getElementById('btnMoveEnd').style.display = 'inline-block';
          document.getElementById('btnDelete').style.display = 'inline-block';
        }

        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'block';
        renderEditArea();
      }

      // Helper to restore state and refresh UI
      function applyState(targetState) {
        if (isSingleWordMode) {
          // Restore core data state for single word mode
          filecontent = targetState.filecontent;
          if (targetState.charDefCodeMap) {
            charDefCodeMap = new Map();
            for (let [k, v] of targetState.charDefCodeMap.entries()) {
              charDefCodeMap.set(k, [...v]);
            }
          }

          // Re-populate active edit keys configuration
          for (let i = 1; i <= singleWordActiveCode.length; i++) {
            activeEditKeys.add(singleWordActiveCode.substring(0, i));
          }

          if (!originalSnapshotState || originalSnapshotState.size === 0) {
            originalSnapshotState = getSystemState();
          }

          // Crucial: Refresh the edit workspace UI to reflect the restored state
          renderEditArea();
        } else {
          // Original Phrase Logic
          filecontent = targetState.filecontent;
          processContent(JSON.stringify(targetState.lines));
        }
      }

      function handleUndo() {
        // Prevent undoing past the initial logged action (index 0).
        // The user can no longer revert to the unlogged state (-1).
        if (currentIndex <= 0) return;

        let oldState = getSystemState();
        currentIndex--; // Move pointer back

        // Apply the AFTER state of the action we stepped back to
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        detectAndRenderChanges(oldState, getSystemState());
        justMovedItem = null;
        renderActionLogs();
      }

      function handleRedo() {
        if (currentIndex >= historyStack.length - 1) return;

        let oldState = getSystemState();
        currentIndex++; // Move pointer forward

        // Apply the AFTER state of the redone action
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        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

        // Apply the AFTER state of the selected action log
        let targetState = historyStack[currentIndex].afterState || historyStack[currentIndex];

        applyState(targetState);

        detectAndRenderChanges(oldState, getSystemState());
        justMovedItem = null;
        renderActionLogs();
      }

      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]);
        }

        // Push the 'before' state
        historyStack.push({
          lines: currentLines,
          filecontent: filecontent,
          charDefCodeMap: charDefMapCopy,
          desc: description,
          afterState: null // Will be populated after the current action completes
        });

        // Always point to the latest state after saving
        currentIndex = historyStack.length - 1;
        renderActionLogs();

        // Asynchronously capture the state AFTER the synchronous action finishes
        let capturedIndex = currentIndex;
        setTimeout(() => {
          if (historyStack[capturedIndex]) {
            let afterLines = mcc0string.split('\n').filter(l => l.trim() !== '');
            let afterCharDefMapCopy = new Map();
            for (let [k, v] of charDefCodeMap.entries()) {
              afterCharDefMapCopy.set(k, [...v]);
            }
            historyStack[capturedIndex].afterState = {
              lines: afterLines,
              filecontent: filecontent,
              charDefCodeMap: afterCharDefMapCopy
            };
          }
        }, 0);
      }

      function handleCancelEdit() {
        // Revert to initial state before any edits in this session were made
        if (historyStack.length > 0) {
          applyState(historyStack[0]);
        }

        // Close the edit interface
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';

        // Reset tracking variables without updating originalSnapshotState
        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        justMovedItem = null;
        activeEditKeys.clear();
        changedWords.clear();
        pendingAction = null;
        historyStack = [];
        currentIndex = -1;
        renderActionLogs();
        updateActionButtonsUI();
      }

      function handleEditCloseOnly() {
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';

        originalSnapshotState = getSystemState();

        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        justMovedItem = null;
        activeEditKeys.clear();
        changedWords.clear();
        pendingAction = null;
        historyStack = [];
        currentIndex = -1;
        renderActionLogs(); // Visually empty out the log UI on close
        updateActionButtonsUI();
      }

```

當我在單字模式中作了改動,點擊cancel或finish之後,如果再次進入詞語模式,對比欄全部變為空,這是不對的,程式應在上一次cancal或finish後,清空對比欄的數據,不能影響到下一次的進程。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

代码: 全选

完全理解你的困擾。狀態殘留導致下一次操作的對比欄位失效,這在切換不同模式時確實是個常見且讓人頭痛的邏輯漏洞。

問題的根源在於你的 **`originalSnapshotState`(原始快照狀態)沒有被正確釋放**。

在 `handleEditSubmit` 中,程式依賴以下邏輯來擷取當前進程的基準點:

```javascript
if (!originalSnapshotState || originalSnapshotState.size === 0) {
  originalSnapshotState = getSystemState();
}

```

但在目前的 `handleCancelEdit` 與 `handleEditCloseOnly` 函數中,`originalSnapshotState` 要麼被原封不動地保留,要麼被更新成當前狀態,**卻從未被清空**。這導致你下一次進入「詞語模式」時,程式誤以為基準點已經存在,從而跳過了擷取新基準點的步驟,最終導致對比欄抓不到正確的差異數據而變為空。

以下是針對這兩個函數的修正程式碼。

### 修正方案

你需要將 `originalSnapshotState` 在退出編輯時強制設為 `null`,強迫下一次的 `handleEditSubmit` 重新抓取乾淨的基準點。

**1. 修正 `handleCancelEdit**`

```javascript
      function handleCancelEdit() {
        // Revert to initial state before any edits in this session were made
        if (historyStack.length > 0) {
          applyState(historyStack[0]);
        }

        // Close the edit interface
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';

        // 【關鍵修正】:徹底清空快照狀態,避免干擾下一次進程
        originalSnapshotState = null; 

        // Reset other tracking variables
        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        justMovedItem = null;
        activeEditKeys.clear();
        changedWords.clear();
        pendingAction = null;
        historyStack = [];
        currentIndex = -1;
        renderActionLogs();
        updateActionButtonsUI();
      }

```

**2. 修正 `handleEditCloseOnly**`

```javascript
      function handleEditCloseOnly() {
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';

        // 【關鍵修正】:這裡原本是 originalSnapshotState = getSystemState(); 
        // 應改為 null,讓下一次重新進入時再透過 handleEditSubmit 抓取最新的基準點
        originalSnapshotState = null; 

        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        justMovedItem = null;
        activeEditKeys.clear();
        changedWords.clear();
        pendingAction = null;
        historyStack = [];
        currentIndex = -1;
        renderActionLogs(); // Visually empty out the log UI on close
        updateActionButtonsUI();
      }

```

### 為什麼這樣改有效?

* **Cancel (取消)**:放棄所有更改並恢復到歷史紀錄的第一筆。此時清空 `originalSnapshotState`、`activeEditKeys` 與 `changedWords`,確保系統狀態完全重置。
* **Finish / Close (完成並關閉)**:保留現有的更改並退出編輯區。將 `originalSnapshotState` 設為 `null` 可以確保下次使用者再次觸發 `handleEditSubmit` 輸入新代碼時,程式能精準地把「那一刻」當作新的比較基準,對比欄就能正常顯示該次進程的改動了。
gemini 3.1 pro
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

domchar5765.html

修正了一個極其嚴重的問題。
附件
domchar5765.html.7z
(14.86 KiB) 已下载 10 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

把「清空」按鈕替換成h1{哈哈倉頡碼表編輯統計工具},加上相應的css。當它下方的選取文件框已有文件時,再次點擊,則將觸發原「清空」按鈕的功能。

需要明確給出所要修改的地方和所要替換的代碼,不用給出全部代碼。所有的注釋都要使用英文。

代码: 全选

把「清空」按鈕替換成h1{哈哈倉頡碼表編輯統計工具},加上相應的css。當它下方的選取文件框已有文件時,再次點擊,則將觸發原「清空」按鈕的功能。

需要明確給出所要修改的地方和所要替換的代碼,不用給出全部代碼。所有的注釋都要使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

gemini 3.1 pro

代码: 全选

Here are the specific modifications you need to make to replace the "Clear" button with the interactive `<h1>` title.

### 1. HTML Modification

Locate the `<button>` tag at the very beginning of your `<body>` and replace it with the new `<h1>` tag.

**Find this code:**

```html
    <button id="clearButton" onclick="resetPage()">清空</button>

```

**Replace it with:**

```html
    <h1 id="mainTitle" onclick="handleTitleClick()">哈哈倉頡碼表編輯統計工具</h1>

```

---

### 2. CSS Modification

Locate the styling for the old clear button in your `<style>` block and replace it with the styling for the new title.

**Find this code:**

```css
      button#clearButton {
        display: block;
        width: 100%;
        padding: 10px;
        background-color: #4caf50;
        color: white;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        background-color: #f44336; /* 紅色清空按鈕 */
      }

```

**Replace it with:**

```css
      /* Styles for the new H1 title */
      h1#mainTitle {
        text-align: center;
        color: #333;
        margin-top: 10px;
        margin-bottom: 20px;
        transition: color 0.2s;
        user-select: none;
      }
      
      /* Active state when a file is loaded, making it act as a clear button */
      h1#mainTitle.active-clear {
        cursor: pointer;
        color: #f44336; /* Red color to indicate clear/reset action */
      }
      
      h1#mainTitle.active-clear:hover {
        color: #da190b;
        text-decoration: underline;
      }

```

---

### 3. JavaScript Modifications

You will need to add a new function to handle the title click and update two existing functions to toggle the clickable state of the title.

**A. Add the new click handler function**
Add this new function anywhere inside your `<script>` tag (for example, right above the `resetPage` function):

```javascript
      // Trigger reset only if a file is currently loaded
      function handleTitleClick() {
        if (filecontent !== '') {
          resetPage();
        }
      }

```

**B. Update `resetPage()` to remove the active state**
Locate the `resetPage` function and add the lines to reset the title's styling when the data is cleared.

**Find this code inside `resetPage(readfile = true)`:**

```javascript
        if (readfile) {
          filecontent = '';
          originalFileName = ''; // Reset the stored filename

```

**Add the title reset logic right below it:**

```javascript
        if (readfile) {
          filecontent = '';
          originalFileName = ''; // Reset the stored filename
          
          // Remove active clear state from the title
          document.getElementById('mainTitle').classList.remove('active-clear');
          document.getElementById('mainTitle').title = '';

```

**C. Update `handleFile()` to activate the clear function**
Locate the `handleFile` function and add the lines to apply the clickable CSS class and hover tooltip when a file is successfully selected.

**Find this code inside `handleFile(event)`:**

```javascript
        infoDiv.textContent = `File Name: ${file.name} (${file.size} bytes)`;
        infoDiv.style.display = 'block';

```

**Add the title activation logic right below it:**

```javascript
        infoDiv.textContent = `File Name: ${file.name} (${file.size} bytes)`;
        infoDiv.style.display = 'block';

        // Make the title act as the clear button now that a file is loaded
        document.getElementById('mainTitle').classList.add('active-clear');
        document.getElementById('mainTitle').title = 'Click to clear data and reset';

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

在Statistics欄目的「重碼統計」中,去掉Deduplication Results欄目,同時調整相應的css。


gemini 3.1 pro

代码: 全选

Here are the modifications required to remove the "Deduplication Results" column, update the CSS layout from three columns to two, and remove the corresponding JavaScript lines to prevent the app from crashing.

### 1. HTML Modification

Locate the `<div class="results-three-col">` block in your HTML. We will rename the class and remove the middle column entirely.

**Find this code:**

```html
          <div class="results-three-col">
            <div class="result-col">
              <div class="textarea-header" style="display: flex; justify-content: space-between; align-items: center">
                <span>Duplication Results (<span id="dupResultCount">0</span>)</span>
                <svg
                  onclick="toggleTextarea('collapseMultipleText')"
                  style="cursor: pointer; width: 20px; height: 20px; fill: #666; transition: fill 0.2s"
                  onmouseover="this.style.fill = '#2196f3'"
                  onmouseout="this.style.fill = '#666'"
                  viewBox="0 0 24 24"
                >
                  <path
                    d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"
                  />
                </svg>
              </div>
              <ul id="collapseMultipleText" class="result-scroll-list"></ul>
              <textarea
                id="collapseMultipleText_textarea"
                class="result-textarea"
                style="display: none; flex: 1; margin-top: 5px; box-sizing: border-box"
              ></textarea>
            </div>
            <div class="result-col">
              <div class="textarea-header" style="display: flex; justify-content: space-between; align-items: center">
                <span>Deduplication Results (<span id="dedupResultCount">0</span>)</span>
                <svg
                  onclick="toggleTextarea('gzhahaText')"
                  style="cursor: pointer; width: 20px; height: 20px; fill: #666; transition: fill 0.2s"
                  onmouseover="this.style.fill = '#2196f3'"
                  onmouseout="this.style.fill = '#666'"
                  viewBox="0 0 24 24"
                >
                  <path
                    d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"
                  />
                </svg>
              </div>
              <ul id="gzhahaText" class="result-scroll-list"></ul>
              <textarea
                id="gzhahaText_textarea"
                class="result-textarea"
                style="display: none; flex: 1; margin-top: 5px; box-sizing: border-box"
              ></textarea>
            </div>
            <div class="result-col">
              <div class="textarea-header" style="display: flex; justify-content: space-between; align-items: center">
                <span>Out of Bounds (<span id="outOfBoundsCount">0</span>)</span>
                <svg
                  onclick="toggleTextarea('outOfBoundsText')"
                  style="cursor: pointer; width: 20px; height: 20px; fill: #666; transition: fill 0.2s"
                  onmouseover="this.style.fill = '#2196f3'"
                  onmouseout="this.style.fill = '#666'"
                  viewBox="0 0 24 24"
                >
                  <path
                    d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"
                  />
                </svg>
              </div>
              <ul id="outOfBoundsText" class="result-scroll-list"></ul>
              <textarea
                id="outOfBoundsText_textarea"
                class="result-textarea"
                style="display: none; flex: 1; margin-top: 5px; box-sizing: border-box"
              ></textarea>
            </div>
          </div>

```

**Replace it with:**

```html
          <div class="results-two-col">
            <div class="result-col">
              <div class="textarea-header" style="display: flex; justify-content: space-between; align-items: center">
                <span>Duplication Results (<span id="dupResultCount">0</span>)</span>
                <svg
                  onclick="toggleTextarea('collapseMultipleText')"
                  style="cursor: pointer; width: 20px; height: 20px; fill: #666; transition: fill 0.2s"
                  onmouseover="this.style.fill = '#2196f3'"
                  onmouseout="this.style.fill = '#666'"
                  viewBox="0 0 24 24"
                >
                  <path
                    d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"
                  />
                </svg>
              </div>
              <ul id="collapseMultipleText" class="result-scroll-list"></ul>
              <textarea
                id="collapseMultipleText_textarea"
                class="result-textarea"
                style="display: none; flex: 1; margin-top: 5px; box-sizing: border-box"
              ></textarea>
            </div>
            <div class="result-col">
              <div class="textarea-header" style="display: flex; justify-content: space-between; align-items: center">
                <span>Out of Bounds (<span id="outOfBoundsCount">0</span>)</span>
                <svg
                  onclick="toggleTextarea('outOfBoundsText')"
                  style="cursor: pointer; width: 20px; height: 20px; fill: #666; transition: fill 0.2s"
                  onmouseover="this.style.fill = '#2196f3'"
                  onmouseout="this.style.fill = '#666'"
                  viewBox="0 0 24 24"
                >
                  <path
                    d="M14 2H6c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"
                  />
                </svg>
              </div>
              <ul id="outOfBoundsText" class="result-scroll-list"></ul>
              <textarea
                id="outOfBoundsText_textarea"
                class="result-textarea"
                style="display: none; flex: 1; margin-top: 5px; box-sizing: border-box"
              ></textarea>
            </div>
          </div>

```

---

### 2. CSS Modification

Update the class name in your `<style>` tag to match the new 2-column layout.

**Find this code:**

```css
      /* --- 3-Column Stats Results Layout --- */
      .results-three-col {
        display: flex;
        gap: 15px;
        height: 480px;
        margin-top: 20px;
        margin-bottom: 20px;
      }

```

**Replace it with:**

```css
      /* --- 2-Column Stats Results Layout --- */
      .results-two-col {
        display: flex;
        gap: 15px;
        height: 480px;
        margin-top: 20px;
        margin-bottom: 20px;
      }

```

**And find the Media Query code:**

```css
      @media (max-width: 600px) {
        #lenDupStatsContainer {
          flex-direction: column;
        }
        .results-three-col {
          flex-direction: column;
          height: auto;
        }
        .result-col {
          max-height: 480px;
        }
      }

```

**Replace it with:**

```css
      @media (max-width: 600px) {
        #lenDupStatsContainer {
          flex-direction: column;
        }
        /* Updated class name for responsive layout */
        .results-two-col {
          flex-direction: column;
          height: auto;
        }
        .result-col {
          max-height: 480px;
        }
      }

```

---

### 3. JavaScript Modification (Crucial for preventing errors)

Because the HTML elements for the Deduplication Results are gone, the JavaScript attempting to populate them will throw an error and break the page. We must remove those specific lines.

**Find this code inside `runDomu1LogicAndGetIntegratedText(rawText)`:**

```javascript
        document.getElementById('collapseMultipleText').innerHTML = createClickableList(collapseLines);
        document.getElementById('collapseMultipleText_textarea').value = collapseLines.join('\n');
        document.getElementById('dupResultCount').textContent = collapseLines.length;

        document.getElementById('gzhahaText').innerHTML = createClickableList(gzhahaLines);
        document.getElementById('gzhahaText_textarea').value = gzhahaLines.join('\n');
        document.getElementById('dedupResultCount').textContent = gzhahaLines.length;

        document.getElementById('outOfBoundsText').innerHTML = createClickableList(outOfBoundsLines);
        document.getElementById('outOfBoundsText_textarea').value = outOfBoundsLines.join('\n');
        document.getElementById('outOfBoundsCount').textContent = outOfBoundsLines.length;

```

**Replace it with:**

```javascript
        document.getElementById('collapseMultipleText').innerHTML = createClickableList(collapseLines);
        document.getElementById('collapseMultipleText_textarea').value = collapseLines.join('\n');
        document.getElementById('dupResultCount').textContent = collapseLines.length;

        // Removed the DOM updates for gzhahaText (Deduplication Results) to prevent JS errors

        document.getElementById('outOfBoundsText').innerHTML = createClickableList(outOfBoundsLines);
        document.getElementById('outOfBoundsText_textarea').value = outOfBoundsLines.join('\n');
        document.getElementById('outOfBoundsCount').textContent = outOfBoundsLines.length;

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

domchar5767.html

去掉了「尹末去重」的列出,因為不需要列出。加了標題,把reset功能加到再次點擊文件名的位置。
附件
domchar5767.html.7z
(14.81 KiB) 已下载 6 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 6032
注册时间: 2022年 11月 18日 17:36
联系:

Re: 工欲善其事,必先利其器:準備更新哈哈倉頡

帖子 ejsoon »

綠色的finish鍵,在hover時,應變為深綠。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复
  • 相似主题
    回复总数
    阅读次数
    最新帖子

在线用户

正浏览此版面之用户: 没有注册用户 和 2 访客