Skip to content

Commit a7e135e

Browse files
authored
Merge pull request #38 from KubrickCode/develop/shlee/26
Add keyboard layout converter module with 15 language support for shortcut
2 parents 9cbb0f2 + c89538a commit a7e135e

13 files changed

+569
-16
lines changed

.claude/CODING_GUIDE.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Coding Style & Guidelines
2+
3+
- Whenever possible, prioritize code readability over code efficiency.
4+
- Always write code that's short and concise. Make good use of early return techniques, and be careful not to create too much depth in conditional statements or loops.
5+
- For object-type properties or types or interfaces, always sort in alphabetical order whenever possible.
6+
- Always keep variable and property names concise but clear.
7+
- Always maintain a clear separation of concerns. However, be careful not to over-segregate, such as through premature optimization.
8+
- You should always write your code in a way that makes it easy to unit test.
9+
- Comments shouldn't be used unless absolutely necessary. Write readable code that can be understood without comments, and only include comments for unavoidable business logic.
10+
- Variable values ​​should be separated into constants whenever possible. Avoid creating magic numbers.
11+
- If a complex implementation is required, always consider using a commercial library or tool instead of coding it yourself.
12+
- Work should always be done agilely, in small units, and in meaningful change units.
13+
- Instead of rushing to implement it, you should always focus on writing clean code that doesn't create bugs and is easy to maintain.
14+
- If you feel like there's too much code in a single file, you should first review the overall structure and figure out how to neatly separate the files.
15+
- Always understand the surrounding code context, and when you see signs of reuse, modularize it to avoid code duplication.
16+
- The depth of loops and conditional statements should be as minimal as possible. It's best to avoid them altogether.
17+
- If a function is likely to have more than three arguments, always consider making them object or struct arguments.
18+
19+
## TypeScript Coding Guidelines
20+
21+
- When using TypeScript, avoid using unsafe type systems such as the any type and type assertions whenever possible.
22+
- Always use Type instead of Interface
23+
- Always use arrow functions outside of a class.

