Automazioni

L’oggetto automation facoltativo in un file .clinkplugin dichiara eventi, stato, impostazioni, comandi, preimpostazioni e slot per scorciatoie. Ogni contributo ha un ID stabile, una versione esatta dello schema e titoli localizzati. L’host crea nomi nel formato plugin/<plugin-id>/<kind>/<id>. Un plugin non può usare lo spazio dei nomi del sistema o di un altro plugin.

Automazioni ›

Come funzionano i plugin

Un pannello sostituisce i tasti, e un'azione gira una volta su un pezzo di testo. Un plugin non ha una schermata propria sulla tastiera. Clink chiama le sue funzioni in momenti precisi, per esempio quando la tastiera si apre o quando finisci una parola, e il plugin può aggiornare la barra spaziatrice o il proprio stato salvato. Le sue impostazioni vengono disegnate nell'app con gli stessi costruttori dei pannelli. WPM Spacebar, qui sotto, è un plugin completo.

def initial():
    return {"on": False}

def settings(state):
    return section("keys.spacebar", [
        toggle("Show typing speed", state["on"], action="toggle"),
    ], title="WPM Spacebar")

def on_action(action, value, state):
    state["on"] = value
    if value:
        claim("spacebar.text")
    else:
        release("spacebar.text")
        space_text(None)
    return state

def on_word(word, state):
    if state["on"]:
        space_text(f"{stats()['wpm']} wpm")
    return state
WPM Spacebar aggiunge un interruttore in Tasti > Barra spaziatrice. Quando è acceso, la barra spaziatrice mostra la tua velocità di digitazione attuale.

I plugin richiedono Clink Pro. Prima di installarne uno da un repository, Clink ti chiede di consentire il codice di quel repository, come fa per pannelli e azioni.

on_key e on_word ricevono ciò che scrivi: è così che funziona un contatore di parole. PyMini non ha accesso alla rete né ai file, quindi un plugin non può inviare ciò che scrivi via internet, e ciò che salva resta in Clink sul tuo dispositivo.

Il tuo primo plugin

La via più rapida è lo script di partenza che l'app scrive per te. Mette un interruttore in Tasti > Barra spaziatrice e, finché è acceso, conta le parole sulla barra spaziatrice. Cinque passi, nessun file da scaricare.

  1. Apri la scheda Plugin, attiva i plugin in alto e tocca + per un nuovo plugin.
  2. L'editor si apre sullo script di partenza. Leggilo una volta: initial(), settings(), on_action(), on_open() e on_word() sono tutto.
  3. Passa all'anteprima. Attiva l'interruttore, poi tocca on_open e on_word qualche volta e guarda la barra spaziatrice simulata e la console.
  4. Salva. Il plugin è attivo nell'elenco e il suo interruttore ora sta anche in Tasti > Barra spaziatrice.
  5. Apri la tastiera ovunque e scrivi. La barra spaziatrice conta insieme a te.
def initial():
    return {"on": False, "words": 0}

def settings(state):
    return section("keys.spacebar", [
        toggle("Count words on the space bar", state["on"], action="toggle"),
    ], title="Word count")

def on_action(action, value, state):
    if action == "toggle":
        state["on"] = value
        if value:
            claim("spacebar.text")
        else:
            release("spacebar.text")
            space_text(None)
    return state

def on_open(state):
    state["words"] = 0
    if state["on"]:
        space_text("0 words")
    return state

def on_word(word, state):
    state["words"] += 1
    if state["on"]:
        space_text(f"{state['words']} words")
    return state
Lo script di partenza, come lo scrive l'app.

Hook

Definisci quelle che ti servono fra queste 33 funzioni e tralascia le altre. Ognuna riceve state come ultimo argomento. Quelle che reagiscono a qualcosa restituiscono state, modificato o no. Quelle che rispondono a una domanda (elements, draw, effects, gli hook di aspetto, haptics, hitboxes, suggestions e correct) restituiscono la loro risposta, e possono comunque modificare state sul posto.

HookQuando viene eseguito
on_automation(command, args, state)on_automation(command, args, state) esegue un comando locale approvato nella sandbox esistente. Può aggiornare la scritta sulla barra spaziatrice, pubblicare uno stato dichiarato o emettere un evento dichiarato. Questo hook non consente inserimento di testo, accesso agli appunti, modifiche persistenti alle impostazioni o richieste esterne.
initial()Una volta sola, prima che esista uno stato salvato. Restituisci un dict con qualsiasi dato che JSON può contenere.
settings(state)Quando le impostazioni del plugin vengono mostrate nell'app. Restituisci i suoi controlli come albero di nodi. Non gira mai nella tastiera.
on_action(action, value, state)Quando viene usato uno dei controlli del plugin. Funziona anche on_action(action, state), senza value.
on_open(state)Quando compare la tastiera. Un buon punto per leggere stats() o impostare la barra spaziatrice.
on_close(state)Quando la tastiera viene chiusa. Lo stato viene salvato subito dopo.
on_key(key, state)Ogni volta che un tasto scrive qualcosa. Gira a ogni pressione, quindi tienilo veloce.
on_word(word, state)Quando una parola è finita, con uno spazio, un suggerimento o uno swipe.
on_backspace(state)Quando viene premuto il tasto cancella. Non riceve testo, solo lo stato.
on_suggestion(word, state)Quando si tocca un suggerimento. word è quello toccato.
on_language(code, state)Quando cambia la lingua di digitazione. code è quella nuova, come en o de.
on_field(kind, state)Quando la tastiera si collega a un campo. kind è default, email, url, number, phone, password o search.
on_tick(state)Una volta al secondo finché la tastiera è sullo schermo, che tu stia scrivendo o no. L'hook per tutto ciò che deve cambiare da solo: un orologio, un conto alla rovescia, una velocità che deve tornare a zero quando ti fermi.
on_swipe(direction, state)Un gesto che parte da un tasto lettera: "left", "right", "up", "up_left" o "up_right". Viene letto solo mentre la digitazione a scorrimento è disattivata. Se l'hook non chiede nulla, il gesto resta un normale tasto; se chiede qualcosa, prima viene tolta la lettera da cui è partito.
elements(state)Cosa offre questo plugin a un layout personalizzato. Restituisci voci element(id, name, icon=, width=), oppure ometti l'hook.
draw(id, state)Il volto di un elemento, come albero di nodi. Gira circa una volta al secondo finché la tastiera è sullo schermo.
effects(state)Effetti luce che questo plugin offre alla pagina Effetti. Restituisci voci light_effect(...), oppure ometti l'hook.
key_styles(state)Stili dei tasti per l’editor dei temi. Restituisci voci key_style(...); vedi Aspetti più sotto.
themes(state)Temi completi per la scheda Plugin dell’editor dei temi. Restituisci voci theme(...).
popups(state)Stili del popup dei tasti. Restituisci voci popup_style(...).
animations(state)Animazioni: qualsiasi combinazione di entrance(...), press_animation(...), letter_animation(...) e transition(...).
backgrounds(state)Sfondi animati. Restituisci voci background(...) fatte di livelli particles(...).
layouts(state)Layout di tastiera. Restituisci voci layout(...).
trails(state)Scie di scorrimento che questo plugin offre al selettore di scie. Restituisci voci trail(...); vedi Aspetti più sotto.
haptics(state)Un feedback aptico per ogni tasto, come dict dai nomi dei tasti alle sensazioni. Viene letto all’apertura della tastiera e circa una volta al secondo.
hitboxes(state)Un’area di tocco per ogni tasto, come dict dai nomi dei tasti a hitbox(...). Viene letto allo stesso ritmo di haptics.
on_touch(key, x, y, state)Dopo ogni tocco: il tasto a cui è andato e il punto di quel tasto in cui si è posato il dito. x e y vanno da -0.5 a 0.5, con 0 al centro.
suggestions(word, state)Parole per la barra dei suggerimenti mentre si scrive word. Viene eseguito ogni volta che la barra si assesta, non a ogni tasto.
correct(word, fix, state)Lo spazio ha appena chiuso word. Restituisci una parola da inserire al suo posto, False per lasciarla com’è stata scritta o None per lasciare la correzione della tastiera.
bar_items(state)Pulsanti e manopole che questo plugin offre alla barra superiore. Restituisci voci bar_button(...) e bar_knob(...), oppure lascia fuori l’hook.
key_art(state)La grafica da dipingere sui tasti, come dict dai nomi dei tasti alle forme. Viene riletta dopo ogni evento che il plugin sente, così uno stato cambiato in un hook si vede sui tasti.
events(state)I nomi degli eventi che on_event vuole sentire, letti una volta al caricamento del plugin. Senza questo hook il plugin sente tutto tranne gli eventi frequenti.
on_event(name, info, state)Un unico hook per tutto quello che succede: open, close, word, backspace, suggestion, language, field, shift e plane, più i frequenti key, key_down, key_up, predictions e tick, che vanno chiesti per nome in events(state).
def on_key(key, state):
    if key == " " and setting("language") == "en":
        state["spaces"] = state.get("spaces", 0) + 1
    return state

