Skip to content

luke-chang/MacishType

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

159 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MacishType

A macOS input method that replicates the system look and feel, extensible via external JavaScript engines.

Highlights

  • 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

Environment

Runtime

  • macOS 14.0 or later

Development

  • Xcode 26.3 or later
  • Swift 5.0
  • macOS 14.0+ SDK

Installation

Requires Xcode (used by make install to build the Release binary).

First install

  1. 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.
  2. make install
    • Builds Release, copies the app to ~/Library/Input Methods/, and kills any running input method process.
  3. Log out and back in.
    • On first install, MacishType won't show up in the input source picker until you log back in.
  4. Open System Settings → Keyboard → Input Sources → + and pick the input sources you want from the MacishType section.

Upgrade

  1. git pull (or however you pull latest).
  2. 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.
  3. make install
    • The Makefile removes the old version, kills the running process, and installs the new one.
  4. 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.

Uninstall

  1. make uninstall
    • Removes the app, Application Scripts, and the user container's Data/ directory.
  2. Log out and back in.
    • Required for MacishType to fully unregister from System Settings.

Development

First-time setup

Same as the Installation flow above, but use make debug instead of make install to deploy a Debug build.

Iteration loop

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 (engine console.*, uncaught exceptions, rejections).
  • make log-history — show recent log history (default 1h; override with LOG_SHOW_LAST=24h).
  • make preview — build and run the CandidateWindow preview app, for iterating on the candidate window UI without running the full IM.

Other targets

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

Writing an engine

External JavaScript engine

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:

Bundled Swift engine

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.tiffExample/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.

Writing your own candidate window

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.

Acknowledgements

Associated-phrase data, downloaded by make prepare from McBopomofo, is used under its MIT License (bundled with the data).

License

See LICENSE.

About

A macOS input method that replicates the system look and feel, extensible via external JavaScript engines.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors