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

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

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

帖子 ejsoon »

給出「上下左右」四種方向箭頭的字符及其unicode。

代码: 全选

這裡為你整理了最常用的「上下左右」標準箭頭字符及其對應的 Unicode 編碼:

### 1. 標準細箭頭 (Standard Arrows)

這是最常見的樣式,廣泛用於文本說明與程式碼中。

| 方向 | 字符 | Unicode (Hex) | Unicode 名稱 |
| --- | --- | --- | --- |
| **上** | ↑ | `U+2191` | UPWARDS ARROW |
| **下** | ↓ | `U+2193` | DOWNWARDS ARROW |
| **左** | ← | `U+2190` | LEFTWARDS ARROW |
| **右** | → | `U+2192` | RIGHTWARDS ARROW |

---

### 2. 粗體/實心箭頭 (Heavy Arrows)

如果你需要視覺上更顯眼的符號,可以使用這組樣式。

| 方向 | 字符 | Unicode (Hex) | Unicode 名稱 |
| --- | --- | --- | --- |
| **上** | ⬆ | `U+2B06` | UPWARDS BLACK ARROW |
| **下** | ⬇ | `U+2B07` | DOWNWARDS BLACK ARROW |
| **左** | ⬅ | `U+2B05` | LEFTWARDS BLACK ARROW |
| **右** | ➡ | `U+27A1` | BLACK RIGHTWARDS ARROW |

---

### 3. 雙線箭頭 (Double Arrows)

常用於表示「推導出」或「強烈指向」。

| 方向 | 字符 | Unicode (Hex) | Unicode 名稱 |
| --- | --- | --- | --- |
| **上** | ⇑ | `U+21D1` | UPWARDS DOUBLE ARROW |
| **下** | ⇓ | `U+21D3` | DOWNWARDS DOUBLE ARROW |
| **左** | ⇐ | `U+21D0` | LEFTWARDS DOUBLE ARROW |
| **右** | ⇒ | `U+21D2` | RIGHTWARDS DOUBLE ARROW |

---

### 4. 三角實心箭頭 (Triangle Pointers)

常用於 UI 設計、菜單或播放控件。

| 方向 | 字符 | Unicode (Hex) | Unicode 名稱 |
| --- | --- | --- | --- |
| **上** | ▲ | `U+25B2` | BLACK UP-POINTING TRIANGLE |
| **下** | ▼ | `U+25BC` | BLACK DOWN-POINTING TRIANGLE |
| **左** | ◀ | `U+25C0` | BLACK LEFT-POINTING TRIANGLE |
| **右** | ▶ | `U+25B6` | BLACK RIGHT-POINTING TRIANGLE |

---

> **小撇步:**
> 在網頁開發 (HTML) 中,你可以使用 `&#x[Hex];` 的格式來調用這些字符,例如 `→` 會顯示為 **→**。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

網頁版:onclick改成onkeydown以使其反應更快。

有重碼時可左右拖動,如「zhyr」。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

改進:
一,當ejcjonline_cchar內存在元素超過範圍時,則可左右拖動。當右邊存在元素超過邊界時,右方中間將生成一個半透明寬24px高3.6vh的向右svg三角形。當左邊存在元素超過邊界時則是向左的三角形。

二,在手機端點擊ejcjonline_key時感覺反應有點慢,把點擊事件改為onmousedown,並察看還有什麼因素致使其反應慢。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

ejime_haha_20260508_190104

不知是否能解決卡頓的問題。
附件
ejime20260508.7z
(128.04 KiB) 已下载 9 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

改進:
ejcjonline_cchar上點擊選字時,仍應是onclick事件,因為手指要按在上面左右滑動。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。

代码: 全选

Here is the modification to separate the event listeners. By applying `pointerdown` strictly to the keyboard area and `click` to the display area, the keys remain instantaneously responsive while the character selection area retains its native swipe-to-scroll functionality.

### JavaScript Modification

Replace the event listener section for the screen click with the following code:

```javascript
// screen click
var ejcjonline_keyboard = document.querySelector(".ejcjonline_keyboard");
var ejcjonline_display = document.querySelector(".ejcjonline_display");
var ek_line = document.querySelectorAll(".ejcjonline_keyboard_line");

// Use pointerdown for the keyboard to ensure instantaneous response
ejcjonline_keyboard.addEventListener("pointerdown", ejcjclick);

// Use click for the display area to allow horizontal touch scrolling
ejcjonline_display.addEventListener("click", ejcjclick);

// click event
function ejcjclick(e) {
	// Prevent default behavior only for pointerdown events on keyboard elements.
	// We specifically exclude .cchar_single so the browser can natively handle swipe-to-scroll.
	if (e.type === "pointerdown" && (
		e.target.classList.contains("ejcjonline_key") || 
		e.target.classList.contains("ejcjonline_spacebarkey") || 
		e.target.classList.contains("ejcjonline_fukey") || 
		e.target.classList.contains("ejcjonline_punkkey")
	)) {
		e.preventDefault();
	}

	// ... [Keep the rest of the existing ejcjclick logic exactly as it is]

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

ejime_haha_20260508_194136

應該改好了。
附件
ejime202605081941.7z
(127.38 KiB) 已下载 10 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

改進:
一,為mcc0update往後的幾個button新增一個父元素,再把這個父元素與它後方的iframe加到另一個父元素中,為這些button添加css。

為這幾個沒有id的button添加id,當它們按下時,應該當切換成高亮,就像標籤頁一樣。

在它們上方增加一個「統計」切換標籤,它們一開始時隱藏,僅當切換至「統計」時才會出現。

二,在「統計」標籤的左邊是「修改」標籤,一開始默認是「修改」。當處於修改狀態時:

2.1首先出現「z:mcc0all的前五項」,每一項都是一個span[display:inline-block],點擊時會高亮,加上美觀的css。

2.2下方增加一個「編碼」輸入框和一個確認按鈕,當輸入並點擊回車時等同於按了確認按鈕。css美化下。輸入框最多只能輸入四個英文字符,且應以「z」開頭,如果不以「z」開頭,則在確認時自動為其加上。

2.3當按下確認時,輸入框消失,之後出現「編輯欄」,它的內容為所輸入的字符所匹配到的數據。

2.4如果輸入的長度是四位,則顯示三行,第一行是前兩位所匹配的數據的前五項,第二行是前三位所匹配的數據的前四項,第三行是前四位所匹配的數據的全部(二字詞和多字詞用不同背景色)。

例如輸入的是「zhyr」,則第一行顯示的是「zh:mcc1all的前五個」,第二行顯示的是「zhy:mcc2all的前四項」,第三行顯示的是「zhy:mcc3和mcc4的所有項」,mcc3和mcc4用不用的背景色。

2.5如果輸入的長度是三位,則只有前兩行。如果輸入的長度是兩位,則只有第一行。這裡說的位數包含前面的「z」。

2.6當點擊其中一個詞,它將高亮。當已有一個詞A高亮時,點擊另一個詞B,如果A處於B後面的任意位置,則把A插入至B的前面,如果A處於B前面的任意位置,則把A插入至B的後面。包括前面的「z:mcc0all的前五項」,每一行都一樣處於這個排序之中。

2.7在調序之後,用程式中的processContent函式更新數據,所有順序發生變化的項,都將顯示不同的背景色。如果有詞被擠佔到另個編碼,則將出現新的條目。

例如當我把「zhyr委託」放到「zh往往」前面,因為「往往」是「zh」的最後一個,則「往往」會掉到「zhh」中,而「zhh」又有「自行」會被擠到「zhho」中,因此將新增「zhh」和「zhho」兩行條目。

2.8在「z:mcc0all的前五項」的上方,將有一個「加詞」輸入框和一個確認按鈕,以及一個加詞區,輸入框的長度為120px。當按下確認或回車時,用原程式的邏輯把它的編碼加上(如果能加的話),然後把這個編碼作為編輯欄的編碼。

在其下方的「加詞欄」將出現所輸入的詞,這個詞可以點選高亮,之後插入到下方的排序中。如果先點擊一個編輯欄中的詞,再點回「加詞欄」,則這個詞將去到加詞欄,並在原數據中刪除。

2.9加詞的確認按鈕的右邊,是一個reset按鈕,當點擊時,它將把加詞欄、編輯欄都回清空並回到初始狀態。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。

代码: 全选

改進:
一,為mcc0update往後的幾個button新增一個父元素,再把這個父元素與它後方的iframe加到另一個父元素中,為這些button添加css。

為這幾個沒有id的button添加id,當它們按下時,應該當切換成高亮,就像標籤頁一樣。

在它們上方增加一個「統計」切換標籤,它們一開始時隱藏,僅當切換至「統計」時才會出現。

二,在「統計」標籤的左邊是「修改」標籤,一開始默認是「修改」。當處於修改狀態時:

2.1首先出現「z:mcc0all的前五項」,每一項都是一個span[display:inline-block],點擊時會高亮,加上美觀的css。

2.2下方增加一個「編碼」輸入框和一個確認按鈕,當輸入並點擊回車時等同於按了確認按鈕。css美化下。輸入框最多只能輸入四個英文字符,且應以「z」開頭,如果不以「z」開頭,則在確認時自動為其加上。

2.3當按下確認時,輸入框消失,之後出現「編輯欄」,它的內容為所輸入的字符所匹配到的數據。

2.4如果輸入的長度是四位,則顯示三行,第一行是前兩位所匹配的數據的前五項,第二行是前三位所匹配的數據的前四項,第三行是前四位所匹配的數據的全部(二字詞和多字詞用不同背景色)。

例如輸入的是「zhyr」,則第一行顯示的是「zh:mcc1all的前五個」,第二行顯示的是「zhy:mcc2all的前四項」,第三行顯示的是「zhy:mcc3和mcc4的所有項」,mcc3和mcc4用不用的背景色。

2.5如果輸入的長度是三位,則只有前兩行。如果輸入的長度是兩位,則只有第一行。這裡說的位數包含前面的「z」。

2.6當點擊其中一個詞,它將高亮。當已有一個詞A高亮時,點擊另一個詞B,如果A處於B後面的任意位置,則把A插入至B的前面,如果A處於B前面的任意位置,則把A插入至B的後面。包括前面的「z:mcc0all的前五項」,每一行都一樣處於這個排序之中。

2.7在調序之後,用程式中的processContent函式更新數據,所有順序發生變化的項,都將顯示不同的背景色。如果有詞被擠佔到另個編碼,則將出現新的條目。

例如當我把「zhyr委託」放到「zh往往」前面,因為「往往」是「zh」的最後一個,則「往往」會掉到「zhh」中,而「zhh」又有「自行」會被擠到「zhho」中,因此將新增「zhh」和「zhho」兩行條目。

2.8在「z:mcc0all的前五項」的上方,將有一個「加詞」輸入框和一個確認按鈕,以及一個加詞區,輸入框的長度為120px。當按下確認或回車時,用原程式的邏輯把它的編碼加上(如果能加的話),然後把這個編碼作為編輯欄的編碼。

在其下方的「加詞欄」將出現所輸入的詞,這個詞可以點選高亮,之後插入到下方的排序中。如果先點擊一個編輯欄中的詞,再點回「加詞欄」,則這個詞將去到加詞欄,並在原數據中刪除。

2.9加詞的確認按鈕的右邊,是一個reset按鈕,當點擊時,它將把加詞欄、編輯欄都回清空並回到初始狀態。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

pro又調用其它模型:

代码: 全选

Here are the modifications required to implement your requested features. All new elements, classes, variables, and comments are written in English.

### 1. CSS Style Additions

Add these styles inside the `<style>` block in the `<head>` section of your HTML file:

```css
/* Tab Navigation Styles */
.tab-container {
  display: flex;
  margin-top: 20px;
  border-bottom: 2px solid #ccc;
}
.tab-button {
  padding: 10px 20px;
  font-size: 16px;
  cursor: pointer;
  background-color: #f1f1f1;
  border: 1px solid #ccc;
  border-bottom: none;
  border-top-left-radius: 4px;
  border-top-right-radius: 4px;
  margin-right: 5px;
}
.tab-button.active {
  background-color: #008CBA;
  color: white;
  border-color: #008CBA;
}

/* Panel Layout Styles */
.mode-panel {
  padding: 15px;
  border: 1px solid #ccc;
  border-top: none;
  background-color: #fafafa;
}

/* Pretty Inputs and Buttons */
.pretty-input {
  padding: 8px;
  font-size: 14px;
  border: 2px solid #ccc;
  border-radius: 4px;
  box-sizing: border-box;
}
.pretty-btn {
  padding: 8px 16px;
  font-size: 14px;
  background-color: #008CBA;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.pretty-btn:hover {
  background-color: #007bb5;
}