def on_close(state):
    haptic("light")
    return state
Due hook, una lettura e un comando dei pannelli.

L'app e la tastiera condividono un'unica copia dello stato. Se attivi un interruttore nell'app, è attivo alla prossima apertura della tastiera. Quello che la tastiera conta è lì la prossima volta che apri le impostazioni del plugin. La tastiera salva lo stato quando si chiude, non dopo ogni tasto.

Suggerimento attuale sulla barra spaziatrice

Leggi il suggerimento principale visualizzato con context()["suggestion"]. Iscriviti all’evento predictions per ricevere le modifiche in info["suggestion"]. Entrambe le operazioni richiedono l’accesso ai dati di digitazione. on_suggestion viene eseguito quando si accetta un suggerimento, non quando cambiano le previsioni. Usa space_text(value or None) per impostare l’etichetta o ripristinarla quando non ci sono suggerimenti. L’azione della barra spaziatrice non cambia.

def initial():
    return {}

def events(state):
    return ["predictions"]

def on_open(state):
    space_text(context()["suggestion"] or None)
    return state

def on_event(name, info, state):
    if name == "predictions":
        space_text(info["suggestion"] or None)
    return state

def on_close(state):
    space_text(None)
    return state

Stato

Lo stato è un unico dict. initial() lo costruisce la prima volta; poi ogni hook riceve lo stesso dict, lo modifica e lo restituisce. Contiene tutto ciò che JSON può contenere: numeri, stringhe, liste, dict annidati. L'app lo salva dopo ogni controllo che usi, la tastiera quando si chiude, ed entrambi leggono lo stesso file, quindi non restano mai in disaccordo a lungo.

def initial():
    return {"session": 0, "total": 0, "longest": ""}

def on_open(state):
    state["session"] = 0        # starts over each time the keyboard opens
    return state

def on_word(word, state):
    state["session"] += 1
    state["total"] += 1         # survives, it is saved when the keyboard closes
    if len(word) > len(state["longest"]):
        state["longest"] = word
    return state

def settings(state):
    return vstack([
        text(f"{state['total']:,} words so far"),
        text(f"Longest: {state['longest'] or '...'}", size=13, color="gray"),
        button("Start over", "reset", style="destructive"),
    ])

def on_action(action, value, state):
    if action == "reset":
        return initial()
    return state
Un conteggio per sessione che on_open azzera, e un totale che resta.

Restituire lo stato è l'abitudine da tenere, ma non è strettamente necessario: il dict è un riferimento, quindi funziona anche modificarlo sul posto. Restituisci un dict diverso, come initial() qui sopra, e quello diventa lo stato.

Impostazioni e sezioni

settings(state) restituisce un albero di nodi fatto con i costruttori dei pannelli: text, toggle, slider, stepper, segmented, button, row, field e i layout. Compare nella pagina del plugin, nella scheda Plugin. Se ne avvolgi una parte in section(anchor, children, title), quella parte compare anche in una delle schermate di impostazioni di Clink, accanto all'impostazione a cui si riferisce.

def settings(state):
    return vstack([
        text("Counts words as you type.", size=13),
        section("keys.spacebar", [
            toggle("Count on the space bar", state["on"], action="toggle"),
            stepper(state["goal"], min=10, max=500, step=10, label="Goal", key="goal"),
        ], title="Word count"),
    ])
L'interruttore e lo stepper compaiono in Tasti > Barra spaziatrice. La didascalia appare solo nella pagina del plugin.

I controlli di cui un plugin ha più spesso bisogno. Ognuno scrive il suo nuovo valore nello stato sotto key, oppure indica una action per on_action, o entrambe le cose. L'elenco completo dei costruttori, layout compresi, è nella pagina dei pannelli.

CostruttoreCosa disegna
toggle(label, on=False, key="", action="")Un interruttore. key scrive True o False in quella chiave di stato.
slider(value, min=0, max=100, step=1, label="", key="", action="")Un cursore fra min e max. key scrive la posizione in quella chiave di stato.
stepper(value, min=0, max=100, step=1, label="", key="", action="")Un valore con − e + accanto, fra min e max.
segmented(options, value=None, key="", action="")Una scelta da un elenco. key scrive l'opzione scelta in quella chiave di stato.
field(key, placeholder="", action="", submit="")Una casella di testo legata a state[key]. Toccandola i tasti scrivono in quella chiave; submit indica il gestore che Invio esegue.
button(label, action="", value=None, insert="", set=None, style="plain", icon="", enabled=True)Un pulsante. insert scrive il suo testo in ciò che stai scrivendo, set unisce chiavi nello stato e action indica un gestore per on_action. style accetta plain, primary, tinted, quiet o destructive.
row(title, subtitle="", detail="", icon="", action="", value=None, insert="")Una riga toccabile: un titolo, una seconda riga, un'icona e un dettaglio a destra.
text(s, size=17, weight="regular", color="", align="leading", lines=0, mono=False)Una riga di testo. weight accetta regular, medium, semibold, bold, heavy, light o thin; align accetta leading, center o trailing; lines limita quante righe occupa; color accetta un nome di colore o #RRGGBB.
Pannelli ›

Dove vanno le sezioni

Un'ancora è l'ID di una scheda in una delle schermate di impostazioni di Clink. Indicala in section() e i controlli del plugin vengono disegnati subito sotto quella scheda, con il nome del plugin sopra. Il modo più semplice per trovarne una: nell'app, apri Altro > Sviluppatore e attiva Mostra gli ID. Ogni scheda riceve un piccolo badge informativo che ne indica l'ID e lo copia al tocco; le pagine mostrano il proprio nella barra del titolo.

Ogni ancora che una sezione può indicare

  • analytics.heatmap
  • analytics.privacy
  • analytics.trends
  • analytics.typing-test
  • automations.rules
  • gestures.accents
  • gestures.cursor
  • gestures.delete
  • gestures.general
  • gestures.suggestions
  • gestures.swipe
  • haptics.feel
  • keys.adaptive
  • keys.faces
  • keys.hitboxes
  • keys.hitmap
  • keys.long-press
  • keys.numberrow
  • keys.onehanded
  • keys.roundness
  • keys.size
  • keys.spacebar
  • keys.spacing
  • keys.split
  • languages.app
  • languages.custom
  • languages.manage
  • languages.packs
  • languages.switch
  • languages.typing
  • layout.arrange
  • layout.arrangement
  • layout.build
  • layout.longpress
  • layout.presets
  • layout.topbar
  • motion.delete
  • motion.entrance
  • motion.glow
  • motion.key-press
  • motion.key-response
  • motion.letters
  • motion.space-response
  • motion.transition
  • popups.style
  • sound.keysounds
  • text.automation
  • text.content
  • text.corrections
  • text.history
  • text.punctuation
  • text.speed
  • text.suggestions
  • text.symbols
  • themes.background
  • themes.canvas
  • themes.theme

keys.spacebar ha un posto tutto suo, nella schermata della barra spaziatrice. Una sezione con un'ancora non in questo elenco compare comunque nella pagina del plugin, quindi un refuso ti costa una scheda, non il plugin.

Prese di controllo

Quando un plugin guida una delle impostazioni di Clink, la persona dovrebbe vederlo, e i due non dovrebbero litigare. claim(control) dice che il plugin ne è proprietario: l'app indica il plugin sulla scheda di quell'impostazione, e il campo del testo della barra spaziatrice resta bloccato finché lo tiene. release(control) la restituisce. Prendi nel on_action che accende la tua funzione, rilascia in quello che la spegne, e riporta il valore a None o al suo valore precedente nello stesso momento. Un plugin disattivato nell'elenco rilascia tutto ciò che teneva.

def on_action(action, value, state):
    if action == "toggle":
        state["on"] = value
        if value:
            claim("spacebar.text")
            space_text("...")
        else:
            release("spacebar.text")
            space_text(None)      # hand the person's own text back
    return state
