Repository navigation
Development
A Swift package (Package.swift, swift-tools-version 6.0, macOS 14 or later). The tree below shows the main files; there are more (providers, plugins, the menu bar panel, the installer).
Sources/
AllInOneIMECore/ pure logic, no AppKit
Composer.swift two-level input state machine: pinyin, Hangul and Hindi, draft, @ commands and the command list, ⌃V and ⌘V, voice gestures, action key
Command.swift @ command definitions and parsing
ActionKey.swift action key
Config.swift config.json
Prompt.swift prompts
Converter.swift one request: reads config and credentials, calls Bedrock, parses the stream, caches
CandidateParser.swift parses the lines of the model's output
BedrockClient.swift ConverseStream client
EventStream.swift AWS event-stream decoding
SigV4.swift request signing
AWSSharedConfig.swift reads ~/.aws/credentials and ~/.aws/config
RewriteStyle.swift rewrite styles and their examples
JargonLibrary.swift the jargon list
InputSourceList.swift adds to / removes from the user's input source list (AppleEnabledInputSources)
LegacyData.swift migrates data from AIPinyin's folders
LanguagePack.swift language packs: the catalog, the installed packs, checks; LanguagePackLibrary.swift: the signed index
LatinEngine.swift English input, with dead keys composed from the keyboard layout
FuzzyPinyin.swift fuzzy pinyin pairs, written as rime_ice.custom.yaml
AppVersion.swift version number
Hangul.swift Korean: the 2-set key table (from the ko-hangul pack) and composing syllables
Hindi.swift Hindi: Latin letters → Devanagari (from the hi-phonetic pack), its engine
Language.swift input languages, and which one a sentence is written in
ScriptCommand.swift script extensions: the protocol's keys, Raycast's mapped onto them, arguments, environment
ScriptOutput.swift what a script printed: text, summary/detail, Alfred Script Filter lists and their actions
ExtensionStore.swift extension folders, bundles (extension.json) and preferences, the file cache, Spotlight discovery
ScriptScheduling.swift status lines taking turns, the room in the background, the command list built only when it changes
Engine.swift, KeyMapping.swift
AllInOneIMERime/ Swift wrapper for librime (Rime.swift)
CRime/ module map for the librime C API
AllInOneIME/ the input method process (InputMethodKit)
main.swift entry point, plus --register / --disable / --status / --settings / --selftest / --install-pack
InputController.swift performs the effects Composer returns: text field, candidate panel, Bedrock, Terminal, clipboard, voice
CandidatePanel.swift candidate panel
SettingsWindow.swift settings window (SwiftUI)
LanguagePacks.swift downloading and installing language packs
UIText.swift interface text (Chinese / English)
VoiceInput.swift microphone and SpeechAnalyzer
SecureInput.swift secure input detection
LiveExtensions.swift script extensions: the index rebuilt in the background, status lines
ScriptTasks.swift script extensions' runs, background runs, notifications
FileSearch.swift search for @open
TerminalLauncher.swift @claude
InputSourceRegistrar.swift registers the input source, edits the input source list
SelfTest.swift make selftest
AllInOneIMESettings/ "AllInOneIME Settings" launcher: opens the input method's settings window with --settings
allinoneime-cli/ dev tool that runs @improve in Terminal
Tests/
AllInOneIMECoreTests/ unit tests for Core; Fixtures/ holds event-stream test data
AllInOneIMERimeTests/ tests with the real librime and ThirdParty/rime-data
Resources/ the three Info.plist files (input method, settings, installer), localized strings, icons, entitlements, rime/default.custom.yaml
Scripts/ screenshots, icons, the disk image, language packs and their index, 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
|
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 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) |
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) |
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), 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, two sets of tests.
-
AllInOneIMECoreTests: pure logic, with a fake pinyin engine and a stubbed network (URLProtocol); no network access, no AWS needed. Among them,AppVersionTestschecks that the versions in the three Info.plist files matchAppVersion.string. -
AllInOneIMERimeTests: 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; the voice part tests on-device recognition with speech synthesized by say (skipped without SpeechAnalyzer). 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 or SELFTEST FAILED (n), and writes the screenshots to OUT.
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. 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
|
— |
voice.png |
8-voice |
8-voice-english-en |
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 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.stringinSources/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; the Chinese ones are translated from them by the
wiki-zhworkflow (Scripts/translate-wiki.py). -
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, then a release (marked Latest) with 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.