Skip to content

Commit 065d5f2

Browse files
authored
Add files via upload
1 parent 3cbff2b commit 065d5f2

1 file changed

Lines changed: 75 additions & 52 deletions

File tree

3code_mobile.html

Lines changed: 75 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
align-items: center;
8383
}
8484

85-
/* 輸入結果顯示區 */
85+
/* 輸入結果顯示區 (現在是可編輯區域) */
8686
#text-output {
8787
width: 100%;
8888
background-color: var(--container-bg);
@@ -97,7 +97,8 @@
9797
white-space: pre-wrap;
9898
word-break: break-word;
9999
color: var(--text-color);
100-
cursor: text;
100+
/* 設置 cursor 為 text,並確保它能顯示游標 */
101+
cursor: text;
101102
}
102103

103104
/* 候選字與輸入區 */
@@ -116,7 +117,7 @@
116117
padding: 8px;
117118
font-size: 1.2rem;
118119
min-height: 40px;
119-
line-height: 1.5;
120+
line-height: 1.5;
120121
border: none;
121122
border-bottom: 1px solid var(--border-color);
122123
background-color: #fafafa;
@@ -240,7 +241,7 @@
240241
</div>
241242

242243
<div id="app-container">
243-
<div id="text-output"></div>
244+
<div id="text-output" contenteditable="true" spellcheck="false"></div>
244245

245246
<div id="input-area">
246247
<div id="main-input">編碼顯示在此</div>
@@ -262,51 +263,46 @@
262263

263264
// 字母鍵的基礎映射
264265
const letter_maps = {
265-
'cn_normal': (key) => key.toUpperCase(), // 中文模式顯示大寫字母 (A-Z)
266-
// 中文模式下,字母鍵的顯示不隨 Shift 改變,維持大寫
266+
'cn_normal': (key) => key.toUpperCase(),
267267
'cn_shifted': (key) => key.toUpperCase(),
268-
'en_normal': (key) => key.toLowerCase(), // 英文模式顯示小寫字母 (a-z)
269-
'en_shifted': (key) => key.toUpperCase() // 英文 Shift 模式顯示大寫字母 (A-Z)
268+
'en_normal': (key) => key.toLowerCase(),
269+
'en_shifted': (key) => key.toUpperCase()
270270
};
271271