La coppia che ogni plugin che prende il controllo ha: prendere all'accensione, restituire allo spegnimento.

Due plugin possono prendere lo stesso controllo; l'app indica quello che lo ha preso per ultimo. Le prese di controllo sono registrate dall'app, quindi prendi da on_action, che gira lì. Una presa fatta dentro la tastiera non viene ricordata.

Comandi e letture

I plugin possono usare tutti i comandi dei pannelli, più sei comandi e due letture tutti loro. I comandi vengono messi in coda e applicati quando la tua funzione è terminata, quindi niente cambia sulla tastiera a metà di una chiamata. Le letture restituiscono i valori com'erano subito prima della chiamata.

ChiamataCosa fa
space_text(text)Mostra una didascalia sulla barra spaziatrice, fino a 32 caratteri. Passa None per tornare al testo che l'utente ha scelto per la barra spaziatrice.
space_language_text(text)Sostituisce la targhetta della lingua nell’angolo della barra spaziatrice, da sola e anche quando quella nativa è spenta. Una riga, fino a 12 caratteri; "" la nasconde e None restituisce il testo nativo.
space_language_flag(language)Mette una bandiera su quella targhetta per un id di lingua come en_GB, presa dalla grafica delle bandiere inclusa. None restituisce il comportamento nativo.
space_language_emoji(language)La stessa targhetta come emoji della bandiera della regione. I tre comandi della targhetta condividono un unico posto, quindi accendere un plugin di targhetta spegne gli altri.
set_setting(name, value)Cambia un'impostazione di Clink per nome, con un valore del tipo giusto: un booleano, un numero nel suo intervallo, una delle sue scelte o del testo. Un nome sconosciuto solleva KeyError. Un valore del tipo sbagliato o fuori intervallo non viene applicato, e la console dell'editor dice cosa si aspetta l'impostazione.
claim(control)Prende in carico una delle impostazioni di Clink. La sua scheda nell'app indica il plugin che la tiene.
release(control)Restituisce l'impostazione. Disattivare un plugin rilascia tutto ciò che aveva preso.
suggest(words)Mette fino a dieci parole tue all'inizio della barra dei suggerimenti. Restano finché se ne tocca una, si preme cancella o cambia il campo, e suggest([]) le toglie prima. Toccarne una la scrive.
banner(text)Mostra per un attimo un breve messaggio sulla tastiera. Per un cenno, non per una conversazione.
press(key)Esegue uno dei tasti della tastiera come se fosse stato toccato: "space" o "delete". Qualsiasi altro nome solleva ValueError.
pick_suggestion(slot)Sceglie il suggerimento nella parte "left", "center" o "right" della barra, proprio come toccarlo. Con una barra scorsa conta ciò che si vede sullo schermo. Dentro on_swipe sceglie dalla barra com'era quando il dito si è posato.
stats()Restituisce un dict con wpm, peak_wpm, keystrokes, words e streak. wpm si aggiorna in tempo reale mentre i plugin sono attivi. I totali vengono da Statistiche e smettono di aggiornarsi se le Statistiche sono disattivate.
setting(name)Legge per nome una delle impostazioni elencate qui sotto. Qualsiasi altro nome solleva KeyError.
context()La stessa istantanea che legge un pannello, più suggestion, shift (off, on o locked) e plane per un plugin. È l’accesso ai dati di digitazione a riempire le chiavi del documento; shift resta leggibile anche senza.
ChiaveCosa contiene
stats()["wpm"]Parole al minuto negli ultimi secondi, contate dai caratteri che arrivano nel campo. Il numero compare uno o due secondi dopo l'inizio, cala durante una pausa e arriva a 0 quando ti fermi. Cancellare non lo aumenta mai.
stats()["peak_wpm"]La velocità migliore mai registrata dalle Statistiche.
stats()["keystrokes"]Tasti premuti in totale, come li contano le Statistiche.
stats()["words"]Parole confermate in totale.
stats()["streak"]Giorni di fila con digitazione, fino a oggi.

setting(name) e set_setting(name, value) condividono un unico elenco di nomi, e anche claim(control) accetta uno qualsiasi di essi. I booleani si leggono come True o False, i numeri come numeri, le scelte come il loro id. L'elenco è lungo apposta: un plugin può reagire a quasi tutto ciò che una persona può impostare nell'app, o guidarlo.

Nomi accettati da setting()

  • analytics
  • emoji.skin_tone
  • emoji.trailing_space
  • gestures.cursor
  • gestures.cursor_style
  • gestures.highlight_shift
  • gestures.plane_slide
  • gestures.predictive_flick
  • gestures.quick_accent
  • gestures.swipe
  • gestures.swipe_delete
  • gestures.swipe_multi_word
  • gestures.swipe_space_commit
  • gestures.swipe_two_thumb
  • gestures.trail
  • gestures.trail_style
  • gestures.trail_width
  • haptics.enabled
  • haptics.intensity
  • haptics.sharpness
  • keys.accents
  • keys.glyph_scale
  • keys.height
  • keys.long_press_hint
  • keys.popup_style
  • keys.popups
  • keys.radius
  • keys.row_spacing
  • keys.spacing
  • keys.uppercase
  • keys.width
  • language
  • language.bar_key
  • language.bar_key_style
  • language.mode
  • layout
  • layout.dismiss_shortcut
  • layout.number_row
  • layout.number_row_scale
  • layout.one_handed
  • layout.one_handed_shortcut
  • layout.one_handed_side
  • layout.one_handed_width
  • layout.split
  • layout.split_gap
  • layout.split_number_row
  • layout.split_shortcut
  • layout.split_space_bar
  • pet.corner
  • pet.enabled
  • pet.species
  • settings.accentHoldDelay
  • settings.accentMoveCancel
  • settings.activateWithIcon
  • settings.adaptiveGrow
  • settings.adaptiveHitboxes
  • settings.adaptivePredictAtWordStart
  • settings.adaptivePredictionWeight
  • settings.adaptiveShrink
  • settings.adaptiveSpace
  • settings.aiAutocorrect
  • settings.aiCompletions
  • settings.aiExtensionEnabled
  • settings.aiSearch
  • settings.aiToolsColorOverrides
  • settings.aiToolsDiffStyle
  • settings.aiToolsDisabled
  • settings.aiToolsLayoutStyle
  • settings.aiToolsOrder
  • settings.aiToolsPromptOverrides
  • settings.aiTranslate
  • settings.alternatingSplit
  • settings.arabicIndicNumerals
  • settings.backgroundEffectOverride
  • settings.clipboardCloseOnPaste
  • settings.clipboardDeleteOnPaste
  • settings.clipboardIgnoreImages
  • settings.clipboardIgnorePinsOnDelete
  • settings.clipboardStyle
  • settings.cursorActivationHaptic
  • settings.cursorLineStride
  • settings.cursorStepHaptic
  • settings.customPanelsStandalone
  • settings.customPetID
  • settings.deleteWordSwipeEngage
  • settings.deleteWordSwipeStride
  • settings.dictationAssist
  • settings.dictationAssistCustom
  • settings.dictationAssistLevel
  • settings.dictationColorScheme
  • settings.dictationGlassCapsule
  • settings.dictationSmartActions
  • settings.dictationStyle
  • settings.dictationVisualStyle
  • settings.dragUpThreshold
  • settings.emojiCategoryOrder
  • settings.emojiCellSpacing
  • settings.emojiColumnCount
  • settings.emojiCrossAxisSwitchesTab
  • settings.emojiCustomSets
  • settings.emojiGlyphScale
  • settings.emojiHiddenCategories
  • settings.emojiHiddenFromPanels
  • settings.emojiRecentsCap
  • settings.emojiRecentsSort
  • settings.emojiRememberCategory
  • settings.emojiRowCount
  • settings.emojiScrollDirection
  • settings.emojiSearchSlot
  • settings.emojiShowABCKey
  • settings.emojiShowBackspaceKey
  • settings.emojiStartCategoryID
  • settings.emojiTabBarSlot
  • settings.emojiTabIconStyle
  • settings.emojiToneHoldDelay
  • settings.extensionOrder
  • settings.extraTopBars
  • settings.formFreeCorners
  • settings.formLayoutEnabled
  • settings.gifShareAsLink
  • settings.glassPerRowMerge
  • settings.glassReleaseResponse
  • settings.gridSwitchAnimation
  • settings.gridSwitchDuration
  • settings.handwritingInkColor
  • settings.handwritingInkGlow
  • settings.handwritingInkStyle
  • settings.handwritingInkWidth
  • settings.hitboxScale
  • settings.iconPickerStyle
  • settings.keyBloomScale
  • settings.keyLighting
  • settings.keyPressGlow
  • settings.keyPressInstant
  • settings.keyPressLinger
  • settings.keySpringDamping
  • settings.keySpringResponse
  • settings.keyboardBottomPadding
  • settings.keyboardLanguages
  • settings.keyboardTopPadding
  • settings.longPressGlyphScale
  • settings.minPressVisible
  • settings.notepadBrowseStyle
  • settings.notepadMode
  • settings.numberRowFontSize
  • settings.numberRowLeadingKeys
  • settings.numberRowTrailingKeys
  • settings.oneHandedCustomKeys
  • settings.oneHandedExtraKeys
  • settings.panelButtonHitboxScale
  • settings.persistentLeadingKeys
  • settings.persistentTrailingKeys
  • settings.petIdleMotion
  • settings.petScale
  • settings.pinyinFuzzyEnabled
  • settings.pluginLooks
  • settings.popupSpringDamping
  • settings.popupSpringResponse
  • settings.predictiveFlickSuggestionPosition
  • settings.predictiveFlickSuggestionsSeparate
  • settings.reduceEffectsOnLowPower
  • settings.repeatAccelStep
  • settings.repeatHoldDelay
  • settings.repeatInitialInterval
  • settings.repeatMinInterval
  • settings.replacementsLayout
  • settings.rowInsets
  • settings.secondaryNeuralModelsEnabled
  • settings.separateActivation
  • settings.separateLanguageLayouts
  • settings.showCaptureOverlay
  • settings.showCorrectionField
  • settings.showHitboxOverlay
  • settings.showIconsBeforeTyping
  • settings.showRecentEmoji
  • settings.showTouchHeatmap
  • settings.showTouchSurfaceBounds
  • settings.showTouchTelemetryOverlay
  • settings.slideUpPickerStyle
  • settings.solidPopupOpacity
  • settings.soundVoice
  • settings.spaceBloomScale
  • settings.spaceCursorActivationDelay
  • settings.spaceCursorDragScale
  • settings.spaceCursorStride
  • settings.spaceLeanMultiplier
  • settings.spaceSpringDamping
  • settings.spaceSpringResponse
  • settings.spatialBiasEnabled
  • settings.spatialBiasGain
  • settings.splitIndices
  • settings.splitOtherPlanes
  • settings.stickerGridSnap
  • settings.stickerPlacements
  • settings.suggestionDebounceDelay
  • settings.suggestionHitboxScale
  • settings.suggestionSegmentLiquidGlass
  • settings.suggestionSegmentStyle
  • settings.suggestionSegmentsFollowTheme
  • settings.suggestionSeparatorColor
  • settings.suggestionSeparatorStyle
  • settings.suggestionTopPadding
  • settings.swipeKeyMorph
  • settings.swipeMorphRadius
  • settings.swipeMorphStrength
  • settings.swipeTrailEndWidth
  • settings.swipeTrailMaxLength
  • settings.swipeTrailStartWidth
  • settings.swipeTrailTrim
  • settings.toolsButtonStyle
  • settings.toolsHiddenFromPanels
  • settings.topBarItems
  • settings.topBarOrder
  • settings.translateLanguageOrder
  • settings.translateLanguagesDisabled
  • settings.translateStyle
  • settings.translateTone
  • settings.vietnameseInputMethod
  • sound.enabled
  • sound.pack
  • sound.volume
  • spacebar.corner
  • spacebar.language_code
  • spacebar.size
  • spacebar.text
  • stickers.enabled
  • text.arithmetic
  • text.auto_capitalize
  • text.auto_punctuation
  • text.autocorrect
  • text.autocorrect_everywhere
  • text.contacts
  • text.conversions
  • text.double_space_period
  • text.learning
  • text.punctuation_spacing
  • text.return_to_letters
  • text.revert_on_delete
  • text.smart_quotes
  • text.suggestions
  • text.suggestions_animation
  • text.suggestions_height
  • text.suggestions_scroll
  • text.suggestions_style
  • theme
  • theme.background
  • theme.dark
  • theme.delete_glyph
  • theme.entrance
  • theme.glyph_press
  • theme.light
  • theme.match_system
  • theme.press_style
  • theme.reactive_background
  • tools.calculator
  • tools.cling
  • tools.clipboard
  • tools.conversion
  • tools.dictation
  • tools.dictionary
  • tools.emoji
  • tools.gif
  • tools.handwriting
  • tools.layout_switcher
  • tools.notepad
  • tools.plugin_switcher
  • tools.profiles
  • tools.replacements
  • tools.textfx
  • tools.theme_switcher
  • tools.translate

Comandi

  • insert()
  • backspace()
  • delete_word()
  • replace()
  • move_cursor()
  • copy()
  • close()
  • haptic()
  • toast()

Come view() di un pannello, settings() deve solo descrivere i controlli. I comandi chiamati da lì vengono ignorati e registrati nella console.

Esempi

Cinque piccoli plugin, ognuno completo. Incollane uno in un nuovo plugin, salva, e gira.

def initial():
    return {"on": True}

def settings(state):
    return section("keys.spacebar", [
        toggle("Language on the space bar", state["on"], action="toggle"),
    ], title="Language badge")

def on_action(action, value, state):
    if action != "toggle":
        return state
    state["on"] = value
    if value:
        claim("spacebar.text")
        space_text(setting("language").upper())
    else:
        release("spacebar.text")
        space_text(None)
    return state

def on_open(state):
    if state["on"]:
        space_text(setting("language").upper())
    return state

def on_language(code, state):
    if state["on"]:
        space_text(code.upper())
    return state
Badge della lingua: la lingua di digitazione sulla barra spaziatrice, aggiornata nel momento in cui cambia.
SHORTCUTS = {"omw": "on my way", "brb": "be right back", "ty": "thank you"}

def on_key(key, state):
    if key != " ":
        return state
    words = context()["before"].split()
    if words and words[-1] in SHORTCUTS:
        # the space is already in the text, so it goes too and comes back after
        replace(len(words[-1]) + 1, SHORTCUTS[words[-1]] + " ")
    return state
Scorciatoie: scrivi omw e uno spazio, ottieni on my way. on_key vede lo spazio dopo che è stato scritto, quindi la sostituzione lo scavalca.
def initial():
    return {"goal": 200, "count": 0}

def settings(state):
    return vstack([
        stepper(state["goal"], min=50, max=2000, step=50, label="Words per session", key="goal"),
        progress(state["count"], total=state["goal"], label=f"{state['count']} of {state['goal']}"),
    ])

def on_open(state):
    state["count"] = 0
    return state

def on_word(word, state):
    state["count"] += 1
    if state["count"] == state["goal"]:
        haptic("medium")
        banner("Goal reached")
    return state
Obiettivo di parole: uno stepper nella pagina del plugin, una barra di avanzamento, e una vibrazione con un banner quando la sessione lo raggiunge.
def initial():
    return {"muted": False, "was_on": True}

def on_field(kind, state):
    # a password or a number field is not a place for key sounds
    quiet = kind in ("password", "number", "phone")
    if quiet and not state["muted"]:
        state["was_on"] = setting("sound.enabled")
        state["muted"] = True
        set_setting("sound.enabled", False)
    elif not quiet and state["muted"]:
        state["muted"] = False
        if state["was_on"]:
            set_setting("sound.enabled", True)
    return state
Campi silenziosi: i suoni dei tasti si spengono in un campo password o numerico e tornano dopo, con on_field, setting() e set_setting().
NAMES = ["sam", "ana", "team"]

def on_key(key, state):
    typing = context()["before"].split(" ")[-1]
    if typing.startswith("@"):
        start = typing[1:].lower()
        suggest([n for n in NAMES if n.startswith(start)])
        state["showing"] = True
    elif state.get("showing"):
        suggest([])                 # the mention is over, give the bar back
        state["showing"] = False
    return state
Menzioni: scrivere @ mette i tuoi nomi nella barra dei suggerimenti.
def on_swipe(direction, state):
    if direction == "left":
        delete_word()
    elif direction == "right":
        press("space")              # the space bar's own path, so it autocorrects
    elif direction == "up":
        pick_suggestion("center")
    elif direction == "up_left":
        pick_suggestion("left")
    elif direction == "up_right":
        pick_suggestion("right")
    return state
