Automations
Use presets or your own rules to change the keyboard as you type, without replacing your saved setup.
State rules apply temporary settings while their conditions match. Combine orientation, window width, appearance, language, layout, input traits, time or declared plugin state. Choose whether all conditions or any condition must match. Missing context never counts as a match for a negative condition.
Event rules run when the keyboard opens, a language or input trait changes, a plugin emits an event, or you tap a shortcut. Commands run once per event. Setting overrides last until the keyboard closes, the rule changes, or you clear session overrides.
Your saved setup comes first
Rules overlay your current saved settings. Drag them to change priority; later rules win. Turning a rule off removes its contribution. Changes you make to the saved setup remain available when an override ends. A profile action is temporary and does not select or rewrite your saved profile.
The compact preset uses 28-point key rows and 80% of your saved width, within the existing size limits. With split enabled, smaller keys leave more space between the halves and keep them near the edges. One-handed mode keeps its own alignment. Percentages always use the saved value and never compound.
Preview uses sample context that you choose. It shows matching conditions, invalid references and expected settings without running commands. The app preview is separate from the live keyboard. Add Automations to the top bar to see active overrides and run shortcut rules there.
When an event occurs
When an event occurs
keyboard.openkeyboard.closekeyboard.languageChangedkeyboard.layoutChangedkeyboard.profileChangedkeyboard.planeChangedinput.changedtime.minuteuser.run
Actions
panel.closeplane.lettersplane.numbersplane.symbolsoverrides.clearpanel.emojipanel.calculatorpanel.conversionpanel.layoutspanel.profiles
Evaluation happens while the keyboard is active. Time rules do not run in the background, and closing events are best effort. Clink cannot identify the host app or change iOS settings. Rules keep the Membership, Full Access and user-tap requirements of the features they use. Importing grants no permissions.
The plugin contract
The optional automation object in a .clinkplugin file declares events, state, settings, commands, presets and shortcut slots. Each contribution has a stable ID, an exact schema version and localized titles. The host creates plugin/<plugin-id>/<kind>/<id> names. A plugin cannot claim a core or another plugin’s namespace.
Published state, event payloads and command arguments accept booleans, bounded numbers and declared choices. Settings can also use bounded text. State and settings use keys in the existing plugin state dictionary. Temporary setting values are restored before saving. Presets are ordinary disabled rules and require explicit selection.
Plugins ›Hooks
| Hook | When it runs |
|---|---|
on_automation(command, args, state) | on_automation(command, args, state) runs an approved local command in the existing sandbox. It can update the space-bar caption, publish declared state or emit a declared event. Text insertion, clipboard access, persistent setting changes and external requests are not permitted effects of this hook. |
automation_emit(name, payload) | Emit a declared event with its typed payload. The host adds the plugin namespace and schema versions; script input cannot supply a user gesture or execution ancestry. |
automation_publish(name, value) | Publish one declared state value for conditions. Values disappear when the keyboard session ends or the plugin becomes unavailable. |
automation_run(slot) | Call from a native plugin control’s action handler. The user maps the declared shortcut slot to a rule in Automations. A plugin cannot silently choose the rule or trigger a tap-only command from a timer. |
Actions
space_textautomation_emitautomation_publish
A declared event and a safe command
This manifest fragment belongs in the automation field of a normal plugin file. Its script publishes whether the keyboard is ready, emits an event and refreshes a caption. In the editor, select that event, require the ready state and choose the caption command. No plugin is installed or enabled by reading this example.
{
"schemaVersion": 1,
"events": [
{
"id": "ready",
"title": {
"en": "Keyboard ready"
},
"version": 1,
"payload": [
{
"id": "ready",
"key": "ready",
"title": {
"en": "Ready"
},
"version": 1,
"type": "bool",
"choices": [],
"minimum": 0,
"maximum": 100,
"defaultValue": null
}
]
}
],
"state": [
{
"id": "ready",
"key": "ready",
"title": {
"en": "Ready"
},
"version": 1,
"type": "bool",
"choices": [],
"minimum": 0,
"maximum": 100,
"defaultValue": null
}
],
"settings": [
{
"id": "show_caption",
"key": "show_caption",
"title": {
"en": "Show caption"
},
"version": 1,
"type": "bool",
"choices": [],
"minimum": 0,
"maximum": 100,
"defaultValue": true
}
],
"commands": [
{
"id": "caption",
"title": {
"en": "Refresh caption"
},
"version": 1,
"arguments": [],
"behavior": "localUI",
"requiresUserGesture": false,
"requiresFullAccess": false
}
],
"presets": [],
"shortcuts": [
"run"
]
}def initial():
return {"ready": False, "show_caption": True}
def on_open(state):
state["ready"] = True
automation_publish("ready", True)
automation_emit("ready", {"ready": True})
return state
def on_automation(command, args, state):
if command == "caption":
space_text("Clink" if state["show_caption"] else None)
return state
def bar_items(state):
return [bar_button("run", "Run automation", icon="bolt.circle")]
def on_action(action, value, state):
if action == "run":
automation_run("run")
return stateSchema and limits
Version 1 supports 128 rules, 24 conditions and 32 actions per rule, with imports up to 1,048,576 bytes. The setting registry exposes 268 targets and documents 39 exclusions. Invalid configuration batches are rejected together; commands stop at the first failure without claiming rollback.
Removed, disabled or incompatible plugin contributions make dependent rules unavailable. Crashes and timeouts disable the failed contribution for the session. Commands have a 20,000-step budget and a short time limit. Bounded queues, cooldowns, chain limits and state stabilization prevent repeated events from running indefinitely.
Widgets
Clink ships three home screen widgets and one Live Activity: a profile switcher, your typing statistics, quick dictation, and the dictation session itself.
The widgets read the shared settings file directly, so they keep working with the app closed.
App Intents
Two intents ship today, and neither opens the app. Switch Keyboard Profile rewrites the shared settings from the widget, and a keyboard already on screen picks the change up at once. End Dictation reaches the microphone session from the Live Activity, launching the app in the background if it has to.
Switching to a profile you made yourself needs Membership, and that check runs inside the widget too. The profiles Clink ships are free.
Clink defines no Shortcuts phrases of its own, so there is nothing to say out loud.
Links
Clink answers its own clink URL scheme. A link opens the app on the page you name, or runs one of the reserved routes.
| Link | What it does |
|---|---|
clink://settings/<page> | Opens a settings page. Add ?anchor=<id> to land on a row. |
clink://<page> | The same page, written short. |
clink://search?q=<text> | Opens search with the query filled in. |
clink://analytics | Opens typing statistics. |
clink://dictation/start | Starts a dictation session. |
clink://dictation/stop | Ends the session and clears the Live Activity. |
clink://paywall | Opens Membership. |
clink://verify | Rechecks your membership, for when an entitlement has gone stale. |
Dictation, membership and verification are reserved, so they cannot be used as page names.
Opening files
Clink registers its file types with iOS, so Files, AirDrop, Messages and any share sheet offer to open one in Clink.