|
60 | 60 |
|
61 | 61 | #search-input { |
62 | 62 | flex-grow: 0; |
63 | | - width: 140px; |
| 63 | + width: 120px; /* 修正: 欄寬設為 120px */ |
64 | 64 | height: 40px; |
65 | 65 | padding: 8px; |
66 | 66 | font-size: 1.0rem; |
|
129 | 129 | height: 40vh; |
130 | 130 | font-size: 1.5rem; |
131 | 131 | line-height: 1.6; |
132 | | - white-space: pre-wrap; /* 修正:將 normal 改為 pre-wrap 以保留連續空白並允許換行 */ |
| 132 | + white-space: pre-wrap; |
133 | 133 | word-break: break-word; |
134 | 134 | color: var(--text-color); |
135 | 135 | cursor: text; |
|
198 | 198 | } |
199 | 199 |
|
200 | 200 | #candidate-display { |
201 | | - /* 修正 1: 調整最小高度 */ |
202 | 201 | display: flex; |
203 | 202 | align-items: center; |
204 | 203 | padding: 8px; |
205 | 204 | gap: 12px; |
206 | 205 | white-space: nowrap; |
207 | 206 | overflow-x: scroll; |
208 | 207 | -webkit-overflow-scrolling: touch; |
209 | | - min-height: 60px; /* 從 48px 調整為 60px */ |
| 208 | + min-height: 70px; /* 修正: 候選字區域高度調高到 70px */ |
210 | 209 | background-color: #fff; |
211 | 210 | box-shadow: 0 1px 0 var(--border-color); |
212 | 211 | } |
|
216 | 215 | } |
217 | 216 |
|
218 | 217 | #candidate-display span { |
219 | | - /* 修正 2: 調整字型大小 */ |
220 | 218 | cursor: pointer; |
221 | 219 | padding: 5px 8px; |
222 | 220 | border-radius: 4px; |
223 | 221 | transition: all 0.2s; |
224 | 222 | user-select: none; |
225 | 223 | white-space: nowrap; |
226 | | - font-size: 1.4rem; /* 從 1.2rem 調整為 1.4rem */ |
| 224 | + font-size: 1.6rem; /* 修正: 候選字型加大到 1.6rem */ |
227 | 225 | flex-shrink: 0; |
228 | 226 | text-align: center; |
229 | 227 | } |
|
275 | 273 | min-width: 0; |
276 | 274 | } |
277 | 275 |
|
| 276 | + /* *** 新增: 強化 Enter 鍵的視覺效果 (data-code="13" 是 KEYCODE_ENTER) *** */ |
| 277 | + .keyboard-key[data-code="13"] .visual-wrapper > span { |
| 278 | + font-size: 1.5rem; |
| 279 | + font-weight: 900; |
| 280 | + } |
| 281 | + .keyboard-key[data-code="13"] { |
| 282 | + font-weight: bold; |
| 283 | + } |
| 284 | + /* *************************************************************** */ |
| 285 | + |
278 | 286 | .keyboard-key.highlighted { |
279 | | - /* 用於 touchstart/mousedown 設置的高亮狀態 */ |
280 | 287 | background-color: var(--key-active-bg) !important; |
281 | 288 | color: white; |
282 | 289 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); |
|
316 | 323 | } |
317 | 324 |
|
318 | 325 | .keyboard-key:active { |
319 | | - /* 作為降級/輔助的點擊視覺效果,但在 touchstart 設置 highlighted 後會被覆蓋 */ |
320 | 326 | background-color: var(--key-active-bg); |
321 | 327 | color: white; |
322 | 328 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2); |
|
350 | 356 |
|
351 | 357 | <div class="search-area"> |
352 | 358 | <input type="text" id="search-input" placeholder="在此輸入文字" maxlength="1"> |
353 | | - <div id="search-result">查詢結果顯示在此</div> |
354 | | - </div> |
| 359 | + <div id="search-result">編碼顯示在此</div> </div> |
355 | 360 |
|
356 | 361 | <div id="toolbar"> |
357 | 362 | <button id="copy-btn">📋 複製文字</button> |
|
385 | 390 | // --- 長按相關狀態 --- |
386 | 391 | let longPressTimer = null; |
387 | 392 | let longPressCommitted = false; |
388 | | - const LONG_PRESS_TIMEOUT = 750; |
| 393 | + const LONG_PRESS_TIMEOUT = 500; |
389 | 394 |
|
390 | 395 | // --- 游標 HTML 定義 --- |
391 | 396 | const CURSOR_HTML = '<span id="main-cursor" class="cursor">\u200c</span>'; |
|
418 | 423 | const KEYCODE_ENTER=0x0d; |
419 | 424 | const KEYCODE_SPACE=0x20; |
420 | 425 |
|
421 | | - // --- 鍵盤配置 (略) --- |
| 426 | + // --- 鍵盤配置 --- |
422 | 427 | var Keyboards = { |
423 | 428 | english: { |
424 | 429 | shifted : false, |
|
514 | 519 | } |
515 | 520 |
|
516 | 521 | /** |
517 | | - * 鍵盤渲染函數 (略) |
| 522 | + * 鍵盤渲染函數 |
518 | 523 | */ |
519 | 524 | function drawKeyboard() { |
520 | 525 | const layout = currentLayout; |
|
539 | 544 | const keyWidth = placeHolderWidth * ratio; |
540 | 545 |
|
541 | 546 | let displayChar = keyChar; |
542 | | - let isSymbolShifted = false; // 新增:判斷是否為數字/符號鍵盤的 Shift 鍵 |
| 547 | + let isSymbolShifted = false; |
543 | 548 |
|
544 | 549 | // --- FIX: 處理數字/符號鍵盤 (Keyboards.number) 的 Shift 顯示 --- |
545 | 550 | if (currentLayout === Keyboards.number && code === 0) { |
|
578 | 583 | // 英文模式 (無論 Shift On/Off): 顯示半形符號 (符合最新要求) |
579 | 584 | displayChar = keyChar; |
580 | 585 | } |
581 | | - // 移除原有的: else if (layout.shifted) { displayChar = layout.upper[keyChar] || keyChar; } |
582 | 586 | } |
583 | 587 | } |
584 | 588 |
|
585 | 589 | // --- 2. 處理功能鍵文字 (MODE, SHIFT 等,覆蓋 QWERTY 邏輯) --- |
586 | 590 | if (code === KEYCODE_STATE) { |
587 | 591 | // 模式切換鍵 |
588 | | - // 根據當前鍵盤判斷 MODE 鍵的顯示狀態 |
589 | 592 | if (currentLayout === Keyboards.english) { |
590 | 593 | // QWERTY 鍵盤上顯示當前模式 |
591 | 594 | displayChar = isEnglishMode ? 'En' : '三碼'; |
|
677 | 680 | } |
678 | 681 |
|
679 | 682 | /** |
680 | | - * 浮層處理函數 (略) |
| 683 | + * 浮層處理函數 (保持不變) |
681 | 684 | */ |
682 | 685 | function emoji(subIndex) { |
683 | 686 | const emojis=[[ |
|
740 | 743 | } |
741 | 744 |
|
742 | 745 | /** |
743 | | - * 處理短按事件:執行編碼或普通字符輸入 (略) |
| 746 | + * 處理短按事件:執行編碼或普通字符輸入 |
744 | 747 | */ |
745 | 748 | function handleShortPress(keyElement) { |
746 | 749 | preferredOffset = -1; |
|
785 | 788 | } |
786 | 789 |
|
787 | 790 | /** |
788 | | - * 處理功能鍵邏輯 (略) |
| 791 | + * 處理功能鍵邏輯 |
789 | 792 | */ |
790 | 793 | function handleFunctionKey(code, keyChar) { |
791 | 794 | switch(code) { |
|
876 | 879 | */ |
877 | 880 | function bindKeyEvents() { |
878 | 881 | document.querySelectorAll('.keyboard-key').forEach(keyElement => { |
879 | | - |
880 | | - // 1. 為了避免重複綁定,先移除可能已經存在的舊事件處理器 (通過儲存在元素上的屬性) |
881 | | - if (keyElement.handlePressDown) { |
882 | | - keyElement.removeEventListener('touchstart', keyElement.handlePressDown); |
883 | | - keyElement.removeEventListener('touchend', keyElement.handlePressUp); |
884 | | - keyElement.removeEventListener('touchcancel', keyElement.handlePressCancel); |
885 | | - keyElement.removeEventListener('mousedown', keyElement.handlePressDown); |
886 | | - keyElement.removeEventListener('mouseup', keyElement.handlePressUp); |
887 | | - keyElement.removeEventListener('mouseleave', keyElement.handlePressCancel); |
888 | | - } |
889 | | - |
890 | | - // 清空舊的 onxxx 屬性 (略) |
891 | 882 | keyElement.onclick = null; |
892 | 883 | keyElement.oncontextmenu = (e) => e.preventDefault(); |
893 | 884 | keyElement.ontouchstart = null; |
|
896 | 887 | keyElement.onmousedown = null; |
897 | 888 | keyElement.onmouseup = null; |
898 | 889 | keyElement.onmouseleave = null; |
899 | | - |
| 890 | + |
900 | 891 | const keyChar = keyElement.dataset.label; |
901 | | - // 修正 altChar 檢查,防止 currentLayout.alt 未定義時出錯 |
902 | | - const altChar = currentLayout.alt ? currentLayout.alt[keyChar] : null; |
| 892 | + const altChar = currentLayout.alt[keyChar]; |
903 | 893 |
|
904 | | - // 1. 處理按下 (Press Down) |
905 | 894 | const handlePressDown = (event) => { |
906 | | - // 只處理左鍵點擊 (滑鼠) 或觸摸事件 |
907 | | - if (event.type === 'mousedown' && event.button !== 0) return; |
908 | | - if (event.type.startsWith('touch')) event.preventDefault(); |
909 | | - |
| 895 | + event.preventDefault(); |
910 | 896 | longPressCommitted = false; |
911 | | - keyElement.classList.add('highlighted'); // **添加高亮** |
| 897 | + keyElement.classList.add('highlighted'); |
912 | 898 |
|
913 | 899 | if (currentLayout === Keyboards.english && altChar) { |
914 | 900 | if (longPressTimer) clearTimeout(longPressTimer); |
|
917 | 903 | }, LONG_PRESS_TIMEOUT); |
918 | 904 | } |
919 | 905 | }; |
920 | | - |
921 | | - // 2. 處理抬起/點擊 (Press Up) |
922 | | - const handlePressUp = (event) => { |
923 | | - // if (event.type.startsWith('touch')) event.preventDefault(); // 移除 preventDefault |
924 | 906 |
|
| 907 | + const handlePressUp = (event) => { |
| 908 | + event.preventDefault(); |
| 909 | + keyElement.classList.remove('highlighted'); |
| 910 | + |
925 | 911 | if (longPressTimer) { |
926 | 912 | clearTimeout(longPressTimer); |
927 | 913 | longPressTimer = null; |
928 | 914 | } |
929 | 915 |
|
930 | | - // 只要不是長按提交的,就視為短按並觸發輸入 |
931 | | - if (!longPressCommitted) { |
| 916 | + if (!longPressCommitted) { |
932 | 917 | handleShortPress(keyElement); |
933 | 918 | } |
934 | | - |
935 | | - // 無論如何,抬起時都移除高亮 |
936 | | - keyElement.classList.remove('highlighted'); |
937 | 919 | longPressCommitted = false; |
938 | 920 | }; |
939 | 921 |
|
940 | | - // 3. 處理取消 (Move/Leave/Cancel) |
941 | | - const handlePressCancel = (event) => { // 接受 event |
942 | | - // **重要修正:touchmove 不再觸發此函數來取消計時器** |
| 922 | + const handlePressCancel = () => { |
943 | 923 | if (longPressTimer) { |
944 | 924 | clearTimeout(longPressTimer); |
945 | 925 | longPressTimer = null; |
946 | 926 | } |
947 | | - // 只有在滑鼠離開 (mouseleave) 或系統明確取消 (touchcancel) 時,才移除高亮。 |
948 | | - if (event.type === 'mouseleave' || event.type === 'touchcancel') { |
949 | | - keyElement.classList.remove('highlighted'); |
950 | | - } |
| 927 | + keyElement.classList.remove('highlighted'); |
951 | 928 | }; |
952 | 929 |
|
953 | | - // 將函數儲存在 DOM 元素上以便移除 |
954 | | - keyElement.handlePressDown = handlePressDown; |
955 | | - keyElement.handlePressUp = handlePressUp; |
956 | | - keyElement.handlePressCancel = handlePressCancel; |
957 | | - |
958 | | - // Touch Events |
959 | 930 | keyElement.addEventListener('touchstart', handlePressDown); |
960 | 931 | keyElement.addEventListener('touchend', handlePressUp); |
961 | | - keyElement.addEventListener('touchcancel', handlePressCancel); // 處理觸摸中斷 |
962 | | - // *** 核心修正:移除 touchmove 綁定 *** |
| 932 | + keyElement.addEventListener('touchmove', handlePressCancel); |
963 | 933 |
|
964 | | - // Mouse Events (用於桌面測試) |
965 | | - keyElement.addEventListener('mousedown', handlePressDown); |
| 934 | + keyElement.addEventListener('mousedown', (e) => { |
| 935 | + if (e.button === 0) { |
| 936 | + handlePressDown(e); |
| 937 | + } |
| 938 | + }); |
966 | 939 | keyElement.addEventListener('mouseup', handlePressUp); |
967 | 940 | keyElement.addEventListener('mouseleave', handlePressCancel); |
968 | 941 | }); |
969 | 942 | } |
970 | 943 |
|
971 | 944 | /** |
972 | | - * 處理單字符輸入 (用於英文/符號模式或 Shift+字母) (略) |
| 945 | + * 處理單字符輸入 (用於英文/符號模式或 Shift+字母) |
973 | 946 | */ |
974 | 947 | function handleCharInput(char) { |
975 | 948 | if (currentLayout.shifted) { |
|
984 | 957 | } |
985 | 958 | } |
986 | 959 |
|
987 | | - // --- 核心輸入法邏輯 (其餘部分略) --- |
| 960 | + // --- 核心輸入法邏輯 (其餘部分保持不變) --- |
988 | 961 |
|
989 | 962 | var currentCode = ''; |
990 | 963 | var currentCandidates = []; |
|
1246 | 1219 | updateOutputDisplay(); |
1247 | 1220 | } |
1248 | 1221 |
|
1249 | | - // --- 工具列與查碼功能 (其餘部分略) --- |
| 1222 | + // --- 工具列與查碼功能 (已調整) --- |
1250 | 1223 |
|
1251 | 1224 | function copyTextToClipboard() { |
1252 | 1225 | const textToCopy = committedText; |
|
1286 | 1259 | resetInputState(); |
1287 | 1260 | } |
1288 | 1261 | } |
| 1262 | + |
1289 | 1263 | function handleSearchInput(event) { |
1290 | 1264 | const charToSearch = searchInput.value; |
1291 | 1265 | if (charToSearch.length === 1) { |
1292 | 1266 | const codes = currentReverseCodemap[charToSearch]; |
1293 | 1267 | if (codes && codes.length > 0) { |
1294 | 1268 | const formattedCodes = codes.map(code => formatCode(code)); |
1295 | | - searchResult.textContent = `編碼: ${formattedCodes.join(' ')}`; |
| 1269 | + searchResult.textContent = `${formattedCodes.join(' ')}`; // 修正: 移除「編碼: 」字樣 |
1296 | 1270 | } else { |
1297 | 1271 | searchResult.textContent = '找不到該字符的編碼。'; |
1298 | 1272 | } |
1299 | 1273 | } else { |
1300 | | - searchResult.textContent = '查詢結果顯示在此'; |
| 1274 | + searchResult.textContent = '編碼顯示在此'; // 修正: 更新為新版預設文字 |
1301 | 1275 | } |
1302 | 1276 | } |
| 1277 | + |
1303 | 1278 | function handleSearchKeydown(event) { if (event.key === 'Backspace') { event.stopPropagation(); } } |
1304 | 1279 |
|
1305 | 1280 | function mapDOMOffsetToTextIndex(targetNode, targetOffset) { |
|
0 commit comments