Architecture & Specs

Key Extension Architecture Technical Guide

Technical architecture documentation covering the deterministic 4-State FSM engine, scaling timers, suppression-first snippet model, and atomic output pipelines in fjFlow.

Deterministic 4-State FSM, race condition prevention, 0ms remapping, and suppression-first snippet expansion architecture

This document details the engineering architecture behind fjFlow’s Key Extension engine, including its state machine, scaling timer algorithms, suppression-first snippet expansion, and output pipeline.


Table of Contents


1. Core Philosophy: Strict Decoupling of Detection and Execution

The key extension engine strictly separates the Detection Phase from the Output Phase.

┌────────────────────────────────────────────────────────┐
│ 1. Detection Phase (FSM Engine)                        │
│   - Receives raw physical keystrokes & timestamps      │
│   - Isolates real-time modifier/layer variations       │
│   - Captures down-state layer snapshot (downCond)      │
│   - Evaluates exact match, prefix match & silence      │
└──────────────────────────┬─────────────────────────────┘
                           │ Commit (Execute / Flush)
┌──────────────────────────▼─────────────────────────────┐
│ 2. Output Phase (Output Pipeline)                      │
│   - Executes target mapping using preserved snapshot   │
│   - Applies synthetic modifier locks (Ctrl/Alt/Win/Shift)│
│   - Sends atomic OS keystrokes via {Blind} mode        │
└────────────────────────────────────────────────────────┘

Even if a user releases the modifier trigger (Space) immediately after striking a key, execution evaluates against the press-down snapshot (g_FsmKey1Cond), preventing modifier release errors.


2. Deterministic 4-State FSM Model

The engine operates on a 4-state deterministic state machine:

  • STATE_READY: Idle state. No keystrokes pending in queue.
  • STATE_WAIT_HOLD: Hold detection state (waiting after pre-silence to see if key remains held).
  • STATE_WAIT_NEXT: Next key / tap detection state (first key withheld, awaiting subsequent strokes or timeout).
  • STATE_WAIT_POST_SILENCE: Post-silence wait state (longest match reached, awaiting brief pause to confirm intention).
stateDiagram-v2
    [*] --> STATE_READY

    state STATE_READY {
        [*] --> Idle
    }

    STATE_READY --> STATE_READY : 1. [a] (Instant tap)<br/>0ms immediate remap
    STATE_READY --> STATE_READY : 2. Active typing (no pre-silence)<br/>Normal pass-through
    STATE_READY --> STATE_WAIT_HOLD : 3. Pre-silence + < a > (hold defined)<br/>Hold timer started (holdMs)
    STATE_READY --> STATE_WAIT_NEXT : 4. Pre-silence + [a] (tap) / Sequence<br/>Withhold 1st stroke (comboMs)

    state STATE_WAIT_HOLD {
        [*] --> HoldWaiting
    }

    STATE_WAIT_HOLD --> STATE_READY : 5. holdMs elapsed (held down)<br/>Execute hold target
    STATE_WAIT_HOLD --> STATE_READY : 6. KeyUp before holdMs (released)<br/>Cancel hold ➔ tap fallback
    STATE_WAIT_HOLD --> STATE_READY : 7. Other key struck before holdMs<br/>Instant cancel ➔ flush pending

    state STATE_WAIT_NEXT {
        [*] --> Pending
    }

    STATE_WAIT_NEXT --> STATE_WAIT_NEXT : 8. Prefix matched (e.g., 'us' in 'usa')<br/>Withhold next key (Timer: Nx ms)
    STATE_WAIT_NEXT --> STATE_WAIT_POST_SILENCE : 9. Longest exact match (e.g., 'usa')<br/>Wait for post-silence (Nx ms)
    STATE_WAIT_NEXT --> STATE_READY : 10. Timeout reached<br/>Execute intermediate match
    STATE_WAIT_NEXT --> STATE_READY : 11. No match / expired<br/>Flush pending keys in order

    state STATE_WAIT_POST_SILENCE {
        [*] --> PostSilenceWait
    }

    STATE_WAIT_POST_SILENCE --> STATE_READY : 12. Post-silence confirmed (pause)<br/>Execute target snippet!
    STATE_WAIT_POST_SILENCE --> STATE_READY : 13. Rapid subsequent key struck<br/>Break combo ➔ flush raw strokes

    STATE_WAIT_HOLD --> STATE_READY : 14. Shift Up<br/>Flush raw lowercase characters
    STATE_WAIT_NEXT --> STATE_READY : 14. Shift Up<br/>Flush raw lowercase characters
    STATE_WAIT_POST_SILENCE --> STATE_READY : 14. Shift Up<br/>Flush raw lowercase characters

3. Variable-Length Sequences (Up to 5 Keystrokes) & Scaling Timer

Detection wait times scale dynamically with sequence length $N$ (up to 5 keystrokes):