Gesti alla Fleksy, il cuore del plugin Flick Gestures: a sinistra cancella una parola, a destra scrive uno spazio, in alto sceglie un suggerimento. I gesti arrivano solo mentre la digitazione a scorrimento è disattivata.

Elementi in un layout

Un layout personalizzato è fatto di tasti ed elementi: una striscia di emoji recenti, una fila di cifre, un pad per il cursore. Un plugin può aggiungere i suoi. elements(state) dice cosa offre e draw(id, state) ne disegna uno, così un tasto può mostrare qualsiasi cosa il plugin sappia calcolare: un grafico della velocità, un orologio, un conto alla rovescia, una batteria tutta tua.

Due hook ne costruiscono uno. elements(state) elenca ciò che offri, una voce per elemento, e l'app lo legge per riempire la palette dell'editor di layout. draw(id, state) riceve uno di quegli id e restituisce cosa disegnare. Un plugin può offrirne diversi; a draw viene chiesto ognuno per nome.

ChiamataCosa fa
element(id, name, icon="", width=2)Un elemento offerto. width si misura in larghezze di tasto, come le conta un layout, e icon è l'SF Symbol che l'editor mostra nella sua palette.
sparkline(values, min=None, max=None, fill=False)Una linea attraverso una serie di numeri, che riempie lo spazio che le dai. Senza min e max si adatta ai propri valori. Può disegnarla solo un plugin, ed è pensata per un tasto.
POINTS = 30

def initial():
    return {"history": []}

def elements(state):
    return [element("sparkline", "WPM sparkline", icon="waveform.path.ecg", width=3)]

def on_tick(state):
    history = state["history"]
    history.append(stats()["wpm"])
    state["history"] = history[-POINTS:]
    return state

def draw(id, state):
    rate = state["history"][-1] if state["history"] else 0
    return hstack([
        sparkline(state["history"], min=0, max=max(60, max(state["history"] or [0])), fill=True),
        text(f"{rate}", size=12, weight="semibold"),
    ], spacing=5, align="center")
Il plugin WPM Sparkline, per intero: offre un elemento e ci disegna gli ultimi trenta secondi della tua velocità.

Un volto è un albero di nodi, come una pagina di impostazioni, ma un tasto non è una pagina di impostazioni: si usano solo i nodi che disegnano e tutto ciò che si tocca viene ignorato. Il tasto appartiene già al layout, quindi dentro non c'è posto per un pulsante.

COSA PUÒ DISEGNARE UN VOLTO

  • sparkline
  • text
  • badge
  • icon
  • progress
  • hstack
  • vstack
  • spacer
  • divider

Dimensionalo per un tasto. width si misura in larghezze di tasto: 1 è una lettera e 3 circa un terzo di riga, e chi lo usa può ridimensionarlo dopo. Il testo ha una riga e si restringe per entrare; i colori seguono di default quello del testo del tasto, così un elemento si intona ai tasti attorno se non ne chiedi un altro. Una sparkline tiene i suoi ultimi 120 punti, più di quanti un tasto possa mostrarne.

import time

def elements(state):
    return [element("clock", "Clock", icon="clock", width=2)]

def draw(id, state):
    return text(time.format("HH:mm"), size=15, weight="semibold")
Un orologio su un tasto: niente stato, niente hook, solo l'ora a ogni richiesta. Gli elementi si ridisegnano da soli circa una volta al secondo.
GOAL = 200

def initial():
    return {"words": 0}

def elements(state):
    return [element("goal", "Word goal", icon="target", width=2)]

def on_open(state):
    state["words"] = 0
    return state

def on_word(word, state):
    state["words"] += 1
    return state

def draw(id, state):
    return vstack([
        text(f"{state['words']}/{GOAL}", size=11),
        progress(state["words"], total=GOAL),
    ], spacing=2)
Un obiettivo di parole per sessione, contato da on_word e disegnato come barra. Gli hook tengono lo stato; draw si limita a leggerlo.

Gli elementi vivono nei layout personalizzati, quindi prima c'è un layout da costruire. I preset non possono ospitarne uno: duplicare un preset nel tuo layout è la prima cosa che l'editor propone.

  1. Installa il plugin e accendilo. I suoi elementi compaiono subito.
  2. Apri Layout, crea o duplica un layout personalizzato e vai su Disponi.
  3. Tocca Elemento, scegli l'elemento del plugin dalla striscia e imposta quanto deve essere largo.

L'anteprima dell'editor disegna ogni elemento offerto dallo script, più o meno grande quanto il tasto che lo ospiterà, sotto il finto spazio. Salva e piazzalo una volta: la tastiera disegna la stessa cosa.

Un elemento mostra soltanto ciò che il plugin disegna: toccarlo non fa nulla, perché il tasto appartiene già al layout. Il layout conserva l'elemento anche con il plugin spento o disinstallato, e il tasto si riempie di nuovo quando il plugin torna.

Pulsanti e manopole nella barra superiore

La striscia sopra i tasti si compone in Layout > Barra superiore, e un plugin può offrirle i propri comandi. bar_items(state) li restituisce: bar_button(...) per qualcosa da toccare, bar_knob(...) per qualcosa da girare. L’elenco viene letto all’apertura della tastiera e di nuovo dopo ogni tocco, così un elemento può comparire e sparire insieme allo stato del plugin.

ChiamataCosa fa
bar_items(state)Tutto ciò che questo plugin offre alla barra, in una lista. Un id deve essere unico solo dentro il plugin; la barra lo conserva accanto all’id del plugin stesso. Un id ripetuto tiene il primo, e un elemento che l’hook smette di restituire non viene più disegnato.
bar_button(id, name, icon="", title="")Un pulsante. name è ciò che l’editor elenca e ciò che legge VoiceOver. icon è un SF Symbol e title fino a 12 caratteri di etichetta accanto: con l’icona e senza title resta solo l’icona, senza icona resta solo il testo. Un pulsante non ha valore né intervallo. Un tocco chiama on_action(id, None, state), e tutto quello che fa succede lì.
bar_knob(id, name, icon="", min=0, max=1, step=0, value=0, setting=None, art=None, rotor=None)Una manopola. min e max sono il suo intervallo, step la fa scattare quando è maggiore di zero e value dice da dove parte. setting= la lega invece a uno dei controlli numerici dell’app, che fornisce allora sia l’intervallo sia la posizione. art= e rotor= la disegnano in Python.
def initial():
    return {"width": 2.0}

def bar_items(state):
    return [
        bar_button("sig", "Sign off", icon="signature", title="Sign off"),
        bar_knob("volume", "Key volume", icon="speaker.wave.2", setting="sound.volume"),
        bar_knob("width", "Trail width", icon="scribble", min=1, max=6, step=0.5, value=state["width"]),
    ]

def on_action(action, value, state):
    if action == "sig":
        insert(" sent from Clink")
    elif action == "width":
        state["width"] = value
    return state
Un pulsante e due manopole, una legata al volume dei tasti

Entrambi tornano da on_action(action, value, state), indirizzati con l’id dell’elemento, così gli elementi di barra di un plugin e i suoi controlli delle impostazioni condividono lo stesso hook. Un tocco manda None. Una manopola manda il suo valore a ogni scatto finché il dito è giù e un’ultima volta quando lo si alza, così un plugin può seguire il trascinamento o aspettare il valore finale.

Una manopola con setting= gira un valore che l’app ha già, scelto dall’elenco Comandi e letture qui sopra, come sound.volume o haptics.intensity. Un nome che l’app non conosce è un KeyError in console. L’intervallo e la posizione iniziale vengono da quel controllo, la copia della tastiera cambia già mentre trascini, così il tasto successivo è già al nuovo livello, e il valore viene salvato quando lasci. Una manopola del volume o dell’aptica portata su da zero riaccende anche il suono o l’aptica per la durata del gesto, così gli scatti si sentono salendo; a salvare quell’interruttore è il set_setting del plugin dentro on_action.

Una manopola si può disegnare in Python. art= è la parte che resta ferma, la ghiera o il corpo, e rotor= è la faccia che gira, da -135 gradi al minimo a +135 gradi al massimo. Entrambe accettano le stesse forme, tinte, sfumature e ombre della grafica dei tasti, fino a sedici forme ciascuna, e unit="key" scala le coordinate sul quadrante quadrato, dove una lancetta con y negativa punta in alto. Al disegno arrivano il colore del testo della barra come "text" e l’accento del tema come "accent". Se le lasci fuori entrambe, la manopola è un anello con l’icona dentro; nell’editor si può anche dare a una manopola già messa nella barra una qualsiasi delle finiture integrate.