/* Word Row & Interactive Word Spans */
.word-row {
  margin: 12px 0;
  padding: 10px;
  background-color: #f5f5f5;
  border-left: 4px solid #008CBA;
  border-radius: 4px;
}
.row-title {
  font-weight: bold;
  margin-bottom: 6px;
  color: #333;
}
.word-span {
  display: inline-block;
  padding: 6px 12px;
  margin: 4px;
  border: 1px solid #ddd;
  border-radius: 4px;
  background-color: #fff;
  cursor: pointer;
  user-select: none;
  transition: all 0.2s;
}
.word-span:hover {
  border-color: #008CBA;
}
.word-span.highlighted {
  background-color: #ffeb3b !important;
  border-color: #fbc02d;
  font-weight: bold;
}
.word-span.changed {
  border: 2px dashed #ff5722;
}

/* Specific background classifications */
.mcc3-word {
  background-color: #e3f2fd;
}
.mcc4-word {
  background-color: #e8f5e9;
}

/* Statistics Active Button Highlight */
.stats-btn {
  margin: 5px;
  padding: 6px 12px;
  cursor: pointer;
}
.stats-btn.active {
  background-color: #4CAF50;
  color: white;
  border: 1px solid #4CAF50;
}

```

---

### 2. HTML Layout Updates

Replace everything from `<button style="display: none" id="mcc0update"...` down to `</iframe>` with this structured setup:

```html
  <div class="tab-container">
    <button id="tabModify" class="tab-button active" onclick="switchMode('modify')">Modify</button>
    <button id="tabStats" class="tab-button" onclick="switchMode('stats')">Statistics</button>
  </div>

  <div id="panelModify" class="mode-panel">
    <div id="addWordSection" style="margin-bottom: 15px;">
      <input type="text" id="addWordInput" class="pretty-input" style="width: 120px;" placeholder="Add word...">
      <button id="btnAddWordConfirm" class="pretty-btn" onclick="handleAddWord()">Confirm</button>
      <button id="btnResetModify" class="pretty-btn" style="background-color: #f44336;" onclick="resetModifyPanel()">Reset</button>
      <div id="addedWordsArea" class="word-row" onclick="handleAddedWordsAreaClick(event)" style="min-height: 40px; border: 1px dashed #aaa; background: #fff;">
        <div class="row-title" style="color: #777;">Added Words Area (Click here to dump selected word)</div>
      </div>
    </div>

    <div id="mcc0TopFiveSection" class="word-row">
      <div class="row-title">z (mcc0all Top 5):</div>
      <div id="mcc0TopFiveContainer"></div>
    </div>

    <div id="codeInputSection" style="margin: 15px 0;">
      <input type="text" id="codeInput" class="pretty-input" maxlength="4" placeholder="Enter code (e.g., zhyr)...">
      <button id="btnCodeConfirm" class="pretty-btn" onclick="handleCodeConfirm()">Confirm</button>
    </div>

    <div id="editorPanel" style="display: none;">
      <div id="editorRow1" class="word-row"></div>
      <div id="editorRow2" class="word-row"></div>
      <div id="editorRow3" class="word-row"></div>
      <div id="cascadeContainer"></div>
    </div>
  </div>

  <div id="panelStats" class="mode-panel" style="display: none;">
    <div id="statsButtonsContainer">
      <button style="display: none" id="mcc0update" class="stats-btn" onclick="showContent('mcc0update')">Update</button>
      <button id="btnMcc0all" class="stats-btn" onclick="handleStatsButtonClick('btnMcc0all', 'mcc0all')">All</button>
      <button id="btnMcc1all" class="stats-btn" onclick="handleStatsButtonClick('btnMcc1all', 'mcc1all')">High Freq</button>
      <button id="btnMcc2all" class="stats-btn" onclick="handleStatsButtonClick('btnMcc2all', 'mcc2all')">Common</button>
      <button id="btnMcc3" class="stats-btn" onclick="handleStatsButtonClick('btnMcc3', 'mcc3')">Double Code</button>
      <button id="btnMcc4" class="stats-btn" onclick="handleStatsButtonClick('btnMcc4', 'mcc4')">Multi Char</button>
    </div>
    
    <div id="statsContentWrapper">
      <iframe
        id="domifr"
        style="width: 100%; height: 480px"
        src="https://ejsuch.vip/wp-content/uploads/2024/05/json3.html"
        frameborder="0"
      ></iframe>
    </div>
  </div>

```

---

### 3. JavaScript Logical Enhancements

Add these functional variables, state trackers, and logical systems inside your `<script>` tag.

#### 3.1 Global State Declarations

Place these variables right below your existing `let mcc4string = '';` line:

```javascript
      // New modification state variables
      let globalMcc0Lines = [];
      let initialStatusMap = {};
      let currentCodeQuery = '';
      let addedWords = [];
      let selectedWord = null; // Object layout: { text: 'word', source: 'pool'|'added' }

```

#### 3.2 Update Existing Functions

Modify `handleFile` and `processContent` to store lines globally and initialize tracking:

```javascript
      function handleFile(event) {
        const file = event.target.files[0];
        if (!file) return;

        document.getElementById('selectcinfile').classList.add('hide');

        const reader = new FileReader();
        reader.onload = function (e) {
          filecontent = e.target.result;
          processContent();
          
          // Initialize structural tracking after initial load
          initialStatusMap = getWordStatusMap();
          renderModifyPanel();
        };
        reader.readAsText(file);
      }

```

Inside `processContent(remcc0 = '')`, change the line assignment logic block to reference `globalMcc0Lines`:

```javascript
        let mcc0Lines;
        if (remcc0 == '') {
          mcc0Lines = lines.slice(xLine + 1, charDefEndLine);
          mcc0Lines = mcc0Lines.filter(line => {
            const parts = line.split('\t');
            if (parts.length > 1) {
              const code = parts[0];
              if (code.startsWith('z')) {
                const restOfCode = code.slice(1);
                if (restOfCode.includes('z')) {
                  return false;
                }
              }
            }
            return true;
          });
          mcc0Lines = mcc0Lines.map(line => {
            const parts = line.split('\t');
            return parts.length > 1 ? parts[1] : line;
          });
          mcc0Lines = mcc0Lines.filter((line, index, arr) => arr.indexOf(line) == index);
          globalMcc0Lines = [...mcc0Lines]; // Store tracking copy
        } else {
          mcc0Lines = JSON.parse(remcc0);
          globalMcc0Lines = [...mcc0Lines]; // Keep sync'd
        }

```

Update your `resetPage` function to clear out these new views dynamically:

```javascript
      // Add key-binding listeners for input fields
      document.addEventListener("DOMContentLoaded", () => {
        document.getElementById('codeInput').addEventListener('keypress', (e) => {
          if (e.key === 'Enter') handleCodeConfirm();
        });
        document.getElementById('addWordInput').addEventListener('keypress', (e) => {
          if (e.key === 'Enter') handleAddWord();
        });
      });

      function resetPage(readfile = true) {
        // ... (Keep your old clears here) ...
        mcc3.clear();
        mcc4.clear();
        mcc0string = '';
        mcc1string = '';
        mcc2string = '';
        mcc3string = '';
        mcc4string = '';
        
        // Clear new custom state variables
        globalMcc0Lines = [];
        initialStatusMap = {};
        resetModifyPanel();

        if (readfile) {
          filecontent = '';
          document.getElementById('cinfile').value = '';
          document.getElementById('output').innerHTML = '';
          document.getElementById('download').innerHTML = '';
          document.getElementById('selectcinfile').classList.remove('hide');
          domifr.contentWindow.ifrmUpdate('{}');
        }
        mcc0update.style.display = 'none';
      }

