Skip to content

Commit f579ec7

Browse files
ertembiyikgreptile-apps[bot]raycastbot
authored
Ertembiyik/update punto (#22624)
* Use Text Input Source Services API to switch layouts * Update CHANGELOG * Update extensions/punto/CHANGELOG.md Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * Fix indentation * Remove redundant layoutSwitchModifier preference, lint * Remove category and source languages unused properties * Remove redundant Equatable conformance * Use swift tools version 5.10 * Update CHANGELOG.md --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: raycastbot <[email protected]>
1 parent 8397c72 commit f579ec7

File tree

7 files changed

+269
-103
lines changed

7 files changed

+269
-103
lines changed

extensions/punto/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Punto Changelog
22

3+
## [Use TIS API to switch layouts] - 2025-11-05
4+
5+
- Uses Text Input Source Services API to switch layouts instead of keyboard shortcuts approach
6+
37
## [Ua layout mapping] - 2025-02-04
48

59
- Added Ukrainian layout mapping to dropdown

extensions/punto/package-lock.json

Lines changed: 103 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/punto/package.json

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -20,67 +20,45 @@
2020
],
2121
"preferences": [
2222
{
23-
"name": "layoutSwitchModifier",
24-
"title": "Layout Switch Shortcut",
25-
"description": "The shortcut to switch the layout of the selected text",
26-
"type": "dropdown",
27-
"required": true,
28-
"data": [
29-
{
30-
"title": "⌘Space",
31-
"value": "command"
32-
},
33-
{
34-
"title": "⌃Space",
35-
"value": "control"
36-
},
37-
{
38-
"title": "⌥Space",
39-
"value": "option"
40-
}
41-
],
42-
"default": "control"
43-
},
44-
{
45-
"name": "cyrLayoutName",
46-
"title": "Cyrillic Layout",
47-
"description": "The name of the Cyrillic layout",
23+
"name": "cyrLayoutID",
24+
"title": "Cyrillic Layout ID",
25+
"description": "The id of the Cyrillic layout",
4826
"type": "dropdown",
4927
"required": true,
5028
"data": [
5129
{
5230
"title": "Russian",
53-
"value": "Russian"
31+
"value": "com.apple.keylayout.Russian"
5432
},
5533
{
5634
"title": "Russian - PC",
57-
"value": "RussianWin"
35+
"value": "com.apple.keylayout.RussianWin"
5836
},
5937
{
6038
"title": "Russian - QWERTY",
61-
"value": "Russian - Phonetic"
39+
"value": "com.apple.keylayout.Russian-Phonetic"
6240
},
6341
{
6442
"title": "Ukrainian",
65-
"value": "Ukrainian - PC"
43+
"value": "com.apple.keylayout.Ukrainian-PC"
6644
},
6745
{
6846
"title": "Ukrainian - Legacy",
69-
"value": "Ukrainian"
47+
"value": "com.apple.keylayout.Ukrainian"
7048
},
7149
{
7250
"title": "Ukrainian - QWERTY",
73-
"value": "Ukrainian-QWERTY"
51+
"value": "com.apple.keylayout.Ukrainian-QWERTY"
7452
}
7553
]
7654
},
7755
{
78-
"name": "latLayoutName",
79-
"title": "Latin Layout",
80-
"description": "The name of the Latin layout",
56+
"name": "latLayoutID",
57+
"title": "Latin Layout ID",
58+
"description": "The id of the Latin layout",
8159
"type": "textfield",
8260
"required": true,
83-
"default": "ABC"
61+
"default": "com.apple.keylayout.ABC"
8462
},
8563
{
8664
"name": "showSuccessHUD",
@@ -93,7 +71,8 @@
9371
}
9472
],
9573
"dependencies": {
96-
"@raycast/api": "^1.89.0"
74+
"@raycast/api": "^1.89.0",
75+
"@raycast/utils": "^1.10.1"
9776
},
9877
"devDependencies": {
9978
"@types/node": "22.10.7",

extensions/punto/src/index.ts

Lines changed: 20 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import {
55
getPreferenceValues,
66
} from "@raycast/api";
77
import { en_ru, ru_en } from "./Dict";
8-
import { exec as Exec } from "child_process";
9-
import { promisify } from "util";
8+
import {
9+
getAvailableInputSourceIds,
10+
selectInputSource,
11+
} from "swift:../swift/Punto";
1012

11-
const exec = promisify(Exec);
1213
interface Preferences {
13-
layoutSwitchModifier: string;
14-
latLayoutName: string;
15-
cyrLayoutName: string;
14+
latLayoutID: string;
15+
cyrLayoutID: string;
1616
showSuccessHUD: boolean;
1717
}
1818

@@ -53,55 +53,29 @@ async function switchKeyboardLayout(
5353
preferences: Preferences,
5454
targetLayout: Layout,
5555
): Promise<void> {
56-
const languages = await getInstalledLayoutNames();
57-
// console.log("installed layout names are " + languages.join(", "));
56+
const languageIds =
57+
(await getAvailableInputSourceIds()) as unknown as string[];
58+
// console.log("installed layout names are " + languageIds.join(", "));
5859
// console.log("target layout is " + targetLayout);
59-
const targetLayoutName =
60+
61+
const targetLayoutID =
6062
targetLayout === Layout.LAT
61-
? preferences.latLayoutName
62-
: preferences.cyrLayoutName;
63-
if (!languages.includes(targetLayoutName)) {
63+
? preferences.latLayoutID
64+
: preferences.cyrLayoutID;
65+
66+
if (!languageIds.includes(targetLayoutID)) {
6467
await showHUD(
6568
"Layout " +
66-
targetLayoutName +
67-
" is not installed. Please install it or update the preferences",
69+
targetLayoutID +
70+
" is not installed. Please install it or update the preferences." +
71+
`Available layouts include: ${languageIds.join(", ")}`,
6872
);
6973
return;
7074
}
7175

72-
const currentLayoutName = await getActiveLayoutName();
73-
// console.log("current layout name is " + currentLayoutName);
74-
75-
if (currentLayoutName === targetLayoutName) {
76-
// console.log("already in target layout");
77-
return;
78-
}
79-
80-
// console.log("switching to " + targetLayoutName);
81-
let attempts = languages.length;
76+
// console.log("switching to " + targetLayoutID);
8277

83-
while (attempts > 0) {
84-
const modifierKey = preferences.layoutSwitchModifier;
85-
await exec(
86-
`osascript -e 'tell application "System Events" to keystroke " " using ` +
87-
modifierKey +
88-
` down'`,
89-
);
90-
const activeLayoutName = await getActiveLayoutName();
91-
// console.log("active layout after switch is " + activeLayoutName);
92-
if (activeLayoutName === targetLayoutName) {
93-
if (preferences.showSuccessHUD) await showHUD("Layout switched!");
94-
// console.log("layout switched");
95-
return;
96-
}
97-
if (currentLayoutName === activeLayoutName) {
98-
break;
99-
}
100-
attempts--;
101-
}
102-
103-
await showHUD("Failed to switch layout, please check the preferences");
104-
// console.log("failed to switch layout");
78+
await selectInputSource(targetLayoutID);
10579
}
10680

10781
function detectLayout(input: string): Layout {
@@ -120,23 +94,3 @@ function switchCharacterLayout(char: string): string {
12094
return ru_en.get(char) ?? char;
12195
}
12296
}
123-
124-
async function getInstalledLayoutNames(): Promise<string[]> {
125-
const result = await exec(
126-
`defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleEnabledInputSources`,
127-
);
128-
return result.stdout
129-
.split("\n")
130-
.filter((line) => line.includes("KeyboardLayout Name"))
131-
.map((line) => line.split("=")[1].trim().replace(/;/g, ""));
132-
}
133-
134-
async function getActiveLayoutName(): Promise<string> {
135-
const result = await exec(
136-
`defaults read ~/Library/Preferences/com.apple.HIToolbox.plist AppleSelectedInputSources`,
137-
);
138-
return result.stdout
139-
.split("\n")
140-
.filter((line) => line.includes("KeyboardLayout Name"))
141-
.map((line) => line.split("=")[1].trim().replace(/;/g, ""))[0];
142-
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.DS_Store
2+
.build/
3+
.swiftpm/
4+
.vscode/
5+
Package.resolved

0 commit comments

Comments
 (0)