Nella barra non arriva niente da solo. Un elemento si mette a mano, come il menu e i suggerimenti.

  1. Installa il plugin e accendilo. I suoi elementi di barra compaiono subito.
  2. Apri Layout > Barra superiore.
  3. Aggiungi il pulsante o la manopola del plugin e trascinalo al suo posto. Per una manopola si sceglie anche una finitura.

I builder saltano una parola chiave che non conoscono, senza errore. setting=, min=, max=, step=, value=, art= e rotor= sono solo di bar_knob, quindi un bar_button a cui ne passi una viene comunque costruito come semplice pulsante e nessuno lo segnala: il lavoro di un pulsante sta in on_action. Al contrario title= è del pulsante, e una manopola lo salta.

Effetti luce

Un plugin può aggiungere la propria illuminazione dei tasti. effects(state) restituisce voci light_effect(...), e ognuna compare nell’app in Effetti > Dai plugin, accanto agli stili inclusi e agli effetti che le persone creano da sé. Un effetto è un dato, non un disegno: una pila di livelli che la tastiera anima da sola. Nello script non gira quindi nulla a ogni fotogramma, e facce, lettere, alone, lampo alla pressione e riposo funzionano come per gli stili inclusi.

ChiamataCosa fa
light_effect(id, name, layers=[...], colors=[], icon="")Un effetto. id deve essere unico solo all’interno del plugin. layers è una lista di light_layer(...), applicata dall’alto verso il basso. colors sono fino a otto stringhe "#rrggbb" che il colore percorre in ciclo; se lo ometti, l’effetto segue l’impostazione Colore della persona.
light_layer(pattern, ...)Un livello: un motivo dalla lista qui sotto. Ogni parola chiave è facoltativa.
COLORS = ["#00e5ff", "#7c4dff", "#ff4081"]

def effects(state):
    return [
        light_effect("tide", "Tide", colors=COLORS, layers=[
            light_layer("solid", low=0.2, high=0.2),
            light_layer("wave", moves="both", speed=0.8, size=1.5,
                        direction="up"),
        ]),
    ]
Una base costante con sopra un’onda lenta che attraversa tre colori. L’onda somma la sua luce alla base e sposta il colore mentre avanza.

Motivi

  • solid
  • pulse
  • wave
  • gradient
  • twinkle
  • sweep
  • rain
  • flicker
  • checker
ChiaveCosa fa
moves="light"Cosa cambia il motivo: "light" per la luminosità, "color" o "both".
mix="add"Come la sua luminosità si combina con i livelli sopra: "add", "max" per tenere la più intensa, o "multiply" per attenuarli come una maschera.
shape="smooth"La salita e la discesa di pulse, wave e gradient: "smooth", "ramp", "step" o "spike".
direction="right"In che direzione avanzano wave, gradient, sweep e rain: "right", "left", "down", "up" o "out" dal centro.
speed=1Da 0 a 4. A 0 il motivo resta fermo.
size=1Da 0,25 a 4: quante volte il motivo si ripete sulla tastiera. Per twinkle stabilisce quanti tasti sono accesi, per sweep la lunghezza della scia.
low=0, high=1La luminosità tra cui si muove il motivo, ognuna da 0 a 1. Un low sopra high lo capovolge.
color_span=1, color_offset=0Quanto avanza il motivo tra i colori e da dove parte, ognuno da 0 a 1.
def effects(state):
    return [
        light_effect("scanner", "Scanner", layers=[
            light_layer("wave", moves="color", speed=0.3),
            light_layer("sweep", mix="multiply", speed=1.2, size=1.5),
        ]),
    ]
Uno sweep usato come maschera. Il primo livello muove solo il colore, e lo sweep decide quali tasti si accendono. Senza colors segue l’impostazione Colore, quindi con Spettro diventa un arcobaleno.

Scegliere un effetto lo copia nelle impostazioni della persona, così continua a funzionare anche a plugin spento. Finché il plugin è attivo e uno dei suoi effetti è in uso, la tastiera rilegge effects(state) all’apertura e poi circa una volta al secondo, e adotta ciò che è cambiato. È così che un effetto segue lo stato, l’ora o stats().

def effects(state):
    # Rounded, so the effect only changes when the pace really does.
    tempo = round(0.3 + min(stats()["wpm"], 120) / 40, 1)
    return [
        light_effect("tempo", "Tempo", colors=["#ff3d7f", "#ffb000"], layers=[
            light_layer("wave", moves="both", speed=tempo, low=0.15),
        ]),
    ]
Tempo, dal plugin Light Show: un’onda che accelera quanto più veloce scrivi. Un livello può cambiare velocità senza che il motivo salti.

Il pulsante effects nell’editor elenca ciò che lo script offre. Per vederne uno in movimento, salva il plugin, scegli l’effetto in Effetti > Dai plugin e guarda la tastiera in cima a quella schermata.

Arrotonda tutto ciò che oscilla, come una velocità di scrittura. Un effetto che torna diverso ogni secondo viene sostituito ogni secondo per niente. Un motivo o una parola chiave sconosciuti sono un ValueError nella console dell’editor, e un effetto senza livelli viene scartato.

Aspetti

Sette hook offrono all’app cose da scegliere: key_styles, themes, popups, animations, backgrounds, trails e layouts. Ognuno restituisce voci costruite con le chiamate qui sotto, che compaiono in Dai plugin accanto alle scelte integrate. Fanno eccezione i temi, che hanno una propria scheda Plugin nell’editor dei temi. Un aspetto è fatto di numeri e parole, non di codice di disegno, quindi nello script non gira niente a ogni fotogramma. Sceglierlo lo copia nelle impostazioni della persona, quindi continua a funzionare con il plugin disattivato, e scegliere poi un’opzione integrata lo mette da parte.

