Skip to content

Commit 0e7a88a

Browse files
authored
Add files via upload
1 parent a6d05f3 commit 0e7a88a

1 file changed

Lines changed: 66 additions & 118 deletions

File tree

3code_mobile.html

Lines changed: 66 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;700&display=swap" rel="stylesheet">
1010
<style>
1111
/* ----------------------------------------------------- */
12-
/* 樣式定義 */
12+
/* 樣式定義 (略,與前版相同) */
1313
/* ----------------------------------------------------- */
1414

1515
:root {
@@ -101,7 +101,10 @@
101101
background-color: var(--primary-color);
102102
color: white;
103103
cursor: pointer;
104-
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
104+
box-shadow: 0 2px 5px rgba
105+
106+
[Image of X]
107+
(0, 0, 0, 0.1);
105108
transition: background-color 0.2s;
106109
}
107110

@@ -440,6 +443,9 @@
440443
let cursorPosition = 0;
441444
let preferredOffset = -1;
442445

446+
// --- ADDED: 簡繁切換狀態 ---
447+
let isSimplifiedMode = false; // 預設為繁體模式
448+
443449
// --- 長按相關狀態 ---
444450
let longPressTimer = null;
445451
let longPressCommitted = false;
@@ -484,7 +490,33 @@
484490
const KEYCODE_DOWN=0xff54;
485491
const KEYCODE_DEL_F=0xff;
486492

487-
// --- 鍵盤配置 ---
493+
// --- 簡繁轉換表 (基礎示範用,完整轉換需要更大的字典) ---
494+
const TS_MAP = {
495+
'體': '体', '碼': '码', '說': '说', '這': '这', '個': '个',
496+
'們': '们', '樂': '乐', '風': '风', '開': '开', '對': '对',
497+
'會': '会', '國': '国', '議': '议', '變': '变', '應': '应',
498+
'學': '学', '發': '发', '廠': '厂', '廣': '广', '裡': '里',
499+
'幾': '几', '為': '为', '從': '从', '後': '后', '麵': '面',
500+
'術': '术', '隻': '只', '頭': '头', '話': '话', '裡': '里'
501+
};
502+
503+
/**
504+
* 執行簡體字轉換
505+
*/
506+
function convertToSimplified(text) {
507+
if (!isSimplifiedMode) {
508+
return text; // 繁體模式,不轉換
509+
}
510+
511+
let simplifiedText = '';
512+
for (const char of text) {
513+
// 簡單的字符替換
514+
simplifiedText += TS_MAP[char] || char;
515+
}
516+
return simplifiedText;
517+
}
518+
519+
// --- 鍵盤配置 (略,與前版相同) ---
488520
var Keyboards = {
489521
english: {
490522
shifted : false,
@@ -493,7 +525,7 @@
493525
'q':'Q', 'w':'W', 'e':'E', 'r':'R', 't':'T', 'y':'Y', 'u':'U', 'i':'I', 'o':'O', 'p':'P',
494526
'a':'A', 's':'S', 'd':'D', 'f':'F', 'g':'G', 'h':'H', 'j':'J', 'k':'K', 'l':'L',
495527

496-
// **修正點:新增底部一排 (zxcvbnm) 的全形字元**
528+
// 底部一排 (zxcvbnm)
497529
'z':'Z', 'x':'X', 'c':'C', 'v':'V', 'b':'B', 'n':'N', 'm':'M',
498530

499531
// 標點符號
@@ -727,67 +759,55 @@
727759
}
728760
}
729761

730-
// --- ADDED: 浮層處理函數 ---
762+
// --- ADDED: 浮層處理函數 (略,與前版相同) ---
731763