```

#### 3.3 Add New Functional Features

Append these custom control functions directly to the bottom of your `<script>` section:

```javascript
      /* Switch Between Panels (Modify vs Statistics) */
      function switchMode(mode) {
        document.getElementById('tabModify').classList.remove('active');
        document.getElementById('tabStats').classList.remove('active');
        document.getElementById('panelModify').style.display = 'none';
        document.getElementById('panelStats').style.display = 'none';

        if (mode === 'modify') {
          document.getElementById('tabModify').classList.add('active');
          document.getElementById('panelModify').style.display = 'block';
          renderModifyPanel();
        } else {
          document.getElementById('tabStats').classList.add('active');
          document.getElementById('panelStats').style.display = 'block';
        }
      }

      /* Statistics Tab View Management */
      function handleStatsButtonClick(btnId, type) {
        const buttons = document.querySelectorAll('#statsButtonsContainer button');
        buttons.forEach(b => b.classList.remove('active'));
        document.getElementById(btnId).classList.add('active');
        showContent(type);
      }

      /* Capture Map word tracking snapshots */
      function getWordStatusMap() {
        let status = {};
        const mapList = [
          { map: mcc0, limit: 5 },
          { map: mcc1all, limit: 5 },
          { map: mcc2all, limit: 4 },
          { map: mcc3, limit: null },
          { map: mcc4, limit: null }
        ];

        mapList.forEach(({ map, limit }) => {
          for (let [key, values] of map) {
            let items = Array.isArray(values) ? values : [values];
            if (limit !== null) items = items.slice(0, limit);
            items.forEach((word, index) => {
              if (word) status[word] = `${key}:${index}`;
            });
          }
        });
        return status;
      }

      /* Render Engine for Modification Workspace */
      function renderModifyPanel() {
        // Render top row (z key)
        const mcc0Container = document.getElementById('mcc0TopFiveContainer');
        mcc0Container.innerHTML = '';
        const zItems = mcc0.get('z') || [];
        zItems.forEach(w => mcc0Container.appendChild(createWordElement(w, 'pool')));

        // Render Added Words Area
        const addedArea = document.getElementById('addedWordsArea');
        // Clear all except title header
        addedArea.innerHTML = '<div class="row-title" style="color: #777;">Added Words Area (Click here to dump selected word)</div>';
        addedWords.forEach(w => addedArea.appendChild(createWordElement(w, 'added')));

        const editorPanel = document.getElementById('editorPanel');
        if (!currentCodeQuery) {
          editorPanel.style.display = 'none';
          return;
        }
        editorPanel.style.display = 'block';

        // Set up active queried elements
        const len = currentCodeQuery.length;
        const k1 = len >= 2 ? currentCodeQuery.slice(0, 2) : '';
        const k2 = len >= 3 ? currentCodeQuery.slice(0, 3) : '';
        const k3 = len >= 4 ? currentCodeQuery : '';

        // Row 1 (Length 2 prefix)
        const row1 = document.getElementById('editorRow1');
        row1.style.display = k1 ? 'block' : 'none';
        row1.innerHTML = `<div class="row-title">${k1} (mcc1all Top 5):</div>`;
        if (k1) {
          (mcc1all.get(k1) || []).slice(0, 5).forEach(w => row1.appendChild(createWordElement(w, 'pool')));
        }

        // Row 2 (Length 3 prefix)
        const row2 = document.getElementById('editorRow2');
        row2.style.display = k2 ? 'block' : 'none';
        row2.innerHTML = `<div class="row-title">${k2} (mcc2all Top 4):</div>`;
        if (k2) {
          (mcc2all.get(k2) || []).slice(0, 4).forEach(w => row2.appendChild(createWordElement(w, 'pool')));
        }

        // Row 3 (Length 4 prefix)
        const row3 = document.getElementById('editorRow3');
        row3.style.display = k3 ? 'block' : 'none';
        row3.innerHTML = `<div class="row-title">${k3} (mcc3 & mcc4 All Items):</div>`;
        if (k3) {
          const m3Items = mcc3.get(k3) || [];
          const m4Items = mcc4.get(k3) || [];
          m3Items.forEach(w => row3.appendChild(createWordElement(w, 'pool', 'mcc3-word')));
          m4Items.forEach(w => row3.appendChild(createWordElement(w, 'pool', 'mcc4-word')));
        }

        // Handle Cascading structural impacts
        renderCascadeRows([k1, k2, k3].filter(Boolean));
      }

      /* HTML Factory Element Generator for Interactive Spans */
      function createWordElement(wordText, source, customClass = '') {
        const span = document.createElement('span');
        span.className = `word-span ${customClass}`;
        span.textContent = wordText;

        const currentStatus = getWordStatusMap();
        const initialLoc = initialStatusMap[wordText];
        const currentLoc = currentStatus[wordText];
        
        if (initialLoc && initialLoc !== currentLoc) {
          span.classList.add('changed');
        }
        if (selectedWord && selectedWord.text === wordText && selectedWord.source === source) {
          span.classList.add('highlighted');
        }

        span.addEventListener('click', (e) => {
          e.stopPropagation();
          handleWordSelection(wordText, source);
        });
        return span;
      }

      /* Structural Cascade Rendering Logic */
      function renderCascadeRows(queriedKeys) {
        const container = document.getElementById('cascadeContainer');
        container.innerHTML = '';

        const currentStatus = getWordStatusMap();
        let cascadeKeys = new Set();

        for (let word in currentStatus) {
          let currentKey = currentStatus[word].split(':')[0];
          if (!queriedKeys.includes(currentKey) && initialStatusMap[word] !== currentStatus[word]) {
            cascadeKeys.add(currentKey);
          }
        }

        cascadeKeys.forEach(key => {
          const div = document.createElement('div');
          div.className = 'word-row';
          div.style.borderLeftColor = '#ff5722';
          div.innerHTML = `<div class="row-title" style="color: #ff5722;">Cascaded Changes: ${key}</div>`;

          let items = [];
          if (key.length === 2) items = (mcc1all.get(key) || []).slice(0, 5);
          else if (key.length === 3) items = (mcc2all.get(key) || []).slice(0, 4);
          else if (key.length === 4) items = [...(mcc3.get(key) || []), ...(mcc4.get(key) || [])];

          items.forEach(w => {
            let cls = '';
            if (key.length === 4) {
              if ((mcc3.get(key) || []).includes(w)) cls = 'mcc3-word';
              else if ((mcc4.get(key) || []).includes(w)) cls = 'mcc4-word';
            }
            div.appendChild(createWordElement(w, 'pool', cls));
          });
          container.appendChild(div);
        });
      }

      /* Confirmation handler for Query Codes */
      function handleCodeConfirm() {
        let val = document.getElementById('codeInput').value.trim().toLowerCase();
        if (!val) return;
        if (!val.startsWith('z')) val = 'z' + val;
        
        currentCodeQuery = val;
        document.getElementById('codeInputSection').style.display = 'none';
        renderModifyPanel();
      }

      /* Core Reordering Positioning Mechanics */
      function handleWordSelection(wordText, source) {
        if (!selectedWord) {
          selectedWord = { text: wordText, source: source };
          renderModifyPanel();
        } else {
          if (selectedWord.text === wordText && selectedWord.source === source) {
            selectedWord = null;
            renderModifyPanel();
            return;
          }

          if (selectedWord.source === 'pool' && source === 'pool') {
            let idxA = globalMcc0Lines.indexOf(selectedWord.text);
            let idxB = globalMcc0Lines.indexOf(wordText);

            if (idxA !== -1 && idxB !== -1) {
              globalMcc0Lines.splice(idxA, 1);
              idxB = globalMcc0Lines.indexOf(wordText);
              if (idxA > idxB) {
                globalMcc0Lines.splice(idxB, 0, selectedWord.text);
              } else {
                globalMcc0Lines.splice(idxB + 1, 0, selectedWord.text);
              }
            }
          } else if (selectedWord.source === 'added' && source === 'pool') {
            let idxB = globalMcc0Lines.indexOf(wordText);
            if (idxB !== -1) {
              globalMcc0Lines.splice(idxB, 0, selectedWord.text);
              addedWords = addedWords.filter(w => w !== selectedWord.text);
            }
          }

          selectedWord = null;
          processContent(JSON.stringify(globalMcc0Lines));
          renderModifyPanel();
        }
      }

      /* Handle selected elements dumped to Added Words area container */
      function handleAddedWordsAreaClick(event) {
        if (selectedWord && selectedWord.source === 'pool') {
          let idxA = globalMcc0Lines.indexOf(selectedWord.text);
          if (idxA !== -1) {
            globalMcc0Lines.splice(idxA, 1);
            if (!addedWords.includes(selectedWord.text)) {
              addedWords.push(selectedWord.text);
            }
          }
          selectedWord = null;
          processContent(JSON.stringify(globalMcc0Lines));
          renderModifyPanel();
        }
      }

      /* Reverse Generation Logic to Derive Codes and Appends Words */
      function handleAddWord() {
        const input = document.getElementById('addWordInput');
        const word = input.value.trim();
        if (!word) return;

        // Try using program data mappings to construct standard key prefixes
        if (word.length >= 2) {
          let c1 = ccharfirst.get(word[0]);
          let c2 = ccharfirst.get(word[1]);
          let c3 = word.length >= 3 ? ccharfirst.get(word[2]) : (ccharsecond.get(word[1]) || 'x');
          
          if (c1 && c2) {
            let assignedCode = ('z' + c1 + c2 + (c3 || '')).slice(0, 4);
            currentCodeQuery = assignedCode;
            document.getElementById('codeInput').value = assignedCode;
            document.getElementById('codeInputSection').style.display = 'none';
            
            if (!globalMcc0Lines.includes(word)) {
              globalMcc0Lines.push(word);
            }
            if (!addedWords.includes(word)) {
              addedWords.push(word);
            }

            processContent(JSON.stringify(globalMcc0Lines));
            input.value = '';
            renderModifyPanel();
            return;
          }
        }
        alert("Unable to calculate dynamic input code configuration framework.");
      }

      /* Clean/Flush local workspace structures */
      function resetModifyPanel() {
        currentCodeQuery = '';
        addedWords = [];
        selectedWord = null;
        document.getElementById('codeInput').value = '';
        document.getElementById('addWordInput').value = '';
        document.getElementById('codeInputSection').style.display = 'block';
        if (filecontent) {
          processContent(JSON.stringify(globalMcc0Lines));
        }
        renderModifyPanel();
      }

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

改進:
一,為mcc0update往後的幾個button新增一個父元素,再把這個父元素與它後方的iframe加到另一個父元素中,為這些button添加css。

為這幾個沒有id的button添加id,當它們按下時,應該當切換成高亮,就像標籤頁一樣。

在它們上方增加一個「統計」切換標籤,它們一開始時隱藏,僅當切換至「統計」時才會出現。

二,在「統計」標籤的左邊是「修改」標籤,一開始默認是「修改」。當處於修改狀態時:

2.1首先新增「編碼」輸入框和確認按鈕,以及reset按鈕,當輸入並點擊回車時等同於按了確認按鈕。

2.2當按下確認時,輸入框消失。如果輸入的是英文,最多只能輸入四個英文字符,且應以「z」開頭,如果不以「z」開頭,則在確認時自動為其加上。

如果輸入的不是英文,則應為中文詞,把這個詞加到原始資料的最後方,執行程式中的processContent函式,得到這個詞的編碼,把它作為輸入編碼。

2.3之後出現「編輯欄」,它的內容為所輸入的編碼所匹配到的數據。

2.4如果輸入編碼的長度是四位,則顯示四行,第一行是「z:mcc0all的前五項」,第二行是前兩位所匹配的數據的前五項,第三行是前三位所匹配的數據的前四項,第四行是前四位所匹配的數據的全部(二字詞和多字詞用不同背景色)。

例如輸入的是「zhyr」,則第一行顯示的是「z:mcc0all的前五個」,第二行顯示的是「zh:mcc1all的前五個」,第三行顯示的是「zhy:mcc2all的前四項」,第四行顯示的是「zhy:mcc3和mcc4的所有項」,mcc3和mcc4用不用的背景色。

每一項都是一個span[display:inline-block],點擊時會高亮,加上美觀的css。如果前面是為一個中文詞編碼,則這個中文詞的背景色將不同。

2.5如果輸入的編碼長度是三位,則只有前三行。如果輸入編碼的長度是兩位,則只有前兩行。這裡說的位數包含前面的「z」。

2.6當點擊其中一個詞,它將高亮。當已有一個詞A高亮時,點擊另一個詞B,如果A處於B後面的任意位置,則把A插入至B的前面,如果A處於B前面的任意位置,則把A插入至B的後面。

2.7在調序之後,用程式中的processContent函式更新數據,所有順序發生變化的項,都將顯示不同的背景色。如果有詞被擠佔到另個編碼,則將出現新的條目。

例如當我把「zhyr委託」放到「zh往往」前面,因為「往往」是「zh」的最後一個,則「往往」會掉到「zhh」中,而「zhh」又有「自行」會被擠到「zhho」中,因此將新增「zhh」和「zhho」兩行條目。

2.8當按下reset按鈕,編輯欄將清空並回到初始狀態。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。

代码: 全选

改進:
一,為mcc0update往後的幾個button新增一個父元素,再把這個父元素與它後方的iframe加到另一個父元素中,為這些button添加css。

為這幾個沒有id的button添加id,當它們按下時,應該當切換成高亮,就像標籤頁一樣。

在它們上方增加一個「統計」切換標籤,它們一開始時隱藏,僅當切換至「統計」時才會出現。

二,在「統計」標籤的左邊是「修改」標籤,一開始默認是「修改」。當處於修改狀態時:

2.1首先新增「編碼」輸入框和確認按鈕,以及reset按鈕,當輸入並點擊回車時等同於按了確認按鈕。

2.2當按下確認時,輸入框消失。如果輸入的是英文,最多只能輸入四個英文字符,且應以「z」開頭,如果不以「z」開頭,則在確認時自動為其加上。

如果輸入的不是英文,則應為中文詞,把這個詞加到原始資料的最後方,執行程式中的processContent函式,得到這個詞的編碼,把它作為輸入編碼。

2.3之後出現「編輯欄」,它的內容為所輸入的編碼所匹配到的數據。

2.4如果輸入編碼的長度是四位,則顯示四行,第一行是「z:mcc0all的前五項」,第二行是前兩位所匹配的數據的前五項,第三行是前三位所匹配的數據的前四項,第四行是前四位所匹配的數據的全部(二字詞和多字詞用不同背景色)。

例如輸入的是「zhyr」,則第一行顯示的是「z:mcc0all的前五個」,第二行顯示的是「zh:mcc1all的前五個」,第三行顯示的是「zhy:mcc2all的前四項」,第四行顯示的是「zhy:mcc3和mcc4的所有項」,mcc3和mcc4用不用的背景色。

每一項都是一個span[display:inline-block],點擊時會高亮,加上美觀的css。如果前面是為一個中文詞編碼,則這個中文詞的背景色將不同。

2.5如果輸入的編碼長度是三位,則只有前三行。如果輸入編碼的長度是兩位,則只有前兩行。這裡說的位數包含前面的「z」。

2.6當點擊其中一個詞,它將高亮。當已有一個詞A高亮時,點擊另一個詞B,如果A處於B後面的任意位置,則把A插入至B的前面,如果A處於B前面的任意位置,則把A插入至B的後面。

2.7在調序之後,用程式中的processContent函式更新數據,所有順序發生變化的項,都將顯示不同的背景色。如果有詞被擠佔到另個編碼,則將出現新的條目。

例如當我把「zhyr委託」放到「zh往往」前面,因為「往往」是「zh」的最後一個,則「往往」會掉到「zhh」中,而「zhh」又有「自行」會被擠到「zhho」中,因此將新增「zhh」和「zhho」兩行條目。

2.8當按下reset按鈕,編輯欄將清空並回到初始狀態。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

gemini pro上了:

代码: 全选

Here are the necessary modifications to your code to achieve the requested functionality. All code additions and comments are in English as requested.

### 1. Add CSS for Tabs and Edit Area

Insert the following CSS rules inside your existing `<style>` block:

```css
      /* === ADDED CSS FOR TABS AND EDIT AREA === */
      .tab-container {
        display: flex;
        border-bottom: 1px solid #ccc;
        margin-bottom: 15px;
        margin-top: 20px;
      }
      .tab-button {
        padding: 10px 20px;
        cursor: pointer;
        border: 1px solid transparent;
        background: transparent;
        font-size: 16px;
        margin-bottom: -1px;
      }
      .tab-button.active {
        border: 1px solid #ccc;
        border-bottom-color: #fff;
        background: #fff;
        border-radius: 5px 5px 0 0;
        font-weight: bold;
        color: #000;
      }
      .tab-content {
        display: none;
        padding: 15px 0;
      }
      .tab-content.active {
        display: block;
      }
      
      .stats-buttons {
        display: flex;
        gap: 8px;
        margin-bottom: 15px;
      }
      .stats-btn {
        padding: 8px 12px;
        cursor: pointer;
        border: 1px solid #ccc;
        border-radius: 4px;
        background: #f9f9f9;
      }
      .stats-btn.active {
        background-color: #4caf50;
        color: white;
        border-color: #388e3c;
      }

      .edit-row {
        margin-bottom: 15px;
        padding: 10px;
        border-bottom: 1px dashed #ccc;
        background: #fafafa;
        border-radius: 5px;
      }
      .edit-item {
        display: inline-block;
        padding: 6px 12px;
        margin: 4px;
        border: 1px solid #ccc;
        border-radius: 4px;
        cursor: pointer;
        background: #fff;
        transition: all 0.2s;
        user-select: none;
      }
      .edit-item:hover { background: #e0e0e0; }
      .edit-item.selected {
        background: #fff9c4 !important;
        border: 2px solid #fbc02d;
        box-shadow: 0 0 5px rgba(251, 192, 45, 0.5);
      }
      .edit-item.mcc3 { background: #e3f2fd; border-color: #90caf9; } /* 2-char words */
      .edit-item.mcc4 { background: #e8f5e9; border-color: #a5d6a7; } /* multi-char words */
      .edit-item.changed { border: 2px dashed #f44336 !important; background: #ffebee; }
      .edit-item.target-word { font-weight: bold; color: #d32f2f; border: 2px solid #d32f2f; }
      /* === END OF ADDED CSS === */

```