ChiamataCosa fa
key_style(id, name, material=, variant=, shape=, shadow=, cap=cap(...))Uno stile dei tasti, applicato al tema aperto nell’editor dei temi, dalla sua scheda Stile. Cambiano solo i campi che nomina: material, variant, shape, glass, fan, i meccanici inner_radius, face_inset, edges, raised e light_angle, shadow (0 è piatto) e outline. I colori restano quelli del tema.
cap(outline="round", corner=None, travel=2, layers=[cap_layer(...)])Un tasto dipinto per uno stile di tasto, impilato da voci cap_layer(...). outline è round o rect, corner sostituisce il raggio e travel dice quanto affonda il tasto quando lo premi.
cap_layer(kind, paint, inset=0, x=0, y=0, blur=0, fade=None, when=[...])Un livello di un tasto dipinto. kind è fill, stroke o inner, paint accetta la grammatica del colore del tasto, un color(...) o un gradient(...), e when limita il livello a pale, dark, pressed, resting e highlighted. Con moves=False il livello resta fermo mentre il tasto affonda.
theme(id, name, background=, keys=, key_text=, style=key_style(...), ...)Un tema completo, nella scheda Plugin dell’editor dei temi. background, keys e key_text sono obbligatori, come colori "#rrggbb"; special, special_text, accent, background_bottom (una sfumatura verso il basso), dark, font e weight sono facoltativi. style=key_style(...) ne imposta la finitura. Sceglierlo lo installa come tema personalizzato.
popup_style(id, name, shape="tile", width=48, height=56, lift=30, ...)La bolla sopra un tasto premuto, in Aspetto > Popup. shape è "tile", "round" o "balloon"; width, height, lift e font_size sono in punti, e response e damping regolano la sua molla.
entrance(id, name, opacity=0, x=0, y=0, scale=1, tilt=0, spin=0, ...)Come arriva la tastiera, in Aspetto > Entrata. opacity, x, y, scale, tilt e spin sono il punto di partenza; torna a riposo con una molla secondo response e damping.
press_animation(id, name, scale=, x=0, y=0, rotation=0, ...)La forma di un tasto tenuto premuto, in Reazioni > Geometria: scale (o scale_x e scale_y), x, y e rotation a pressione piena.
letter_animation(id, name, scale=, x=0, y=0, rotation=0, anchor="center")L’effetto breve che una lettera fa a ogni tocco, in Reazioni > Lettere: gli stessi numeri al culmine, più anchor.
transition(id, name, x=0, y=0, scale=1, tilt=0, fade=True, duration=None)Il passaggio tra lettere, 123 e #+=, in Aspetto > Transizione. x e y dicono quanto si spostano i vecchi tasti, come frazione della tastiera, e i nuovi arrivano dal lato opposto. Accetta anche scale, tilt, fade e duration.
background(id, name, layers=[...], colors=[])Uno sfondo animato, in Aspetto > Sfondo: fino a quattro livelli particles(...) e fino a otto colori "#rrggbb".
particles(shape="glow", count=40, size=4, speed=20, direction="none", ...)Un livello di uno sfondo. shape è "dot", "glow", "streak", "ring" o "square"; count, size, speed, direction, spread, gravity, wobble, life, twinkle e opacity danno forma al movimento, e burst lancia particelle da ogni tasto premuto.
layout(id, name, rows=[...], left=[], right=[])Un layout, in Layout > Disposizione. Ogni riga è una lista di tasti: una stringa è una lettera, layout_key(...) tutto il resto. left e right mettono fino a tre tasti accanto alla barra spaziatrice. Sceglierlo installa un normale layout personalizzato.
layout_key(glyph, action="insert", width=1)Un tasto che non è una semplice lettera. action è uno fra insert, spacer, shift, delete, space, return, numbers, emoji, globe, tab, left, right, undo, redo o dismiss, e width si misura in tasti.
trail(id, name, layers=[...], colors=[])Una scia di scorrimento. layers sono voci trail_line, trail_stamps e trail_head, disegnate nell’ordine, e colors è la tavolozza a cui puntano.
trail_line(width=1, tail_width=1, color=-1, glow=0, dash=0, gap=0, band=0, flow=0)Il tratto lungo lo scorrimento. tail_width assottiglia il capo vecchio, color=-1 sfuma tutta la tavolozza lungo la linea, e dash, gap, band e flow la spezzano o la mettono in movimento.
trail_stamps(shape="dot", size=1, spacing=14, scatter=0, spin=0, twinkle=0, color=-1)Forme lasciate cadere lungo lo scorrimento: dot, ring, square, diamond, star, spark o heart. spacing è la distanza fra loro in punti, scatter le butta fuori dalla linea, spin le fa girare e twinkle le fa comparire e sparire.
trail_head(shape="dot", size=1.5, pulse=0, opacity=1, color=-1, glow=0)Il segno sotto il polpastrello. pulse lo fa respirare e glow gli sparge luce intorno.
def animations(state):
    return [
        entrance("swoop", "Swoop", y=90, scale=0.94, tilt=-20,
                 response=0.5, damping=0.72),
        press_animation("dip", "Dip", scale=0.93, y=2),
        letter_animation("bounce", "Bounce", y=-7, scale=1.12,
                         anchor="bottom"),
        transition("glide", "Glide", x=0.3, scale=0.96),
    ]
Un’animazione per ogni tipo. Ognuna compare in Dai plugin nella propria scheda di Aspetto.
def backgrounds(state):
    return [
        background("snowfall", "Snowfall", colors=["#ffffff", "#cfe8ff"], layers=[
            particles(shape="dot", count=70, size=2.2, speed=28,
                      direction="down", spread=12, wobble=10, life=9),
            particles(shape="glow", count=0, size=3, life=1,
                      burst=8, burst_speed=90),
        ]),
    ]
Snowfall: fiocchi che scendono ondeggiando e un soffio di luce da ogni tasto premuto.

Restituisci nodi trail da trails(state). Ogni scia ha una tavolozza colors e livelli come trail_line. Con color=-1, la linea sfuma i colori lungo lo scorrimento. Attiva il plugin, poi scegli la sua scia in Dai plugin nel selettore delle scie. Memorizzare elementi grafici nello stato non disegna una scia.

def trails(state):
    return [trail("rainbow", "Rainbow", colors=["#ff0000", "#ff8800", "#ffff00", "#00cc44", "#0066ff", "#9900ff"],
        layers=[trail_line(width=1.3, tail_width=0.2, color=-1)])]
Scia arcobaleno

Un aspetto viene copiato quando lo si sceglie, quindi cambiare lo script in seguito non cambia un aspetto già in uso; va scelto di nuovo. Una parola chiave scritta male, una parola fuori dalla sua lista o del testo dove va un numero è un errore nella console dell’editor, e i numeri restano negli intervalli usati dai controlli dell’app.

Grafica sui tasti

key_art(state) restituisce un dict dai nomi dei tasti alla grafica, e la tastiera la dipinge dentro il tasto. I nomi dei tasti sono quelli di haptics, con i ripieghi letters e keys. La grafica è una lista di shape(...), una forma sola, un livello art([...]) o una lista di livelli, e ogni livello passa al disegno successivo con il proprio orologio, così un tasto può portare due cose che si muovono a velocità diverse.

ChiamataCosa fa
art(shapes, animate=0, curve="ease_out")Un livello che si anima. Passagli un disegno nuovo e il livello ci scivola dentro in animate secondi lungo curve: ease_out, linear, ease_in, ease_in_out o spring. Più livelli sullo stesso tasto si animano ciascuno per conto suo.
shape(kind, anchor="center", unit="pt", x=0, y=0, size=, width=, height=, fill=, stroke=, ...)Una cosa disegnata: circle, rect, capsule, line, path, text o icon. Sta a x e y da un anchor del tasto, in punti o, con unit="key", in frazioni del tasto. fill e stroke accettano un colore esadecimale, "text", "accent", un color(...) o un gradient(...), e poi ci sono line_width, corner, trim_from, trim_to, rotation, opacity, blur e fino a tre ombre.
graph(values, min=None, max=None, width=0.8, height=0.25, stroke=, fill=)Una serie di numeri aperta in forme ordinarie, da unire a una lista di forme o da usare da sola. Restano i 60 campioni finiti più recenti, i limiti altrimenti vengono dalla serie stessa, e fill aggiunge un tracciato chiuso fino alla base sotto la linea.
color(value, opacity=1)Una tinta: un colore esadecimale, oppure "text" o "accent" risolto rispetto al tasto su cui finisce, con opacity.
gradient(kind, colors, stops=[], start=, end=, center=, radius=0.5)Una sfumatura lineare o radiale attraverso una lista di colori. stops li colloca, start ed end orientano quella lineare, center e radius piazzano quella radiale.
shadow(color, radius=4, x=0, y=0)Un’ombra sotto una forma, fino a tre. Un velo che riempie il tasto segue gli angoli del tasto invece di squadrarli; un bagliore invece esce dal tasto.

La grafica viene riletta dopo ogni evento che il plugin sente, ed è tutto qui il giro: cambi lo stato in on_event(name, info, state) e da lì disegni in key_art(state). events(state) nomina gli eventi voluti e viene letto una volta al caricamento del plugin. Senza di esso un plugin sente tutto tranne key, key_down, key_up, predictions e tick, che fanno girare uno script a ogni pressione o ogni secondo e vanno chiesti. I cambi di maiuscola e di piano ridisegnano la grafica che qualcuno ascolti o no, e arrivano anche in context().

def initial():
    return {"shift": "off"}

def events(state):
    return ["shift"]

def on_event(name, info, state):
    state["shift"] = info["state"]
    return state

def key_art(state):
    if state["shift"] != "locked":
        return {}
    lamp = shape("circle", anchor="top_right", x=-7, y=7, size=5,
                 fill="accent", shadow=shadow("accent", radius=4))
    return {"shift": art([lamp], animate=0.12)}
Una spia del blocco maiuscole, disegnata dal plugin invece che incorporata nella tastiera

Forme

  • circle
  • rect
  • capsule
  • line
  • path
  • text
  • icon

Sedici forme per tasto, contate su tutti i suoi livelli, e 64 punti per tracciato; quello che avanza viene lasciato cadere. Una forma si disegna dentro un riquadro dei suoi width e height, e un riquadro senza spessore non dipinge niente: una linea orizzontale ha quindi bisogno di un piccolo height suo, con i punti nel mezzo (height=0.03, points=[[0, 0.5], [1, 0.5]]), altrimenti non compare e non lo dice.

Feedback aptico, aree di tocco e correzioni

