Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .claude/CODING_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Coding Style & Guidelines

- Whenever possible, prioritize code readability over code efficiency.
- 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.
- For object-type properties or types or interfaces, always sort in alphabetical order whenever possible.
- Always keep variable and property names concise but clear.
- Always maintain a clear separation of concerns. However, be careful not to over-segregate, such as through premature optimization.
- You should always write your code in a way that makes it easy to unit test.
- 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.
- Variable values ​​should be separated into constants whenever possible. Avoid creating magic numbers.
- If a complex implementation is required, always consider using a commercial library or tool instead of coding it yourself.
- Work should always be done agilely, in small units, and in meaningful change units.
- Instead of rushing to implement it, you should always focus on writing clean code that doesn't create bugs and is easy to maintain.
- 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.
- Always understand the surrounding code context, and when you see signs of reuse, modularize it to avoid code duplication.
- The depth of loops and conditional statements should be as minimal as possible. It's best to avoid them altogether.
- If a function is likely to have more than three arguments, always consider making them object or struct arguments.

## TypeScript Coding Guidelines

- When using TypeScript, avoid using unsafe type systems such as the any type and type assertions whenever possible.
- Always use Type instead of Interface
- Always use arrow functions outside of a class.
6 changes: 1 addition & 5 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ src/extension/*.spec.ts
src/extension/*.spec.js

# Development dependencies
src/extension/node_modules/
src/web-view/node_modules/

# Development config files
Expand Down Expand Up @@ -37,9 +36,6 @@ public/*.gif
node_modules/
frontend/

# Old build outputs (no longer used)
src/extension/out/

# Development files
.env
.env.*
Expand Down Expand Up @@ -94,4 +90,4 @@ coverage/
.nyc_output/

# Logs
*.log
*.log
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ _Execute any command instantly from your status bar - no more hunting through me
- ⚡ **Instant Access**: Click any button to run terminal commands or VS Code functions
- 🎨 **Color-Coded**: Distinguish different command types with custom colors
- 📱 **Smart Terminals**: Each command gets its own named terminal session
- ⌨️ **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

<div align="center">

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

### Multi-Language Keyboard Support

- **🌐 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
- **🔄 Auto-Translation** - Shortcuts automatically map between keyboard layouts and transliteration systems
- **⌨️ Natural Typing** - Use your native keyboard layout without switching to English
- **🎯 Smart Matching** - Type `ㅂ` on Korean keyboard to trigger `q` shortcuts, or `あ` in Japanese to match romaji equivalents
- **🇯🇵 Japanese Support** - Hiragana, Katakana, and Romaji conversion using WanaKana library
- **🇨🇳 Chinese Support** - Traditional and Simplified Chinese with Pinyin conversion
- **🇮🇳 Hindi Support** - Devanagari script with IAST transliteration support

### Pro Tips

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

### 📚 Helpful References

Expand Down Expand Up @@ -226,14 +238,15 @@ _Comprehensive sidebar panel for command management_

## 🆚 Why Choose Quick Command Buttons?

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

## 🛠️ Commands Reference

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"activationEvents": [
"onStartupFinished"
],
"main": "./out/src/main.js",
"main": "./src/extension/out/src/main.js",
"contributes": {
"commands": [
{
Expand Down
6 changes: 6 additions & 0 deletions src/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,11 @@
"jest": "^30.1.3",
"ts-jest": "^29.4.1",
"typescript": "5.9.2"
},
"dependencies": {
"@indic-transliteration/sanscript": "^1.3.3",
"convert-layout": "^0.11.1",
"pinyin": "^4.0.0",
"wanakana": "^5.3.1"
}
}
44 changes: 44 additions & 0 deletions src/extension/src/command-executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,50 @@ describe("command-executor", () => {

expect(result).toBeUndefined();
});

it("should find item with Korean character matching English shortcut", () => {
const result = findShortcutItem(items, "ㅁ");

expect(result).toEqual(items[0]);
});

it("should find item with Korean character ㅠ matching English shortcut b", () => {
const result = findShortcutItem(items, "ㅠ");

expect(result).toEqual(items[1]);
});

it("should find item with Russian character matching English shortcut", () => {
const result = findShortcutItem(items, "ф");

expect(result).toEqual(items[0]);
});

it("should handle Arabic characters", () => {
const arabicItems = [
{
command: { name: "test1", shortcut: "z" } as ButtonConfig,
description: "",
label: "Test 1",
},
];
const result = findShortcutItem(arabicItems, "ض");

expect(result).toEqual(arabicItems[0]);
});

it("should handle Hebrew characters", () => {
const hebrewItems = [
{
command: { name: "test1", shortcut: "e" } as ButtonConfig,
description: "",
label: "Test 1",
},
];
const result = findShortcutItem(hebrewItems, "ק");

expect(result).toEqual(hebrewItems[0]);
});
});

describe("determineButtonExecutionType", () => {
Expand Down
12 changes: 11 additions & 1 deletion src/extension/src/command-executor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from "vscode";
import { ButtonConfig } from "./types";
import { TerminalExecutor, QuickPickCreator } from "./adapters";
import { findMatchingShortcut } from "./keyboard-layout-converter";

export type QuickPickItem = {
label: string;
Expand Down Expand Up @@ -32,8 +33,17 @@ export const findShortcutItem = (
): QuickPickItem | undefined => {
if (inputValue.length !== 1) return undefined;

const shortcuts = items
.map((item) => item.command.shortcut)
.filter((shortcut): shortcut is string => Boolean(shortcut));

const matchingShortcut = findMatchingShortcut(inputValue, shortcuts);

if (!matchingShortcut) return undefined;

return items.find(
(item) => item.command.shortcut?.toLowerCase() === inputValue.toLowerCase()
(item) =>
item.command.shortcut?.toLowerCase() === matchingShortcut.toLowerCase()
);
};

Expand Down
143 changes: 143 additions & 0 deletions src/extension/src/keyboard-layout-converter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import {
generateKeyVariants,
findMatchingShortcut,
} from "./keyboard-layout-converter";

describe("generateKeyVariants", () => {
it("should return input key with case variations for single character", () => {
const variants = generateKeyVariants("q");
expect(variants).toContain("q");
expect(variants).toContain("Q");
});

it("should include Korean conversion for q key", () => {
const variants = generateKeyVariants("q");
expect(variants).toContain("ㅂ");
});

it("should include English conversion for Korean key", () => {
const variants = generateKeyVariants("ㅂ");
expect(variants).toContain("q");
});

it("should return original input for non-single character input", () => {
const variants = generateKeyVariants("abc");
expect(variants).toEqual(["abc"]);
});

it("should return original input for empty string", () => {
const variants = generateKeyVariants("");
expect(variants).toEqual([""]);
});

it("should handle Russian characters", () => {
const variants = generateKeyVariants("й");
expect(variants).toContain("q");
});

it("should handle Arabic characters", () => {
const variants = generateKeyVariants("ض");
expect(variants).toContain("z");
});

it("should handle Hebrew characters", () => {
const variants = generateKeyVariants("ק");
expect(variants).toContain("e");
});

it("should handle German characters", () => {
const variants = generateKeyVariants("ü");
expect(variants).toContain("[");
});

it("should handle Spanish characters", () => {
const variants = generateKeyVariants("ñ");
expect(variants).toContain(";");
});

it("should handle Greek characters", () => {
const variants = generateKeyVariants("θ");
expect(variants).toContain("u");
});

it("should handle Japanese hiragana characters", () => {
const variants = generateKeyVariants("あ");
expect(variants).toContain("a");
});

it("should handle Japanese katakana characters", () => {
const variants = generateKeyVariants("ア");
expect(variants).toContain("a");
});

it("should handle Chinese characters", () => {
const variants = generateKeyVariants("你");
expect(variants).toContain("你");
});

it("should handle Hindi Devanagari characters", () => {
const variants = generateKeyVariants("अ");
expect(variants).toContain("a");
});

it("should handle numbers and special characters", () => {
const variants = generateKeyVariants("1");
expect(variants).toContain("1");
});
});

describe("findMatchingShortcut", () => {
const shortcuts = ["q", "t", "g", "n"];

it("should find exact match for English character", () => {
const result = findMatchingShortcut("q", shortcuts);
expect(result).toBe("q");
});

it("should find match for Korean character mapping to English", () => {
const result = findMatchingShortcut("ㅂ", shortcuts);
expect(result).toBe("q");
});

it("should find match for Russian character mapping to English", () => {
const result = findMatchingShortcut("й", shortcuts);
expect(result).toBe("q");
});

it("should return undefined for non-matching character", () => {
const result = findMatchingShortcut("x", shortcuts);
expect(result).toBeUndefined();
});

it("should return undefined for multi-character input", () => {
const result = findMatchingShortcut("qt", shortcuts);
expect(result).toBeUndefined();
});

it("should handle case insensitive matching", () => {
const result = findMatchingShortcut("Q", shortcuts);
expect(result).toBe("q");
});

it("should handle empty shortcuts array", () => {
const result = findMatchingShortcut("q", []);
expect(result).toBeUndefined();
});

it("should handle shortcuts with undefined values", () => {
const result = findMatchingShortcut("q", ["q", undefined as any, "t"]);
expect(result).toBe("q");
});

it("should match Korean ㅅ with t shortcut", () => {
const shortcutsWithT = ["s", "t", "g"];
const result = findMatchingShortcut("ㅅ", shortcutsWithT);
expect(result).toBe("t");
});

it("should match Korean ㄴ with s shortcut", () => {
const shortcutsWithS = ["s", "t", "g"];
const result = findMatchingShortcut("ㄴ", shortcutsWithS);
expect(result).toBe("s");
});
});
Loading