272272
const key_display_maps = {
273273
// [0, 0] Chinese Normal (Default)
274274
'cn_normal': {
275-
// Numbers/Symbols: 中文輸入法的預設符號 (數字輸出,符號編碼)
276275
'1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0',
277276
';': ';', "'": '〃', ',': ',', '.': '.', '/': '/',
278-
'lang_toggle': '三碼', // 顯示 '三碼' (當前模式)
277+
'lang_toggle': '三碼',
279278
'case_shift': '⇧',
280-
'backspace': '⌫', // 顯示 '⌫'
281-
'enter': '↵', // 顯示 '↵'
282-
' ': ' ' // 修正: 顯示 ' ' (單一空白字元)
279+
'backspace': '⌫',
280+
'enter': '↵',
281+
' ': ' ' // 空白鍵顯示 ' '
283282
},
284283
// [0, 1] Chinese Shifted (與 cn_normal 相同,除了 case_shift 鍵本身)
285284
'cn_shifted': {
286285
'1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0',
287286
';': ';', "'": '〃', ',': ',', '.': '.', '/': '/',
288287
'lang_toggle': '三碼',
289-
'case_shift': '⬆', // 只有 Shift 鍵的視覺改變
288+
'case_shift': '⬆',
290289
'backspace': '⌫',
291290
'enter': '↵',
292291
' ': ' '
293292
},
294293
// [1, 0] English Normal (Lowercase)
295294
'en_normal': {
296295
'1': '1', '2': '2', '3': '3', '4': '4', '5': '5', '6': '6', '7': '7', '8': '8', '9': '9', '0': '0',
297-
// 英文模式預設符號
298296
';': ';', "'": "'", ',': ',', '.': '.', '/': '/',
299-
'lang_toggle': 'En', // 顯示 'En' (當前模式)
297+
'lang_toggle': 'En',
300298
'case_shift': '⇧',
301299
'backspace': '⌫',
302300
'enter': '↵',
303301
' ': ' '
304302
},
305303
// [1, 1] English Shifted (Uppercase + Secondary Symbols)
306304
'en_shifted': {
307-
// Shifted Symbols (直接輸出)
308305
'1': '!', '2': '@', '3': '#', '4': '$', '5': '%', '6': '^', '7': '&', '8': '*', '9': '(', '0': ')',
309-
// 英文上檔符號
310306
';': ':', "'": '"', ',': '<', '.': '>', '/': '?',
311307
'lang_toggle': 'En',
312308
'case_shift': '⬆',
@@ -330,23 +326,46 @@
330326
(isShiftActive ? 'cn_shifted' : 'cn_normal');
331327
const map = key_display_maps[mapKey];
332328

333-
// 1. 檢查是否為英文模式下的直接輸出 (字母, 數字, 符號)
329+
// 1. 檢查是否為英文模式下的直接輸出
334330
if (isEnglishMode) {
335331
if (code.match(/^[a-z]$/)) {
336-
// 英文模式下,字母直接輸出大小寫
337332
return letter_maps[mapKey](code);
338333
}
339-
if (code.match(/^[0-9;'./,<> ]$/)) { // 包含 space
340-
// 英文模式下,數字和符號直接輸出半形/上檔符號
341-
return map[code] || code; // map[code] 對於數字和符號會輸出正確的上檔符號或數字,對於 ' ' 則輸出 ' '
334+
if (code.match(/^[0-9;'./,<> ]$/)) {
335+
return map[code] || code;
342336
}
343337
return null;
344338
}
345-
346-
// 2. 檢查是否為中文模式下的直接輸出 (無,中文模式下的數字輸出在 handleChineseInput 中處理)
347339
return null;
348340
}
349341

342+
// --- 核心工具函數:管理游標與輸入 ---
343+
344+
// 確保游標在 contenteditable 元素的末尾
345+
function focusAndMoveCaretToEnd() {
346+
textOutput.focus();
347+
const range = document.createRange();
348+
const selection = window.getSelection();
349+
350+
// 將範圍設定為 contenteditable 元素的所有內容
351+
range.selectNodeContents(textOutput);
352+
// 將游標收縮到範圍的末尾 (即文字末尾)
353+
range.collapse(false);
354+
355+
selection.removeAllRanges();
356+
selection.addRange(range);
357+
}
358+
359+
// 在游標位置插入文字 (適用於 contenteditable)
360+
function insertTextAtCaret(text) {
361+
textOutput.focus();
362+
// 這是模擬鍵盤輸入最可靠的方式,它會在游標位置插入文字
363+
document.execCommand('insertText', false, text);
364+
// 由於 execCommand 可能不會移動到絕對末尾,確保游標被重新定位
365+
focusAndMoveCaretToEnd();
366+
}
367+
368+
350369
// --- 舊有代碼區塊 ---
351370

352371
// DOM 元素
@@ -387,7 +406,6 @@
387406
}
388407

389408
async function loadSecondaryCodemap() {
390-
// 注意:這裡假設您的編碼檔名為 3codes2.txt 並且在同目錄
391409
const fileName = './3codes2.txt';
392410
try {
393411
const response = await fetch(fileName);
@@ -404,6 +422,7 @@
404422
mainInput.textContent = `錯誤:無法載入編碼檔 ${fileName}。請確認檔案存在。`;
405423
}
406424
resetInputState();
425+
focusAndMoveCaretToEnd(); // 載入後立即定位游標
407426
}
408427

409428
// --- 處理中文模式輸入的核心函數 ---
@@ -413,10 +432,9 @@
413432
// 1. 數字鍵處理 (直接輸出半形數字)
414433
if (inputChar.match(/^[0-9]$/)) {
415434
if (currentCandidates.length > 0) {
416-
selectCandidate(0); // 如果有編碼,先上字
435+
selectCandidate(0);
417436
}
418-
// 中文模式的數字直接輸出數字
419-
textOutput.textContent += inputChar;
437+
insertTextAtCaret(inputChar); // 插入數字
420438
return;
421439
}
422440

@@ -425,16 +443,18 @@
425443
if (currentCode.length > 0) {
426444
currentCode = currentCode.slice(0, -1);
427445
updateStateAndDisplay();
428-
} else if (textOutput.textContent.length > 0) {
429-
textOutput.textContent = textOutput.textContent.slice(0, -1);
446+
} else {
447+
// 游標位置刪除
448+
textOutput.focus();
449+
document.execCommand('delete', false, null);
430450
}
431451
return;
432452
}
433453
if (code === 'enter') {
434454
if (currentCandidates.length > 0) {
435455
selectCandidate(0);
436456
} else {
437-
textOutput.textContent += '\n';
457+
insertTextAtCaret('\n');
438458
}
439459
return;
440460
}
@@ -443,7 +463,7 @@
443463
if (currentCandidates.length > 0) {
444464
selectCandidate(0);
445465
} else {
446-
textOutput.textContent += ' '; // 輸出全形空白
466+
insertTextAtCaret(' '); // 輸出全形空白
447467
}
448468
return;
449469
}
@@ -466,35 +486,36 @@
466486
function handleEnglishInput(code) {
467487
// 1. 處理功能鍵
468488
if (code === 'backspace') {
469-
if (textOutput.textContent.length > 0) {
470-
textOutput.textContent = textOutput.textContent.slice(0, -1);
471-
}
489+
textOutput.focus();
490+
document.execCommand('delete', false, null); // 游標位置刪除
472491
return;
473492
}
474493
if (code === 'enter') {
475-
textOutput.textContent += '\n';
494+
insertTextAtCaret('\n');
476495
return;
477496
}
478497
if (code === ' ') {
479-
textOutput.textContent += ' '; // 輸出半形空白
498+
insertTextAtCaret(' '); // 輸出半形空白
480499
return;
481500
}
482501

483502
// 2. 處理英文字母、數字和符號
484503
const outputChar = getOutputChar(code);
485504
if (outputChar) {
486-
textOutput.textContent += outputChar;
505+
insertTextAtCaret(outputChar);
487506
}
488507
}
489508

490509
// --- 核心狀態更新與顯示函數 (中文模式專用) ---
491510
function updateStateAndDisplay() {
511+
// 修正: 僅顯示編碼,不加自定義游標
492512
mainInput.textContent = formatCode(currentCode);
513+
493514
currentCandidates = currentCodemap[currentCode] || [];
494515

495516
if (currentCode.length === MAX_CODE_LENGTH) {
496517
if (currentCandidates.length === 1) {
497-
textOutput.textContent += currentCandidates[0];
518+
insertTextAtCaret(currentCandidates[0]); // 使用 insertTextAtCaret
498519
resetInputState();
499520
return;
500521
} else if (currentCandidates.length === 0) {
@@ -532,7 +553,7 @@
532553
function selectCandidate(index) {
533554
const selectedCandidate = currentCandidates[index];
534555
if (selectedCandidate) {
535-
textOutput.textContent += selectedCandidate;
556+
insertTextAtCaret(selectedCandidate); // 使用 insertTextAtCaret
536557
resetInputState();
537558
}
538559
}
@@ -542,9 +563,10 @@
542563
currentCode = '';
543564
currentCandidates = [];
544565

545-
// 更新輸入提示文字
566+
// 更新輸入提示文字 (純文字)
546567
mainInput.textContent = isEnglishMode ? '英文模式' : '編碼顯示在此';
547568
showCandidates();
569+
focusAndMoveCaretToEnd(); // 重設後確保游標在輸出區
548570
}
549571

550572
// --- 格式化編碼顯示 ---
@@ -560,23 +582,20 @@
560582
// --- 更新鍵盤按鍵顯示 ---
561583
function updateKeyboardDisplay() {
562584
const map = getCurrentDisplayMap();
563-
// 中文模式下,字母鍵的視覺始終是 letter_maps.cn_normal (大寫 A-Z)
564585
const letterMap = isEnglishMode ? (isShiftActive ? letter_maps.en_shifted : letter_maps.en_normal) : letter_maps.cn_normal;
565586

566587
document.querySelectorAll('.key[data-code]').forEach(keyElement => {
567588
const code = keyElement.dataset.code;
568589

569590
if (map[code]) {
570-
// 處理非字母鍵和特殊功能鍵 (包括 ' ')
571591
keyElement.textContent = map[code];
572592
} else if (code.match(/^[a-z]$/)) {
573-
// 處理字母鍵 (a-z)
574593
keyElement.textContent = letterMap(code);
575594
}
576595
});
577596
}
578597

579-
// --- 動態生成手機優化鍵盤 (符號鍵位置修正) ---
598+
// --- 動態生成手機優化鍵盤 (無變動) ---
580599
function generateMobileKeyboard() {
581600
const layout_mobile = [
582601
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
@@ -598,7 +617,6 @@
598617

599618
let code = keyText.toLowerCase();
600619

601-
// 設定 data-code 和樣式
602620
if (keyText === 'Back') {
603621
code = 'backspace';
604622
key.classList.add('function-key');
@@ -618,23 +636,25 @@
618636

619637
key.dataset.code = code;
620638

621-
// 初始顯示: 根據初始模式 (中文, normal) 顯示正確的圖示或文字
622639
const initialMap = getCurrentDisplayMap();
623640
if (initialMap[code]) {
624641
key.textContent = initialMap[code];
625642
} else if (code.match(/^[a-z]$/)) {
626643
key.textContent = letter_maps.cn_normal(code);
627644
} else {
628-
key.textContent = keyText; // 保險措施
645+
key.textContent = keyText;
629646
}
630647

631648
// --- 鍵盤點擊事件 ---
632-
key.addEventListener('click', () => {
649+
key.addEventListener('click', (event) => {
650+
// 阻止點擊鍵盤時,文字輸出區失去焦點
651+
event.preventDefault();
652+
633653
if (code === 'lang_toggle') {
634654
isEnglishMode = !isEnglishMode;
635655
isShiftActive = false;
636656
updateKeyboardDisplay();
637-
resetInputState(); // 模式切換後重設輸入狀態並更新提示文字
657+
resetInputState();
638658
} else if (code === 'case_shift') {
639659
isShiftActive = !isShiftActive;
640660
updateKeyboardDisplay();
@@ -656,7 +676,8 @@
656676
* 複製文字
657677
*/
658678
function copyTextToClipboard() {
659-
const textToCopy = textOutput.textContent;
679+
// contenteditable 區域的文字內容
680+
const textToCopy = textOutput.textContent;
660681
if (!textToCopy) {
661682
alert('輸出區沒有文字可以複製!');
662683
return;
@@ -682,6 +703,7 @@
682703
}
683704
document.body.removeChild(tempInput);
684705
}
706+
focusAndMoveCaretToEnd(); // 複製後,游標保持活躍
685707
}
686708

687709
/**
@@ -691,6 +713,7 @@
691713
if (confirm('確定要清空所有已輸入的文字嗎?')) {
692714
textOutput.textContent = '';
693715
resetInputState();
716+
focusAndMoveCaretToEnd(); // 清空後確保游標在輸出區
694717
}
695718
}
696719

0 commit comments

Comments
 (0)