haptics(state) e hitboxes(state) restituiscono dict con i nomi dei tasti come chiavi: la lettera stessa, "space", "delete", "return", "shift" o "globe", poi "letters" per ogni lettera non nominata e "keys" per tutto il resto. I tasti che un plugin non nomina mantengono le impostazioni della persona. Entrambe le tabelle vengono lette all’apertura della tastiera e circa una volta al secondo, quindi per loro non gira niente a ogni tasto.

ChiamataCosa fa
feel(style=None, intensity=None, sharpness=None)Un feedback aptico: style è "soft", "light", "medium", "heavy", "rigid" o "off", e intensity e sharpness (da 0 a 1) lo regolano. Va bene anche una sola parola di stile.
hitbox(scale=1, x=0, y=0)Un’area di tocco: x e y spostano il bersaglio del tasto di quella frazione della sua dimensione, al massimo mezzo tasto, e scale lo ingrandisce o lo rimpicciolisce. Un numero da solo è uno scale.
def haptics(state):
    return {
        "space": "heavy",
        "return": "rigid",
        "delete": feel(intensity=0.45, sharpness=0.9),
    }
Heavy Space: una barra spaziatrice più pesante, un invio netto e una cancellazione leggera.

on_touch(key, x, y, state) viene eseguito dopo ogni tocco, una volta gestito, con il punto del tasto in cui si è posato il dito. La posizione è misurata rispetto al tasto disegnato, non al suo bersaglio spostato, quindi un plugin può spostare ogni tasto verso dove viene davvero toccato senza inseguire il proprio spostamento.

def initial():
    return {"keys": {}}

def on_touch(key, x, y, state):
    if len(key) != 1:
        return state
    n, ax, ay = state["keys"].get(key, [0, 0.0, 0.0])
    n = min(n + 1, 50)
    ax += (x - ax) / n
    ay += (y - ay) / n
    state["keys"][key] = [n, ax, ay]
    return state

def hitboxes(state):
    boxes = {}
    for key in state["keys"]:
        n, x, y = state["keys"][key]
        if n >= 12:
            boxes[key] = hitbox(x=round(x * 0.6, 2), y=round(y * 0.6, 2))
    return boxes
Adaptive Hitbox: impara dove viene davvero toccato ogni tasto e sposta il suo bersaglio di parte della distanza.

suggestions(word, state) mette parole in testa alla barra mentre si scrive una parola. correct(word, fix, state) viene eseguito una volta per parola quando lo spazio la chiude, con la correzione della tastiera o None, anche con la correzione automatica disattivata. Restituisci una parola per inserirla, False per lasciare la parola com’è stata scritta o None per lasciar decidere la tastiera. Vince il primo plugin che risponde, e cancellare subito dopo lo annulla come qualsiasi correzione automatica.

SHORT = {"brb": "be right back", "omw": "on my way", "idk": "I don't know"}

def suggestions(word, state):
    long = SHORT.get(word.lower())
    return [long] if long else []

def correct(word, fix, state):
    if len(word) > 1 and word.isupper():
        return False
    return None
Shorthand: propone «be right back» mentre si scrive brb e tiene la correzione automatica lontana dalle parole in maiuscolo.

Nessuno dei due hook viene eseguito nei campi password. Il suggerimento di correzione nella barra mostra la correzione della tastiera, non quello che restituirebbe correct, e una tabella cambiata in on_touch raggiunge la tastiera al ciclo successivo.

Provare nell'editor

L'anteprima dell'editor è una tastiera sostitutiva. Disegna settings(state) dal vivo, attiva qualsiasi hook con un tocco usando una parola, un tasto o un tipo di campo di esempio, mostra la barra spaziatrice come l'ha lasciata il plugin ed elenca tutto ciò che il plugin ha chiesto alla tastiera. Lì stats() restituisce numeri inventati, quindi una velocità compare senza scrivere.

  1. Usa i controlli nell'anteprima. Ognuno esegue on_action e ridisegna.
  2. Tocca i pulsanti degli hook nell'ordine in cui lo farebbe la tastiera: on_open, poi on_word o on_key qualche volta, poi on_close.
  3. Leggi la console. I comandi compaiono come li hai scritti, le righe di print() sotto, e un errore indica la sua riga.
  4. Ricarica per ripartire con lo stato azzerato. Modificare lo script non lo azzera da solo.

L'anteprima non ha un documento, quindi insert() e replace() si limitano a registrare. Per provarli, salva e scrivi in un campo qualsiasi con la tastiera aperta.

Limiti

Quando un plugin arriva come file o da un repository, Clink lo controlla prima di salvarlo. Deve stare sotto i 64.000 byte e le 1600 righe, definire almeno un hook, importare solo i moduli consentiti e non contenere nessuno di questi elementi:

Non consentito nei plugin condivisi

  • __
  • exec(
  • eval(
  • open(
  • compile(

Moduli che un plugin condiviso può importare

  • json
  • math
  • random
  • re
  • sys
  • time

Ogni chiamata a un hook riceve 2.000.000 passi dell'interprete, gli stessi di un rendering di pannello. Però on_key gira a ogni pressione, quindi un lavoro pesante lì rallenta la scrittura molto prima di arrivare a quel limite.

Il file

Un plugin è un unico documento JSON. id resta stabile tra gli aggiornamenti, version è testo libero mostrato nell'elenco, icon è il nome di un SF Symbol e source è lo script con gli a capo con escape. Condividilo dall'ispettore dell'editor, oppure importane uno con il pulsante freccia della scheda Plugin.

{
  "id": "word-count",
  "name": "Word count",
  "icon": "text.word.spacing",
  "summary": "Counts words on the space bar",
  "version": "1.0",
  "author": "You",
  "enabled": true,
  "source": "def initial():\n    return {\"on\": False}\n..."
}
Un .clinkplugin, abbreviato.

I plugin si condividono come file .clinkplugin, un documento JSON con dentro lo script. Si pubblicano tramite un repository come i pannelli, con una cartella di file, un manifest e una release. Il repository ufficiale è anti-ltd/clink-plugins.

Repository ›

Quando qualcosa non succede

Il più delle volte è una di queste.

SintomoCosa controllare
Non succede nullaI plugin sono disattivati in cima alla scheda Plugin, il plugin è disattivato nell'elenco, o non c'è un abbonamento Clink Pro. In tutti e tre i casi la tastiera non esegue alcun plugin.
Il testo della barra spaziatrice è disattivatoUn plugin l'ha presa. La riga sotto il campo dice quale; spegni l'interruttore di quel plugin, o il plugin stesso, per riavere il campo.
La sezione non compareL'ancora è scritta male. Confrontala con Mostra gli ID nell'app, e ricorda che la sezione compare comunque nella pagina del plugin, che è dove guardare per prima.
Un tasto elemento è vuotoIl plugin dietro è spento, disinstallato, o il suo draw(id, state) ha sollevato un errore. Il layout tiene il tasto comunque, quindi si riempie di nuovo appena il plugin torna acceso. L'errore è nella console dell'editor.
Un elemento non cambia maidraw viene ancora chiamato, quindi è lo stato dietro a non muoversi. Ciò che alimenta il volto va aggiornato da un hook: on_tick per qualcosa che cambia da sé, on_word o on_key per qualcosa che segue la digitazione.
set_setting() non ha fatto nullaUn nome che non è nell'elenco sopra solleva KeyError. Un valore del tipo sbagliato o fuori intervallo viene saltato, e la console dell'editor dice cosa si aspetta l'impostazione. Un'impostazione riservata torna anche indietro senza abbonamento.
Il conteggio si è azzeratoLa tastiera salva lo stato quando si chiude, non a ogni tasto. Una tastiera terminata dal sistema a metà sessione perde ciò che i suoi plugin hanno contato da quando si è aperta. Tieni i totali negli hook lato app quando conta.
La digitazione sembra lentaQualcosa di pesante gira in on_key. Spostalo in on_word, fanne di meno, o metti in cache ciò che calcola nello stato.
Un effetto manca da Dai pluginPlugin deve essere attivo e il plugin abilitato, ed effects(state) deve restituire un light_effect con almeno un livello. Tocca effects nell’editor per vedere cosa è tornato, e cerca un ValueError nella console.
Un elemento della barra superiore non fa niente quando lo si usaUn tocco e una manopola lasciata arrivano entrambi a on_action(action, value, state), agganciati all’id dell’elemento e non al suo nome. Una manopola scrive un’impostazione solo se bar_knob ne nomina una in setting=; senza quello il valore resta al plugin, e un pulsante non scrive mai un’impostazione.
PyMini ›
Scarica dall’App Store