### 2. Update HTML Body Structure

Replace your existing buttons (`mcc0update` up to `mcc4`) and the `iframe` with the following tab structure:

```html
    <div class="tab-container">
      <button id="tabEdit" class="tab-button active" onclick="switchTab('edit')">Edit</button>
      <button id="tabStats" class="tab-button" onclick="switchTab('stats')">Statistics</button>
    </div>

    <div id="contentEdit" class="tab-content active">
      <div id="inputArea">
        <label for="codeInput" style="font-weight: bold;">Encode Input: </label>
        <input type="text" id="codeInput" placeholder="Enter code (e.g. zhy) or word">
        <button id="submitCode" onclick="handleEditSubmit()">Submit</button>
        <button id="resetEditBtn" onclick="handleEditReset()">Reset</button>
      </div>
      <div id="editArea" style="display: none;">
        <div id="editRows"></div>
        <button onclick="handleEditReset()" style="margin-top: 15px;">Close / Reset Edit</button>
      </div>
    </div>

    <div id="contentStats" class="tab-content">
      <div class="stats-buttons" id="statsBtnContainer">
        <button class="stats-btn" style="display: none" id="mcc0update" onclick="showContent('mcc0update', this)">Update</button>
        <button class="stats-btn" id="btnMcc0All" onclick="showContent('mcc0all', this)">All</button>
        <button class="stats-btn" id="btnMcc1All" onclick="showContent('mcc1all', this)">High Freq</button>
        <button class="stats-btn" id="btnMcc2All" onclick="showContent('mcc2all', this)">Common</button>
        <button class="stats-btn" id="btnMcc3" onclick="showContent('mcc3', this)">Repeat</button>
        <button class="stats-btn" id="btnMcc4" onclick="showContent('mcc4', this)">Multi</button>
      </div>
      <iframe
        id="domifr"
        style="width: 100%; height: 480px"
        src="https://ejsoon.vip/wp-content/uploads/2024/05/json3.html"
        frameborder="0"
      ></iframe>
    </div>
    ```

### 3. JavaScript Updates
**3.1 Update `showContent` to support active button styling:**
Find your existing `showContent` function and modify its signature and top logic to look like this:

```javascript
      function showContent(type, btnElement) {
        const domifr = document.getElementById('domifr');
        const mcc0update = document.getElementById('mcc0update');

        // Apply active styling to the clicked stats button
        if (btnElement) {
          document.querySelectorAll('#statsBtnContainer .stats-btn').forEach(btn => btn.classList.remove('active'));
          btnElement.classList.add('active');
        }
        
        // ... (keep all your existing if-else logic here) ...

```

**3.2 Insert New Edit Area Logic:**
Add the following JavaScript blocks right before the closing `</script>` tag:

```javascript
      // === ADDED EDIT AREA VARIABLES AND FUNCTIONS ===
      let currentEditCode = '';
      let currentTargetWord = '';
      let selectedEditItem = null;
      let activeEditKeys = new Set();
      let changedWords = new Set();

      // Listen for Enter key on the code input
      document.addEventListener('DOMContentLoaded', () => {
        const inputEl = document.getElementById('codeInput');
        if(inputEl) {
          inputEl.addEventListener('keypress', function (e) {
            if (e.key === 'Enter') handleEditSubmit();
          });
        }
      });

      function switchTab(tab) {
        document.getElementById('tabEdit').classList.toggle('active', tab === 'edit');
        document.getElementById('tabStats').classList.toggle('active', tab === 'stats');
        document.getElementById('contentEdit').classList.toggle('active', tab === 'edit');
        document.getElementById('contentStats').classList.toggle('active', tab === 'stats');
      }

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

        activeEditKeys.clear();
        changedWords.clear();
        selectedEditItem = null;

        // Determine if English input
        if (/^[a-zA-Z]+$/.test(val)) {
          if (val[0].toLowerCase() !== 'z') val = 'z' + val;
          val = val.substring(0, 4).toLowerCase();
          currentEditCode = val;
          currentTargetWord = '';
        } else {
          // It's a Chinese word
          currentTargetWord = val;
          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          
          // Append to end if it doesn't exist
          if (!lines.includes(val)) {
            lines.push(val);
            processContent(JSON.stringify(lines)); // Refresh data mappings
          }

          // Dynamically compute the prefix code up to length 4
          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);
        }

        // Initialize base rows depending on code length
        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);

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

      function handleEditReset() {
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';
        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        activeEditKeys.clear();
        changedWords.clear();
      }

      function renderEditArea() {
        let container = document.getElementById('editRows');
        container.innerHTML = '';

        // Sort keys: 'z' first, then sorted by length, then alphabetical
        let sortedKeys = Array.from(activeEditKeys).sort((a, b) => {
          if (a === 'z') return -1;
          if (b === 'z') return 1;
          if (a.length !== b.length) return a.length - b.length;
          return a.localeCompare(b);
        });

        sortedKeys.forEach(k => {
          let label = '';
          let items = [];

          // Assign labels and fetch specific mapping constraints per prompt requirement
          if (k === 'z') {
            label = 'z: mcc0all top 5';
            (mcc0.get('z') || []).forEach(w => items.push({ w: w, cls: '' }));
          } else if (k.length === 2) {
            label = `${k}: mcc1all top 5`;
            (mcc1all.get(k) || []).slice(0, 5).forEach(w => items.push({ w: w, cls: '' }));
          } else if (k.length === 3) {
            label = `${k}: mcc2all top 4`;
            (mcc2all.get(k) || []).slice(0, 4).forEach(w => items.push({ w: w, cls: '' }));
          } else if (k.length === 4) {
            label = `${k}: all items from mcc3 and mcc4`;
            (mcc3.get(k) || []).forEach(w => items.push({ w: w, cls: 'mcc3' })); // 2 chars
            (mcc4.get(k) || []).forEach(w => items.push({ w: w, cls: 'mcc4' })); // multi chars
          }

          if (items.length > 0) {
            let rowDiv = document.createElement('div');
            rowDiv.className = 'edit-row';
            let titleDiv = document.createElement('div');
            titleDiv.textContent = label;
            titleDiv.style.fontWeight = 'bold';
            titleDiv.style.marginBottom = '8px';
            rowDiv.appendChild(titleDiv);

            items.forEach(item => {
              let span = document.createElement('span');
              span.textContent = item.w;
              span.className = `edit-item ${item.cls}`;
              
              if (item.w === selectedEditItem) span.classList.add('selected');
              if (item.w === currentTargetWord) span.classList.add('target-word');
              if (changedWords.has(item.w)) span.classList.add('changed');

              span.onclick = () => handleItemClick(item.w);
              rowDiv.appendChild(span);
            });
            container.appendChild(rowDiv);
          }
        });
      }

      function getSystemState() {
        // Snapshot to track word movement and cascade effects
        let state = new Map();
        state.set('z', mcc0.get('z') || []);
        for (let [k, v] of mcc1all) state.set(k, v.slice(0, 5));
        for (let [k, v] of mcc2all) state.set(k, v.slice(0, 4));
        for (let [k, v] of mcc3) {
          if (!state.has(k)) state.set(k, []);
          state.get(k).push(...v);
        }
        for (let [k, v] of mcc4) {
          if (!state.has(k)) state.set(k, []);
          state.get(k).push(...v);
        }
        return state;
      }

      function detectAndRenderChanges(oldState, newState) {
        let wordToOldPos = new Map();
        for (let [k, vals] of oldState) {
          vals.forEach((w, i) => wordToOldPos.set(w, `${k}-${i}`));
        }

        changedWords.clear();
        for (let [k, vals] of newState) {
          let oldVals = oldState.get(k) || [];
          
          if (JSON.stringify(vals) !== JSON.stringify(oldVals)) {
            activeEditKeys.add(k); // Show entirely new cascade rows
          }
          
          vals.forEach((w, i) => {
            if (wordToOldPos.get(w) !== `${k}-${i}`) {
              changedWords.add(w); // Outline item with red border
              activeEditKeys.add(k); // Ensure destination row is unhidden
            }
          });
        }
        renderEditArea();
      }

      function handleItemClick(word) {
        if (!selectedEditItem) {
          selectedEditItem = word;
          renderEditArea();
        } else {
          if (selectedEditItem === word) {
            selectedEditItem = null; // Toggle selection off
            renderEditArea();
            return;
          }

          // Order adjustment logic
          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          let idxA = lines.indexOf(selectedEditItem);
          let originalIdxB = lines.indexOf(word);

          if (idxA > -1 && originalIdxB > -1) {
            let oldState = getSystemState();

            // Extract A
            lines.splice(idxA, 1);
            let idxB = lines.indexOf(word); // Recalculate B index after splice
            
            // If A was strictly behind original B, push A right in front of new B
            if (idxA > originalIdxB) {
              lines.splice(idxB, 0, selectedEditItem);
            } else {
              // If A was strictly in front of original B, push A right after new B
              lines.splice(idxB + 1, 0, selectedEditItem); 
            }

            // Sync structural changes globally
            processContent(JSON.stringify(lines));
            
            let newState = getSystemState();
            detectAndRenderChanges(oldState, newState);
          }
          selectedEditItem = null;
        }
      }
      // === END OF ADDED EDIT AREA FUNCTIONS ===

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

修正:
程式邏輯有誤。比如我加詞「zmyr確認」,之後我把它調到「zmy一些」的前面,則可能受影響的應該只有zmy,zmyr這些編碼的字。並且碼表中還出現了很多重覆的詞。

為codeInput加上美觀的css,當前面的碼表沒有載入時,下方的兩個標籤都應該不顯示。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。



甩給thinking:

代码: 全选

這套修改方案解決了重複字詞的 Bug(原因在於 `processContent` 重複執行時沒有清空 Map 容器,導致舊資料不斷疊加),並為輸入框加上了美觀的 CSS 樣式,同時確保在尚未載入檔案時隱藏整個標籤頁系統。

請將以下修改應用至您的程式碼中:

### 1. 更新 CSS 樣式 (CSS Updates)

請在 `<style>` 區塊中添加 `#codeInput` 的樣式,並確保標籤頁容器在初始狀態下隱藏:

```css
      /* === UPDATED & ADDED CSS FOR CODE INPUT AND TAB SYSTEM === */
      #mainTabSystem {
        display: none; /* Hidden by default until file is loaded */
      }

      #codeInput {
        padding: 8px 12px;
        font-size: 14px;
        border: 1px solid #ccc;
        border-radius: 4px;
        outline: none;
        transition: border-color 0.2s, box-shadow 0.2s;
        width: 220px;
        margin-right: 6px;
      }
      #codeInput:focus {
        border-color: #4caf50;
        box-shadow: 0 0 5px rgba(76, 175, 80, 0.3);
      }

      .tab-container {
        display: flex;
        border-bottom: 1px solid #ccc;
        margin-bottom: 15px;
        margin-top: 20px;
      }
      .tab-button {
        padding: 10px 20px;
        cursor: pointer;
        border: 1px solid transparent;
        background: transparent;
        font-size: 16px;
        margin-bottom: -1px;
      }
      .tab-button.active {
        border: 1px solid #ccc;
        border-bottom-color: #fff;
        background: #fff;
        border-radius: 5px 5px 0 0;
        font-weight: bold;
        color: #000;
      }
      .tab-content {
        display: none;
        padding: 15px 0;
      }
      .tab-content.active {
        display: block;
      }
      
      .stats-buttons {
        display: flex;
        gap: 8px;
        margin-bottom: 15px;
      }
      .stats-btn {
        padding: 8px 12px;
        cursor: pointer;
        border: 1px solid #ccc;
        border-radius: 4px;
        background: #f9f9f9;
      }
      .stats-btn.active {
        background-color: #4caf50;
        color: white;
        border-color: #388e3c;
      }

      .edit-row {
        margin-bottom: 15px;
        padding: 10px;
        border-bottom: 1px dashed #ccc;
        background: #fafafa;
        border-radius: 5px;
      }
      .edit-item {
        display: inline-block;
        padding: 6px 12px;
        margin: 4px;
        border: 1px solid #ccc;
        border-radius: 4px;
        cursor: pointer;
        background: #fff;
        transition: all 0.2s;
        user-select: none;
      }
      .edit-item:hover { background: #e0e0e0; }
      .edit-item.selected {
        background: #fff9c4 !important;
        border: 2px solid #fbc02d;
        box-shadow: 0 0 5px rgba(251, 192, 45, 0.5);
      }
      .edit-item.mcc3 { background: #e3f2fd; border-color: #90caf9; }
      .edit-item.mcc4 { background: #e8f5e9; border-color: #a5d6a7; }
      .edit-item.changed { border: 2px dashed #f44336 !important; background: #ffebee; }
      .edit-item.target-word { font-weight: bold; color: #d32f2f; border: 2px solid #d32f2f; }

```