.vscodeignore

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ src/extension/*.spec.ts
77
src/extension/*.spec.js
88

99
# Development dependencies
10-
src/extension/node_modules/
1110
src/web-view/node_modules/
1211

1312
# Development config files
@@ -37,9 +36,6 @@ public/*.gif
3736
node_modules/
3837
frontend/
3938

40-
# Old build outputs (no longer used)
41-
src/extension/out/
42-
4339
# Development files
4440
.env
4541
.env.*
@@ -94,4 +90,4 @@ coverage/
9490
.nyc_output/
9591

9692
# Logs
97-
*.log
93+
*.log

README.md

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ _Execute any command instantly from your status bar - no more hunting through me
4141
-**Instant Access**: Click any button to run terminal commands or VS Code functions
4242
- 🎨 **Color-Coded**: Distinguish different command types with custom colors
4343
- 📱 **Smart Terminals**: Each command gets its own named terminal session
44+
- ⌨️ **Multi-Language Keyboard Shortcuts**: Use shortcuts in your native keyboard layout - Korean `` automatically maps to English `t`, Japanese `` to `a`, etc. Supports 15 languages including Korean, Japanese, Chinese, Hindi, Russian, Arabic, and more
4445

4546
<div align="center">
4647

@@ -161,12 +162,23 @@ Use `Ctrl+Shift+P` → `Toggle Configuration Target` or the button in Configurat
161162
- **`Ctrl+Shift+;`** - Open command palette
162163
- **Single keys** - Quick command execution in groups (e.g., press `g` then `s` for Git Status)
163164

165+
### Multi-Language Keyboard Support
166+
167+
- **🌐 Global Compatibility** - Works seamlessly with 15 keyboard layouts and input methods: Korean, Russian, Arabic, Hebrew, German, Spanish, Czech, Greek, Persian, Belarusian, Ukrainian, Kazakh, Japanese, Chinese, and Hindi
168+
- **🔄 Auto-Translation** - Shortcuts automatically map between keyboard layouts and transliteration systems
169+
- **⌨️ Natural Typing** - Use your native keyboard layout without switching to English
170+
- **🎯 Smart Matching** - Type `` on Korean keyboard to trigger `q` shortcuts, or `` in Japanese to match romaji equivalents
171+
- **🇯🇵 Japanese Support** - Hiragana, Katakana, and Romaji conversion using WanaKana library
172+
- **🇨🇳 Chinese Support** - Traditional and Simplified Chinese with Pinyin conversion
173+
- **🇮🇳 Hindi Support** - Devanagari script with IAST transliteration support
174+
164175
### Pro Tips
165176

166177
- Use `$(icon-name)` syntax for VS Code icons in button names
167178
- Group related commands to keep status bar clean
168179
- Use `executeAll: true` for monitoring multiple processes
169180
- Mix terminal commands with VS Code API calls seamlessly
181+
- Type shortcuts in your native keyboard layout - no need to switch to English!
170182

171183
### 📚 Helpful References
172184

@@ -226,14 +238,15 @@ _Comprehensive sidebar panel for command management_
226238

227239
## 🆚 Why Choose Quick Command Buttons?
228240

229-
| Feature | Quick Command Buttons | Other Extensions |
230-
| ----------------------------- | ------------------------------ | -------------------- |
231-
| **Intelligent Grouping** | ✅ Unlimited nesting | ❌ Limited or none |
232-
| **Visual Configuration** | ✅ Drag & drop UI | ❌ Manual JSON only |
233-
| **Multiple Access Points** | ✅ Status bar + Tree + Palette | ❌ Single method |
234-
| **Smart Terminal Management** | ✅ Named, organized terminals | ❌ Generic terminals |
235-
| **Mixed Command Types** | ✅ Terminal + VS Code API | ❌ Usually one type |
236-
| **Real-time Updates** | ✅ Instant configuration sync | ❌ Restart required |
241+
| Feature | Quick Command Buttons | Other Extensions |
242+
| ----------------------------- | -------------------------------------------------------- | -------------------- |
243+
| **Intelligent Grouping** | ✅ Unlimited nesting | ❌ Limited or none |
244+
| **Visual Configuration** | ✅ Drag & drop UI | ❌ Manual JSON only |
245+
| **Multiple Access Points** | ✅ Status bar + Tree + Palette | ❌ Single method |
246+
| **Smart Terminal Management** | ✅ Named, organized terminals | ❌ Generic terminals |
247+
| **Mixed Command Types** | ✅ Terminal + VS Code API | ❌ Usually one type |
248+
| **Multi-Language Keyboards** | ✅ 15 languages (Korean, Japanese, Chinese, Hindi, etc.) | ❌ English only |
249+
| **Real-time Updates** | ✅ Instant configuration sync | ❌ Restart required |
237250

238251
## 🛠️ Commands Reference
239252

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"activationEvents": [
3232
"onStartupFinished"
3333
],
34-
"main": "./out/src/main.js",
34+
"main": "./src/extension/out/src/main.js",
3535
"contributes": {
3636
"commands": [
3737
{

src/extension/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,11 @@
1717
"jest": "^30.1.3",
1818
"ts-jest": "^29.4.1",
1919
"typescript": "5.9.2"
20+
},
21+
"dependencies": {
22+
"@indic-transliteration/sanscript": "^1.3.3",
23+
"convert-layout": "^0.11.1",
24+
"pinyin": "^4.0.0",
25+
"wanakana": "^5.3.1"
2026
}
2127
}

src/extension/src/command-executor.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,50 @@ describe("command-executor", () => {
207207

208208
expect(result).toBeUndefined();
209209
});
210+
211+
it("should find item with Korean character matching English shortcut", () => {
212+
const result = findShortcutItem(items, "ㅁ");
213+
214+
expect(result).toEqual(items[0]);
215+
});
216+
217+
it("should find item with Korean character ㅠ matching English shortcut b", () => {
218+
const result = findShortcutItem(items, "ㅠ");
219+
220+
expect(result).toEqual(items[1]);
221+
});
222+
223+
it("should find item with Russian character matching English shortcut", () => {
224+
const result = findShortcutItem(items, "ф");
225+
226+
expect(result).toEqual(items[0]);
227+
});
228+
229+
it("should handle Arabic characters", () => {
230+
const arabicItems = [
231+
{
232+
command: { name: "test1", shortcut: "z" } as ButtonConfig,
233+
description: "",
234+
label: "Test 1",
235+
},
236+
];
237+
const result = findShortcutItem(arabicItems, "ض");
238+
239+
expect(result).toEqual(arabicItems[0]);
240+
});
241+
242+
it("should handle Hebrew characters", () => {
243+
const hebrewItems = [
244+
{
245+
command: { name: "test1", shortcut: "e" } as ButtonConfig,
246+
description: "",
247+
label: "Test 1",
248+
},
249+
];
250+
const result = findShortcutItem(hebrewItems, "ק");
251+
252+
expect(result).toEqual(hebrewItems[0]);
253+
});
210254
});
211255

212256
describe("determineButtonExecutionType", () => {

src/extension/src/command-executor.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as vscode from "vscode";
22
import { ButtonConfig } from "./types";
33
import { TerminalExecutor, QuickPickCreator } from "./adapters";
4+
import { findMatchingShortcut } from "./keyboard-layout-converter";
45

56
export type QuickPickItem = {
67
label: string;
@@ -32,8 +33,17 @@ export const findShortcutItem = (
3233
): QuickPickItem | undefined => {
3334
if (inputValue.length !== 1) return undefined;
3435

36+
const shortcuts = items
37+
.map((item) => item.command.shortcut)
38+
.filter((shortcut): shortcut is string => Boolean(shortcut));
39+
40+
const matchingShortcut = findMatchingShortcut(inputValue, shortcuts);
41+
42+
if (!matchingShortcut) return undefined;
43+
3544
return items.find(
36-
(item) => item.command.shortcut?.toLowerCase() === inputValue.toLowerCase()
45+
(item) =>
46+
item.command.shortcut?.toLowerCase() === matchingShortcut.toLowerCase()
3747
);
3848
};
3949

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import {
2+
generateKeyVariants,
3+
findMatchingShortcut,
4+
} from "./keyboard-layout-converter";
5+
6+
describe("generateKeyVariants", () => {
7+
it("should return input key with case variations for single character", () => {
8+
const variants = generateKeyVariants("q");
9+
expect(variants).toContain("q");
10+
expect(variants).toContain("Q");
11+
});
12+
13+
it("should include Korean conversion for q key", () => {
14+
const variants = generateKeyVariants("q");
15+
expect(variants).toContain("ㅂ");
16+
});
17+
18+
it("should include English conversion for Korean key", () => {
19+
const variants = generateKeyVariants("ㅂ");
20+
expect(variants).toContain("q");
21+
});
22+
23+
it("should return original input for non-single character input", () => {
24+
const variants = generateKeyVariants("abc");
25+
expect(variants).toEqual(["abc"]);
26+
});
27+
28+
it("should return original input for empty string", () => {
29+
const variants = generateKeyVariants("");
30+
expect(variants).toEqual([""]);
31+
});
32+
33+
it("should handle Russian characters", () => {
34+
const variants = generateKeyVariants("й");
35+
expect(variants).toContain("q");
36+
});
37+
38+
it("should handle Arabic characters", () => {
39+
const variants = generateKeyVariants("ض");
40+
expect(variants).toContain("z");
41+
});
42+
43+
it("should handle Hebrew characters", () => {
44+
const variants = generateKeyVariants("ק");
45+
expect(variants).toContain("e");
46+
});
47+
48+
it("should handle German characters", () => {
49+
const variants = generateKeyVariants("ü");
50+
expect(variants).toContain("[");
51+
});
52+
53+
it("should handle Spanish characters", () => {
54+
const variants = generateKeyVariants("ñ");
55+
expect(variants).toContain(";");
56+
});
57+
58+
it("should handle Greek characters", () => {
59+
const variants = generateKeyVariants("θ");
60+
expect(variants).toContain("u");
61+
});
62+
63+
it("should handle Japanese hiragana characters", () => {
64+
const variants = generateKeyVariants("あ");
65+
expect(variants).toContain("a");
66+
});
67+
68+
it("should handle Japanese katakana characters", () => {
69+
const variants = generateKeyVariants("ア");
70+
expect(variants).toContain("a");
71+
});
72+
73+
it("should handle Chinese characters", () => {
74+
const variants = generateKeyVariants("你");
75+
expect(variants).toContain("你");
76+
});
77+
78+
it("should handle Hindi Devanagari characters", () => {
79+
const variants = generateKeyVariants("अ");
80+
expect(variants).toContain("a");
81+
});
82+
83+
it("should handle numbers and special characters", () => {
84+
const variants = generateKeyVariants("1");
85+
expect(variants).toContain("1");
86+
});
87+
});
88+
89+
describe("findMatchingShortcut", () => {
90+
const shortcuts = ["q", "t", "g", "n"];
91+
92+
it("should find exact match for English character", () => {
93+
const result = findMatchingShortcut("q", shortcuts);
94+
expect(result).toBe("q");
95+
});
96+
97+
it("should find match for Korean character mapping to English", () => {
98+
const result = findMatchingShortcut("ㅂ", shortcuts);
99+
expect(result).toBe("q");
100+
});
101+
102+
it("should find match for Russian character mapping to English", () => {
103+
const result = findMatchingShortcut("й", shortcuts);
104+
expect(result).toBe("q");
105+
});
106+
107+
it("should return undefined for non-matching character", () => {
108+
const result = findMatchingShortcut("x", shortcuts);
109+
expect(result).toBeUndefined();
110+
});
111+
112+
it("should return undefined for multi-character input", () => {
113+
const result = findMatchingShortcut("qt", shortcuts);
114+
expect(result).toBeUndefined();
115+
});
116+
117+
it("should handle case insensitive matching", () => {
118+
const result = findMatchingShortcut("Q", shortcuts);
119+
expect(result).toBe("q");
120+
});
121+
122+
it("should handle empty shortcuts array", () => {
123+
const result = findMatchingShortcut("q", []);
124+
expect(result).toBeUndefined();
125+
});
126+
127+
it("should handle shortcuts with undefined values", () => {
128+
const result = findMatchingShortcut("q", ["q", undefined as any, "t"]);
129+
expect(result).toBe("q");
130+
});
131+
132+
it("should match Korean ㅅ with t shortcut", () => {
133+
const shortcutsWithT = ["s", "t", "g"];
134+
const result = findMatchingShortcut("ㅅ", shortcutsWithT);
135+
expect(result).toBe("t");
136+
});
137+
138+
it("should match Korean ㄴ with s shortcut", () => {
139+
const shortcutsWithS = ["s", "t", "g"];
140+
const result = findMatchingShortcut("ㄴ", shortcutsWithS);
141+
expect(result).toBe("s");
142+
});
143+
});

0 commit comments

Comments
 (0)