Repository navigation
Development
The root is the macOS app; Core/ is the core every platform shares, a Swift package of its own (it builds and tests
on its own, on macOS, Linux and Windows); Windows/ is the Windows package, the start of the Windows app.
Core/ the core every platform shares, a Swift package of its own
Sources/
AllInOneIMECore/ Bedrock client, SigV4, prompts, the two-level input state machine (including commands
and the voice gesture, off for now: Features.voiceInput); no AppKit dependency
AllInOneIMEText/ the interface text and its language (UIString, InterfaceText, InterfaceLanguage, and
StringCatalog, which reads the catalog where there are no .lproj bundles); all the
settings launcher links (AllInOneIMECore re-exports it)
AllInOneIMEQuickJS/ QuickJS-ng (its C sources in CQuickJS/), the JavaScript of plugins and @js where there
is no JavaScriptCore (Windows, Linux, Android)
AllInOneIMERime/ librime wrapper
CRime/ module map for the librime C API
AllInOneIMECLI/ the command line, allinoneime-cli, run by each platform's own main
Tests/ the core's unit tests, AllInOneIMECoreTests (see Tests below)
Scripts/ the core package's own scripts
Resources/ Localizable.xcstrings: the core's interface text
LanguagePacks/ the Korean and Hindi language packs (ko-hangul, hi-phonetic), for make packs
Examples/extensions/ an example of every script extension feature
Plugins/ @x, @threads, @bsky, @weibo and the script plugin @xe, which the plugin library publishes
Sources/ the macOS app
AllInOneIME/ the InputMethodKit input method, candidate panel, settings window, speech recognition
(voice input, off for now)
AllInOneIMEMac/ what only the Mac has (key layouts, code signing, Spotlight, AppleScript, the updater's
install), and Platform.mac
AllInOneIMESettings/ the "AllInOneIME Settings" launcher: opens the input method's settings window with --settings
AllInOneIMEInstaller/ "Install AllInOneIME" on the disk image: installs, updates and uninstalls
allinoneime-cli/ the Mac's main for AllInOneIMECLI (make cli builds .build/release/allinoneime-cli)
Tests/ the macOS app's tests, AllInOneIMEMacTests, and the Rime tests, AllInOneIMERimeTests (see
Tests below)
Windows/ the Windows package, the start of the Windows app
Resources/ the three Info.plist files (input method, settings, installer), Localizable.xcstrings
(the app's own interface text), icons (AppIcon.png), entitlements, Signing/ (the
Developer ID provisioning profile), rime/default.custom.yaml
Scripts/ screenshots, icons, the disk image, language packs and their index, importing
translations, wiki translation, real-app test, fixtures
ThirdParty/ librime and rime-ice, downloaded by make deps (not in the repo)
For the input method's own development options, see ~/Library/Input Methods/AllInOneIME.app/Contents/MacOS/AllInOneIME --help.
| Target | What it does |
|---|---|
make / make app
|
Builds and packages build/AllInOneIME.app. With PROFILE=<profile> SIGN_IDENTITY=<a certificate it lists>, a build with the keychain entitlements: it can sync keys through iCloud Keychain. Without the provisioning profile (a development build), keys stay in the login keychain and can't sync |
make deps |
Downloads and verifies librime 1.16.1 and rime-ice 2026.06.30, precompiles the dictionaries (into ThirdParty/) |
make build |
Builds the input method only (swift build) |
make settings-app |
Packages build/AllInOneIME Settings.app
|
make installer-app |
Packages build/Install AllInOneIME.app, the installer on the disk image |
make install |
Packages both apps, installs them in ~/Library/Input Methods and ~/Applications, registers the input method and adds it to the input source list |
make uninstall |
Disables it, removes it from the input source list, deletes both apps |
make pack |
The Chinese language pack: build/packs/zh-pinyin-<version>.tar.xz, its SHA-256 and its packs.json entry |
make packs |
Every language pack: Chinese, and Korean and Hindi from Core/LanguagePacks/ (ko-hangul, hi-phonetic) |
make install-pack |
Installs the Chinese pack from this checkout's dictionaries into the installed input method, without a download |
make status |
Status of the installed input method; see Installation and Troubleshooting |
make test |
Unit tests and Rime tests (swift test), and make strings-check
|
make strings |
Adds new interface text to the String Catalog (Resources/Localizable.xcstrings), marks what's unused stale; see Interface text
|
make selftest |
Drives the input method with a simulated text field, with real Rime and real Bedrock |
make screenshots |
Runs make selftest, then generates the screenshots in docs/ and docs/en/
|
make panel-images |
Captures the menu bar panel on screen (frosted glass, a row swiped open): docs/panel-*.png, docs/en/panel-*.png
|
make dmg |
The release installer, build/AllInOneIME-<version>.dmg (universal) and its .sha256. It needs the Developer ID Application certificate it's signed with, in the keychain, and DIST_PROFILE, the provisioning profile that grants the input method its keychain entitlements (see Release checklist); with NOTARY_PROFILE (a notarytool keychain profile) also notarized and stapled. Then make update-zip
|
make update-zip |
The in-app update, build/AllInOneIME-<version>-update.zip: the same two apps as the disk image's installer |
make update-manifest |
site/public/update.json and update.json.sig, signed with the releases' key from the login keychain; UPDATE_SUMMARY_EN="…" UPDATE_SUMMARY_ZH="…" give its summary |
make update-test |
The in-app updater end to end: a temporary install, a test server on 127.0.0.1, a test key (never the installed app) |
make realtest |
Typing test through the installed input method, in a real text field of another app |
make realtest-build / make realtest-run
|
Only builds / only runs the real-app test program |
make realtest-when-unlocked |
Runs realtest-run once, automatically, after the screen is unlocked (a temporary launchd job) |
make realtest-cancel |
Cancels that job |
make cli |
Builds allinoneime-cli
|
make icon |
Regenerates Resources/AppIcon.icns and the menu bar icon Resources/icon.tiff from Resources/AppIcon.png
|
make clean |
Deletes .build and build
|
make distclean |
Also deletes ThirdParty
|
Variables: CONFIG (default release), SIGN_IDENTITY (default Apple Development; ad-hoc signing if it isn't found), DIST_IDENTITY (make dmg's, default Developer ID Application), PROFILE (make app's provisioning profile, for the keychain entitlements), DIST_PROFILE (make dmg's provisioning profile, Resources/Signing/AllInOneIME-DeveloperID.provisionprofile; empty stops the build), NOTARY_PROFILE (empty: not notarized), OUT (the selftest's output folder, default /tmp/allinoneime-selftest), REALTEST_OUT (default ~/Library/Logs/AllInOneIME/realtest).
make, make app, make selftest and make screenshots all leave build/AllInOneIME.app behind. It has the same bundle ID as the installed copy, so macOS may launch it instead of the installed one; delete it when you're done (make install moves it away).
make test: swift test in Core/, then in the root, three sets of tests.
-
AllInOneIMECoreTests(Core/Tests/): pure logic, with a fake pinyin engine and a stubbed network (URLProtocol); no network access, no AWS needed. -
AllInOneIMEMacTests(Tests/): the macOS app's own code. Among them,AppVersionTestschecks that the versions in the three Info.plist files matchAppVersion.string. -
AllInOneIMERimeTests(Tests/): uses the real librime andThirdParty/rime-data(make testrunsmake depsfirst).
make selftest: packages build/AllInOneIME.app, then runs AllInOneIME --selftest, which drives the real input controller with a simulated text field: real Rime, real Bedrock requests (with your config.json and AWS configuration; this costs a little), Spotlight search. It doesn't touch the clipboard or the microphone, and doesn't actually open files or Terminal; the settings window is tested on a temporary copy of the config, leaving your config.json unchanged. At the end it prints SELFTEST PASSED, SELFTEST FAILED (n), SELFTEST INCOMPLETE or SELFTEST SKIPPED (see below), and writes the screenshots to OUT.
make selftest and make realtest don't depend on your settings. The self-test uses a config of its own (Amazon
Bedrock with the AWS profile and region from yours, everything else the defaults); the real-app test sets the provider
to Bedrock while it runs and puts yours back. What they can't check at the moment is skipped, not failed: live Bedrock
checks without credentials for that profile, anything typed while secure input is on (a password field), Apple
Intelligence's checks where the on-device model isn't available, and the Electron harness's without ELECTRON_HARNESS;
a locked screen ends the self-test at once. The summary counts passed, failed and skipped checks, with why each was
skipped, and the exit status says how the run went: 0 (PASSED) when every check ran and passed, 1 (FAILED) when one
failed, 2 when none failed but some were skipped or couldn't run (INCOMPLETE, or SKIPPED when nothing that types
ran; BLOCKED: the real-app test started on a locked screen). 2 isn't a pass: make selftest and make realtest fail
on it too. The self-test's --…-only subsets exit the same way, and its last line says the same for scripts:
SELFTEST-RESULT {"passed":N,"failed":N,"skipped":N,"status":"pass|fail|incomplete"} (0.6.8).
make realtest: tests the installed input method, so make install first. It starts a separate app with a real NSTextView and types through the system's text input path; it switches input sources for the test and back afterwards. The screen must be unlocked; the test window holds the foreground for about a minute, so don't type meanwhile. Results are in ~/Library/Logs/AllInOneIME/realtest/output.txt, with REALTEST PASSED when it passes (INCOMPLETE, SKIPPED or BLOCKED when checks were skipped, as above). When the screen is locked, use make realtest-when-unlocked; its log is ~/Library/Logs/AllInOneIME/realtest-watch.log.
allinoneime-cli: runs @improve in Terminal with the same Core code as the input method, reading the same config.json; options override it:
make cli
.build/release/allinoneime-cli --mode improve --styles concise,jargon "this is a blocker bug your team need fix it asap"
.build/release/allinoneime-cli --output es,fr,de "running 10 minutes late, start without me"
.build/release/allinoneime-cli --bench # built-in sentences, reports latencyOther options: --provider (bedrock, anthropic, gemini or openai), --profile, --region, --model, --jargon FILE, --raw (prints the model's raw output), --dump FILE (saves the raw event-stream response, for test data), --version.
make screenshots is make selftest followed by Scripts/make-readme-images.swift, once for docs/ (Chinese) and
once for docs/en/ (English). The self-test renders the candidate panel and the settings window; the script puts each
panel under a text line with the inline text. The English images come from their own scenes, not the Chinese ones
with an English interface: English typed, @translate into Spanish, French and German, @question asked in English.
| Image |
docs/ (Chinese) |
docs/en/ (English) |
|---|---|---|
demo.png |
2-translate-pinyin + 3-translate
|
en-translate-draft + en-translate
|
improve.png |
1b-sentence-pinyin + 4-final-light
|
en-improve-draft + en-improve
|
english.png |
7-english-light + 7b-chinese-output
|
— |
commands.png |
10-palette + 11-question
|
en-palette + en-question
|
open.png |
12-open + 12b-open-path
|
the same, -en
|
panel-dark.png |
4-final-dark |
en-improve-dark |
settings.png |
6-settings |
6-settings-en (English input, translating into Spanish, French, German) |
dashboard.png |
14-dashboard |
14-dashboard-en |
The menu bar panel's frosted glass can't be rendered offscreen (nothing is behind it), so make panel-images shows
the real panel with sample data over a colorful window and captures it with screencapture (AllInOneIME --showcase).
The content comes from real AI answers and differs each time, so look it over before committing. Delete
build/AllInOneIME.app afterwards.
The interface comes in nine languages (from 0.6.5). Interface text is written in the code as
tr(<Simplified Chinese>, <English>): Simplified Chinese and English come from the code, the other languages from the
String Catalogs, Core/Resources/Localizable.xcstrings for the core's text and Resources/Localizable.xcstrings for
the app's own (merged and compiled into each app by the Makefile with Xcode's xcstringstool), keyed by the English
text with %@ for each interpolation.
- After adding or changing text, run
make strings(make testfails until then; the self-tests' text, in*SelfTest.swift, isn't for translators). - Where one English text means two things (
make stringsreports them, andmake strings-checkfails on them), give one a key of its own, a short dotted name for where or how it is used, such askey: "remove.delete"for a "Remove" that deletes; its English is the key's English value in the catalog. - Adding a language is adding its column to the catalog: reviewed translations, one JSON file per language
(
"core\t<key>"or"app\t<key>"→ its translation), go in withScripts/import-translations.py <folder>, which never touches English and refuses a translation whose placeholders aren't its key's. - The core's own texts take the interface language's code and look it up the same way (
InterfaceText.lookup, which each app sets: its.lprojbundles on macOS; elsewhereStringCatalogreads the catalog's tables).
- The bundle ID and input source ID stay
com.aipinyin.inputmethod.AIPinyin: macOS uses it to remember the added input method, microphone access and preferences, so changing it would make users add and authorize it again. It is also the log subsystem. - When you change the wording of a prompt, increment
Prompt.version(@improve) orPrompt.commandVersion(@question) as the comments inPrompt.swiftsay; they are part of the cache key.
- Change the version in all four places together:
CFBundleShortVersionString(andCFBundleVersion) inResources/Info.plist,Resources/Settings-Info.plistandResources/Installer-Info.plist, andAppVersion.stringinCore/Sources/AllInOneIMECore/AppVersion.swift.AppVersionTestschecks that they match. - Update
README.mdandCHANGELOG.md(English) together with the ChineseREADME.zh-CN.mdandCHANGELOG.zh-CN.md. - Update the English wiki pages, then the Chinese ones from them: by hand, or by the
wiki-zhworkflow (Scripts/translate-wiki.py). Each Chinese page ends with a comment naming the SHA-256 of the English page it was translated from; the workflow skips a page whose comment names the current one, so after translating by hand, put the new SHA-256 there. -
make screenshots(andmake panel-imagesif the panel changed), then check the images indocs/anddocs/en/: the English ones should have no Chinese in them. - Delete
build/AllInOneIME.app:rm -rf build/AllInOneIME.app. -
make dmg NOTARY_PROFILE=<profile>: the disk image notarized and stapled (from 0.6.3 on, users no longer allow it under Privacy & Security → Open Anyway), and the update archive. The input method carries the Developer ID provisioning profile (Resources/Signing/AllInOneIME-DeveloperID.provisionprofile,DIST_PROFILE), which grants its keychain entitlements. The build stops if the profile doesn't list the signing certificate, provision all devices, or have 30 days left (Scripts/profile-entitlements.sh), or if the signature lacks the keychain entitlements, and it runs the signed input method once (macOS kills one whose profile doesn't fit). An emptyDIST_PROFILEstops it too: a release without the entitlements would lose sight of every key that moved into iCloud Keychain. The profile lists the two Developer ID G1 certificates only: before signing with another (G2), make a new profile in the developer portal (by hand: the App ID's signing isn't left to Xcode) that lists it, and commit it. - A release
v<version>(marked Latest) with both files, the changelog in English first, then Chinese, and for each change that shows, its screenshot (docs/en/in the English half,docs/in the Chinese half), linked at the commit so they never change. - Once the release's files download:
make update-manifest UPDATE_SUMMARY_EN="…" UPDATE_SUMMARY_ZH="…", commitsite/public/update.jsonand its.sig, and deploy the site; the apps check once a day (AllInOneIME --check-updateshows what they see).