### 2. 更新 HTML 結構 (HTML Body Updates)

將整個標籤頁系統包裹在 `id="mainTabSystem"` 的容器中,使其在初始未載入碼表時保持隱藏:

```html
    <div id="mainTabSystem">
      <div class="tab-container">
        <button id="tabEdit" class="tab-button active" onclick="switchTab('edit')">Edit</button>
        <button id="tabStats" class="tab-button" onclick="switchTab('stats')">Statistics</button>
      </div>

      <div id="contentEdit" class="tab-content active">
        <div id="inputArea">
          <label for="codeInput" style="font-weight: bold;">Encode Input: </label>
          <input type="text" id="codeInput" placeholder="Enter code (e.g. zhy) or word">
          <button id="submitCode" onclick="handleEditSubmit()">Submit</button>
          <button id="resetEditBtn" onclick="handleEditReset()">Reset</button>
        </div>
        <div id="editArea" style="display: none;">
          <div id="editRows"></div>
          <button onclick="handleEditReset()" style="margin-top: 15px;">Close / Reset Edit</button>
        </div>
      </div>

      <div id="contentStats" class="tab-content">
        <div class="stats-buttons" id="statsBtnContainer">
          <button class="stats-btn" style="display: none" id="mcc0update" onclick="showContent('mcc0update', this)">Update</button>
          <button class="stats-btn" id="btnMcc0All" onclick="showContent('mcc0all', this)">All</button>
          <button class="stats-btn" id="btnMcc1All" onclick="showContent('mcc1all', this)">High Freq</button>
          <button class="stats-btn" id="btnMcc2All" onclick="showContent('mcc2all', this)">Common</button>
          <button class="stats-btn" id="btnMcc3" onclick="showContent('mcc3', this)">Repeat</button>
          <button class="stats-btn" id="btnMcc4" onclick="showContent('mcc4', this)">Multi</button>
        </div>
        <iframe
          id="domifr"
          style="width: 100%; height: 480px"
          src="https://ejsoon.vip/wp-content/uploads/2024/05/json3.html"
          frameborder="0"
        ></iframe>
      </div>
    </div>
    ```

### 3. JavaScript 邏輯修正與優化 (JavaScript Updates)

**3.1 修正 `resetPage` 與 `processContent` 函式:**
必須在 `processContent` 的最開頭**清空所有 Map**,以防調序或加詞重新計算時產生重複項目。同時控制 `#mainTabSystem` 的顯示與隱藏。

```javascript
      function resetPage(readfile = true) {
        cchardefine.clear();
        ccharfirst.clear();
        ccharsecond.clear();
        mcc0.clear();
        mcc0moog.clear();
        mcc1all.clear();
        mcc1.clear();
        mcc1moog.clear();
        mcc2all.clear();
        mcc2.clear();
        mcc2moog.clear();
        mcc3.clear();
        mcc4.clear();
        mcc0string = '';
        mcc1string = '';
        mcc2string = '';
        mcc3string = '';
        mcc4string = '';
        if (readfile) {
          filecontent = '';
          document.getElementById('cinfile').value = '';
          document.getElementById('output').innerHTML = '';
          document.getElementById('download').innerHTML = '';
          document.getElementById('selectcinfile').classList.remove('hide');
          domifr.contentWindow.ifrmUpdate('{}');
          // Hide tab system when page is reset
          document.getElementById('mainTabSystem').style.display = 'none'; 
        }
        mcc0update.style.display = 'none';
      }

      function processContent(remcc0 = '') {
        // CRITICAL FIX: Clear all maps before recalculating to prevent duplicate words
        mcc0.clear();
        mcc0moog.clear();
        mcc1all.clear();
        mcc1.clear();
        mcc1moog.clear();
        mcc2all.clear();
        mcc2.clear();
        mcc2moog.clear();
        mcc3.clear();
        mcc4.clear();

        const lines = filecontent.split('\n').map(line => line.replace(/\r$/, ''));

        // Process cchardefine
        let charDefStart = lines.indexOf('%chardef begin') + 1;
        let charDefEnd = lines.findIndex((line, i) => i > charDefStart && line.includes('z'));
        if (charDefEnd === -1) charDefEnd = lines.length;

        const charDefLines = lines.slice(charDefStart, charDefEnd);
        document.getElementById('output').innerHTML = `cchardefine 行數: ${charDefLines.length}<br>`;

        charDefLines.forEach(line => {
          const [code, char] = line.split('\t');
          if (code && char) cchardefine.set(char, code);
        });

        // Process ccharfirst and ccharsecond
        for (let [char, code] of cchardefine) {
          ccharfirst.set(char, code[0]);
          ccharsecond.set(char, code[1] || 'x');
        }

        // Process mcc0string
        let xLine = -1;
        for (let i = lines.length - 1; i >= 0; i--) {
          if (lines[i].startsWith('x')) {
            xLine = i;
            break;
          }
        }
        let charDefEndLine = lines.indexOf('%chardef end');
        if (xLine === -1 || charDefEndLine === -1) return;

        let mcc0Lines;
        if (remcc0 == '') {
          mcc0Lines = lines.slice(xLine + 1, charDefEndLine);
          mcc0Lines = mcc0Lines.filter(line => {
            const parts = line.split('\t');
            if (parts.length > 1) {
              const code = parts[0];
              if (code.startsWith('z')) {
                const restOfCode = code.slice(1);
                if (restOfCode.includes('z')) {
                  return false;
                }
              }
            }
            return true;
          });
          mcc0Lines = mcc0Lines.map(line => {
            const parts = line.split('\t');
            return parts.length > 1 ? parts[1] : line;
          });
          mcc0Lines = mcc0Lines.filter((line, index, arr) => arr.indexOf(line) == index);
        } else {
          mcc0Lines = JSON.parse(remcc0);
        }
        mcc0string = mcc0Lines.join('\n');
        document.getElementById('output').innerHTML += `mcc0string 行數: ${mcc0Lines.length}<br>`;

        // Process mcc0
        const mcc0Values = mcc0Lines.slice(0, 5);
        mcc0.set('z', mcc0Values);

        // Process mcc0moog
        if (mcc0Values[1]) mcc0moog.set('zz', mcc0Values[1]);
        if (mcc0Values[2]) mcc0moog.set('zzz', mcc0Values[2]);
        if (mcc0Values[3]) mcc0moog.set('zzx', mcc0Values[3]);
        if (mcc0Values[4]) mcc0moog.set('zxz', mcc0Values[4]);

        // Process mcc1string and mcc4string
        const remainingLines = mcc0Lines.slice(5);
        mcc1string = remainingLines.filter(line => line.length === 2).join('\n');
        mcc4string = remainingLines.filter(line => line.length >= 3).join('\n');

        // Process mcc1all
        mcc1string.split('\n').forEach(line => {
          if (line) {
            const firstChar = line[0];
            const code = ccharfirst.get(firstChar);
            if (code) {
              const key = `z${code}`;
              if (!mcc1all.has(key)) mcc1all.set(key, []);
              mcc1all.get(key).push(line);
            }
          }
        });

        // Process mcc1 and mcc2string
        for (let [key, values] of mcc1all) {
          const top5 = values.slice(0, 5);
          mcc1.set(key, top5);
        }
        mcc2string = mcc1string
          .split('\n')
          .filter(line => {
            return !Array.from(mcc1.values()).flat().includes(line);
          })
          .join('\n');

        // Process mcc1moog
        for (let [key, values] of mcc1) {
          if (values[1]) mcc1moog.set(key + 'z', values[1]);
          if (values[2]) mcc1moog.set(key + 'zz', values[2]);
          if (values[3]) mcc1moog.set(key + 'zx', values[3]);
          if (values[4]) mcc1moog.set(key + 'xz', values[4]);
        }

        // Process mcc2all
        mcc2string.split('\n').forEach(line => {
          if (line && line.length >= 2) {
            const firstCode = ccharfirst.get(line[0]) || '';
            const secondCode = ccharfirst.get(line[1]) || '';
            const key = `z${firstCode}${secondCode}`;
            if (!mcc2all.has(key)) mcc2all.set(key, []);
            mcc2all.get(key).push(line);
          }
        });

        // Process mcc2 and mcc3string
        for (let [key, values] of mcc2all) {
          const top2 = values.slice(0, 4);
          mcc2.set(key, top2);
        }
        mcc3string = mcc2string
          .split('\n')
          .filter(line => {
            return !Array.from(mcc2.values()).flat().includes(line);
          })
          .join('\n');

        // Process mcc2moog
        for (let [key, values] of mcc2) {
          if (values[1]) mcc2moog.set(key + 'z', values[1]);
          if (values[2]) mcc2moog.set('z' + key[1] + 'z' + key[2], values[2]);
          if (values[3]) mcc2moog.set('zz' + key[1] + key[2], values[3]);
        }

        // Process mcc3
        mcc3string.split('\n').forEach(line => {
          if (line && line.length >= 2) {
            const firstCode = ccharfirst.get(line[0]) || '';
            const secondCode = ccharfirst.get(line[1]) || '';
            const thirdCode = ccharsecond.get(line[1]) || 'x';
            const key = `z${firstCode}${secondCode}${thirdCode}`;
            if (!mcc3.has(key)) mcc3.set(key, []);
            mcc3.get(key).push(line);
          }
        });

        // Process mcc4
        mcc4string.split('\n').forEach(line => {
          if (line && line.length >= 3) {
            const codes = line
              .slice(0, 3)
              .split('')
              .map(char => ccharfirst.get(char) || '');
            const key = `z${codes.join('')}`;
            if (!mcc4.has(key)) mcc4.set(key, []);
            mcc4.get(key).push(line);
          }
        });

        // 生成時間戳
        const now = new Date();
        const yyyy = now.getFullYear();
        const mm = String(now.getMonth() + 1).padStart(2, '0');
        const dd = String(now.getDate()).padStart(2, '0');
        const hh = String(now.getHours()).padStart(2, '0');
        const min = String(now.getMinutes()).padStart(2, '0');
        const sec = String(now.getSeconds()).padStart(2, '0');
        const timestamp = `${yyyy}${mm}${dd}_${hh}${min}${sec}`;

        // Generate download content
        const newContent = [
          ...lines.slice(0, xLine + 1),
          ...generateNewSection(),
          '%chardef end',
          ...lines.slice(charDefEndLine + 1)
        ]
          .map(line => {
            if (line.startsWith('%version')) {
              return `%version ${timestamp}`;
            }
            return line;
          })
          .join('\n');
        const blob = new Blob([newContent], { type: 'text/plain' });
        const url = URL.createObjectURL(blob);
        const size = blob.size;
        document.getElementById('download').innerHTML =
          `<a href="${url}" download="haha_${timestamp}.txt">下載文件 (${size} bytes)</a>`;

        // Show the tab system now that data is successfully loaded
        document.getElementById('mainTabSystem').style.display = 'block';
      }

```

**3.2 更新互動控制邏輯(添加 Enter 鍵監聽與編輯欄邏輯):**
將這段代碼放在 `</script>` 前,它包含了全新的 `switchTab`、`handleEditSubmit`、以及與修正後 `processContent` 完美連動的排序調校機制:

