This handbook briefs AI coding assistants on the vChewing (唯音) macOS repository. Use only English or zh-Hant-TW for docs/comments/reviews; zh-Hans is allowed only in filename stems ending with -CHS.
- Purpose: Native Zhuyin / Bopomofo input method for macOS with optional phonetic and stroke keyboards, simplified ↔ traditional isolation, and sandboxed distribution installers.
- Implementation: Pure Swift modules layered on AppKit/IMK. C(++)/ObjC(++) bridges exist only where Swift cannot interface directly with legacy assets.
- Primary packages:
vChewing_MainAssembly4Darwin: IMK front-end (SessionCtl, InputSession, UI bridges, sandbox glue).vChewing_Typewriter: Typing FSM, session core protocol (SessionCoreProtocol), Tekkon integration, user preference wiring, cassette/stroke handling.vChewing_Homa: DAG-DP assembler (sentence assembler) with candidate override, consolidation, revolver, and perception hooks.vChewing_Tekkon: Keyboard parsers, Zhuyin/Bopomofo composer, stroke cassette parser, phonabet utilities.vChewing_LangModelAssembly: LM instantiation facade, user phrase memory, perception override, associated phrases.- Shared dependencies (
vChewing_Shared,vChewing_SwiftExtension,vChewing_OSFrameworkImpl, etc.) supply utilities, result-builder UI DSL, notifications, and AppKit wrappers.
- Lexicon assets: Provided by remote Swift Package plugin
VanguardTextMapPlugin(fromvChewing-VanguardLexiconrepository). Compiled factory lexicons (.txtMap+.revlookuppairs) are injected intovChewing_MainAssembly4Darwinduring build-time. The runtime backend isVanguardTrie.TextMapTrie(sorted-array key index with binary search, on-demand VALUES parsing, bounded parsed-entry cache).
- Authoritative toolchain: macOS 14.7+ (Sonoma recommended), Xcode 15.3+ with bundled Swift 5.10 or newer.
- Runtime target: macOS 12 Monterey and newer. Older macOS support lives in another repo.
- Build system: Swift Package Manager (SwiftPM) 6.2.4+ via
Package.swiftroot manifest. App bundle assembly and universal binary scripting viaMakefilewithBundleAppsCommandPlugin. - CLI builds:
- Universal binary release:
make release(builds arm64 + x86_64, creates signed .app bundles inBuild/Products/Release/). - Archive with dSYMs:
make archive(creates.xcarchivein Xcode Archives folder). - Debug native build:
make debug(single-arch, target output in.build/debug/). - Package-only tests:
cd Packages/vChewing_Typewriter && swift build && swift test.
- Universal binary release:
- First-time setup:
make update(fetches/generates lexicons) thenmake release. Ensure Xcode DerivedData location is set to "Relative to Workspace" to satisfy make recipes.
Packages/vChewing_MainAssembly4Darwin/.../SessionController/SessionCtl.swift: IMK entry point. All NSEvent handling funnels throughInputSession*files.Packages/vChewing_Typewriter/Sources/Typewriter/InputHandler/: FSM split across triage, composition, candidate handling, and commissions.Packages/vChewing_Typewriter/Sources/Typewriter/Session/:SessionCoreProtocol— shared session base protocol withswitchState()/resetInputHandler()default implementations.Packages/vChewing_Homa/Sources/Homa/: Assembler core (Homa_Assembler.swift,Homa_PathFinder.swift, candidate/consolidation APIs, etc.).Packages/vChewing_Tekkon/Sources/Tekkon/: Keyboard parsers, composer, Zhuyin constants.Packages/vChewing_LangModelAssembly/Sources/LangModelAssembly/: LM instantiators, perception override, associated phrase derivation.Packages/vChewing_OSFrameworkImpl/: AppKit result-builder DSL for SettingsCocoa window, etc.Packages/vChewing_CandidateWindow/: The Candidate window.Plugins/BundleApps/: CommandPlugin that assembles.appbundles and optional.xcarchivearchives (codesigning, entitlements, SPM bundle filtering).Makefile: Root-level automation for universal binary builds (swift build --arch arm64/x86_64,lipomerge), lexicon toolchain integration, and CommandPlugin invocation.Installer/: SwiftUI installer app + pkg resources.
- Event capture: IMK
SessionCtlreceives NSEvents and marshals them intoKBEventstructures. - FSM triage:
InputHandlerin Typewriter interprets events, orchestrates Tekkon composer, updates the Homa assembler, and switchesIMEStateinstances. - Composer: Tekkon manages Zhuyin/phonetic/stroke buffers, auto-correction, cassette mode, and exposes inline display strings.
- Assembler: Homa Assembler builds DAG segments, snapshots perception intelligences, exposes candidate / consolidation / revolver APIs, and emits
assembledSentencefor UI rendering. - Language Models:
LMAssemblymerges factory lexicons (viaFactoryTextMapLexiconbacked by Vanguard TextMap format), user phrases, exclusion lists, associated phrase suggestions, and perception override data, etc. - UI update:
SessionCtlrefreshes candidate window, composition buffer, tooltips, notifications, symbol menu.
Reference algorithm.md for the deep algorithm write-up (zh-Hant).
- Language: Code comments, docs, and commit messages in English or zh-Hant. (zh-Hans only in files if filenamestem ends with
-CHS.) - UI: AppKit only. No Interface Builder nibs/storyboards. Keep UI work on the main actor. Most AppKit Window views are implemented using AppKit Result Builder DSL.
- Preferences: Extend
UserDef,PrefMgrProtocol, andPrefMgrtogether. Avoid nakedUserDefaults.standardaccess except in constrained scenarios. - User data paths: Avoid hard-coded user data paths except where necessary in package test targets.
- State machine: Prefer new
IMEStateenum cases and explicit transition APIs over boolean shortcuts.SessionCoreProtocol(Typewriter) providesswitchState()/resetInputHandler()default implementations shared by mock tests and production; extendInputHandlerProtocolfor per-event triage logic. - Conditional APIs: Guard platform-specific code (
#if canImport(Darwin)) as needed; keep Linux compatibility inTypewriterpackage and its local dependencies. - Bundle resources: SPM
#bundlemacro expands toBundle.modulefrom the auto-generated accessor. For packages with runtime resource lookup (e.g.,LangModelAssembly), use customBundle.currentSPMaccessor that checksresourceURLfirst, then falls back tobundleURL. This avoids codesign sandbox violations from files at.app/root. - ObjC(++)/C(+=) style: Follow Google Style Guide formatting for Objective-C(++) and C(++).
- Licensing: Preserve MIT-NTL banners. Respect LGPL for Homa, Megrez legacy sources (if exists), and Tekkon; avoid mixing incompatible license assets.
- Lexicon tooling: Factory lexicons are compiled by remote
VanguardTextMapPlugin(Swift Package plugin fromvChewing-VanguardLexiconrepository) and injected intovChewing_MainAssembly4Darwinat build-time via SPM build plugins. The runtime backend isVanguardTrie.TextMapTrie(sorted-array key index with binary search, on-demand VALUES parsing, bounded parsed-entry cache). Do not modify or commit generated lexicon assets; they are transient build artifacts.
- Unit tests live alongside each Swift package (
swift test). Focus on deterministic cases that mirror reported issues. - Typewriter and MainAssembly packages host end-to-end style tests; consider snapshotting
PrefMgrstate before/after. - When touching Tekkon or Homa, craft stress tests covering multi-syllable input, perception overrides, cursor edge cases.
- Use
swift test --filterto run targeted suites when debugging CI regressions.
- Commit format:
ModuleName // SubModuleName: Change.(Conventional Commit semantics kept terse.) Example:Typewriter // FSM: Fix cursor guard. - Reviews: Highlight functional impact, state machine ramifications, and test coverage. Mention regression risk if tests are missing.
- Dependencies: Prefer SwiftPM-targeted adjustments. When external patches are unavoidable, document rationale in code comments and PR description.
- Installer: Keep pkg scripts idempotent.
pkgPreInstall.sh/pkgPostInstall.shmust remain sandbox safe.
- Honor language restrictions in new text.
- Update
.stringswhen adding user-visible strings. - Gate new APIs through protocols as needed.
- Run relevant
swift testtargets. - Align new keyboard layouts with Tekkon parsers and symbol tables.
Questions from contributors should reference this file first; escalate only when guidance is missing or conflicting.