$$\text{Detection Window} = N \times x \text{ (ms)}$$

  • With base interval $x = 300\text{ms}$:
    • 1 Key: $300\text{ms}$ (single tap evaluation)
    • 2 Keys: $600\text{ms}$ (e.g., hh, eq)
    • 3 Keys: $900\text{ms}$ (e.g., usa, btw)
    • 4 Keys: $1200\text{ms}$ (e.g., date, mail)
    • 5 Keys: $1500\text{ms}$ (e.g., azasu, yoros)
  • Longer sequences automatically grant a wider, relaxed typing window for confident input without rush.

4. Snippet Expansion Advantages: Suppression-First Model

4.1 Structural Flaws of Traditional “Post-hoc Delete” Snippet Tools

Traditional text expansion tools almost exclusively rely on a “Post-hoc Delete” sequence: the abbreviation is printed on screen, a series of synthetic Backspace strokes erases it, and the expanded string is pasted.

In multi-lingual or East Asian IME environments, this model suffers from 3 fatal flaws:

  1. Divergence of Key Count and Character Count: In English, typing btw (3 keys) produces 3 characters, erased cleanly by 3 Backspaces. In Japanese Romaji IME, typing a-z-a-s-u (5 keys) produces only 3 characters (あ・ざ・す). Tools cannot reliably query rendered character counts from Windows or IMEs, causing excess Backspaces that accidentally destroy existing, valuable text.
  2. Collision with Active Inline IME Composition: Sending Backspaces during uncommitted IME composition closes suggestion candidates, breaks phrase segments, and corrupts the input queue.
  3. Screen Flicker and Visual Noise: The cycle of “Render ➔ Backspace Erase ➔ Insert” creates perceptible visual flicker and layout stuttering.

4.2 fjFlow’s Suppression-First & Deferred Execution Model

The fjFlow key extension engine does not draw keys on screen and delete them later. Instead, keystrokes are withheld (suppressed) in memory during evaluation. Once the sequence matches, the target text is injected cleanly without sending a single Backspace. If unmatched, pending keys flush instantly in exact sequence.

Evaluation Metric Traditional Snippet Tools fjFlow Sequence Combos (Up to 5 Keys)
Existing Text Safety High risk of over-deletion (Destructive) 100% Non-destructive (Zero Backspaces sent)
IME Mode Independence Behavior breaks depending on IME state Completely transparent via physical key hook
Visual Ergonomics (UX) Visible flicker (Type ➔ Erase ➔ Insert) Zero flicker; target string appears cleanly
Cancellation Safety Difficult to revert accidental triggers Tap Shift once to cancel and flush raw characters

4.3 The Paradigm Shift of Expanding to 5 Keystrokes

Extending sequence combos from 2 keys to 5 keys unlocks an entirely new domain of productivity:

  • 2-Key Domain (Symbols & Quick Shortcuts): eq ➔ =, pt ➔ (), br ➔ [], s; ➔ Center window. Designed to access punctuation and window commands without lifting hands from home row.
  • 3 to 5-Key Domain (Natural Language Snippets & Boilerplates):
    • English Abbreviations: btw (by the way), omw (on my way), afaik (as far as I know)
    • Japanese Greetings: azasu (ありがとうございます), otk (お疲れ様です), yoros (よろしくお願いいたします)
    • System & Contact: mail1 (Email address), tel1 (Phone number), date (Today’s date)

Over 90% of intuitive human mnemonic abbreviations and consonant contractions fit within 3 to 5 keystrokes. With 5-keystroke support, fjFlow transcends simple keyboard remapping to become a reliable, non-destructive snippet engine for multi-lingual desktop environments.


5. Instant 0ms Remapping via Silence Bypass

Enclosing trigger keys in brackets ([]) completely bypasses pre- and post-silence detection:

  • Single Tap [a]: Executed with 0ms latency the moment the key is struck, replacing it immediately (pure key remap).
  • Sequence [ca]: Committed immediately upon striking the final key a, bypassing post-silence wait times.

6. Smart Coexistence of Intermediate Matches and Longest Prefix

When both us ➔ _ (2 keys) and usa ➔ United States (3 keys) are registered:

  1. Striking u ➔ s identifies _ as a candidate match while detecting that usa remains a valid prefix.
  2. The FSM holds state STATE_WAIT_NEXT and preserves _ as an intermediate target while the timer runs.
  3. If a arrives, it transitions to usa and commits United States.
  4. If the timer expires without further input, it commits the intermediate candidate _.
  5. If an unrelated key arrives, it flushes all pending keystrokes in raw order.

7. Safe Combo Cancellation via Shift Key

To safely cancel a pending sequence and output raw letters:

  1. Press the Shift key during combo evaluation.
  2. FsmOnShiftDown() triggers and immediately halts all pending timers without outputting text.
  3. Release the Shift key. FsmFlushOnShiftUp() flushes all buffered keys (u, s, a) in safe lowercase form.

8. Race Condition Elimination via Serialized Low-Level Hooks

To prevent keystroke order inversion (sh ➔ hs) during fast typing:

  • All mapping trigger keys are registered under the default layer (L0) as serialized $key hooks.
  • Because all keystrokes pass sequentially through the single FSM pipeline, race conditions between OS native dispatch and hook dispatch are structurally eliminated.