```javascript
      // === ADDED EDIT AREA VARIABLES AND FUNCTIONS ===
      let currentEditCode = '';
      let currentTargetWord = '';
      let selectedEditItem = null;
      let activeEditKeys = new Set();
      let changedWords = new Set();

      // Setup Event Listeners
      document.addEventListener('DOMContentLoaded', () => {
        const inputEl = document.getElementById('codeInput');
        if (inputEl) {
          inputEl.addEventListener('keypress', function (e) {
            if (e.key === 'Enter') handleEditSubmit();
          });
        }
      });

      function switchTab(tab) {
        document.getElementById('tabEdit').classList.toggle('active', tab === 'edit');
        document.getElementById('tabStats').classList.toggle('active', tab === 'stats');
        document.getElementById('contentEdit').classList.toggle('active', tab === 'edit');
        document.getElementById('contentStats').classList.toggle('active', tab === 'stats');
      }

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

        activeEditKeys.clear();
        changedWords.clear();
        selectedEditItem = null;

        // Check if English input
        if (/^[a-zA-Z]+$/.test(val)) {
          if (val[0].toLowerCase() !== 'z') val = 'z' + val;
          val = val.substring(0, 4).toLowerCase();
          currentEditCode = val;
          currentTargetWord = '';
        } else {
          // Chinese phrase input
          currentTargetWord = val;
          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          
          // Append if it doesn't exist
          if (!lines.includes(val)) {
            lines.push(val);
            processContent(JSON.stringify(lines));
          }

          // Compute target code prefix
          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);
        }

        // Initialize display rows depending on code length
        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);

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

      function handleEditReset() {
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';
        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        activeEditKeys.clear();
        changedWords.clear();
      }

      function renderEditArea() {
        let container = document.getElementById('editRows');
        container.innerHTML = '';

        let sortedKeys = Array.from(activeEditKeys).sort((a, b) => {
          if (a === 'z') return -1;
          if (b === 'z') return 1;
          if (a.length !== b.length) return a.length - b.length;
          return a.localeCompare(b);
        });

        sortedKeys.forEach(k => {
          let label = '';
          let items = [];

          if (k === 'z') {
            label = 'z: mcc0all top 5';
            (mcc0.get('z') || []).forEach(w => items.push({ w: w, cls: '' }));
          } else if (k.length === 2) {
            label = `${k}: mcc1all top 5`;
            (mcc1all.get(k) || []).slice(0, 5).forEach(w => items.push({ w: w, cls: '' }));
          } else if (k.length === 3) {
            label = `${k}: mcc2all top 4`;
            (mcc2all.get(k) || []).slice(0, 4).forEach(w => items.push({ w: w, cls: '' }));
          } else if (k.length === 4) {
            label = `${k}: all items from mcc3 and mcc4`;
            (mcc3.get(k) || []).forEach(w => items.push({ w: w, cls: 'mcc3' }));
            (mcc4.get(k) || []).forEach(w => items.push({ w: w, cls: 'mcc4' }));
          }

          if (items.length > 0) {
            let rowDiv = document.createElement('div');
            rowDiv.className = 'edit-row';
            let titleDiv = document.createElement('div');
            titleDiv.textContent = label;
            titleDiv.style.fontWeight = 'bold';
            titleDiv.style.marginBottom = '8px';
            rowDiv.appendChild(titleDiv);

            items.forEach(item => {
              let span = document.createElement('span');
              span.textContent = item.w;
              span.className = `edit-item ${item.cls}`;
              
              if (item.w === selectedEditItem) span.classList.add('selected');
              if (item.w === currentTargetWord) span.classList.add('target-word');
              if (changedWords.has(item.w)) span.classList.add('changed');

              span.onclick = () => handleItemClick(item.w);
              rowDiv.appendChild(span);
            });
            container.appendChild(rowDiv);
          }
        });
      }

      function getSystemState() {
        let state = new Map();
        state.set('z', mcc0.get('z') || []);
        for (let [k, v] of mcc1all) state.set(k, v.slice(0, 5));
        for (let [k, v] of mcc2all) state.set(k, v.slice(0, 4));
        for (let [k, v] of mcc3) {
          if (!state.has(k)) state.set(k, []);
          state.get(k).push(...v);
        }
        for (let [k, v] of mcc4) {
          if (!state.has(k)) state.set(k, []);
          state.get(k).push(...v);
        }
        return state;
      }

      function detectAndRenderChanges(oldState, newState) {
        let wordToOldPos = new Map();
        for (let [k, vals] of oldState) {
          vals.forEach((w, i) => wordToOldPos.set(w, `${k}-${i}`));
        }

        changedWords.clear();
        for (let [k, vals] of newState) {
          let oldVals = oldState.get(k) || [];
          
          if (JSON.stringify(vals) !== JSON.stringify(oldVals)) {
            activeEditKeys.add(k); 
          }
          
          vals.forEach((w, i) => {
            if (wordToOldPos.get(w) !== `${k}-${i}`) {
              changedWords.add(w); 
              activeEditKeys.add(k); 
            }
          });
        }
        renderEditArea();
      }

      function handleItemClick(word) {
        if (!selectedEditItem) {
          selectedEditItem = word;
          renderEditArea();
        } else {
          if (selectedEditItem === word) {
            selectedEditItem = null;
            renderEditArea();
            return;
          }

          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          let idxA = lines.indexOf(selectedEditItem);
          let originalIdxB = lines.indexOf(word);

          if (idxA > -1 && originalIdxB > -1) {
            let oldState = getSystemState();

            lines.splice(idxA, 1);
            let idxB = lines.indexOf(word); 
            
            if (idxA > originalIdxB) {
              lines.splice(idxB, 0, selectedEditItem);
            } else {
              lines.splice(idxB + 1, 0, selectedEditItem); 
            }

            processContent(JSON.stringify(lines));
            
            let newState = getSystemState();
            detectAndRenderChanges(oldState, newState);
          }
          selectedEditItem = null;
        }
      }
      // === END OF ADDED EDIT AREA FUNCTIONS ===

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

修正:
當點擊一個詞A,再點擊另一個詞B,則應把詞A放到詞B的前面,而詞B不一定是被擠出去的那個,而是按當前排序,取前五個或前四個,最後一個才會被擠出去。

為submit加上css。去掉它右邊的reset。

在最下方的「close/reset edit」左邊新增兩個按鈕,一個是put to the end,另一個是delete。

當點擊end再點擊一個詞(或先點擊一個詞再點擊end)這個詞將去到末尾。

當點擊delete再點擊一個詞(或先點擊一個詞再點擊delete)這個詞將被刪除。

三個按鈕都加上css。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。


gemini pro會玩否:

代码: 全选

Here are the precise modifications to implement the new ordering logic, remove the reset button next to submit, add the new action buttons, and style them.

All code additions and comments are in English as requested.

### 1. Update CSS

Add the following CSS rules to your `<style>` block to style the submit button and the new action buttons:

```css
      /* === ADDED CSS FOR SUBMIT AND EDIT ACTIONS === */
      #submitCode {
        padding: 8px 16px;
        background-color: #2196f3;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        transition: background-color 0.2s;
      }
      #submitCode:hover {
        background-color: #0b7dda;
      }

      .edit-actions {
        margin-top: 20px;
        display: flex;
        gap: 10px;
      }
      .edit-action-btn {
        padding: 8px 16px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        transition: all 0.2s;
        background-color: #e0e0e0;
        color: #333;
      }
      .edit-action-btn:hover { background-color: #bdbdbd; }
      .edit-action-btn.active-state {
        box-shadow: 0 0 0 3px #333;
        font-weight: bold;
        transform: translateY(-2px);
      }
      
      #btnMoveEnd { background-color: #ff9800; color: white; }
      #btnMoveEnd:hover { background-color: #e68a00; }
      
      #btnDelete { background-color: #f44336; color: white; }
      #btnDelete:hover { background-color: #da190b; }
      
      #btnCloseEdit { background-color: #9e9e9e; color: white; }
      #btnCloseEdit:hover { background-color: #7d7d7d; }
      /* === END OF ADDED CSS === */

```

### 2. Update HTML Structure for the Edit Area

Replace your existing `<div id="contentEdit" ...>` entirely with the updated structure below. This removes the top reset button and adds the bottom action buttons:

```html
      <div id="contentEdit" class="tab-content active">
        <div id="inputArea">
          <label for="codeInput" style="font-weight: bold;">Encode Input: </label>
          <input type="text" id="codeInput" placeholder="Enter code (e.g. zhy) or word">
          <button id="submitCode" onclick="handleEditSubmit()">Submit</button>
          </div>
        <div id="editArea" style="display: none;">
          <div id="editRows"></div>
          
          <div class="edit-actions">
            <button id="btnMoveEnd" class="edit-action-btn" onclick="handleEditAction('end')">Put to the end</button>
            <button id="btnDelete" class="edit-action-btn" onclick="handleEditAction('delete')">Delete</button>
            <button id="btnCloseEdit" class="edit-action-btn" onclick="handleEditReset()">Close / Reset Edit</button>
          </div>

        </div>
      </div>

```

### 3. Update JavaScript Variables and Functions

Find your existing Edit Area JavaScript section. Make the following changes:

**3.1 Add the `pendingAction` state variable:**
Right below where you declare `changedWords`, add `pendingAction`:

```javascript
      let currentEditCode = '';
      let currentTargetWord = '';
      let selectedEditItem = null;
      let activeEditKeys = new Set();
      let changedWords = new Set();
      let pendingAction = null; // <-- NEW: Tracks if 'end' or 'delete' is active

```

**3.2 Update `handleEditReset`:**
Clear the `pendingAction` state when resetting:

```javascript
      function handleEditReset() {
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';
        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        activeEditKeys.clear();
        changedWords.clear();
        
        // Clear pending action states
        pendingAction = null;
        updateActionButtonsUI();
      }

```

**3.3 Add the New Action Functions:**
Insert these functions right above your existing `handleItemClick` function:

```javascript
      // === NEW FUNCTIONS FOR ACTIONS ===
      function handleEditAction(action) {
        if (selectedEditItem) {
           // If a word is already selected, apply the action immediately
           applyActionToWord(action, selectedEditItem);
           selectedEditItem = null;
           pendingAction = null;
        } else {
           // Toggle the pending state (waiting for a word to be clicked)
           if (pendingAction === action) {
               pendingAction = null; // Untoggle if clicked again
           } else {
               pendingAction = action;
           }
        }
        updateActionButtonsUI();
        renderEditArea();
      }

      function updateActionButtonsUI() {
        const btnEnd = document.getElementById('btnMoveEnd');
        const btnDel = document.getElementById('btnDelete');
        if(btnEnd) btnEnd.classList.toggle('active-state', pendingAction === 'end');
        if(btnDel) btnDel.classList.toggle('active-state', pendingAction === 'delete');
      }

      function applyActionToWord(action, word) {
          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          let idx = lines.indexOf(word);
          if (idx === -1) return;

          let oldState = getSystemState();

          if (action === 'delete') {
              lines.splice(idx, 1);
          } else if (action === 'end') {
              lines.splice(idx, 1);
              lines.push(word); // Move to the very end of the array
          }

          processContent(JSON.stringify(lines));
          let newState = getSystemState();
          detectAndRenderChanges(oldState, newState);
      }
      // === END OF NEW ACTION FUNCTIONS ===

```

**3.4 Replace `handleItemClick` with the Updated Logic:**
Replace your entire existing `handleItemClick` function with this updated version, which now handles the new insertion logic (strictly putting A before B) and listens for the new action states:

```javascript
      function handleItemClick(word) {
        // If an action (delete or end) is pending, apply it instead of selecting
        if (pendingAction) {
            applyActionToWord(pendingAction, word);
            pendingAction = null;
            selectedEditItem = null;
            updateActionButtonsUI();
            return;
        }

        if (!selectedEditItem) {
          selectedEditItem = word;
          renderEditArea();
        } else {
          if (selectedEditItem === word) {
            selectedEditItem = null; // Toggle selection off
            renderEditArea();
            return;
          }

          // Order adjustment logic: Put A right in front of B
          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          let idxA = lines.indexOf(selectedEditItem);
          let originalIdxB = lines.indexOf(word);

          if (idxA > -1 && originalIdxB > -1) {
            let oldState = getSystemState();

            // Extract A
            lines.splice(idxA, 1);
            
            // Recalculate B's index after A is removed from the array
            let idxB = lines.indexOf(word); 
            
            // ALWAYS insert A right before B's current index
            lines.splice(idxB, 0, selectedEditItem);

            // Sync structural changes globally
            processContent(JSON.stringify(lines));
            
            let newState = getSystemState();
            detectAndRenderChanges(oldState, newState);
          }
          selectedEditItem = null;
        }
      }

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

mcc3和mcc4不再需要用顏色區分。

在編輯區右邊,新增一半的對比區,列出在修改前的詞。這些詞將不可點擊。如果是手機端,這些原先的詞將用小一點的字列在下方。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。

代码: 全选

Here are the precise modifications to meet your specifications.

The CSS rules for `.mcc3` and `.mcc4` have been combined into a uniform styling (removing unique colors). A dual-panel split layout has been implemented for desktop view, which falls back to placing a compact, smaller-text historical preview section underneath the active row on mobile devices.

All code edits, functional mechanisms, and code comments are fully written in English.

### 1. Update CSS Styles

Replace the CSS rules under `<style>` regarding the tab system, input code, and edit rows with this updated stylesheet:

```css
      /* === UPDATED CSS FOR CODE INPUT, EDIT ACTIONS, AND SPLIT PREVIEW === */
      #mainTabSystem {
        display: none; /* Hidden by default until file is loaded */
      }

      #codeInput {
        padding: 8px 12px;
        font-size: 14px;
        border: 1px solid #ccc;
        border-radius: 4px;
        outline: none;
        transition: border-color 0.2s, box-shadow 0.2s;
        width: 220px;
        margin-right: 6px;
      }
      #codeInput:focus {
        border-color: #4caf50;
        box-shadow: 0 0 5px rgba(76, 175, 80, 0.3);
      }

      #submitCode {
        padding: 8px 16px;
        background-color: #2196f3;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        transition: background-color 0.2s;
      }
      #submitCode:hover {
        background-color: #0b7dda;
      }

      .tab-container {
        display: flex;
        border-bottom: 1px solid #ccc;
        margin-bottom: 15px;
        margin-top: 20px;
      }
      .tab-button {
        padding: 10px 20px;
        cursor: pointer;
        border: 1px solid transparent;
        background: transparent;
        font-size: 16px;
        margin-bottom: -1px;
      }
      .tab-button.active {
        border: 1px solid #ccc;
        border-bottom-color: #fff;
        background: #fff;
        border-radius: 5px 5px 0 0;
        font-weight: bold;
        color: #000;
      }
      .tab-content {
        display: none;
        padding: 15px 0;
      }
      .tab-content.active {
        display: block;
      }
      
      .stats-buttons {
        display: flex;
        gap: 8px;
        margin-bottom: 15px;
      }
      .stats-btn {
        padding: 8px 12px;
        cursor: pointer;
        border: 1px solid #ccc;
        border-radius: 4px;
        background: #f9f9f9;
      }
      .stats-btn.active {
        background-color: #4caf50;
        color: white;
        border-color: #388e3c;
      }

      /* Split layout container: Adaptive for desktop and mobile */
      .split-container {
        display: flex;
        gap: 20px;
        width: 100%;
        box-sizing: border-box;
      }
      .split-pane {
        flex: 1;
        min-width: 0;
      }
      .split-pane.pane-original {
        border-left: 2px dashed #bbb;
        padding-left: 20px;
      }

      .edit-row {
        margin-bottom: 15px;
        padding: 10px;
        border-bottom: 1px dashed #ccc;
        background: #fafafa;
        border-radius: 5px;
      }
      .edit-item {
        display: inline-block;
        padding: 6px 12px;
        margin: 4px;
        border: 1px solid #ccc;
        border-radius: 4px;
        cursor: pointer;
        background: #fff;
        transition: all 0.2s;
        user-select: none;
      }
      .edit-item:hover { background: #e0e0e0; }
      .edit-item.selected {
        background: #fff9c4 !important;
        border: 2px solid #fbc02d;
        box-shadow: 0 0 5px rgba(251, 192, 45, 0.5);
      }
      
      /* mcc3 and mcc4 color distinctions removed, using default layout style */
      .edit-item.mcc3, .edit-item.mcc4 { 
        background: #fff; 
        border-color: #ccc; 
      }
      
      .edit-item.changed { border: 2px dashed #f44336 !important; background: #ffebee; }
      .edit-item.target-word { font-weight: bold; color: #d32f2f; border: 2px solid #d32f2f; }

      /* Static preview rules for the original unmodifiable elements */
      .preview-item {
        display: inline-block;
        padding: 6px 12px;
        margin: 4px;
        border: 1px dashed #aaa;
        border-radius: 4px;
        background: #f5f5f5;
        color: #777;
        cursor: not-allowed;
        user-select: none;
      }
      
      /* Mobile view responsive block fallback */
      .mobile-original-preview {
        display: none;
        margin-top: 6px;
        padding: 4px 8px;
        background: #f0f0f0;
        border-radius: 4px;
        font-size: 11px;
        color: #666;
      }
      .mobile-original-preview span {
        margin-right: 8px;
        text-decoration: line-through;
      }

      .edit-actions {
        margin-top: 20px;
        display: flex;
        gap: 10px;
      }
      .edit-action-btn {
        padding: 8px 16px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-size: 14px;
        transition: all 0.2s;
        background-color: #e0e0e0;
        color: #333;
      }
      .edit-action-btn:hover { background-color: #bdbdbd; }
      .edit-action-btn.active-state {
        box-shadow: 0 0 0 3px #333;
        font-weight: bold;
        transform: translateY(-2px);
      }
      
      #btnMoveEnd { background-color: #ff9800; color: white; }
      #btnMoveEnd:hover { background-color: #e68a00; }
      
      #btnDelete { background-color: #f44336; color: white; }
      #btnDelete:hover { background-color: #da190b; }
      
      #btnCloseEdit { background-color: #9e9e9e; color: white; }
      #btnCloseEdit:hover { background-color: #7d7d7d; }

      @media (max-width: 768px) {
        .split-container {
          flex-direction: column;
        }
        .split-pane.pane-original {
          display: none; /* Hide the right-side layout wrapper on mobile viewports */
        }
        .mobile-original-preview {
          display: block; /* Activate context injection below active elements instead */
        }
      }

```

### 2. Update HTML Body Structure

Replace the `<button>` tags and `<iframe>` inside the body container with this layout module wrapped in `#mainTabSystem`:

```html
    <div id="mainTabSystem">
      <div class="tab-container">
        <button id="tabEdit" class="tab-button active" onclick="switchTab('edit')">Edit</button>
        <button id="tabStats" class="tab-button" onclick="switchTab('stats')">Statistics</button>
      </div>

      <div id="contentEdit" class="tab-content active">
        <div id="inputArea">
          <label for="codeInput" style="font-weight: bold;">Encode Input: </label>
          <input type="text" id="codeInput" placeholder="Enter code (e.g. zhy) or word">
          <button id="submitCode" onclick="handleEditSubmit()">Submit</button>
        </div>
        <div id="editArea" style="display: none;">
          
          <div class="split-container">
            <div class="split-pane pane-active">
              <div id="editRows"></div>
            </div>
            
            <div class="split-pane pane-original">
              <div style="font-weight: bold; font-size: 16px; margin-bottom: 12px; color: #555;">Original Baseline View (Unmodifiable)</div>
              <div id="originalRows"></div>
            </div>
          </div>
          
          <div class="edit-actions">
            <button id="btnMoveEnd" class="edit-action-btn" onclick="handleEditAction('end')">Put to the end</button>
            <button id="btnDelete" class="edit-action-btn" onclick="handleEditAction('delete')">Delete</button>
            <button id="btnCloseEdit" class="edit-action-btn" onclick="handleEditReset()">Close / Reset Edit</button>
          </div>
        </div>
      </div>

      <div id="contentStats" class="tab-content">
        <div class="stats-buttons" id="statsBtnContainer">
          <button class="stats-btn" style="display: none" id="mcc0update" onclick="showContent('mcc0update', this)">Update</button>
          <button class="stats-btn" id="btnMcc0All" onclick="showContent('mcc0all', this)">All</button>
          <button class="stats-btn" id="btnMcc1All" onclick="showContent('mcc1all', this)">High Freq</button>
          <button class="stats-btn" id="btnMcc2All" onclick="showContent('mcc2all', this)">Common</button>
          <button class="stats-btn" id="btnMcc3" onclick="showContent('mcc3', this)">Repeat</button>
          <button class="stats-btn" id="btnMcc4" onclick="showContent('mcc4', this)">Multi</button>
        </div>
        <iframe
          id="domifr"
          style="width: 100%; height: 480px"
          src="https://ejsoon.vip/wp-content/uploads/2024/05/json3.html"
          frameborder="0"
        ></iframe>
      </div>
    </div>
    ```

### 3. Update JavaScript Core
Add the new state storage map definition variables inside your script wrapper, modify `processContent` to support active system initialization tracking, and insert the state calculation routines along with the render processors:

**3.1 Declaration Context Adjustments**
Add `originalSnapshotState` to tracking variables at the top of your custom editing block variables:

```javascript
      // === UPDATED EDIT DATA MANAGEMENT BLOCK ===
      let currentEditCode = '';
      let currentTargetWord = '';
      let selectedEditItem = null;
      let activeEditKeys = new Set();
      let changedWords = new Set();
      let pendingAction = null; 
      let originalSnapshotState = new Map(); // <-- NEW: Holds frozen immutable benchmark maps for visualization panels

```

**3.2 Update `resetPage` and `processContent**`
Make sure to clean the target views and reset the system maps properly during lifecycle calls. Add the clear method down inside `resetPage` and append the dynamic tab control flag at the end of `processContent`:

```javascript
      // Add inside resetPage(readfile = true) block right below innerHTML clear handlers:
      if (readfile) {
          // ... previous fields clear ...
          document.getElementById('mainTabSystem').style.display = 'none'; 
          originalSnapshotState.clear();
      }

```

```javascript
      // Add at the very bottom of processContent(remcc0 = '') function right after download innerHTML binding:
      // Show the tab system now that data is successfully loaded
      document.getElementById('mainTabSystem').style.display = 'block';

```

**3.3 Update Interaction Logic & Comparison Renderer**
Replace your operational script methods (`handleEditSubmit`, `handleEditReset`, `renderEditArea`, `handleItemClick`, etc.) with these enhanced definitions that calculate the baseline snapshot on search and output side-by-side components:

```javascript
      // Setup Keyboard Interceptor Event Listeners
      document.addEventListener('DOMContentLoaded', () => {
        const inputEl = document.getElementById('codeInput');
        if (inputEl) {
          inputEl.addEventListener('keypress', function (e) {
            if (e.key === 'Enter') handleEditSubmit();
          });
        }
      });

      function switchTab(tab) {
        document.getElementById('tabEdit').classList.toggle('active', tab === 'edit');
        document.getElementById('tabStats').classList.toggle('active', tab === 'stats');
        document.getElementById('contentEdit').classList.toggle('active', tab === 'edit');
        document.getElementById('contentStats').classList.toggle('active', tab === 'stats');
      }

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

        activeEditKeys.clear();
        changedWords.clear();
        selectedEditItem = null;
        pendingAction = null;
        updateActionButtonsUI();

        // 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)) {
            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);

        // CRITICAL FIX: Lock unchanged baseline structural footprint before user interaction
        originalSnapshotState = getSystemState();

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

      function handleEditReset() {
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';
        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        activeEditKeys.clear();
        changedWords.clear();
        pendingAction = null;
        originalSnapshotState.clear();
        updateActionButtonsUI();
      }

      function getSystemState() {
        let state = new Map();
        state.set('z', mcc0.get('z') || []);
        for (let [k, v] of mcc1all) state.set(k, v.slice(0, 5));
        for (let [k, v] of mcc2all) state.set(k, v.slice(0, 4));
        for (let [k, v] of mcc3) {
          if (!state.has(k)) state.set(k, []);
          state.get(k).push(...v);
        }
        for (let [k, v] of mcc4) {
          if (!state.has(k)) state.set(k, []);
          state.get(k).push(...v);
        }
        return state;
      }

      function renderEditArea() {
        let activeContainer = document.getElementById('editRows');
        let originalContainer = document.getElementById('originalRows');
        
        activeContainer.innerHTML = '';
        originalContainer.innerHTML = '';

        let sortedKeys = Array.from(activeEditKeys).sort((a, b) => {
          if (a === 'z') return -1;
          if (b === 'z') return 1;
          if (a.length !== b.length) return a.length - b.length;
          return a.localeCompare(b);
        });

        let currentState = getSystemState();

        sortedKeys.forEach(k => {
          let label = '';
          let activeItems = [];
          let originalItems = originalSnapshotState.get(k) || [];

          if (k === 'z') {
            label = 'z: mcc0all top 5';
            (mcc0.get('z') || []).forEach(w => activeItems.push({ w: w, cls: '' }));
          } else if (k.length === 2) {
            label = `${k}: mcc1all top 5`;
            (mcc1all.get(k) || []).slice(0, 5).forEach(w => activeItems.push({ w: w, cls: '' }));
          } else if (k.length === 3) {
            label = `${k}: mcc2all top 4`;
            (mcc2all.get(k) || []).slice(0, 4).forEach(w => activeItems.push({ w: w, cls: '' }));
          } else if (k.length === 4) {
            label = `${k}: mcc3 & mcc4 items`;
            (mcc3.get(k) || []).forEach(w => activeItems.push({ w: w, cls: 'mcc3' }));
            (mcc4.get(k) || []).forEach(w => activeItems.push({ w: w, cls: 'mcc4' }));
          }

          if (activeItems.length > 0 || originalItems.length > 0) {
            // Render active editable line elements
            let activeRowDiv = document.createElement('div');
            activeRowDiv.className = 'edit-row';
            
            let activeTitle = document.createElement('div');
            activeTitle.textContent = label;
            activeTitle.style.fontWeight = 'bold';
            activeTitle.style.marginBottom = '8px';
            activeRowDiv.appendChild(activeTitle);

            activeItems.forEach(item => {
              let span = document.createElement('span');
              span.textContent = item.w;
              span.className = `edit-item ${item.cls}`;
              
              if (item.w === selectedEditItem) span.classList.add('selected');
              if (item.w === currentTargetWord) span.classList.add('target-word');
              if (changedWords.has(item.w)) span.classList.add('changed');

              span.onclick = () => handleItemClick(item.w);
              activeRowDiv.appendChild(span);
            });

            // Mobile-Only context insertion rendering logic block
            let mobilePreviewDiv = document.createElement('div');
            mobilePreviewDiv.className = 'mobile-original-preview';
            mobilePreviewDiv.innerHTML = '<strong>Orig: </strong>';
            if (originalItems.length === 0) {
              mobilePreviewDiv.innerHTML += '<em>None</em>';
            } else {
              originalItems.forEach(w => {
                let mSpan = document.createElement('span');
                mSpan.textContent = w;
                mobilePreviewDiv.appendChild(mSpan);
              });
            }
            activeRowDiv.appendChild(mobilePreviewDiv);
            activeContainer.appendChild(activeRowDiv);

            // Desktop Right-Pane Companion Display rendering logic
            let originalRowDiv = document.createElement('div');
            originalRowDiv.className = 'edit-row';
            
            let originalTitle = document.createElement('div');
            originalTitle.textContent = `${label} (Baseline)`;
            originalTitle.style.fontWeight = 'bold';
            originalTitle.style.marginBottom = '8px';
            originalRowDiv.appendChild(originalTitle);

            if (originalItems.length === 0) {
              let emptySpan = document.createElement('span');
              emptySpan.textContent = 'None';
              emptySpan.style.fontStyle = 'italic';
              emptySpan.style.color = '#999';
              originalRowDiv.appendChild(emptySpan);
            } else {
              originalItems.forEach(w => {
                let span = document.createElement('span');
                span.textContent = w;
                span.className = 'preview-item';
                originalRowDiv.appendChild(span);
              });
            }
            originalContainer.appendChild(originalRowDiv);
          }
        });
      }

      function detectAndRenderChanges(oldState, newState) {
        let wordToOldPos = new Map();
        for (let [k, vals] of originalSnapshotState) {
          vals.forEach((w, i) => wordToOldPos.set(w, `${k}-${i}`));
        }

        changedWords.clear();
        for (let [k, vals] of newState) {
          let origVals = originalSnapshotState.get(k) || [];
          
          if (JSON.stringify(vals) !== JSON.stringify(origVals)) {
            activeEditKeys.add(k); 
          }
          
          vals.forEach((w, i) => {
            if (wordToOldPos.get(w) !== `${k}-${i}`) {
              changedWords.add(w); 
              activeEditKeys.add(k); 
            }
          });
        }
        renderEditArea();
      }

      function handleEditAction(action) {
        if (selectedEditItem) {
           applyActionToWord(action, selectedEditItem);
           selectedEditItem = null;
           pendingAction = null;
        } else {
           if (pendingAction === action) {
               pendingAction = null;
           } else {
               pendingAction = action;
           }
        }
        updateActionButtonsUI();
        renderEditArea();
      }

      function updateActionButtonsUI() {
        const btnEnd = document.getElementById('btnMoveEnd');
        const btnDel = document.getElementById('btnDelete');
        if (btnEnd) btnEnd.classList.toggle('active-state', pendingAction === 'end');
        if (btnDel) btnDel.classList.toggle('active-state', pendingAction === 'delete');
      }

      function applyActionToWord(action, word) {
          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          let idx = lines.indexOf(word);
          if (idx === -1) return;

          let oldState = getSystemState();

          if (action === 'delete') {
              lines.splice(idx, 1);
          } else if (action === 'end') {
              lines.splice(idx, 1);
              lines.push(word); 
          }

          processContent(JSON.stringify(lines));
          let newState = getSystemState();
          detectAndRenderChanges(oldState, newState);
      }

      function handleItemClick(word) {
        if (pendingAction) {
            applyActionToWord(pendingAction, word);
            pendingAction = null;
            selectedEditItem = null;
            updateActionButtonsUI();
            return;
        }

        if (!selectedEditItem) {
          selectedEditItem = word;
          renderEditArea();
        } else {
          if (selectedEditItem === word) {
            selectedEditItem = null; 
            renderEditArea();
            return;
          }

          let lines = mcc0string.split('\n').filter(l => l.trim() !== '');
          let idxA = lines.indexOf(selectedEditItem);
          
          if (idxA > -1) {
            let oldState = getSystemState();

            // Extract A from lines
            lines.splice(idxA, 1);
            
            // Recalculate target position index for element B
            let idxB = lines.indexOf(word); 
            if (idxB > -1) {
              // ALWAYS place word A directly in front of word B
              lines.splice(idxB, 0, selectedEditItem);
              
              processContent(JSON.stringify(lines));
              let newState = getSystemState();
              detectAndRenderChanges(oldState, newState);
            }
          }
          selectedEditItem = null;
        }
      }

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

domchar57

json上面的按鈕不行了?
附件
domchar57.7z
(6.86 KiB) 已下载 10 次
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
一枝独秀
一枝独秀
帖子: 5751
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 179 次
被圈友点赞: 207 次
联系:

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

帖子 ejsoon »

修正:
當點擊stats-btn系列按鈕時,無法執行,因為btnElement沒有定義。

把「close/reset edit」分成兩個按鈕,一個是close,把整個編輯欄關掉,再次顯示編碼輸入框,另一個是reset,撤回所有的改動(加詞的改動不算)。close放到最右邊。

只需給出所要修改的地方,所有的代碼和注釋都要使用英文。




這回gemini pro分配任務給flash不再另行通知:

代码: 全选

Here are the necessary modifications to fix the undefined `btnElement` error and split the "Close / Reset Edit" action into two separate buttons as requested. All code and comments have been written in English.

---

### 1. Fix the `btnElement` Error in `showContent`

Locate the `showContent(type)` function inside the `<script>` tag. Replace the `if (btnElement)` block with code that correctly targets the event trigger or the elements using `this`.

**Find:**

```javascript
      function showContent(type) {
        const domifr = document.getElementById('domifr');
        const mcc0update = document.getElementById('mcc0update');

        // Apply active styling to the clicked stats button
        if (btnElement) {
          document.querySelectorAll('#statsBtnContainer .stats-btn').forEach(btn => btn.classList.remove('active'));
          btnElement.classList.add('active');
        }

        if (type === 'mcc0all') {

```

**Replace with:**

```javascript
      function showContent(type, btnElement) {
        const domifr = document.getElementById('domifr');
        const mcc0update = document.getElementById('mcc0update');

        // Apply active styling to the clicked stats button
        if (btnElement) {
          document.querySelectorAll('#statsBtnContainer .stats-btn').forEach(btn => btn.classList.remove('active'));
          btnElement.classList.add('active');
        }

        if (type === 'mcc0all') {

```

> *Note: By adding `btnElement` as the second parameter, it will correctly grab the `this` reference passed from your inline HTML `onclick` attributes (e.g., `onclick="showContent('mcc0all', this)"`).*

---

### 2. Update the HTML Buttons Layout

Locate the `.edit-actions` container in the HTML layout. Replace the single close/reset button with two independent buttons, ensuring the Close button is positioned on the far right.

**Find:**

```html
          <div class="edit-actions">
            <button id="btnMoveEnd" class="edit-action-btn" onclick="handleEditAction('end')">Put to the end</button>
            <button id="btnDelete" class="edit-action-btn" onclick="handleEditAction('delete')">Delete</button>
            <button id="btnCloseEdit" class="edit-action-btn" onclick="handleEditReset()">Close / Reset Edit</button>
          </div>

```

**Replace with:**

```html
          <div class="edit-actions">
            <button id="btnMoveEnd" class="edit-action-btn" onclick="handleEditAction('end')">Put to the end</button>
            <button id="btnDelete" class="edit-action-btn" onclick="handleEditAction('delete')">Delete</button>
            <button id="btnResetEdit" class="edit-action-btn" onclick="handleEditResetOnly()">Reset Edit Changes</button>
            <button id="btnCloseEdit" class="edit-action-btn" onclick="handleEditCloseOnly()">Close</button>
          </div>

```

---

### 3. Add the New JavaScript Logic for Close and Reset

Locate the `handleEditReset()` function inside the `<script>` tag and replace it with two separate handlers: one to close and clear UI states, and one to strictly revert modifications back to the original layout footprint.

**Find:**

```javascript
      function handleEditReset() {
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';
        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        activeEditKeys.clear();
        changedWords.clear();
        pendingAction = null;
        originalSnapshotState.clear();
        updateActionButtonsUI();
      }

```

**Replace with:**

```javascript
      // Closes the editing interface and returns to the input box viewport
      function handleEditCloseOnly() {
        document.getElementById('inputArea').style.display = 'block';
        document.getElementById('editArea').style.display = 'none';
        document.getElementById('codeInput').value = '';
        currentEditCode = '';
        currentTargetWord = '';
        selectedEditItem = null;
        activeEditKeys.clear();
        changedWords.clear();
        pendingAction = null;
        originalSnapshotState.clear();
        updateActionButtonsUI();
      }

      // Reverts all ordering/deletion modifications back to the initial baseline state 
      function handleEditResetOnly() {
        if (!originalSnapshotState || originalSnapshotState.size === 0) return;

        // Build a temporary map to re-track the original order of items in mcc0string
        let originalOrderMap = new Map();
        for (let [key, words] of originalSnapshotState) {
          words.forEach(word => {
            if (!originalOrderMap.has(word)) {
              originalOrderMap.set(word, true);
            }
          });
        }

        let currentLines = mcc0string.split('\n').filter(l => l.trim() !== '');
        
        // Filter out items that existed originally to rebuild them in baseline order
        let newlyAddedWords = currentLines.filter(word => !originalOrderMap.has(word));
        let restoredOriginalLines = [];
        
        // Reconstruct the exact original sequence from the base file tracking array
        let rawFileLines = filecontent.split('\n').map(line => line.replace(/\r$/, ''));
        let xLine = -1;
        for (let i = rawFileLines.length - 1; i >= 0; i--) {
          if (rawFileLines[i].startsWith('x')) { xLine = i; break; }
        }
        let charDefEndLine = rawFileLines.indexOf('%chardef end');
        
        if (xLine !== -1 && charDefEndLine !== -1) {
          let rawMcc0Lines = rawFileLines.slice(xLine + 1, charDefEndLine);
          rawMcc0Lines = rawMcc0Lines.filter(line => {
            const parts = line.split('\t');
            if (parts.length > 1 && parts[0].startsWith('z')) {
              if (parts[0].slice(1).includes('z')) return false;
            }
            return true;
          }).map(line => {
            const parts = line.split('\t');
            return parts.length > 1 ? parts[1] : line;
          }).filter((line, index, arr) => arr.indexOf(line) == index);

          // Retain only items that were not completely deleted by the user
          let activeOriginalWords = new Set(currentLines.filter(word => originalOrderMap.has(word)));
          restoredOriginalLines = rawMcc0Lines.filter(word => activeOriginalWords.has(word));
        }

        // Merge keeping newly added words intact at the end
        let mergedRevertedLines = [...restoredOriginalLines, ...newlyAddedWords];

        // Process back into application structures and redraw the canvas layout
        processContent(JSON.stringify(mergedRevertedLines));
        changedWords.clear();
        selectedEditItem = null;
        pendingAction = null;
        updateActionButtonsUI();
        renderEditArea();
      }

```
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复

在线用户

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