Skip to content

Development

weijianz edited this page Oct 12, 2026 · 4 revisions

中文

Code structure

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, 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
    AppVersion.swift        version number
    Engine.swift, KeyMapping.swift, Language.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
    InputController.swift   performs the effects Composer returns: text field, candidate panel, Bedrock, Terminal, clipboard, voice
    CandidatePanel.swift    candidate panel
    SettingsWindow.swift    settings window (SwiftUI)
    UIText.swift            interface text (Chinese / English)
    VoiceInput.swift        microphone and SpeechAnalyzer
    SecureInput.swift       secure input detection
    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 two Info.plist files, localized strings, icons, entitlements, rime/default.custom.yaml
Scripts/                  screenshots, icons, real-app test, fixture generation
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.

make targets

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 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 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).

Tests

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, AppVersionTests checks that the versions in the two Info.plist files match AppVersion.string.
  • AllInOneIMERimeTests: uses the real librime and ThirdParty/rime-data (make test runs make deps first).

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, the microphone or the sentence mode setting, 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, and restores the sentence mode switch too. 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 latency

Other options: --profile, --region, --model, --jargon FILE, --raw (prints the model's raw output), --dump FILE (saves the raw event-stream response, for test data), --version.

Screenshots

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.

Conventions

  • 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) or Prompt.commandVersion (@question) as the comments in Prompt.swift say; they are part of the cache key.

Release checklist

  1. Change the version in all four places together: CFBundleShortVersionString (and CFBundleVersion) in Resources/Info.plist, Resources/Settings-Info.plist and Resources/Installer-Info.plist, and AppVersion.string in Sources/AllInOneIMECore/AppVersion.swift. AppVersionTests checks that they match.
  2. Update CHANGELOG.md.
  3. Update README.md and CHANGELOG.md (English) together with the Chinese README.zh-CN.md and CHANGELOG.zh-CN.md.
  4. Update the English wiki pages along with the Chinese ones.
  5. make screenshots (and make panel-images if the panel changed), then check the images in docs/ and docs/en/: the English ones should have no Chinese in them.
  6. Delete build/AllInOneIME.app: rm -rf build/AllInOneIME.app.
  7. make dmg, then a release with the changelog in Chinese and English and, for each change that shows, its screenshot (docs/ in the Chinese half, docs/en/ in the English half), linked at the commit so they never change.

Clone this wiki locally