diff --git a/.claude/CODING_GUIDE.md b/.claude/CODING_GUIDE.md new file mode 100644 index 00000000..661ba764 --- /dev/null +++ b/.claude/CODING_GUIDE.md @@ -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. diff --git a/.vscodeignore b/.vscodeignore index b1ee4106..674fc386 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -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 @@ -37,9 +36,6 @@ public/*.gif node_modules/ frontend/ -# Old build outputs (no longer used) -src/extension/out/ - # Development files .env .env.* @@ -94,4 +90,4 @@ coverage/ .nyc_output/ # Logs -*.log \ No newline at end of file +*.log diff --git a/README.md b/README.md index 255c40d4..6879aad4 100644 --- a/README.md +++ b/README.md @@ -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
@@ -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 @@ -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 diff --git a/package.json b/package.json index e41999ef..755096be 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "activationEvents": [ "onStartupFinished" ], - "main": "./out/src/main.js", + "main": "./src/extension/out/src/main.js", "contributes": { "commands": [ { diff --git a/src/extension/package.json b/src/extension/package.json index 13ae601d..be4f9519 100644 --- a/src/extension/package.json +++ b/src/extension/package.json @@ -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" } } diff --git a/src/extension/src/command-executor.test.ts b/src/extension/src/command-executor.test.ts index 39ae3bb7..604e8c19 100644 --- a/src/extension/src/command-executor.test.ts +++ b/src/extension/src/command-executor.test.ts @@ -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", () => { diff --git a/src/extension/src/command-executor.ts b/src/extension/src/command-executor.ts index f3b4814a..1ffac6cb 100644 --- a/src/extension/src/command-executor.ts +++ b/src/extension/src/command-executor.ts @@ -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; @@ -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() ); }; diff --git a/src/extension/src/keyboard-layout-converter.test.ts b/src/extension/src/keyboard-layout-converter.test.ts new file mode 100644 index 00000000..b1d9fb78 --- /dev/null +++ b/src/extension/src/keyboard-layout-converter.test.ts @@ -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"); + }); +}); diff --git a/src/extension/src/keyboard-layout-converter.ts b/src/extension/src/keyboard-layout-converter.ts new file mode 100644 index 00000000..b3c0de5e --- /dev/null +++ b/src/extension/src/keyboard-layout-converter.ts @@ -0,0 +1,180 @@ +import kr from "convert-layout/kr"; +import ru from "convert-layout/ru"; +import ar from "convert-layout/ar"; +import he from "convert-layout/he"; +import de from "convert-layout/de"; +import es from "convert-layout/es"; +import cs from "convert-layout/cs"; +import gr from "convert-layout/gr"; +import fa from "convert-layout/fa"; +import by from "convert-layout/by"; +import uk from "convert-layout/uk"; +import kk from "convert-layout/kk"; +import * as wanakana from "wanakana"; +import pinyin from "pinyin"; +import * as sanscript from "@indic-transliteration/sanscript"; + +type LayoutConverter = { + toEn: (text: string) => string; + fromEn: (text: string) => string; +}; + +const LAYOUT_CONVERTERS: Array<{ converter: LayoutConverter; name: string }> = [ + { converter: kr, name: "kr" }, // Korean (한국어) + { converter: ru, name: "ru" }, // Russian (русский) + { converter: ar, name: "ar" }, // Arabic (العربية) + { converter: he, name: "he" }, // Hebrew (עברית) + { converter: de, name: "de" }, // German (Deutsch) + { converter: es, name: "es" }, // Spanish (Español) + { converter: cs, name: "cs" }, // Czech (Čeština) + { converter: gr, name: "gr" }, // Greek (Ελληνικά) + { converter: fa, name: "fa" }, // Persian/Farsi (فارسی) + { converter: by, name: "by" }, // Belarusian (Беларуская) + { converter: uk, name: "uk" }, // Ukrainian (Українська) + { converter: kk, name: "kk" }, // Kazakh (Қазақша) +]; + +const japaneseConverter: LayoutConverter = { + toEn: (text: string): string => { + if (wanakana.isJapanese(text)) { + return wanakana.toRomaji(text); + } + return text; + }, + fromEn: (text: string): string => { + if (wanakana.isRomaji(text)) { + return wanakana.toHiragana(text); + } + return text; + }, +}; + +const chineseConverter: LayoutConverter = { + toEn: (text: string): string => { + try { + const pinyinResult = pinyin(text, { + style: pinyin.STYLE_NORMAL, + heteronym: false, + segment: false, + }); + return pinyinResult.flat().join(""); + } catch { + return text; + } + }, + fromEn: (text: string): string => { + return text; + }, +}; + +const hindiConverter: LayoutConverter = { + toEn: (text: string): string => { + try { + return sanscript.t(text, "devanagari", "iast"); + } catch { + return text; + } + }, + fromEn: (text: string): string => { + try { + return sanscript.t(text, "iast", "devanagari"); + } catch { + return text; + } + }, +}; + +const ADVANCED_CONVERTERS: Array<{ converter: LayoutConverter; name: string }> = + [ + { converter: japaneseConverter, name: "ja" }, // Japanese (日本語) + { converter: chineseConverter, name: "zh" }, // Chinese (中文) + { converter: hindiConverter, name: "hi" }, // Hindi (हिन्दी) + ]; + +const ALL_CONVERTERS = [...LAYOUT_CONVERTERS, ...ADVANCED_CONVERTERS]; + +export const generateKeyVariants = (inputKey: string): string[] => { + if (!inputKey || inputKey.length !== 1) { + return [inputKey]; + } + + const variants = new Set(); + variants.add(inputKey.toLowerCase()); + variants.add(inputKey.toUpperCase()); + + const processConversion = (conversionFn: (text: string) => string) => { + const convertedKey = conversionFn(inputKey); + if ( + convertedKey && + convertedKey !== inputKey && + convertedKey.length === 1 + ) { + variants.add(convertedKey.toLowerCase()); + variants.add(convertedKey.toUpperCase()); + } + }; + + try { + for (const layout of ALL_CONVERTERS) { + try { + processConversion(layout.converter.toEn); + processConversion(layout.converter.fromEn); + } catch (error) { + console.warn(`Error in layout converter "${layout.name}":`, error); + continue; + } + } + } catch (error) { + console.warn("Error generating key variants:", error); + } + + return Array.from(variants); +}; + +export const findMatchingShortcut = ( + inputValue: string, + shortcuts: string[] +): string | undefined => { + if (inputValue.length !== 1) { + return undefined; + } + + try { + const inputVariants = new Set( + generateKeyVariants(inputValue).map((v) => v.toLowerCase()) + ); + + const exactCaseMatch = shortcuts.find((shortcut) => { + if (!shortcut) return false; + return shortcut === inputValue; + }); + if (exactCaseMatch) { + return exactCaseMatch; + } + + const directCaseMatch = shortcuts.find((shortcut) => { + if (!shortcut) return false; + return shortcut.toLowerCase() === inputValue.toLowerCase(); + }); + if (directCaseMatch) { + return directCaseMatch; + } + + const layoutMatch = shortcuts.find((shortcut) => { + if (!shortcut) return false; + + const shortcutVariants = generateKeyVariants(shortcut); + return shortcutVariants.some((variant) => + inputVariants.has(variant.toLowerCase()) + ); + }); + + return layoutMatch; + } catch (error) { + console.warn("Error in findMatchingShortcut:", error); + return shortcuts.find( + (shortcut) => + shortcut && shortcut.toLowerCase() === inputValue.toLowerCase() + ); + } +}; diff --git a/src/extension/src/types/convert-layout.d.ts b/src/extension/src/types/convert-layout.d.ts new file mode 100644 index 00000000..39496b58 --- /dev/null +++ b/src/extension/src/types/convert-layout.d.ts @@ -0,0 +1,91 @@ +type LayoutConverter = { + toEn(text: string): string; + fromEn(text: string): string; +}; + +declare module "convert-layout/kr" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/ru" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/ar" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/he" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/de" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/es" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/cs" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/gr" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/fa" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/by" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/uk" { + const converter: LayoutConverter; + export default converter; +} + +declare module "convert-layout/kk" { + const converter: LayoutConverter; + export default converter; +} + +declare module "wanakana" { + export const isJapanese: (text: string) => boolean; + export const isRomaji: (text: string) => boolean; + export const toRomaji: (text: string) => string; + export const toHiragana: (text: string) => string; +} + +declare module "pinyin" { + type PinyinOptions = { + style?: number; + heteronym?: boolean; + segment?: boolean; + }; + + type PinyinFunction = { + (text: string, options?: PinyinOptions): string[][]; + STYLE_NORMAL: number; + }; + + const pinyin: PinyinFunction; + export default pinyin; +} + +declare module "@indic-transliteration/sanscript" { + export const t: (text: string, from: string, to: string) => string; +} diff --git a/src/extension/tsconfig.json b/src/extension/tsconfig.json index 7468fc1f..09480ada 100644 --- a/src/extension/tsconfig.json +++ b/src/extension/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "module": "commonjs", "target": "ES2020", - "outDir": "../../out", + "outDir": "./out", "lib": ["ES2020", "dom"], "sourceMap": true, "rootDir": ".", diff --git a/src/extension/yarn.lock b/src/extension/yarn.lock index 6fb040ea..5588a299 100644 --- a/src/extension/yarn.lock +++ b/src/extension/yarn.lock @@ -292,6 +292,19 @@ dependencies: tslib "^2.4.0" +"@indic-transliteration/common_maps@^1.0.2": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@indic-transliteration/common_maps/-/common_maps-1.0.5.tgz#a63e653fbf6e15e8152086044e1bae2c24c1f3ee" + integrity sha512-XbWDA5AXGE+Nh4uGr/yN9ZM8avRBy4F1KQL+DLgQGOdsQ390lcW4fga0NSjg4C/rOpMd0rHZv2YFV3Bq3UbpkQ== + +"@indic-transliteration/sanscript@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@indic-transliteration/sanscript/-/sanscript-1.3.3.tgz#2d70995b8efde50035cd89b0b33516c0e8d8b568" + integrity sha512-zNGeARmQTPIlubwgEhl/JumpwTPHrdT/cNsQeCL+G67SQmjJe3qRnMIYghXiVt7+KDso/pU1Ky2ZfD/RBISfJQ== + dependencies: + "@indic-transliteration/common_maps" "^1.0.2" + toml "^2.3.6" + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -1064,11 +1077,23 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +commander@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-1.1.1.tgz#50d1651868ae60eccff0a2d9f34595376bc6b041" + integrity sha512-71Rod2AhcH3JhkBikVpNd0pA+fWsmAaVoti6OR38T76chA7vE3pSerS0Jor4wDw+tOueD2zLVvFOw5H0Rcj7rA== + dependencies: + keypress "0.1.x" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +convert-layout@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/convert-layout/-/convert-layout-0.11.1.tgz#823bd72268e1a959f2433348a44a9c7185bba22e" + integrity sha512-Zy+yE2AqFZUYZxvwS/He7DmnwEahGdy/GyiM33YdxoixyI1YZCYYup2OEYnrhn5r34L9YRa+PZC2ooVmQHY9oQ== + convert-source-map@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" @@ -1796,6 +1821,11 @@ json5@^2.2.3: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +keypress@0.1.x: + version "0.1.0" + resolved "https://registry.yarnpkg.com/keypress/-/keypress-0.1.0.tgz#4a3188d4291b66b4f65edb99f806aa9ae293592a" + integrity sha512-x0yf9PL/nx9Nw9oLL8ZVErFAk85/lslwEP7Vz7s5SI1ODXZIgit3C5qyWjw4DxOuO/3Hb4866SQh28a1V1d+WA== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -2026,6 +2056,13 @@ picomatch@^4.0.2: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== +pinyin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pinyin/-/pinyin-4.0.0.tgz#9c15fd9d45c08b6813221fc602f77759429bfa4f" + integrity sha512-vHpV5K+vpp6XUUpZNGRDuHoN+1xcmieM3EWlH4QjSX2kkpG/gVOwpqwV9EOJ9x9c9UERFKeLml5XVSukE/PLgQ== + dependencies: + commander "~1.1.1" + pirates@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.7.tgz#643b4a18c4257c8a65104b73f3049ce9a0a15e22" @@ -2249,6 +2286,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toml@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/toml/-/toml-2.3.6.tgz#25b0866483a9722474895559088b436fd11f861b" + integrity sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ== + ts-jest@^29.4.1: version "29.4.1" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.4.1.tgz#42d33beb74657751d315efb9a871fe99e3b9b519" @@ -2355,6 +2397,11 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" +wanakana@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/wanakana/-/wanakana-5.3.1.tgz#e20798fb9687d926d4aad25874bc8e66096818ed" + integrity sha512-OSDqupzTlzl2LGyqTdhcXcl6ezMiFhcUwLBP8YKaBIbMYW1wAwDvupw2T9G9oVaKT9RmaSpyTXjxddFPUcFFIw== + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" diff --git a/versions/quick-command-buttons-0.2.3.vsix b/versions/quick-command-buttons-0.2.3.vsix index fa13a93c..e1db72cb 100644 Binary files a/versions/quick-command-buttons-0.2.3.vsix and b/versions/quick-command-buttons-0.2.3.vsix differ