| Core model | Type pinyin, browse Chinese candidates, preview English only when it is ready. |
| Runtime lane | Developer/tester lane with BilineIMEDev.app, Settings, broker, and local host smoke. |
| Current boundary | Chinese candidate ranking, paging, raw cursor editing, and commit state remain source of truth. |
BilineIME has one primary interaction model:
- Type Chinese pinyin.
- Browse Chinese candidates.
- Optionally inspect English preview for visible Chinese candidates.
- Commit either the Chinese candidate or its ready English preview.
The boundary is strict:
- Chinese candidate generation, ranking, paging, and commit state remain the source of truth.
- English preview is an overlay, not a separate input mode.
- Turning bilingual capability off yields a plain Chinese-first pinyin workflow.
- Translation preview must never block Chinese typing, browsing, raw cursor editing, or commit.
| Workflow | Behavior |
|---|---|
| Candidate browsing | =/] expand and move down while preserving the intended column across short rows; -/[ move up and collapse at the top. |
| Layer switching | Shift+Tab switches between Chinese commit and ready English commit for the highlighted cell. |
| Uppercase Latin | Shift+letter inserts uppercase Latin directly when idle; while composing it stays in marked composition as an uppercase Latin segment, with marked preedit showing parser syllable or initial spaces around it. |
| Raw pinyin cursor | Option+←/→ moves by pinyin block; Command+←/→ jumps to start/end. |
| Composition deletion | Option+Backspace deletes one pinyin block; Command+Backspace deletes to the raw cursor start. |
| Candidate panel | The custom AppKit panel shows only candidates in candidate mode; raw-buffer-only composition keeps a compact raw buffer fallback. |
Plain ←/→ only browse candidates when the raw pinyin cursor is at the end of the composition. If the cursor is in the middle, those keys continue moving the raw pinyin cursor inside the marked text instead of touching the host document.
- Native macOS
InputMethodKitinput method app:BilineIMEDev.app. - SwiftUI Settings app with
Translation,Input Settings,Appearance, andStatus. - Rime-backed simplified and traditional schemas with user dictionaries.
- Rime language-model support through
librime-octagram, with grammar model assets bundled in the dev app. - Custom AppKit bilingual candidate panel with compact and expanded presentation.
- Inline marked-text preedit with raw pinyin cursor editing.
- Broker-mediated Settings/IME coordination through
BilineBrokerDev,BilineCommunicationHub, shared configuration storage, and shared credential storage. - Alibaba Cloud translation behind user-managed shared Keychain credentials.
- Unified dev lifecycle through
bilinectl, with Make targets as thin wrappers. - Local real-host smoke harness for
TextEdit:candidate-popup,browse,browse-boundary,commit,mixed-uppercase,settings-refresh, andfull. - Local performance harness covering package synthetic suites, TextEdit host hot paths, and first-launch cold path.
- Unsigned tester removal packages for safe uninstall and two-phase deep clean; install/update packaging is paused until it can reuse the full transaction.
| Core model | Established. Chinese-first bilingual preview is the fixed product boundary. |
| Dev lane | Usable. Install, remove, reset, diagnose, broker coordination, tester packaging, and host smoke are supported workflows. |
| Host smoke | The harness covers popup, browsing, boundary navigation, mixed uppercase, commit, and safe-boundary settings refresh. The current post-lifecycle-change real-host baseline is pending a fresh artifact-backed run. |
| Release lane | Paused. The reserved BilineIME target remains, but notarized release packaging is not currently supported. |
Full machine handoff lives in
docs/development-handoff.md. The shortest dev
path is:
make bootstrap
make project
make test
make build-ime
make install-imeUseful day-to-day commands:
make build-settings
make build-broker
make remove-ime
make reset-ime
make prepare-release-env
make diagnose-ime
make dev-pkg
make verifyNotes:
- Build products go to
~/Library/Caches/BilineIME/DerivedData. BilineIME.xcodeprojis generated fromproject.yml; regenerate it locally instead of committing it.- Do not launch the IME app directly with
open; letimklaunchagentown activation.
The repository treats host verification as three observable phases:
- Install bundles.
- Bring the input source to an enabled, selectable state.
- Run source-ready host smoke.
Use these entrypoints:
make install-ime
make smoke-ime-host-check
make smoke-ime-host-prepare
make smoke-ime-host SMOKE_SCENARIO=full
make perf-ime-smoke
make perf-ime-host PERF_SCENARIO=full
make perf-ime-host-first-launchWhat each step means:
make install-imeinstalls the dev IME, Settings app, broker, and local diagnostics state. Routine reinstalls preserve the stable bundle paths and input-source registration; they do not force-enable the source or perform a global LaunchServices reset.- Tester install/update pkg generation is paused. Fresh installs and updates
use the checkout's transaction-based
bilinectl installlane, which owns the app pair, broker, registration, verification, and rollback together. make smoke-ime-host-checkreports readiness asbundle-missing,source-missing,source-disabled,source-not-selectable,source-not-selected, orready.make smoke-ime-host-prepareonly opens System Settings and prints remediation. During a local agent session, Computer Use may complete the ordinary System Settings steps and then re-run the readiness check.make smoke-ime-hostis the supported automated real-host behavior smoke entrypoint. It is local-only, never a CI gate, and drives exactly one existing untitled, unmodified, blankTextEditdocument. Host smoke and host performance targets run throughscripts/with-test-awake.sh, so macOS keeps the display and user session awake only for the command's lifetime.make perf-ime-hostuses the same TextEdit safety rules for host performance baselines. Performance artifacts omit typed text by default; opt in withPERF_INCLUDE_TEXT=1only when the content is safe to retain.make perf-ime-host-first-launchmeasures cold input-method launch from source selection and first probe key through app launch, input-controller initialization, first composition, panel render, and first candidate commit.first-launchis not a clean pkg first-use measurement. Clean first-use must be recorded separately on a clean user or VM because source enrollment and macOS trust prompts are outside the IME process.
Readiness only proves that macOS registers the source as enabled/selectable.
On macOS 26, TISSelectInputSource may report the new source globally while an
already-focused host still uses its previous input context. The harness
therefore requires a fresh TextEdit-owned composition snapshot from one IME
process and performs a Finder → TextEdit focus round trip before retrying. A
one-time logout may still be necessary when a newly installed source does not
appear in System Settings at all; it is not part of routine reinstalls.
For any other interactive test, use the same bounded wrapper directly:
./scripts/with-test-awake.sh <command> [arguments...]The wrapper releases its power assertions automatically when the command exits. It keeps display/idle-sleep assertions bound to that command and renews macOS's short-lived user-active assertion while the command is alive. It does not bypass an explicit lock, lid close, or authentication prompt.
For local development, the default is automated real-host verification:
bilinectl owns repeatable key sequences, assertions, telemetry, and state
restoration; Computer Use prepares or inspects UI-only state. Manual typing is
an additional exploratory check, not the default completion gate.
The IME and Settings app coordinate through the broker:
BilineBrokerDevis the user-scoped coordination process.BilineCommunicationHubis the shared client facade used by both the IME and the Settings app.- Shared configuration is persisted through the broker-backed configuration store.
- Alibaba credentials are persisted through a shared Keychain-backed vault, with legacy file fallback retained only for migration/recovery.
- Engine-sensitive settings apply only at safe lifecycle boundaries, not in the middle of live composition.
For prerelease tester distribution:
make dev-pkgEach run produces two new unsigned, no-payload removal packages in
build/dist:
BilineIMEDev-Uninstall-<version>.pkgBilineIMEDev-DeepClean-<version>.pkg
Install/update pkg generation is intentionally paused. The current package
environment has no package-aware bilinectl transaction or app/broker protocol
compatibility handshake, so an app-only update could pair new apps with an
incompatible old broker, while a package-local broker restart would duplicate
the lifecycle and weaken rollback. Fresh installs and all updates therefore use
the checkout's transaction-based bilinectl install dev --scope user|system
lane. The build removes stale install/update pkg artifacts from build/dist so
they cannot be mistaken for current output. Older safe-uninstall and deep-clean
versions may remain for rollback/recovery; the command lists the two files
created by the current run.
DeepClean is intentionally destructive and requires a logged-in non-root
console user. It first runs the user-scope cleanup as that user so Keychain,
defaults, and user data never resolve to /var/root; only after that succeeds
does it run the root-owned system cleanup. Either phase failing fails the
package with its diagnostic output intact.
The removal packages are unsigned; see
docs/development-handoff.md for exact lifecycle
guidance.
| development handoff | New machine setup, lifecycle, smoke, credentials, failure recovery. |
| architecture | Product model, module boundaries, broker/storage, verification model. |
| engineering standards | Coding, testing, and host-smoke engineering rules. |
| acceptance checklist | Delivery, behavior, install, and verification checklist. |
| IME risks | Known risks, gaps, and next high-value host smoke coverage. |
| ADRs | Architectural decisions and historical context. |
Now
- Keep simplified and traditional Rime schemas stable.
- Keep Chinese candidate quality and consumed-span behavior correct.
- Keep raw pinyin cursor editing reliable in marked text.
- Keep
Shift+ASCII letteraligned with Apple Chinese input behavior: direct uppercase Latin insertion while idle, marked-composition uppercase Latin segments while composing, and continued pinyin composition after those segments. Marked preedit may show parser syllable or abbreviated-initial spaces, but raw input and committed candidate text stay unspaced. - Keep broker-backed Settings/IME coordination boring and predictable.
- Keep the unsigned tester removal lane fail-closed and restore install/update
packaging only after it can reuse a package-aware
bilinectltransaction.
Next
- Expand host smoke beyond the current baseline into punctuation, raw-buffer
behavior, editing keys,
Shift+Tabpersistence, phrase/tail commits, and mixed Chinese/Latin stress cases, including uppercase Latin segments followed by more pinyin inside composition and marked preedit syllable/initial spacing. - Turn current engine-side future toggles into real behavior where appropriate, especially smart spelling and emoji candidates.
- Tighten docs and diagnostics around source enrollment edge cases after transactional install, reset, and two-phase deep clean.
Later
- Restore a supported release packaging lane for the reserved
BilineIMEtarget. - Add broader host coverage beyond
TextEditonce the baseline lane stays stable. - Revisit richer release/distribution UX after the dev/tester lane no longer needs frequent recovery guidance.
BilineIME is GPL-3.0 licensed. Runtime dependencies, bundled data, and major architecture decisions should stay documented and attributable. Generated build artifacts and local project files remain untracked.