732764
/**
733765
* 處理表情符號/編輯器的浮層顯示與隱藏
734-
* @param {string} type - 'emoji' 或 'editor'
735-
* @param {number|boolean} show - 0, 1 (emoji sub index) 或 false (hide)
736766
*/
737767
function overlay(type, show) {
738-
// 使用現有的 overlayDiv 元素
739768
const div = overlayDiv;
740769

741-
// 隱藏/清理舊浮層
742770
const isShowing = div.dataset.show;
743771
if (isShowing) {
744772
div.style.display = 'none';
745773
div.innerHTML = '';
746774
delete div.dataset.show;
747-
// 如果點擊了當前正在顯示的按鈕,則隱藏後返回
748775
if (isShowing == show && div.className == type) return;
749776
}
750777

751778
if (show === false) return;
752779

753-
// --- 修正點: 取得鍵盤的實際位置和尺寸,使其覆蓋整個按鍵區 (#keyboard) ---
754780
const keyboard = document.getElementById('keyboard');
755-
const rect = keyboard.getBoundingClientRect(); // 取得鍵盤元素在視窗中的位置和尺寸
781+
const rect = keyboard.getBoundingClientRect();
756782

757783
const width = rect.width;
758-
const height = rect.height; // 取得按鍵區的實際高度
784+
const height = rect.height;
759785

760-
// 配置浮層 div
761786
div.dataset.show = show;
762787
div.style.display = 'block';
763-
div.className = type; // 應用 class for styling
788+
div.className = type;
764789

765-
// 確保浮層與鍵盤位置重合,使用 fixed 定位來精確覆蓋鍵盤 (#keyboard)
766790
div.style.position = 'fixed';
767791
div.style.left = rect.left + 'px';
768-
div.style.top = rect.top + 'px'; // 從鍵盤頂部開始
792+
div.style.top = rect.top + 'px';
769793
div.style.width = width + 'px';
770794
div.style.height = height + 'px';
771795

772796
const div2 = document.createElement('DIV');
773797
div2.style.width = width + 'px';
774798
div2.style.height = height + 'px';
775-
div2.style.overflowY = 'auto'; // 讓內容可滾動
799+
div2.style.overflowY = 'auto';
776800

777-
// --- FIX: Add Flexbox setup for 10-column layout ---
778801
if (type === "emoji") {
779802
div2.style.display = 'flex';
780803
div2.style.flexWrap = 'wrap';
781804
div2.style.alignContent = 'flex-start';
782-
// 調整 padding 以容納按鍵間距
783805
div2.style.padding = '3px';
784806
}
785-
// --- END FIX ---
786807

787-
div.innerHTML = ''; // 清空內容
808+
div.innerHTML = '';
788809
div.appendChild(div2);
789-
// --- 結束修正點 ---
790-
810+
791811
if (type === "emoji") {
792812
const emojis = [[
793813
"😀","😁","😂","😃","😄","😅","😆","😇","😈","😉",
@@ -815,41 +835,28 @@
815835

816836
const emojiSet = emojis[show] || emojis[0];
817837
const rowLength = 10;
818-
const displayRows = 4; // <--- 固定顯示 4 行
838+
const displayRows = 4;
819839

820-
// 由於 CSS 樣式新增了 2px margin,這裡需要調整寬度計算
821-
// 為了簡單起見並使用 flexbox,我們設定按鈕寬度為 10%
822-
823-
const keyHeight = height / displayRows; // <--- 按鈕高度為浮層高度的 1/4
840+
const keyHeight = height / displayRows;
824841

825842
for (let i = 0; i < emojiSet.length; i++) {
826843
const one = document.createElement('button');
827844
one.textContent = emojiSet[i];
828845

829-
// --- FIX: 使用百分比寬度和計算高度確保 10 欄精確排列 ---
830-
one.style.width = `calc(${100 / rowLength}% - 4px)`; /* 10% 減去左右 margin (2px*2) */
831-
one.style.height = keyHeight - 4 + "px"; /* 高度減去上下 margin (2px*2) */
846+
one.style.width = `calc(${100 / rowLength}% - 4px)`;
847+
one.style.height = keyHeight - 4 + "px";
832848

833849
one.style.fontSize = '1.8rem';
834-
// 移除內聯樣式,讓 CSS 來控制按鍵外觀
835-
// one.style.border = 'none';
836-
// one.style.background = 'none';
837-
// one.style.margin = '0';
838-
// one.style.padding = '0';
850+
839851
div2.appendChild(one);
840852
}
841853

842854
div2.addEventListener('click', function(e) {
843-
// 確保點擊到的是 button
844855
if (e.target.tagName !== 'BUTTON') return;
845856

846857
const text = e.target.textContent;
847-
// 過濾掉可能存在的空白佔位符
848858
if (text && text.length > 0 && text !== ' ')
849859
commitText(text);
850-
851-
// **修正 1:點擊後不再隱藏浮層,以滿足連續輸入的需求**
852-
// overlay("emoji", false); // <--- 已移除此行
853860
});
854861

855862
} else if (type === "editor") {
@@ -888,82 +895,16 @@
888895
overlay("editor", 0);
889896
}
890897

891-
// --- ADDED: 語音輸入邏輯 ---
892-
893-
/**
894-
* 啟動 Web Speech API 語音辨識
895-
*/
896-
function startVoiceInput() {
897-
// 檢查瀏覽器是否支援 Web Speech API
898-
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
899-
900-
if (!SpeechRecognition) {
901-
alert('抱歉,您的瀏覽器不支援 Web 語音輸入功能 (Speech Recognition API)。建議使用 Chrome 瀏覽器。');
902-
return;
903-
}
904-
905-
// 建立 Recognition 物件
906-
const recognition = new SpeechRecognition();
907-
908-
// 設定:
909-
recognition.lang = 'cmn-Hant-TW'; // 繁體中文
910-
recognition.continuous = false; // 每次只錄製一次
911-
recognition.interimResults = false; // 只傳回最終結果
912-
913-
// 儲存原始 UI 狀態
914-
const originalMainInput = mainInput.textContent;
915-
916-
// 處理事件
917-
recognition.onstart = function() {
918-
mainInput.textContent = '🔊 正在聆聽... 請開始說話。';
919-
candidateDisplay.innerHTML = '<span style="color: red; font-weight: bold;">[語音辨識中...]</span>';
920-
};
921-
922-
recognition.onresult = function(event) {
923-
// 取得最終結果
924-
const transcript = event.results[event.results.length - 1][0].transcript;
925-
926-
// 將結果提交到輸出區
927-
if (transcript) {
928-
commitText(transcript);
929-
}
930-
931-
// 恢復 UI 狀態
932-
recognition.onend();
933-
};
934-
935-
recognition.onerror = function(event) {
936-
alert('語音辨識發生錯誤: ' + event.error + ' (請檢查麥克風權限)');
937-
console.error('Speech Recognition Error:', event.error);
938-
// 恢復 UI 狀態
939-
recognition.onend();
940-
};
941-
942-
recognition.onend = function() {
943-
// 恢復 UI 狀態
944-
mainInput.textContent = originalMainInput;
945-
// 重新繪製工具列
946-
showCandidates();
947-
};
948-
949-
try {
950-
recognition.start();
951-
} catch (e) {
952-
alert('啟動語音輸入失敗,請檢查瀏覽器麥克風權限設定。');
953-
console.error('Recognition start failed:', e);
954-
// 恢復 UI 狀態
955-
mainInput.textContent = originalMainInput;
956-
showCandidates();
957-
}
958-
}
898+
// --- REMOVED: startVoiceInput function ---
959899

960900
// --- ADDED: 工具列邏輯 ---
961901

962902
function showToolbar() {
963903
candidateDisplay.innerHTML = '';
964904

905+
// **CHANGE: Replaced '🎤' with '簡/繁' toggle**
965906
const toolbarItems = [
966-
{title:"🎤",tool:"1"},
907+
{title: isSimplifiedMode ? "簡" : "繁", tool:"1"}, // 簡繁切換鍵
967908
{title:"😊",tool:"2"}, // Emoji 1
968909
{title:"☀",tool:"3"}, // Emoji 2
969910
{title:"🌀",tool:"4"}, // Placeholder
@@ -987,7 +928,10 @@
987928
function handleToolbarClick(target) {
988929
const tool = target.dataset.tool;
989930
if (tool === "1") {
990-
startVoiceInput(); // <-- 呼叫新的語音輸入函式
931+
// **CHANGE: Simplified/Traditional Mode Toggle**
932+
isSimplifiedMode = !isSimplifiedMode;
933+
showCandidates(); // Redraw toolbar to update "簡" / "繁" label
934+
console.log("Mode switched to:", isSimplifiedMode ? "Simplified" : "Traditional");
991935
} else if (tool === "2") {
992936
overlay("emoji", 0);
993937
} else if (tool === "3") {
@@ -1055,7 +999,6 @@
1055999
*/
10561000
function handleFunctionKey(code, keyChar) {
10571001
// 確保在處理功能鍵時隱藏浮層
1058-
// **注意:如果用戶在 emoji 浮層上點擊了功能鍵(例如切換到 QWERTY),則需要關閉浮層。**
10591002
if (code === KEYCODE_SWITCH_english || code === KEYCODE_SWITCH_number || code === KEYCODE_STATE || code === KEYCODE_DEL || code === KEYCODE_ENTER || code === KEYCODE_SPACE) {
10601003
overlayDiv.style.display = 'none';
10611004
delete overlayDiv.dataset.show;
@@ -1338,9 +1281,15 @@
13381281
}
13391282
}
13401283

1284+
/**
1285+
* 提交文字 (包含簡繁轉換邏輯)
1286+
*/
13411287
function commitText(textToCommit) {
1342-
committedText = committedText.slice(0, cursorPosition) + textToCommit + committedText.slice(cursorPosition);
1343-
cursorPosition += textToCommit.length;
1288+
// **CHANGE: 在提交前進行簡繁轉換**
1289+
const finalCommitText = convertToSimplified(textToCommit);
1290+
1291+
committedText = committedText.slice(0, cursorPosition) + finalCommitText + committedText.slice(cursorPosition);
1292+
cursorPosition += finalCommitText.length;
13441293
preferredOffset = -1;
13451294
resetInputState();
13461295
}
@@ -1387,7 +1336,7 @@
13871336

13881337
async function loadSecondaryCodemap() {
13891338
const fileName = './3codes2.txt';
1390-
let textToParse = `# 這是備用的模擬編碼表 (用於測試)\n測\tqwe\n試\trty\n工\taaa\n夫\tqwe\n「\t;'\n」\t';\n這\tqwe\n`;
1339+
let textToParse = `# 這是備用的模擬編碼表 (用於測試)\n測\tqwe\n試\trty\n工\taaa\n夫\tqwe\n這\tqwe\n代\tqwe\n碼\tzxc\n說\tzxc\n裡\tqwe\n個\tzxc\n對\tqwe\n會\trty\n國\taaa\n議\tqwe\n變\tqwe\n應\tqwe\n學\trty\n發\taaa\n廠\tqwe\n廣\tqwe\n裡\tqwe\n幾\tqwe\n為\trty\n從\taaa\n後\tqwe\n麵\tqwe\n術\tqwe\n隻\trty\n頭\taaa\n話\tqwe\n裡\tqwe\n「\t;'\n」\t';\n`;
13911340
let statusMessage = '編碼表載入中...';
13921341

13931342
try {
@@ -1475,14 +1424,12 @@
14751424

14761425
/**
14771426
* 顯示候選字或工具列
1478-
* **修正 2:確保浮層開啟時,工具列(表情符號切換/關閉鍵)依然顯示。**
14791427
*/
14801428
function showCandidates() {
14811429
candidateDisplay.innerHTML = '';
14821430

14831431
// 1. 如果浮層開啟 (Emoji/Editor)
14841432
if (overlayDiv.dataset.show) {
1485-
// 修正:浮層開啟時,必須顯示工具列,讓用戶可以切換頁面或關閉浮層。
14861433
showToolbar();
14871434
return;
14881435
}
@@ -1507,6 +1454,7 @@
15071454

15081455
currentCandidates.forEach((candidate, index) => {
15091456
const span = document.createElement('span');
1457+
// **NOTE: 候選字在顯示時不轉換,僅在選字 commitText 時才轉換。**
15101458
span.textContent = candidate;
15111459
span.onclick = () => selectCandidate(index);
15121460
candidateDisplay.appendChild(span);

0 commit comments

Comments
 (0)