[Unreleased]
Fixed
layout/nicola.yab/nicola_us.yab: Fixed a bug where the number-row left/right thumb shift produced duplicate "6"/"7" for the keys "4" and "5"
- Since the very first commit, both the left and right thumb-shift number rows had duplicated bracket symbols as
4:[ 5:] 6:[ 7:], and the Japanese corner brackets 「」 did not exist anywhere on the number row
- Fixed to
4:「 5:」 (Muhenkan+4 → 「, Henkan+5 → 」), leaving 6:[ 7:] unchanged
[1.10.0] - 2026-07-19
Added
- Added cell copy/paste (history-based) to the "Layout Editor" tab
- Select a cell and press "Copy" and its value is automatically pushed to the top of the history (up to 4 entries, retained even across shift planes)
- The history is shown as a row of buttons, and a single click pastes directly into the selected cell (because it does not go through the text field, the result is exactly the same value as the source cell, including the kana conversion of romaji)
- Copying the same value again does not create a duplicate; it simply moves it to the top of the history
- US layout: Added the ability to impersonate thumb keys with the left/right Alt keys
- Because the US layout has no Muhenkan/Henkan keys on either side of the space bar, a setting was added to let the left/right Alt keys impersonate
left_thumb_key/right_thumb_key (left_alt_impersonates_thumb_key/right_alt_impersonates_thumb_key, default false). This lets awase accomplish, on its own, the same thing as an external remapper like PowerToys
- It only takes effect while the engine is ON; while OFF the keys act as normal Alt so Alt+Tab and the like are not broken. Left and right can be configured independently, and while held down the state is fixed at the moment of the keypress to prevent stuck modifiers
- Shown in the settings screen (awase-settings) only when the US layout is selected
- US layout: Added a composing-time fallback for the Space thumb key and immediate emission of Shift+Space
- When Space is assigned as a thumb key, a solo tap can now emit
VK_SPACE (advancing conversion candidates) even while the candidate window is shown (unlike Muhenkan/Henkan, emitting a raw VK_SPACE during composing is a legitimate IME function)
- Shift+Space is emitted immediately as a literal space without waiting for chord detection
- The settings are
space_thumb_ignore_composing_guard/space_thumb_shift_literal (default true). Shown in the settings screen only when Space is a thumb key
- Settings screen: Automatically reset/restore the default hotkey values when switching between the US and JIS layouts
- Switching to the US layout now clears the JIS-oriented default hotkey values (Muhenkan/Henkan keys, etc.) to empty, and switching back to JIS restores the JIS defaults
Fixed
- BUG-31: Fixed a bug where characters were silently lost during continuous typing in Microsoft Teams
- App: Microsoft Teams (
TeamsWebView, Chromium-based, Vk mode). IME: Google Japanese Input (GJI)
- Reproduction: Immediately after confirming with Enter, even though the state was warm (
GjiFsm::OnWarm), a physical NativeF2Down (non-TSF) event unconditionally issued MarkCold+GjiCompositionReset, killing the warm-maintenance timer, so subsequent romaji got caught in a per-VK confirm race and some was lost (field report: part of "5せっしょん" was silently dropped)
- Added a guard to ignore
NativeF2Down while warm
- BUG-30: Fixed a bug where characters that had been correctly entered were mistakenly backspaced while the GJI candidate window was visible
- The candidate window's SHOW event (edge-triggered) and its live visible state (level-triggered) were separate sensors, and there was no protection during visibility when
SuspectedLiteral was detected. Added a veto that holds off backspacing while visible, and unified the literal-detect logic across TSF/Chrome
- Alt thumb-key impersonation: Fixed a bug where the
OsModifierHeld bypass disabled NICOLA chord detection entirely
- The vk rewrite itself was being done correctly, but a separate path derived from
GetAsyncKeyState (modifiers.alt) was still returning the physical Alt press as-is, so BypassReason::OsModifierHeld was misfiring in three places. Fixed to correct modifiers.alt during impersonation
- Alt thumb-key impersonation: Fixed a bug where in some environments the vk arrived as the generic
VK_MENU, so impersonation never triggered
- In some environments the
WH_KEYBOARD_LL vkCode arrives as the generic VK_MENU rather than the left/right-distinguished VK_LMENU/VK_RMENU, so a comparison based only on direct matching was always false. Fixed to distinguish left/right using the LLKHF_EXTENDED flag
- Settings screen: Fixed a bug where the "Apply" button was always disabled
awase-settings.exe's config-reload notification was using FindWindowW to look for a nonexistent window class name (awase_msg_window), so the notification never reached awase.exe (awase_tray_window). Fixed to the correct class name, and on failure it now explicitly logs via log::warn!
- Settings screen: Fixed a bug where
config.toml path resolution was asymmetric with awase.exe
awase.exe can specify an explicit path via a command-line argument, but awase-settings.exe ignored it, risking editing a different config.toml. Aligned the priority order, and also added a feature that always displays the path being edited at the top of the screen
- Settings screen: Fixed an issue where long validation-warning text was cut off by the button row and not shown
Changed
- Greatly simplified the GJI/Chrome cold-start warmup architecture
- After several days of real-machine soak testing (60+ cold events, zero suspected literals) confirmed no corruption, the wait-queue (the
ColdReason×long_idle matrix of WarmupKind::FreshF2/ReWarmup/ProbeWithSettle etc.) and the sacrificial-key mechanism (SacrificialWarmupCoro/ImeOffOnWarmupFsm) were physically removed, consolidating onto the per-VK confirm approach that sends romaji one character at a time and confirms each
- Unified the per-VK confirm loops that were duplicated between Chrome (
probe_fsm.rs) and TSF (gji_warmup_coro.rs) into a shared implementation (net -82 lines)
- Finalized the addition of scan codes to Chrome VK sends, promoting it from experimental to permanent behavior
- As the foundation supporting the above, added
StepCoro::prime() (self-priming without a dummy input) to the internal dependency crate timed-fsm
[1.9.1] - 2026-07-12
Added
- Added a "Layout Editor" tab to the settings screen (edit .yab in place)
- Merged the standalone
awase-yab-editor binary into awase-settings and deleted it (the code is worth reusing, but a separate binary was judged not worth it; the cost of keeping two binaries in sync across CI, distribution, and the installer outweighed the practical benefit)
- All four planes (normal / left thumb shift / right thumb shift / little-finger shift) can be edited via the flow: click a cell in a keyboard-style grid to select it → choose the type (keystroke / literal / special key / none) and edit → apply
- The former "Romaji" and "Key sequence" types were consolidated into "keystroke," auto-detected by the input (whether it is alphabetic). If you enter a key that does not exist on a JIS keyboard, an error is shown in real time and the apply button is disabled
- Even if you enter symbols/alphanumerics in full-width, they are automatically converted to half-width, so the input side does not need to be conscious of half/full width
- Supports Open / Save / Save As / Reload, and the Ctrl+S / Ctrl+O / Ctrl+Shift+S / F5 shortcuts
Fixed
- Fixed an issue where the settings screen's "Layout Editor" tab (formerly the Preview tab) did not display / crashed
- Unified
layouts_dir/config.toml path resolution onto awase::paths::resolve_relative_to_exe, fixing a bug where layout/nicola.yab directly under the workspace root could not be found when launched from under target/debug/ via cargo run/cargo build
- Re-showed the tab that had been hidden on 2026-07-06 (
c3fa08e) on the grounds that "the layout preview implementation was not solid yet"
- Fixed a bug where opening the tab caused a silent forced termination (the cause was that
egui::Grid does not allow cursor movement via in-row add_space(). Blocked by the two issues above, it had never once been rendered with real data, so this was a latent bug no one had hit)
- Added log file output (
awase-settings.log) + a panic hook to awase-settings. Because the GUI subsystem has no console, panics had previously left nothing in any log
- Fixed an issue where
.yab was loaded synchronously every time the tab was opened, lengthening the interval between window creation and the first render; changed to lazy loading
[1.9.0] - 2026-07-11
Added
- Persistent "IME-ON half-width alphanumeric" toggle via a left Shift solo tap (BUG-25, MS-IME support)
- Added a persistent toggle where a solo tap of the left Shift key (press → release without any intervening key) switches to half-width alphanumeric input while keeping the IME open. Another solo tap returns to normal kana input
- This replaces BUG-15's "half-width alphanumeric while Shift is held" (hold approach). It keeps the existing safety net that cancels out MS-IME's false detection of a Shift solo tap during a Shift+character chord (the Shift plane of
.yab), while removing only the ASCII pass-through layer (shift_plane_halfwidth) that was specific to the hold approach
- A right Shift solo tap always performs only the safety-net restore, and pressing it during the persistent toggle also acts as an "emergency release"
- For GJI (Google Japanese Input), three entry mechanisms were tested on real hardware — scan-code-tagged VK injection, IMC write, and scan=0 VK injection — but none worked (CapsLock contamination, or
SendInput not even reaching the hook), so the feature was disabled for GJI (MS-IME only). See docs/known-bugs.md BUG-25 and docs/experiments.md entries 06–09
- Changed Ctrl+Henkan pressed while the IME is ON to reset to hiragana + romaji + CapsLock OFF
- Previously it only responded to an IME OFF→ON transition, but now, even when the IME is already ON, pressing Ctrl+Henkan re-confirms the input mode by aligning it to hiragana, romaji, and CapsLock OFF
- Added a guard that targets only the case of a Ctrl-only modifier plus a single VK_CONVERT press, so that unrelated IME-ON paths such as Ctrl+Shift+Henkan (the EngineOn combo) are not falsely detected
[1.8.9] - 2026-07-11
Added
US (ANSI 104) keyboard support
- Added US-layout-specific scan-code ⇔ physical-position tables (
scan_to_pos_us/pos_to_scan_us) to crates/awase-windows/src/scanmap.rs, and wired them to be selected according to KeyboardModel::{Jis,Us}
- Reintroduced the
general.keyboard_model ("jis"/"us") setting. The field of the same name had been removed on 2026-07-06 as "never once wired up"; this time it was revived after actually wiring it all the way through .yab parsing and HookConfig (the hook's scan_to_pos table selection)
- Corrected
KeyboardModel::Us.row_sizes()'s row0 from 13 to 12 (the grave key is treated as off-grid since it has no physical key mapping. The old value was an error in never-executed, unverified code)
- Added the US-layout
layout/nicola_us.yab (the columns for JIS-only keys removed from layout/nicola.yab. The values of the shared scan codes are reused as-is)
- Extended
AppConfig::validate to warn at startup validation when keyboard_model = "us" yet defaults that assume Muhenkan/Henkan keys (left_thumb_key/right_thumb_key and each hotkey under [keys]) remain
- Added JIS/US selection, layout-preview tracking, and thumb-key candidates to the settings screen (awase-settings)
- The Linux/macOS side is not covered this time (remains fixed at
KeyboardModel::Jis)
Discovered that assigning Ctrl/Alt/Win to a thumb key does not work (engine/tests.rs)
- We once recommended
VK_LMENU/VK_RMENU (Alt) as a thumb-key substitute for the US layout, but
it turned out that the KeyDown of that key itself immediately hits ModifierState::is_os_modifier_held() →
the OsModifierHeld of bypass_reason, so it never enters PendingThumb at all and is
passed straight through (chord detection itself does not work). Not just Alt but
Ctrl/Win are likewise impossible
- This constraint is pinned by
test_ctrl_alt_win_thumb_key_never_enters_pending_due_to_os_modifier_bypass.
Shift is not subject to is_os_modifier_held(), so it can reach PendingThumb
(test_thumb_alone_timeout_suppressed_when_thumb_is_os_modifier
confirms the suppression of a solo tap), but its interaction with the Shift-plane feature is untested
- Also added a defense to
timeout_pending_thumb that, when the thumb key is an OS modifier,
suppresses raw VK emission of a solo tap regardless of composing (propagating
modifier_key into ClassifiedEvent/PendingThumbData)
- Withdrew Alt from the recommendations in config.rs / config.toml.sample / awase-settings, and
now suggest F13-F24 (assuming physical remapping on a programmable keyboard) / VK_SPACE as substitutes
Fixed the emergency Engine OFF via solo Muhenkan repeats being too prone to misfiring
- Simply mashing the Muhenkan key to recover from a misread conv mode right after waking from sleep (stuck at katakana) caused the "solo triple-tap of Muhenkan turns the engine OFF" mechanism (ADR-055) — prepared as an emergency escape for a stuck Ctrl — to misfire, setting
user_enabled to false in a real-machine case (2026-07-08). Once triggered, user_enabled did not come back with normal key operations like Ctrl+Henkan (ime_on), leaving a "nothing fixes it" state
- Raised the required repeat count from 3 to 5 (
SOLO_OFF_TRIGGER_COUNT, src/engine/nicola_fsm.rs) to make misfires less likely
- Now shows a tray notification when triggered, so the user can immediately understand "the engine has emergency-stopped" and "it can be recovered with
Ctrl+Shift+Henkan." See the addendum in docs/adr/055-engine-off-solo-triple.md
Fixed a bug in Chrome/Edge where a single misread of the conv mode actually locked GJI into katakana (BUG-19)
- The
GetForegroundWindow()-based conv reading (get_ime_conversion_mode_raw_timeout) could momentarily pick up an incorrect katakana conv while focus bounced between Chrome_WidgetWin_1 and the GJI candidate popup (Windows.UI.Input.InputSite.WindowClass), and ConvModeMgr unconditionally committed this
- Eager warmup (
send_eager_tsf_warmup) took the committed misread at face value and actually sent VK_DBE_KATAKANA → the transient misread got locked in as GJI's real state, turning all subsequent input into katakana, and furthermore the KatakanaShadowOff rescue logic repeatedly bounced IME OFF/ON, inducing leading-character literal leaks
- Added debouncing limited to a non-katakana→katakana transition (the same "confirm over 2 consecutive ticks" pattern as
ImeKindDebounce) to ConvModeMgr::update_from_conv
- Addendum: The above only protected the warmup side (
ConvModeMgr); classify_conv_transition (belief updates, engine sync such as KatakanaShadowOff) was re-interpreting raw conv directly and was defenseless against the same misread. Changed classify_conv_transition's argument from conv: u32 to a ConvMode derived from ConvModeMgr::get(), so that warmup and belief/engine-sync reference the same committed value. See docs/known-bugs.md BUG-19
- Addendum 2: Even in a debounced state, if a conv misread fired
KatakanaShadowOff/NativeToggleShadowOff right after the user explicitly turned the IME OFF, it would spoof UserImeSetIntent{Command} and directly rewrite desired_open, causing a separate-path recurrence where the engine returned to ON on its own. Separated this into EngineSync::ReportOpenInference, which no longer rewrites desired_open but merely records it as an ObserverReported (ObservationSource::ConvOpenInference, Medium confidence). The actual correction decision is delegated to the existing drift correction (BUG-20 already fixed the OFF direction too). Also added a source-aware gate that never fires a correction from this observation alone as long as there has never been any explicit user intent
Fixed a bug during Chrome input where a single misdetection of the CLSID-based IME kind caused GjiFsm to be rebuilt per word and cold to keep firing (BUG-17)
- The
gji-io-monitor worker thread was propagating single flips of ITfInputProcessorProfileMgr::GetActiveProfile — which it polls every 2 seconds — to the main thread as-is via WM_IME_KIND_CHANGED, and set_active_ime_kind unconditionally created a new warmup strategy (GjiFsm/MsImeStrategy) on every kind change → discarding the established OnWarm/OnComposing
- The very fact that Chrome cold-start reinit sends a real
VK_IME_OFF→VK_IME_ON toggle became the trigger for the misdetection, forming a self-amplifying loop of "reinit → misdetection → GjiFsm rebuild → next word is cold too → reinit again → …" (in field logs, cold_seq climbed 392→401 per word, and two "StartComposition while engine off" warnings were observed at 2146ms intervals, nearly matching the CLSID polling period)
- Added
ImeKindDebounce to tsf/gji_monitor.rs so that a new kind is not committed until it is observed on 2 consecutive ticks. See docs/known-bugs.md BUG-17
- Addendum 3: Even after debouncing,
cold_warmup.rs::preamble() (run on every cold warmup) unconditionally wrote conv_mode.get() back to the real IME, so a once-mistakenly-committed belief kept being re-asserted on every focus bounce, leaving a path that self-amplified. Added ConvModeMgr::needs_conv_restore_write/mark_conv_restore_written to limit the restore write to the same committed mode to just once
- Addendum 4: The above throttle was wired only into the IMM32 write-back paths of
cold_warmup.rs/probe_io.rs, leaving only output/mod.rs::send_eager_tsf_warmup — the very thing the root-cause analysis had named (the charset selection of eager warmup, which fires at high frequency on every confirm key) — defenseless. Applied the same throttle here too, limiting the sending of charset-changing warmup keys (F1/F0 family) for an already-committed mode to once
- Addendum 5: Since the user confirmed they had never manually selected katakana / half-width alphanumeric from the IME tray and have no plans to do so, a
DIAG_FORCE_HIRAGANA_CHARSET diagnostic flag was added, starting an experiment that disables the charset-following logic (F1/F0 warmup, IMM32 conv_target write-back, F1 leading warmup prefix) wholesale and always treats it as Hiragana (observation/logging themselves continue; only acting on it is stopped)
Fixed a bug where the first character right after turning the IME ON, though correctly converted, got an unnecessary BS correction (BUG-24)
is_partial_literal() (tsf/warmup/literal_detect_fsm.rs) was judging partial literals not by the confirmation signal of the romaji it just sent (candidate window SHOW / GJI I/O change) but by the unrelated proxy indicators nc_fired/gji_resumed (whether there was a response to a different F2 warmup key) that had been settled before sending. Right after ColdReason::requires_settle() (FocusChange/NativeF2Consumed/SetOpenTrue — which can occur even when the IME is already ON), under DIAG_DISABLE_PROACTIVE_TSF_WARMUP (a diagnostic flag that disables cold-start proactive warmup wholesale and relies only on reactive detection), the confirmation send that is the source of this proxy indicator is itself unconditionally skipped, so nc_fired became structurally always false, and it reproduced reliably on real hardware
- Also identified and fixed a side effect where
composition_fsm.rs::ConfirmKeyDown and platform.rs::on_reinject_key unconditionally issued MarkCold/GjiCompositionReset even for warm confirm keys (Enter/Space/Escape) (the extra cold marking during continuous typing was increasing the exposure of literal-detect)
- Added a mechanism (the
literal_session_confirmed family in tsf/observer.rs, DIAG_LITERAL_SESSION_SKIP) that, once literal-detect has been confirmed once within an IME session (from the start of typing to the candidate window HIDE), skips the detection process itself until the session ends, so as not to slow down responsiveness
- The above alone did not fix the first character of the session itself (the one that first passes through
is_partial_literal()), so ultimately the design was changed to, for the first character of a session only, send the romaji's VKs one at a time and confirm the sent VK's own CompositionConfirmed/SuspectedLiteral before sending the next VK (ProbeAction::TransmitSingleVk, gji_warmup_coro.rs). The "which VK's effect is it" ambiguity that arose from sending two VKs together was structurally resolved by inserting a deliberate confirmation point between VK sends. The symptom's resolution was confirmed on real hardware
- See docs/known-bugs.md BUG-24
[1.8.6] - 2026-07-07
Fixed
- Fixed Teams (TeamsWebView) being misclassified as still a target of IMM32 cross-process control
- Microsoft Teams's main window class
TeamsWebView was not listed in IMM32_UNAVAILABLE_CLASSES and was handled as the Standard classification, so unreliable IMM32 open-status reads were being attempted despite it being Chromium-based like the Chrome family. Added it to be classified as Imm32Unavailable / TsfNative like Chrome_*
- Along with this, added
sanitize_focus_probe_open_status() to the completion handling of kp_stage_focus_probe, adding a defense that, if the current_app_profile at apply time does not support IMM32 open status (Imm32Unavailable/TsfNative), forcibly discards probe.ime_on. This stops trusting a stale probe.ime_on even for apps whose classification changes later
Changed
- Wired empirically-learned IMM32 capabilities into focus-profile determination (unreachable-path audit B5)
ImmCapabilityStore (which learns from ImmGetDefaultIMEWnd=NULL detection and exceeding an IME detection-miss threshold, and persists to cache.toml) had, until now, only learned and saved, with no consumer that changed behavior
- On focus updates, classes whose static classification is
Standard but whose learned value is Unavailable are now demoted to Imm32Unavailable (so that IMM-broken apps not listed in the static list no longer incur wasteful SendMessageTimeoutW)
- Promotion is not performed (static Imm32-unavailable / TSF-native knowledge takes priority). Demotion self-releases via
Works recovery learning. Demotions are auditable via the [imm-learning] profile demoted INFO log, and mislearning is cleared by hand-editing [imm_capability] in cache.toml
- Note: learned entries already accumulated in an existing cache.toml take effect immediately. If an unexpected demotion occurs, check the logs
- Cleanup based on the unreachable-path audit (2026-07-06, 5 parallel perspectives)
- Removed 2 orphan WM handlers (
WM_PROCESS_DEFERRED / WM_IME_KEY_DETECTED) and a group of enum variants with zero construction sites (ImeEffect::RequestRefresh, ActivationState::Pending+PendingReason, InactiveReason::NonTextFocus, DecisionOrigin, DetectionSource::UiaAsync, ConvModeAuthority::TemporarilyUnowned, ColdReason::SessionExpired, ChordKind::CtrlHenkanImeOn+ImeEvent::ChordStarted)
- Removed an event-driven wakeup mechanism that was firing on nothing (
win32-async::AtomicWatcher + notify_all, the write-only composition_probe counter) (waiting had already been replaced by polling)
- Simplified an always-false condition (
used_eager_path), folded away EffectOrigin/SetOpen.origin that had become always-true, and removed a dead write (GjiState::OnCold.saw_native_f2)
- All of these were confirmed via repository-wide grep and individual verification of the evidence chain as "values that do not exist at runtime / paths that are not taken." No behavior change
Fixed
- MS-IME cold start — Fixed leading-character literalization right after IME ON (BUG-13, "を"→"wお")
MsImeStrategy assumed "MS-IME is always warm" and had no cold-start protection, so sends within ~130-300ms (measured) right after an IME OFF→ON transition literalized the leading VK
- Introduced a confirm-then-transmit of the same type as the GJI probe rather than a fixed wait: when
ImeModeFsm has not confirmed NATIVE, romaji is deferred to MsImeReadyCoro, and it is sent the moment the NATIVE bit is confirmed via IMC_GETCONVERSIONMODE polling (10ms interval)
MS_IME_READY_CONFIRM_MS (400ms) is a safety valve only (in environments where IMC cannot be read, it forces the send + a give-up latch to prevent every keypress from becoming a probe)
- See docs/known-bugs.md BUG-13
Added
- Detect conflicting MS-IME key assignments and guide the user to disable them
- When MS-IME's "Muhenkan key = IME-off" / "Henkan key = IME-on" assignments are enabled, a solo tap of Muhenkan/Henkan that awase passes through flips the IME state on the OS side only, diverging from belief (real machine 2026-07-06: a Muhenkan tap 92ms after IME ON produced "IME OFF, Engine ON," turning thumb-shift input into raw romaji)
- On the first
WM_IME_KIND_CHANGED where the active IME is confirmed to be MS-IME, it reads IsKeyAssignmentEnabled / KeyAssignmentMuhenkan / KeyAssignmentHenkan under HKCU\Software\Microsoft\IME\15.0\IMEJP\MSIME, and on a conflict shows a warning popup. "Yes" directly opens ms-settings:regionlanguage-jpnime (the Microsoft IME settings page)
- The check itself is skipped while GJI is in use. The registry is read-only (no automatic rewriting/disabling is performed)
Changed
- Removed settings that had lost their meaning
keyboard_model: layout parsing was fixed to JIS and it had never once been wired up (the settings screen's jis/us selection had no effect)
output_mode: already replaced by per-app auto-injection method selection (InjectionMode); it was a write-only dead setting
hook_mode: consolidated onto Relay (Filter was a legacy path untested since the arrival of the relay-family features, so it was removed implementation and all)
- Leaving these keys in an old config.toml is harmless (they are ignored). Also deleted the pre-separation old built-in settings GUI (gui/main.rs, an orphan file)
confirm_mode survives as a live setting (still defaults to wait). The n-gram-family detailed settings are wired so the UI enables them only when the "n-gram prediction" mode is selected
- Added UI for per-app overrides and the emergency-escape key to the settings screen
- New "Per-app" tab:
app_overrides (force_text / force_bypass / force_vk / force_tsf) and post_bypass (pass-through of tmux prefixes etc.) can now be edited from the GUI
- Key settings tab: added
engine_off_solo_triple (solo triple-tap to turn the engine OFF, for emergency escape when Ctrl is stuck)
- Temporarily hid the "Per-app" and "Preview" tabs from the side panel
- "Per-app" is an advanced feature, so its GUI-ification was deferred, leaving it to direct editing of
config.toml
- "Preview" (layout preview) is temporarily hidden because its implementation is not solid yet (future work)
- Both implementations themselves remain, and
config.toml's app_overrides / post_bypass / layout settings are valid as before
[1.8.5] - 2026-07-06
Fixed
- Fixed an issue where the settings screen became unusable across DPI transitions on multiple displays (a7d3e53, 7cb1625, 7207413)
- Moving to a display with a different DPI scale caused the window not to fit on the destination monitor, pushing the Apply/Cancel buttons off-screen
- Auto-clamp to the monitor size + moved the buttons to an always-visible bottom panel + set a minimum window size
- Narrow widths are handled by reflow (reduced-scale keyboard diagram, wrapping of keymap rows), and a scrollbar is shown only when it still doesn't fit. Increased the default width to 580px (previously the right edge of the preview was cut off)
[1.8.4] - 2026-07-06
Added
- Added F13-F24 to the thumb-key choices (8951418)
- For users who assign F13-F24 to thumb positions on a programmable keyboard (QMK/ZMK etc.)
- Also added parsing of VK_F13–VK_F24 to
VkCode::from_name
[1.8.3] - 2026-07-06
Fixed
- Fixed an issue where the Engine always went OFF about 500ms after focusing Edge/Chrome (BUG-07) (0d67f20)
- The TsfGate's bypass-commit handling was injecting a false
write_focus_probe(false) observation on every refresh without running a probe (the false premise that "non-TSF windows have no Japanese IME," a revert of ce45b82)
- In Imm32Unavailable, which has no real observation path, the false Low false dominated belief and was uncorrectable. The architecture_guard pins the call sites of
write_focus_probe to the real probe path
- Fixed an issue where messages originating from worker threads did not reach the main thread (BUG-09) (69f271d)
post_to_main_thread's PostMessageW(NULL) is "a post to the calling thread itself," so WM_IME_KIND_CHANGED from gji-io-monitor was being lost → even in an MS-IME environment the warmup strategy stayed as the default GjiFsm and wandered (a split-brain where the detection layer is correct but only the output layer is broken)
- Changed to
PostThreadMessageW(engine_thread_id()) + an IME-kind pull sync at message-loop start. Confirmed on real hardware a switch to MsImeStrategy 3ms after MicrosoftIme detection
- Fixed an issue where pressing a physical hiragana key did not turn the IME ON in MS-IME (BUG-10) (9d5040b)
- The unconditional Suppress of the physical F2 (VK_DBE_HIRAGANA) in TSF mode was paired with the GJI strategy's "F2 substitute send" contract, but in MsImeStrategy no substitute was sent, making it eat-and-run (Engine ON but real IME OFF divergence)
- Limited the Suppress to the GJI strategy (
f2_warmup_owned), passing the physical key through for MS-IME
- Defense against JIS-kana input caused by a kana-lock flip from synthetic VK_KANA (BUG-08) (b38d67f)
- A synthetic VK_KANA pair at an interval impossible for a physical press (135µs) was passed through, flipping GJI from romaji to JIS-kana input
- Swallow
LLKHF_INJECTED-tagged VK_KANA in the hook, and added diagnostic logging on all VK_KANA arrivals to identify the injection source
Reverted / Disabled
- Disabled the application of UIA asynchronous focus classification (BUG-11/BUG-12) (d941721, f88e89b)
- The delivery fix for BUG-09 exposed two-stage real harm in a receive handler that ran for the first time ever (cache-key mix-up → Edge permanently NonText; even after fixing, a recurrence from the structural mismatch between (pid,class) cache granularity and the browser's within-window element granularity)
- Changed the handler to log-only, deliberately reverting to the proven behavior from before the delivery fix. Sync classification (known classes, WS_EX_NOIME, MSAA) remains as before
- Withdrew the firing of JIS-kana auto-restore (restore_roman) under TsfNative (92fddc8, f88e89b)
- Under MS-IME × TsfNative, conv's ROMAN=0 is a false positive, and the restore write bounced conv back and forth between 0x19⇄0x09, inducing spurious Engine/IME ON during direct input
- Changed to a spec that fires only in the
is_roman_reliable=true context (effectively disabled on the TsfNative idle path). See docs/experiments.md entry 03 for the background
[1.8.2] - 2026-07-05
Fixed
- Fixed an issue in LINE etc. where subsequent input characters were lost while the drain queue was being processed (233b6e3)
- If a new Unicode character arrived while
UnicodeColdWarmupFsm was in flight, the FSM was overwritten and deferred_chars was lost
- Added
push_deferred_unicode_chars to TickableFsm so that an in-flight FSM can be appended to, avoiding the overwrite
- Fixed an issue where GJI warmup (VK_A+BS) misfired right after startup in an MS-IME environment (b694ffc)
- The gji_monitor thread did not send
WM_IME_KIND_CHANGED at startup, delaying the switch to MsImeStrategy
- After the initial IME-kind detection, it now unconditionally posts
WM_IME_KIND_CHANGED, applying the correct warmup strategy from right after startup
Refactoring
- Extracted the unicode-cold-warmup logic into helper methods (60a0b79)
- Added
start_unicode_cold_warmup / flush_unicode_cold_deferred_chars to WindowsPlatform, consolidating duplicate code between send_keys and dispatch_gji_response
- Reduced deep nesting and duplication via method extraction (b0cd4e9, c4900a3, f5023b4)
- Consolidated duplicate blocks in
probe_io.rs with fmt_conv() / store_gji_warmup_if_probing()
- Turned a 2-line idle calculation into a function with
observer::gji_idle_ms() (removed 6 sites)
- Turned the 4-line
gji_on_event + dispatch_gji_response pattern into 1 line with dispatch_gji_event() (5 methods)
- Turned the 3-line IME OFF→ON notification pattern into 1 line with
Output::on_f22_f21_sent() (2 sites)
- Turned VK-run formatting into a function with
KeyInjector::format_vk_run() (shared by the 3 send_vk_runs* methods)
- Unified 7 raw
SendInput sites onto the existing win32::send_input_safe() and removed the unsafe blocks
[1.8.1] - 2026-07-05
Fixed
- Fixed an issue where the IME's internal recognition (belief) and the actual OS-side IME state diverged during fast focus transitions such as Alt+Tab (dd6f208, 08ce474, 11ee689, 435e2d3)
- Fixed the GJI force-IME-ON guard to look at "the actually active IME kind" rather than "the survival of the GJI process," resolving double-sending of VK_DBE_HIRAGANA in an MS-IME environment
- Wired a settle barrier (
settle_until) right after a focus transition, guarding all call paths of Engine/ImeEffect::SetOpen (keyboard input, focus-change notifications, IME-refresh polling, hotkeys, etc.) in a single sweep at the two sites execute_from_loop/execute_from_hook
- Unified onto
is_effectively_tsf_native the 5 sites that had misjudged Windows Terminal (CASCADIA_HOSTING_WINDOW_CLASS) as "non-TSF native," overlooking that the classification priority of AppImeProfile::from_class_name never classifies it as TsfNative
- Fixed an issue where the BrokenAppBootstrap force guard overrode the user's explicit OFF intent (a06ad69)
[1.8.0] - 2026-07-05
Added
- Established an observation-admission layer (ObservationAdmission) via FocusEpoch + ImmLikeTicket (604cf99, 569bf0f, 67e63ad, 1345d95) (ADR-077)
- Manages focus transitions by epoch, structurally rejecting stale observations to fundamentally prevent the misobservation chains of ImmCross apps
- Added shadow-grace suppression to ImmCrossProbe, enforcing epoch matching on all paths
- Added rejection counters and dump-time logging to make the observation-admission situation visible
- Changed the settings screen so thumb keys can be selected via a dropdown (9e1621a)
- Left/right thumb-key assignments can now be changed directly from a dropdown UI, applying setting changes immediately
- MSI/ZIP can now be downloaded directly from the website (877b1af, 15c6ff5, 21eadb1)
- Added links to download the latest GitHub Releases assets directly from awase.cc
Fixed
- Fixed an issue in ImmCross apps where the engine stopped while still in kana mode after IME ON (81f6576, e4378c6)
- Suppressed the ObservedKana observation right after IME ON in ImmCross apps, resolving the case where the engine was mistakenly deactivated
- Fixed an issue in MS-IME + ImmCross apps where romaji mode was not maintained at IME ON (91631a0, 1969cd3, 4c02f2e)
- Force ROMAN mode before IME ON in MS-IME, and skip forcing romaji when ObservedKana
- Unified IME-kind determination from process existence to the CLSID API, supporting dynamic IME switching (e6456b2, ce252fe)
- Abolished the GJI-fixed policy and determine the IME kind via the CLSID API at runtime, supporting dynamic switching of GJI/MS-IME
- Fixed an issue where a black console window appeared when launching the settings screen (49d4f31)
- Fixed thumb-key changes from the settings screen to apply immediately (6cd1178)
- Fixed to not perform immediate drift correction on HwndCache-derived intents (d418035)
- Classified XamlExplorerHostIslandWindow as NonText (a8d9a00)
Refactoring
- Migrated all ImmCrossProbe to epoch matching and removed shadow grace (f478ee7)
- Abolished TipDetector's file cache and unified onto an in-process OnceLock only (9e0ecf0)
[1.7.1] - 2026-07-02
Fixed
- Fixed an issue in ImmCross apps like LINE where the engine stopped after switching from GJI alphanumeric to hiragana (4bcf8b0)
- Because GJI does not set the ROMAN bit even in alphanumeric mode,
classify_transition could not detect the return to hiragana, so ObservedEisu lingered and the engine became Inactive(NotRomajiInput)
- Added a stale-recovery branch to
classify_ime_snapshot that auto-resets to AssumedRomaji the moment conv_mode changes to non-alphanumeric in an ImmCross app
Refactoring
- Unified ImmCrossProbe's input_mode updates to go through
classify_fetched_snapshot (c2c8b55)
- Applied the three-layer separation of Observe → pure decision → belief to the ImmCrossProbe handler, removing inline determination
[1.7.0] - 2026-07-02
Added
- Auto-detect a state where alphanumeric direct input is happening with the IME left ON (ObservedEisu) and switch to IME OFF (direct input) (30e6c5f, 1ef82ca, 754a7a4) (ADR-074)
- Added
InputModeState::ObservedEisu to explicitly distinguish "shadow=ON but actually half-width alphanumeric direct input"
- When
idle_conv_check detects ObservedEisu, it automatically issues IME OFF + Engine deactivation
- The case where
ObservedEisu lingers right after SetOpen(true) is reset to AssumedRomaji, immediately reactivating the Engine
- Replaced the tray menu's "Reset internal state" with a process restart (a720c7a)
- When the FSM / IME state is broken, the entire process can now be restarted with mouse operations alone to return to a completely clean state
- Positioned as the only reliable means of resetting runtime-determined state, such as the in-process fixing of the GJI kind determination (
active_ime_kind) (ADR-073)
Changed
- Extended warmup-send VKs to support Charset (hiragana / katakana / JIS-kana) (536d93a)
- Consolidated the TSF warmup send when GJI is inactive (e.g. when MS-IME is in use) into a single decision point, eliminating skip omissions
- Introduced a single gate for conv_mode changes via
ConvModePolicy (2826248, cc9f745, af3b776) (ADR-064)
- Added a
conv_mutation_allowed gate to Output, making writes to conv_mode verifiable in one place
- Replaced
is_romaji_mode: bool with the ConvModePolicy type to clarify the meaning
Fixed
- Fixed a group of bugs where Ctrl+Henkan / Ctrl+Muhenkan did not work correctly in MS-IME (f527a18, baff902, 301e911, 48a667a)
- Fixed a bug where Ctrl+Henkan did not make MS-IME hiragana / did not return from half-width alphanumeric
- Unified
MsImeDirectStrategy's DirectInput switch onto the idempotent VK_IME_OFF
- Once GJI is confirmed, forbid mistaken demotion to MS-IME (in-process fixing, ADR-073)
- Fixed IME misrecognition / keystroke loss on ImmCrossProbe / GJI+TsfNative focus (496926c, 489cdf1, 15ac8d8, 500dab6) (ADR-071, ADR-075)
- Fixed a bug where
ImmCrossProbe misrecognized the IME state on Qt / GJI focus
- Changed the GJI+TsfNative IME OFF send from a
VK_KANJI toggle to the idempotent VK_IME_OFF
- Fixed a bug where a subsequent key was lost while a TSF cold-start probe was running, turning "にゅうりょく" into "にうりょく" (moved ownership of the deferred VK queue from the probe machine to
TsfWarmupCoordinator)
- Fixed an IME/Engine desync caused by a re-sync omission of
conv_mode_authority (e2199e7, c887da8, 1f96598) (ADR-072)
- Re-sync
conv_mode_authority on every IME apply completion, resolving the issue where a stale authority value lingered right after a panic reset or on the second Ctrl+Henkan
- Made the
already_matched determination consistent across all IME kinds, and fixed it to not mistakenly mix in candidate_was_seen
- Fixed a group of IME/NICOLA-engine state-sync bugs in katakana / JIS-kana mode (66bfc33, 58fa2ac, e8b09de, 927f2a2, 0f652ae)
- Fixed a bug where the NICOLA engine stayed OFF after switching to katakana mode / was returned to hiragana after selecting half-width katakana in the tray
- Correctly reflect JIS-kana / katakana mode changes made via the taskbar into belief
- Suppressed the mistaken downgrade of HanKata → ZenKata
- Fixed Engine state-sync omissions / misdetection caused by idle-conv-check (0f75b5b, ea3da7f, ed862bb, a325df7, 29bc9c4)
- Fixed a bug where the Engine did not recover on a katakana+shadow=OFF or HanAlpha→Hiragana transition
- Fixed Chrome IME-state misrecognition after ALT+TAB, and a tray desync (
user_enabled=true but ime_on=false)
- Consolidated GJI cold-start / partial-literal countermeasures into SacrificialWarmup (VK_A+BS) (6c1732d, 22c3905)
- Replaced with a warmup based on
VK_IME_OFF→ON + write_bytes detection, ensuring compatibility with vim etc.
- Fixed a bug where the engine got stuck on the first keystroke after waking the PC from sleep (c4df99b) (ADR-076)
- Fixed a bug where, right after waking from sleep,
read_ime_state_fast temporarily returned is_japanese_ime=false and the engine deactivated with NotJapaneseIme
- Compute the grace-period determination before
set_is_japanese_ime inside apply_focus_probe, suppressing downgrades to false while shadow grace is active (updates to true are always allowed)
- Made the grace protection of
imc_open and is_japanese_ime symmetric
- Fixed a bug where a named-mutex race caused startup to fail on process restart (bb84696)
- A bug in the "process restart" tray feature added in this version itself
Internal
- Completed all 21 tasks of the cohesion refactor, H-1 through M-5: resolved circular dependencies among type definitions, removed direct OS-global calls from the state layer, split
PlatformState into FocusStore/GateStore/KeymapStore and Runtime into FocusTracker/RefreshScheduler/ImeCoordinator (ADR-069) (a7a1e23, 811036a, 8aad49a)
- Renamed
ApplyBelief → OpenBelief and consolidated into the reduce_open_belief pure function (concentrating the IME-apply decision logic in one place and making it easy to unit-test) (ADR-070) (1e09e90, 8c34984)
- Introduced a
RuntimeOutbox/RuntimeRequest event bus and removed the Output → Runtime reverse dependency (with_app()) (4782e3e, 7568180)
- Extracted
TsfWarmupCoordinator / KeyInjector / ImeApplyPlanner from the bloated Output (03ebfc9, e1993bf, 080e96d)
- Moved
ConvMode/Charset determination to the nicola crate and made it pure functions, relaxed the #[cfg(windows)] gate to per-module granularity, and added Linux unit tests (ADR-065) (b1ab86f, 5ee724b, b27a218)
[1.6.0] - 2026-06-29
Added
- Full Microsoft IME support (45edf19, 56cd9a5)
- Idempotent ON/OFF control via
MsImeDirectStrategy (VK_DBE_HIRAGANA / VK_DBE_ALPHANUMERIC) (ADR-063)
- Switch between GJI / MS-IME at runtime via
WM_IME_KIND_CHANGED
- Dynamic GJI CLSID discovery via TSF EnumProfiles (55233f0, 005f0a6)
- Migrated from process-name dependence to CLSID-based IME-kind determination
- Persist the CLSID to
cache.toml to reduce the cold-start cost after a restart
Changed
- Migrated GJI IME control to VK_IME_ON/OFF (0x16/0x1A) — no config1.db patch needed (b271aee, 8c39b8e)
- Completely removed
gji.rs (config1.db patching, process management) (-692 lines)
- Removed "GJI Setup / Teardown" from the tray menu; no first-install work required
- Confirmed VK_IME_ON/OFF behavior on real hardware in Chrome / WezTerm / Windows Terminal (2026-06-28)
- Removed the
gji_keybinds_ok flag and the Observer's config1.db monitoring loop
- Unified IME-kind determination onto a CLSID basis (2d5cfe9)
- Removed the
gji_write_idle_ms heuristic to improve determination accuracy
Fixed
- Fixed a bug in WezTerm where the first character after Enter (e.g. "な") suddenly disappeared (3ffbe66)
- Promote
nc_fired in ReinjectConfirmKey + TSF mode to suppress LiteralDetect misdetection
- Fixed a BS burst caused by LiteralDetect misfiring while MS-IME is active (699ab5f)
- Panic reset now returns to romaji-hiragana even from katakana / JIS-kana states (a88bb36, 6ee20bf)
- Force
NATIVE | FULLSHAPE | ROMAN via IMC_SETCONVERSIONMODE and drop KATAKANA
- Return to romaji-hiragana even from half-width katakana (including JIS-kana input mode) / full-width katakana
[1.5.0] - 2026-06-27
Added
- Added "Reset internal state" to the tray menu (1591593)
- When the IME state or FSM is broken, the internal state can be initialized with mouse operations alone
- Can be reliably reset even in situations where the keyboard is not working properly (ADR-052)
- Introduced StepCoro — a timer-driven coroutine foundation (e548e63)
- Replaced
GjiWarmupFsm / LiteralDetectFsm / SacrificialWarmupFsm / TsfProbeMachine with StepCoro (ADR-053)
- Intermediate variables of FSM states can be written as local variables of the coroutine, greatly reducing code volume
- Promoted to the
timed_fsm::coro module (118b3cf)
- Persist InjectionMode in cache.toml (c6fed60)
- Added
InjectionModeStore to remember the injection mode across sessions
- Reduce the cold-start cost after a restart (ADR-058)
- Merged
imm_cache.toml into cache.toml (10918c2)
- InjectionMode post-hoc promotion: auto-promote on GJI write_bytes observation (68c24e1)
- Judge via WriteTransferCount whether the keys sent in Unicode mode right after focus were processed by GJI
- If GJI is working, auto-promote injection_mode to Tsf (ADR-062)
- Unicode long-cold support: UnicodeColdWarmupFsm (70d3fed)
- After a long idle in Unicode mode, send a VK_IME_ON poke and confirm GJI has started up before sending characters
- Hold off character sending until GJI responds to prevent partial literals
- Added a startup check for competing software (9875c2c)
- If a NICOLA-capable IME such as Yamabuki is running at the same time, warn via a balloon notification (ADR-060)
Changed
- Migrated auto-start from schtasks to the HKCU\Run registry (0d09d80)
- Starts immediately at logon (no 30-second delay needed), and can be registered without administrator privileges (ADR-059)
- Old schtasks tasks are automatically deleted at startup (584897a)
- Added Windows Terminal to force_tsf (4e49ee1)
- Reliably enable GJI composition, improving conversion accuracy in Windows Terminal
Fixed
- Skip IME-key injection while the Win key is held (6469f51)
- Fixed the malfunction where Win+A opened the Start menu (ADR-061)
- Prevent mistaken IME-key sends on virtual-desktop switch (a97173a)
- Fixed a focus-guard omission where IME keys were mistakenly sent to LINE etc. right after switching virtual desktops
- Remove partial-literal remnants with BS (f3ff84d)
- When TSF literal recovery gives up, delete the literal characters left in the terminal with BS
- Turned the resend after partial-literal detection into SacrificialWarmup (62ad28b)
- Unified the resend after partial-literal detection onto a safe warmup flow
- Added IPC settle waiting after an early HIDE in SacrificialWarmup Phase2 (88d562f)
- After the candidate window is HIDEd early, wait until the GJI IPC completes before releasing the next input
- Changed Unicode long-cold from a lone VK_IME_ON to a VK_IME_ON+VK_A+BS sacrificial key (6cb175f)
- Reliably increase GJI's WriteTransferCount to improve cold-determination accuracy
- Record the shadow value as a surrogate observation under TsfNative/Imm32Unavailable (1e77002)
- Even in windows where IMM32 is unavailable, save shadow_on as an observed value to stabilize state estimation
Internal
- Bulk-replaced
#[allow] with #[expect] (all crates) (c2b0685)
- Adapts to the strengthened lints of Rust 2024 edition; the compiler warns once the suppression reason is no longer needed
- Made
unsafe extern explicit (Rust 2024 lint compliance) (f645d3c)
- Migrated awase-settings to edition 2024 (ee2487c)
- Removed GjiWarmupFsm / StartLiteralDetect (dead-code removal after the StepCoro migration) (f01d401)
- Raised MSRV to 1.85 (
timed_fsm::coro uses async/await syntax)
[1.4.0] - 2026-06-25
Added
- Added the [[post_bypass]] setting: a setting to NICOLA-skip the keystroke immediately following a command key, such as tmux (
Ctrl+B) or screen (a67ebb5)
- The target key and wait time can be specified in the
[[post_bypass]] section
- Added tmux-oriented comments and a config example to the TOML sample (49b8343)
- Introduced SacrificialWarmup: resolve the partial literalization of Chrome cold-start and WezTerm long-idle with a self-sacrificing VK_A+BS batch (28fc97a, 16a288d)
- Pre-correct Chrome's "kおのなかで"-type partial literal in the warmup phase
- Resolve the case where WezTerm enters an Engine-ON/IME-OFF state after a long idle
- Limited to long-cold + TSF mode only to prevent performance degradation in normal times (d02ec44)
- Implemented ImeModeFsm + ChromeGjiReinitFsm: added an FSM that, after sending F22→F21, confirms GJI has settled into Hiragana mode before allowing the next input (2c8d647)
- Improved the Chrome LiteralDetector to a WriteTransferCount basis: judge composition completion by the number of bytes written to the pipe, suppressing mistaken BS (c7ef500)
- Update belief on all F21/F22 send paths + suppress FocusChange right after a VK: raise the reliability of the IME-mode state and reduce unnecessary re-probes (1a0e54e)
Fixed
Complete fix of the Ctrl+J problem
- Fixed an issue where Ctrl+J did not reach tmux while the IME was ON (32a037d)
- TsfGate was mistakenly sending Ctrl+J during IME ON to the GJI warmup path
- Re-fixed the issue where Ctrl+J was hijacked by GJI (ee0b1fd)
- Fixed an issue where a modifier's early release after a Ctrl bypass mistakenly suppressed J↑ (bd51ea1)
- Fixed to NICOLA-skip the next key right after a Ctrl+key bypass (ddb6b58)
- Eliminated the case where the key right after a bypass was mistakenly judged as a thumb-shift chord
Stabilization of SacrificialWarmup
- Fixed a bug where it terminated immediately at gate=Bypass (0426fb6)
- Send VK_A+BS in the same batch to prevent a character flash (af906b1)
- Take the write_bytes baseline before sending VK_A and adjusted the threshold to 350B (26bc0fe)
- Force-reset GJI with F22→F21 on a Chrome cold timeout (9630acb)
- After confirming Chrome is warm, wait for HIDE before resending the real romaji (2355761)
- Fixed the IPC race where "おお" disappeared on an early HIDE after a VK_A+BS atomic batch (123efb1)
- Fixed a drain-ordering bug where
composition_was_seen became false on the first tick (e40a2eb)
WezTerm
- Fixed the second-character literalization after a WezTerm long-idle with the PendingGjiConfirm state (7041b20)
- Sync injection_mode right after focus, and re-initialize with F22→F21 on Unicode long-cold (6ecb8e9)
- Fixed the HWND cast type to
*mut c_void (implicit conversion to isize is UB at the Windows API boundary) (ff1f092)
GJI FSM
- Dispatch
WarmupComplete immediately on StartProbe in Unicode mode (fix for the OnCold stuck bug) (444e9a6)
- Fixed to maintain the proactive probe on a FocusChange right after ImeOn (782f10d)
- Added a generation guard to FocusChange spawn_local polling (preventing double application) (90c9962)
Other
- Added a
post_bypass field to ValidatedConfig (build-error fix) (089858d)
- Downgraded the Unknown → real-value IME state transition from a drift WARN to an initial-confirm DEBUG (4347928)
- Fixed a type mismatch where a
u64 was being passed to sleep_ms (ddc6be3)
Internal
- Ported HoldingGate / GateAction to the timed-fsm crate: consolidate the gate logic in the library layer to improve reusability (ebbc9e4)
- Added
to_hwnd() to HwndId to consolidate raw casts in one place (1932948)
- Reference
gji_candidate_visible via env (removed direct polling) (19f4372)
- Removed the unnecessary
plan / observations fields from SacrificialResend (61c4c12)
[1.3.0] - 2026-06-23
Fixed
- Fixed an issue where ALT+TAB could not be pressed consecutively (d6cb1a4)
- When sending F21/F22 (IME ON/OFF keys) on every focus change, ALT was temporarily released, so the ALT+TAB switcher mistook it as "ALT released = confirm"
- F21/F22 are GJI-only virtual VKs, so they work correctly even when sent while holding ALT
- Fixed the "kお" cold-start bug after a GJI long-idle (e4a6248, c571acf)
- Fixed the case where, after GJI is idle for 8–10 seconds or more, the first keystroke partially literalizes ("こ"→"kお")
- Improved to batch F2 together even on a medium idle (7–10 seconds) when GJI times out unresponsive (2159dca)
- Fixed partial literals ("kお", "seつぞく") (5562b6a, d54f5b1, 125d2c1)
- Set
gji_resumed after a GJI I/O response to rescue partial literals
- Enable LiteralDetect in TSF mode as well (preventing the recurrence of "seつぞく")
- Fixed the partial-literal BS count from
chars.len() to a fixed 2
- Improved the Chrome-path LiteralDetector (c279fc7)
- Switched to
new_gji_resumed to suppress literal misdetection after a GJI resume
- probe-fsm: Fixed F2 double-sending / delay (d195c43, 4de228b)
- Suppress F2 double-sending into the TSF batch on the ReWarmup/non-eager path
- Skip fresh F2 + NameChangeWait when GJI is pre-idle to reduce delay
- composition-fsm: Fixed to correctly maintain the Long cold state (c96598f)
Added
- Created GjiWarmupFsm: introduced a GJI-cold-start-warmup-dedicated FSM to make the warm-up logic independent (fcd1b82, f768944)
- Created LiteralDetectFsm: a literal-detection FSM shared by the warm path and GJI post-transmit (8608062, 660ee19)
- Created ChromeProbe: swapped
pending_tsf to Box<dyn TickableFsm> and added a Chrome-dedicated probe (51901c0)
- Added ColdKind::Medium: classify GJI idle time into Long / Medium / Short to optimize the warmup strategy (bf3eade)
- Transmit immediately when the candidate is visible in NameChangeWait: shorten the probe wait time in WezTerm by up to 300ms (25182eb)
Internal
- Defined the TickableFsm trait:
TsfProbeMachine / GjiWarmupFsm / LiteralDetectFsm / ChromeProbe implement a common interface (d22e987)
- Defined the ImeWarmupStrategy trait: abstracted so that
GjiFsm / MsImeStrategy can be handled through a unified interface (eb8b9d4)
- Large-scale GjiFsm refactoring: separated
ProbeStatus into Authorized+Executing and removed 3 Cells, moved OutputActiveGuard to Output (0ca92b2, 65e0a1a)
- transport refactoring: extracted
PassthroughQueue and consolidated the F2 case into PhysicalKeyDisposition::plan() (3cfc57b, 005ed17)
[1.2.0] - 2026-06-21
Added
- Added GjiFsm: introduced an FSM that infers GJI's (Google Japanese Input's) internal composition state (b152c7e)
- Phase 2a: connected GjiFsm to Output and wired FocusChange / ImeOn / ImeOff / WarmupComplete (bb228c2)
- Phase 2b: wired CompositionReset / KeyInput and turned
is_composition_warm into an FSM (d49c516)
- Phase 3: switched
is_composition_warm to the GjiFsm SSOT (2b6d25f)
- Phase 4: removed the legacy epoch warm tracking and consolidated GjiFsm as the SSOT (588ea32)
- Enhanced panic logging: added a hook that records the location and message on a panic to
awase.log (de0226a)
- Auto-generate the changelog page: added a GitHub Actions workflow to auto-generate
changelog.html from CHANGELOG.md (efc4310, 6fb7a38)
Fixed
- Fixed the F2+S race of WezTerm TSF cold start and the false-positive BS after
gji_resumed (b754277)
- Fixed hiragana injection from IME ON carry-over on a CoreWindow cache miss (1c5cc91)
- Fixed a
debug_assert panic where StartProbe was overwritten during a NICOLA chord (a5a9412)
- Chrome: Fixed to force-send the programmatic F2 even when the f2_gji_long_idle flag is enabled (43dca5a)
- probe: Fixed to reset
consecutive_count on SetOpenTrue as well (cbd1946)
- tray: Explicitly handle
WM_CLOSE to prevent an unintended shutdown (c924eed)
Internal
- probe-fsm: organized the FSM layer boundary by introducing
TransmitPlan / ProbeObservations (3c36f21)
- chord management: fully consolidated into ImeStateHub (Phase 2 complete) (fd17da0)
- Consolidated chord start/end decisions into the reducer (a7218e0)
- Promoted
pending_warmup_on_keyup to CompositionFsm (f3b0448)
- transport: separated
suppress_physical into PhysicalKeyDisposition (8a045bb)
- Clippy pedantic compliance (CI Rust 1.96) (881f824)
[1.1.1] - 2026-06-20
Fixed
- Fixed the IME-OFF Engine-ON state after a Chrome → WezTerm focus switch (12dd094, 56f5e49)
- Changed to force-send GJI F21 ignoring
shadow_on on entry to TsfNative
- Changed the belief on a TsfNative cache miss from carry-over (true) to the safe default OFF
- Extended the focus cache TTL from 5 seconds to 1 hour (resolving the issue where leaving and returning to a window with IME ON was treated as a cache miss)
- Skip cache saving for short focus (< 100ms) (preventing notification popups etc. from overwriting a normal state)
- Fixed the LiteralDetect false positive after WezTerm gji_resumed (the case where "あ" became "a") (da8dad1)
- Implemented early composition confirmation after WezTerm gji_resumed (detect the GJI I/O change to shorten the wait time) (aa8a79d)
- Fixed the comp-probe RUNTIME re-entrant borrow bug (
shadow_on / jp were always false) (2225578)
- nicola-fsm: Fixed a bug where the shift counter lingered on a solo repeat (a53344b)
Internal
- Added GJI process I/O statistics (ReadOperationCount / ReadTransferCount) to the monitoring log (93236a8)
[1.1.0] - 2026-06-20
Important Changes
- Changed the GJI keybindings from F13/F14 to F21/F22 (7f8291f)
- F21/F22 are virtual keys that do not exist on a real keyboard and do not generate VT escape sequences
- The Nop binding configuration in WezTerm / Windows Terminal is no longer needed
- When upgrading, be sure to re-run "Set up Google Japanese Input" from the tray menu
Added
- Automatic GJI keybind monitoring: if the F21/F22 entries are erased from config1.db, detect it within 30 seconds and auto-re-register (58557e9)
- Tray menu expansion: added GJI teardown and an auto-start toggle (e67cb49)
Fixed
- Fixed leading-character literalization after a WezTerm long-idle ("こ"→"ko") (LiteralDetect + BS resend approach) (84e6942)
- Fixed the GJI IME-ON failure bug: F21 was ignored due to a missing
DirectInput\tF21\tIMEOn entry (9d11cd7)
- Fixed a partial literal in Teams / Chrome (conversion failure of "kおんな"→"こんな") (3744457, 040f8f8)
- Fixed the LiteralDetect false positive after a GJI long-idle (a6b4c0d)
Performance
- Shortened the probe wait time right after a focus change from 300ms to 100ms (improving input responsiveness) (23052fb)
Documentation
- Updated F13/F14 → F21/F22 across all awase.cc pages, and removed the WezTerm Nop configuration steps
Internal
- Removed the KeySeq mechanism of the TSF probe (dead code) (550781f)
[1.0.1] - 2026-06-15
Fixed
- Fixed the "んい→に" conversion bug in Chrome VK mode (71a4d68)
- Fixed a bug where a stale
ime_on=false lingered on entry to Imm32Unavailable (bfad1a8)
- Fixed KeyUp while Ctrl/Alt/Win is held to be bypassed symmetrically with
on_key_down (5904d67)
- executor: Fixed the mistaken firing of a deferred timer by running the Timer immediately in Relay mode (71ebfb9)
- executor: Fixed the
push of the event queue (VecDeque) to push_back (3823f12)
Documentation
- Heavily renewed the landing page (added technical differentiation and the origin of the name)
- Added a Usage page (usage.html) (covering the settings screen, all config.toml items, and emergency-operation procedures)
- Added an Internals page (internals.html)
- Greatly expanded the FAQ
- Shift misses during fast typing
- Idempotent IME control rather than a toggle in Google IME
- Four symptoms that tend to occur with other tools (Yamabuki R etc.) and their countermeasures
- F21/F22 Nop configuration steps for Windows Terminal / WezTerm
- Unified terminology in comments (IMM → IMM32, IME-ON/OFF → IME ON/OFF, Henkan/Muhenkan → Henkan/Muhenkan)
Removed
- Removed
awase-gji-setup.exe from the distribution (the feature is already integrated into the awase main body)
Internal
config: promoted to a #[serde(default)] struct and removed the group of 18 default_* functions (-142 lines)
vk: introduced enum VkMarker to type-unify the marker selection by bool/fn_ptr
fsm / nicola_fsm / ngram: cleaned up duplicate code, wrapper functions, and dead code (total -270 lines)
- rustfmt / clippy formatting
[1.0.0] - 2026-06-14
The first stable release.
Full Changelog: https://github.com/cuzic/awase/compare/v0.1.0...v1.0.0