A macOS input method that replicates the system look and feel, extensible via external JavaScript engines.
- Native-looking candidate window — horizontal / vertical / expandable layouts, with accent color matching the foreground app
- Write engines in JavaScript — hot-reloaded from a picked folder, with a native Settings UI auto-generated from
manifest.json
- macOS 14.0 or later
- Xcode 26.3 or later
- Swift 5.0
- macOS 14.0+ SDK
Requires Xcode (used by make install to build the Release binary).
make prepare- Downloads external data files and licenses pinned in
Scripts/HandleExternalResources.lock. Required after a fresh clone (and again any time the lock is bumped). Subsequent builds verify these files exist; if missing, the build halts with a reminder to re-run this.
- Downloads external data files and licenses pinned in
make install- Builds Release, copies the app to
~/Library/Input Methods/, and kills any running input method process.
- Builds Release, copies the app to
- Log out and back in.
- On first install, MacishType won't show up in the input source picker until you log back in.
- Open System Settings → Keyboard → Input Sources → + and pick the input sources you want from the MacishType section.
git pull(or however you pull latest).make prepare- Re-downloads external resources to whatever the lock currently pins. Safe to run after every pull; if the lock didn't change, the result is the same content.
make install- The Makefile removes the old version, kills the running process, and installs the new one.
- Log out and back in (only if needed).
- Updates usually take effect immediately. But if a change doesn't seem to apply — or a newly added input source doesn't show up in the picker — logging out and back in is worth trying.
make uninstall- Removes the app, Application Scripts, and the user container's
Data/directory.
- Removes the app, Application Scripts, and the user container's
- Log out and back in.
- Required for MacishType to fully unregister from System Settings.
Same as the Installation flow above, but use make debug instead of make install to deploy a Debug build.
After a code change:
make debug— rebuild and reinstall in one step.make log— stream live OSLog output from the input method.make log-js— stream JS-originated logs (engineconsole.*, uncaught exceptions, rejections).make log-history— show recent log history (default 1h; override withLOG_SHOW_LAST=24h).make preview— build and run the CandidateWindow preview app, for iterating on the candidate window UI without running the full IM.
make build— build Debug without installing.make release/make release-universal— build Release (current architecture or universal binary).make prepare— re-download external resources to current lock state (run after a lock bump).make update-resources— bump pinned upstream SHAs to their current default-branch HEAD and re-prepare.make clean— clean build artifacts.make clean-resources— remove downloaded external resources.make reload— restart the input method without rebuilding (e.g. after editing installed bundle resources in place).
A MacishType JavaScript engine is a folder with manifest.json and an entry module (e.g. index.js). Load it from MacishType's own Settings → JS → Engine folder → Choose Folder…; the host watches the folder with FSEvents and reloads on edit, so iterating doesn't require restarting the input method.
Start here:
Engines/README.md— full engine-writing guide (manifest, lifecycle, event mutators, Settings schema, runtime globals)Engines/ExampleEngine/— reference engine to copy and editEngines/Utils/MacishType.d.ts— TypeScript definitions for IDE autocomplete, type checking, and bundler integration
Subclass InputEngine, override engineID and handleKey, and register the subclass in the static InputEngine.engines dictionary keyed by its engineID. Then declare the matching input source in MacishType/Info.plist under ComponentInputModeDict (replace YourEngineID below):
<!-- under tsVisibleInputModeOrderedArrayKey -->
<string>net.lukechang.inputmethod.MacishType.YourEngineID</string>
<!-- under tsInputModeListKey -->
<key>net.lukechang.inputmethod.MacishType.YourEngineID</key>
<dict>
<key>TISInputSourceID</key>
<string>net.lukechang.inputmethod.MacishType.YourEngineID</string>
<key>tsInputModeIsVisibleKey</key>
<true/>
<key>tsInputModeMenuIconFileKey</key>
<string>YourEngineID/MenuIcon.tiff</string>
<key>TISIconLabels</key>
<dict>
<key>Primary</key>
<string>YourLabel</string>
</dict>
<!-- ... see the Example entry for additional keys -->
</dict>Engine resources (icons, dictionaries, etc.) live in MacishType/<engineID>Engine/Resources/ and are staged into <bundle>/Resources/<engineID>/ at build time by the Stage Engine Resources run-script phase (the Engine suffix is stripped — e.g. ExampleEngine/Resources/ExampleMenuIcon.tiff → Example/ExampleMenuIcon.tiff). Because each engine's resources land in their own subfolder, file names don't need to be unique across engines.
Important
The source Resources/ folder must be excluded from the default Copy Resources phase, otherwise resources end up in the bundle root and collide across engines. In Xcode, select the Resources/ folder → File Inspector → Build Rules → switch Apply to Each File to Apply Once to Folder.
For menu and palette icons, Scripts/GenerateIcon.swift produces a system-style rounded-square TIFF with a character cut out — e.g. ./Scripts/GenerateIcon.swift 例 ExampleEngine/Resources/ExampleMenuIcon.
Reference: MacishType/ExampleEngine/ — the bundled Swift engine, including associated-phrase mode, a Settings form, and the resources layout.
The bundled MacishType/MacishCandidateWindow/ is one implementation of CandidateWindowImpl, the abstract base that defines the override points (apply, updateCandidates, show, hide, handleNavigation, commitSelectedCandidate, etc.). To build a different candidate window, create a parallel folder under MacishType/ with your own CandidateWindowImpl subclass and any supporting types, then add a Style case in CandidateWindow.swift and a matching arm in the activeImpl switch that constructs your subclass.
Shared types (Candidate, CandidateWindowConfiguration, CandidateWindowDelegate) live in CandidateWindow.swift.
Associated-phrase data, downloaded by make prepare from McBopomofo, is used under its MIT License (bundled with the data).
See LICENSE.