Skip to content

Align the keyboards, candidate surfaces and toolbar with the reference skin - #138

Merged
Slacker-LLC merged 95 commits into
mainfrom
align/letter-hints
Oct 15, 2026
Merged

Slacker-LLC merged 95 commits into
mainfrom
align/letter-hints

Conversation

@Slacker-LLC

@Slacker-LLC Slacker-LLC commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Summary

Each gap against the reference product this branch found, closed from its own skin files. Later passes in the branch took the key faces, the pressed colours, the launcher pages, the number page, the panel headers and the expanded page. docs/DECISIONS.md records every quoted value with its file and line.

  • 26-key letter hints. Each letter key's hint now matches the reference key by key (English a=- s=/ d=: f=; …, and the full-width forms in Chinese mode); an up swipe still types the hint, and a long press now offers only the two letter cases (upper first), as the reference does. The comma key gained an up-swipe full stop.
  • Key weights, the nine-key column and candidate spacing. A and L are half a letter wider (the reference's 15:10), and 分词/退格 follow the same ratio. The nine-key and stroke side column is now 删除 / 重输 / 发送 with the send key twice as tall; the separate 0 key is gone — the space bar carries a corner 0 and an up swipe types it, behind the same "up swipe digits" setting. Candidate items pad 8dp per side instead of 12dp.
  • The nine-key reading rail. During composition the rail is now the same flat list the symbols use: one reading per row at one text size, a hairline between rows, and no state drawn on any item — the pre-edit says which reading is current. The list keeps the user's scroll position until the open digits change, instead of re-centring on every keystroke.
  • The nine-key URL rail. In an address or e-mail field the rail lists the reference's own eight URL fragments (. / www. .com .cn http wap. ftp:, input_kbd_pinyin9.xml:11) and hides the + editor cell, instead of the user's punctuation.
  • The first candidate. Accent-coloured text at the medium weight with no background of its own (cls_option_first_canditem, style.xml:551-554), instead of bold black text on a filled key tile.
  • Candidate order. A word whose reading covers every typed letter now outranks a candidate that only matched the settled part of the input: typing dang offers 当 / 党 / 档 first and dangdi offers 当地 first, instead of a fragment split (da + ng) carrying its settled-prefix words up the list.
  • The expanded candidate page. It opens with 全部 / 单字 as one option group over the list (input_more_cand.xml:9-13; 单字 keeps single characters, 全部 is the page's default), the list sits in one rounded card (radius 11, 2 edge, cls_morecand_bk), and the backspace floats in that card's bottom-right corner at 25.2% by 20.2% of the list, rounding only that corner with the card's own radius (bkround="0,0,11,0"). Every cell is the same flat label with a hairline edge (cls_morecands_button): the skin gives the expanded list no first-item class, so no cell carries the accent tile. The corner key deletes one letter of the open composition and the page stays open on the shorter list.

The reference's left syllable list on the expanded page (FilterListLayout name="more_cand_filter_list", commented 音节过滤) is not in this change: the 26-key path has no syllable-fixing state yet, so the list would have nothing to filter by.

  • The 26-key bottom row. It is now the reference's own seven keys — 123 / ,。 / , / 空格 / 。 / 中/英 / 确定 — in the reference's own weights (style.xml:957-978), so the two symbol keys the skin marks visible="true" are there (input_kbd_pinyin26.xml:153,156) and no key borrows their room. Their label is the class's own text (full-width in Chinese, ASCII in English) and their uri_adapter_symbol gives / and . in an address or e-mail field. The English merged key now leads with the period and carries the comma in its corner, as the skin draws it (input_kbd_english26.xml:154-161), where our version had the two the other way round.

  • The nine-key 1 cell. It is the reference's @# key (input_kbd_pinyin9.xml:23-28): the label names the symbols the key offers, a tap and an up swipe still type the digit, and a long press opens the reference's own seven-symbol bubble / - _ 1 @ # . instead of our invented @ # /. The nine-key board no longer carries a 分词 key: the reference has none — ButtonSplit text="分词" appears once in the whole skin, on the 26-key row (input_kbd_pinyin26.xml:92). The 26-key 分词 key also drops the invented corner mark and long-press popup its class never declares (cls_button_shift, style.xml:915-920).

  • The candidate strip. It now carries the reference's own [4] 候选列表(308) [1] [3] 更多候选(40) [4] layout (input_toolbar_typing.xml:16-30): both end insets, the hairline split between the list and the expand key, the 3-unit gap ahead of it, and the expand key's transparent pill press shape (cls_cand_list_weight_no_speech 308, style.xml:530-534; cls_candidate_split filled with @color/canditem_split, style.xml:504-506; cls_btn_morecand over cls_bk_toolbar_button, style.xml:231-235). The key keeps its 48dp touch target, our own rule: the reference's box is 40 units.

  • The idle toolbar. Its icons follow the reference's own row — clipboard, emoji, then the board selector (input_toolbar_idle.xml:21-25) — inside a strip that scrolls when they do not all fit, the strip's slack sitting in a spacer at its own end (toolbar_left_container with sliderenable="true" sliderstopslowdown="true", line 14; toolbar_right_spacer, line 34). 收起 stays its own button at the right, outside the strip (line 40). The tools the reference lists that this build does not ship (translate, 繁简, 单手, handwriting lookup) stay out of the row.

  • The number / decimal page. It is now the reference's own number layout (input_kbd_number_from26.xml:5-42), the page its 26-key 123 key opens: the symbol rail over the return key on the left, three digit columns whose last cell is 符号 / 0 / ., and a delete / space / send column at the reference's weights 66 / 75 / 75 / 75 / 66, with send twice as tall as the keys above it. The old 3x3 grid over a 返回 / 空格 / 0 row and the @ key are gone — no number page declares one. In a phone field the symbol cell carries the dialer's # and the period cell its +, so *, + and # stay reachable.

  • The stroke grid. The board's centre is the reference's 3x3 (input_kbd_stroke.xml:16-66): 一 丨 丿 / 丶 折 通配 / @# :;, every cell with the skin's corner digit typed by an up swipe, the punctuation cells committing themselves and the @# cell opening the reference's own / - _ @ # . 7 list on a long press. The bottom row of the stroke and nine-key boards now takes the reference's 45.5 / 134 / 45.5.

  • The symbol page's categories. They sit in one bottom strip beside the page's own return key, the row the reference gives them: the tab container is weighted 85 against that key's 13.1 (input_kbd_symbol.xml:44-47) and scrolls (cls_symbol_tab_container, style.xml:707-710), and each category is a transparent pill whose label darkens when it is current (cls_input_kbd_symbol_option, style.xml:715-724). The cells stay five to a row where the reference draws ten (input_kbd_symbol.xml:6-15): ten across a phone would put each cell under our 48dp touch target.

  • The symbol page's 123 and backspace. They close the page's symbol row at the reference's own two ends (input_kbd_symbol.xml:31-41, weight 15 each against the seven cells' 70), which a five-a-row grid cannot give them, so they take the ends of the tab strip's row. Both keep our 48dp touch target and use actions this page family already has.

  • The symbol history. The board's toolbar now lists the symbols the user has typed, in the row the reference keeps for it (input_toolbar_quickinput.xml:4-18, hidden until the board needs it): the candidate strip's own shape — a 4-unit lead inset, the scrolling list at weight 308, the hairline split, a 3-unit gap, the 40-unit close key and a 4-unit tail — with 36-unit cells over candidate-sized text in the candidate text colour and 4 units between cells (cls_history_symbollist_button, style.xml:1061-1070; cls_history_symbollist, style.xml:1055-1058). A tap commits through the same path the board's own cells use, so the list re-reads itself; the close key ends the strip for that visit while the board stays open, and a user who has typed no symbols sees the plain toolbar.

  • The launcher pages' tile grid. The tools page and the keyboard switcher now draw the reference's own grid (input_settings.xml:13-45, input_switch_keyboard.xml:13-29; cls_settings_bk_adaptive, style.xml:616-619): a container 88.9% of the page wide with its own paddings, tiles filling their cells 4 columns by 2 rows with the grid capped at 192 units, and each tile a radius-14 face — setting_button_bk under it, setting_button_pushedbk while pressed, the theme colour when it is the chosen one — with its name in keybutton_text_3, white on the chosen tile (cls_settings_button, style.xml:626-647; values/colors.xml:15,87-93). Our own card colour stays, a tile never shrinks under the 48dp touch target (the page scrolls instead), and an entry list longer than a page takes a third row rather than the reference's page dots.

  • The floating keyboard's tip. The reference meets the floating keyboard with a tip in the toolbar row (input_toolbar_tip.xml:44-49): one pill across the row (cls_toolbar_tip_container bkround="999" over keybutton_bk, style.xml:1273-1275) with its own 3.333% horizontal padding (style.xml:442), a 3.571% lead space (style.xml:1277), a left-aligned ellipsized label in keybutton_text (cls_toolbar_tip_label, style.xml:1265-1270) and one round close key as wide as it is tall, filled toolbar_tip_close_button_bk and pressed toolbar_tip_close_button_pushedbk — black 4%/14% light, white 7%/17% dark (style.xml:1307-1313; values/colors.xml:163-164). The row is ported with our own truthful wording, 轻点顶部把手可恢复默认键盘, because our floating keyboard docks again on a tap of its handle where the reference asks for a drag to the bottom edge. The close key settles the tip for that stay afloat and the next one shows it again; 收起 keeps its place and the tip gives the row back to candidates, associations and the symbol history whenever they have something to say.

  • Our own clipboard and message suggestions. They now wear the reference's quick-input row (input_toolbar_quickinput.xml:36-51 for the clipboard, :20-32 for the message): the text at cand_font in canditem_text, left-aligned in a scrolling container, with the glyph that names where it came from riding above its right shoulder as a superscript — the text box inset 0.055/0.111 of the row (line 42), the glyph at the top edge at fi_toolbar_tip_superscript's 30 units against cand_font's 56 (style.xml:138, :83; lines 44-45) — and the row ends with the reference's own close key borrowed from cls_history_close_button through cls_btn_more_cand_button_adaptive (lines 48-49). Ours were filled key-coloured pills at the body size behind a back chevron, which no reference toolbar draws. The system's autofill chips keep the back control — they are the platform's own views in the same strip — and every row keeps our 48dp touch target.

  • The pre-edit's own half of the page. The pinyin block now sits in a wrapper of at most half the page's width (maxwidth="halfMaxWidth", input_toolbar_typing.xml:6), with a 5-unit lead of its own under the row's 3-unit top inset (lines 5, 6) and the other half left to the empty control the reference keeps beside it (line 18). The string scrolls inside the cap, as the reference's own composition container does (sliderenable="true" over cls_comp_container, style.xml:575-577), and the block carries the container's own press tint while it is edited (comp_pushedbk — black 8% light, white 12% dark, values/colors.xml:84). Ours ran the full width of the row in portrait and stopped at a 160dp cap of our own making in landscape, with no press feedback at all.

  • The pre-edit string. It is now the reference's two-part composition (cls_comp_half over cls_comp_right, style.xml:580-587): the syllables the user has fixed — the nine-key reading tapped in the rail — carry the accent, the unconverted tail keeps the key text colour, and both take comp_font's own size (style.xml:79). A divider the decoder inserted stays plain, because it is not a syllable the user chose.

  • The key fills trade places on press. keybutton_pushed_bk is keybutton_func_bk — a white key presses to the grey key's colour — and keybutton_func_pushed_bk is keybutton_bk, so a grey key flashes white; the theme-coloured action key keeps its own pressed recipe (values/colors.xml:19-23, values/dark_colors.xml:18-22, cls_button_enter, style.xml:810-818). Each cap also carries the skin's one-unit shadow line under it (shadow_y_offset="1" over keybutton_white_shadow / keybutton_gray_shadow, black 30% light / 40% dark).

  • Each pressed surface carries the colour its own class names. The candidate cells brighten to canditem_pushedbk (white light / white 8% dark), the expanded page's cells to morecands_button_pushedbk, the rail rows fill with the white key, the symbol-category tabs take one neutral pill with labels at 60% and 85%, the long-press bubble cells fill with the accent, and the corner marks, the idle toolbar's glyphs and the candidate split take their own named colours (cls_option_canditem, cls_vertical_symbol_list_button, cls_input_kbd_symbol_option, cls_fusiyin_bubble_button, style.xml:1025-1030, :715-724, :1135-1139; values/colors.xml:14,16-17,30,81-83).

  • A face presses onto the page it lies on, not into its own shade. Every press colour the skin names for a tile or a toolbar key is the same recipe — black 6% light / white 4% dark over the ground (toolbar_idle_button_pushedbk, setting_button_pushedbk, values/colors.xml:45,89, values/dark_colors.xml:43,87) — so the tools and switcher tiles, the settings rows and the idle toolbar's keys sink toward their page on both skins, where a shade derived from the resting colour brightens them on the dark one.

  • The launcher pages read their own classes. The chosen tile on the keyboard switcher keeps its card and colours its name and glyph with the accent (cls_settings_option_mode_switch_button declares no selected fill, only selectedtextcolor / ficon_textcolor_selected, style.xml:649-668), where the settings page's cls_settings_button fills it. The English board's 中/英 key answers in the accent (keyboard_current_english, cls_button_en2cn, style.xml:897-904), the direction the key points.

  • The number page's return key wears the theme colour. cls_button_back_26key takes cls_button_back, whose cls_bk_theme_circle fills with theme_color and presses to the white key's own fill (style.xml:857-858, :838-843, :282-286). The label stays ours: the reference's is a private-use glyph of its icon font.

  • The nine-key and stroke bottom rows are white, not grey, either side of the space bar. Both name cls_keyboard9_button_func_white_circle for 123 (input_kbd_pinyin9.xml:88, input_kbd_stroke.xml:70) and the same white circle under the 中/英 key's own colours (:98, :82), leaving only the 符号 key above them grey.

  • The panel header's close key is the reference's own circle. Both pages the reference gives a header open with cld_toolbar_close_button: a circle filled toolbar_idle_button_bk (white 80% light / white 12% dark) pressing to toolbar_idle_button_pushedbk (black 6% / white 4%), its glyph in action_button_pushedtext — the button-glyph colour, not the idle toolbar's icon grey (input_settings.xml:5-8, input_switch_keyboard.xml:5-8, style.xml:266-273; values/colors.xml:38,43,45). It paints 40dp inside the 48dp touch target, where our old header drew a bare arrow that also fell into the tools page's tile-icon rule and took the accent.

  • The expanded page wears the colours its own classes name. The list sheet is morecands_list_bk (white 40% light / 6% dark) edged at 2 units with morecands_list_border (black 6% / white 10%) and rounded 11, each cell repeating that border at 1 unit; the 全部 / 单字 group is morecands_filter_container_bk (black 4% / white 6%) with a white chosen chip on the light skin and black at 24% over the pill on the dark one, its label filter_button_selecttext and the other chip left at keybutton_text_2; the corner backspace takes morecands_backspace_bk and its pressed shade, this page's own opaque greys (style.xml:1141-1149, :1157-1166, :773-780; values/colors.xml:121-128). The chosen chip is one more face that sinks on the dark skin where ours brightened.

  • Four classes are checked and left unported, with the reason recorded. cls_quickinput_button, cls_morecands_scroll and cls_keyboard_button_add_custom_symbol are declared but instantiated by no shipped layout, so they name no behaviour to port; cls_period_comma_fusiyin only repeats the 40% corner-mark colour our hint already carries.

Two side changes the alignment work needed: a debug build can coexist with a release install through openimeAppIdSuffix, and the two clipboard retention tests that assert an empty history now hold a precondition (this app must not be the active IME), plus the rail stress case types the 0 through the space bar's up swipe.

Affected areas

  • Keyboard layout, insets, floating keyboard
  • Build, tests or documents

Verification

  • :app:testDebugUnitTest — 514 tests, 0 failed
  • :app:lintDebug
  • :app:assembleDebug
  • :app:compileDebugAndroidTestKotlin
  • git diff --check
  • Instrumented suite on a local emulator — 159 tests, 3 skipped, 0 failed on emulator-5558 API 31, run after every batch in this branch; the per-keyboard volume run holds emulator-5556. CI covers API 26 / 29 / 31 / 34 as well.

The candidate-presentation instrumented test now asserts the expanded page's card and filter chips instead of the bare scrolling grid it used to be, the new floating-tip test covers the banner's own proportions, its close key settling the tip for the stay afloat, and the collect key staying reachable under it, and the new pre-edit test covers the half-page cap and the string staying inside it.

Test device, Android version and commands:

  • CI: x86_64 emulators, API 26 / 29 / 31 / 34.
  • ./gradlew :app:testDebugUnitTest :app:lintDebug :app:assembleDebug

Change log and version

  • I added each user-visible change to [Unreleased] in CHANGELOG.md. Internal changes do not need an entry.
  • I did not change VERSION. Only a release PR changes it. See docs/RELEASE.md.
  • The PR title states the result. It becomes the commit title, and the description becomes the commit body.

Privacy and delivery

  • The change has no passwords, clipboard content, recordings, device logs or personal paths.
  • The change keeps the source and license of each model, dictionary and third-party file.
  • If the change affects user data or settings formats, the description explains upgrade compatibility.

🤖 Generated with Claude Code

Slacker-LLC and others added 7 commits October 14, 2026 22:46
The corner marks were an older scheme of our own (a=~, s=!, d=@ …). They now
follow the reference product's map (ficon_text in input_kbd_pinyin26.xml and
input_kbd_english26.xml): the same key carries the full-width mark in Chinese
and the ASCII mark in English, the top row keeps 1–0, and a, s, z, c, m are the
same in both languages.

A long press now opens only the letter in both cases, upper first, as the
reference does (long_press_bubble_text); the mark stays on the swipe-up gesture
(fusiyin_keycode). The comma/period key also commits the mark above it on a
swipe up.

Co-Authored-By: Claude Code <noreply@anthropic.com>
… reference skin

- The 26-key draws A and L half a letter wider (skin weight 15 against 10) and
  the last row's 分词 / 退格 with the same ratio, instead of nine equal letters.
- The nine-key and stroke side column is 删除 / 重输 / 发送 with send twice as
  tall; the separate zero key goes away and the space bar carries the corner 0
  (an up swipe types it, behind the 上滑数字 setting).
- Candidate items pad 8dp per side, so more candidates fit on the strip.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The rail during composition is now the same flat list the symbols use: one
reading per row at one text size, a hairline between rows, and no state drawn
on any item — the pre-edit says which reading is current. The list keeps the
user's scroll position until the open digits change, instead of re-centring on
every keystroke.

The reference ships one FilterListLayout for both jobs and gives the reading
the symbol item's own style (cls_pinyin9_filter_button inherits
cls_vertical_symbol_list_button, style.xml:1023-1032), with syllables one
weight heavier at the same size (style.xml:112,121-122). The native-side
evidence is recorded in docs/DECISIONS.md.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The separate zero key went away with the reference-aligned side column, so the
rail case swipes up on the space bar, the way the key works.

Co-Authored-By: Claude Code <noreply@anthropic.com>
When this app is the user's input method its service is bound in the test
process and its clipboard listener captures every copy — including the fixture
these two tests copy — by design, so the queued panel load they check is
indistinguishable from the listener. They now hold the precondition they need
instead of failing; a system IME active (CI) runs them as before.

Co-Authored-By: Claude Code <noreply@anthropic.com>
-PopenimeAppIdSuffix=.dev installs beside the release build (the owner's phone
runs the .dev variant); release builds never pass it.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Typing "dang" also produced the split da + "ng", and no syllable starts
with "ng", so that path can never complete a reading of the input; the
words for the settled "da" prefix (打, score 7798) nonetheless entered the
candidate set and outranked the complete reading 当 (6899), so the space
bar committed 打 and 当地 fell to rank 6 of "dangdi".

Candidates now carry a coverage rank: a word whose reading covers the whole
input sorts above one that only matches a prefix of it, and frequency
decides inside each rank. "dang" -> 当 党 档, "dangdi" -> 当地 荡涤.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Slacker-LLC and others added 4 commits October 15, 2026 01:31
An address or e-mail field now gets `. / www. .com .cn http wap. ftp:` on
the nine-key rail instead of the punctuation marks, and no + cell,
because that list belongs to the field. The values are the reference nine-key
layout's own `uri_filter_list` attribute; only that layout declares one.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference's first-item class takes the candidate font one weight
heavier and the accent as its text colour, with no background
(cls_option_first_canditem, style.xml:551-554; first_cand_font weight 500
against 400; first_cand_text_color rgba(79,132,255,1)). openIME drew it
bold on a key-coloured tile.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The expanded candidates used to be the key grid itself: one bordered
tile per candidate with the first one filled in the accent. The
reference's 更多候选 page is a different surface, and its skin file says
how it is built.

The page now opens with 全部 / 单字 as one option group over the list
(input_more_cand.xml:9-13), the list sits in one rounded card (radius 11,
2 edge, cls_morecand_bk), and the backspace floats in that card's
bottom-right corner at 25.2% by 20.2% of the list, rounding only that
corner with the card's own radius (bkround="0,0,11,0"). Every cell is
the same flat label with a hairline edge (cls_morecands_button): the
skin gives the expanded list no first-item class, so no cell carries the
accent tile.

单字 keeps single characters and 全部 is the page's default, as the
options declare. The corner key deletes one letter of the open
composition, so the page stays open on the shorter list.

The reference's left syllable list (FilterListLayout, 音节过滤) is not
in this change: the 26-key path has no syllable-fixing state yet.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The URL rail, the first-candidate accent, the candidate ranking fix and
the expanded page were user-visible changes without an entry.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@Slacker-LLC Slacker-LLC changed the title Align key hints, key weights and the nine-key reading rail with the reference skin Align letter hints, key weights, the nine-key rail, the candidate order and the expanded page with the reference skin Oct 14, 2026
The 26-key bottom row gains the reference's two symbol keys beside the
space bar, in Chinese and English, with the address-field marks in a URL
or e-mail field, and each key takes the reference's own weight. The
English merged key now leads with the period and carries the comma in
its corner, as the skin draws it, instead of the other way round.

The nine-key 1 cell becomes the reference's @# key: the label names the
symbols it offers, a tap and an up swipe still type the digit, and a long
press opens the reference's own seven-symbol bubble. The board has no
分词 key because the reference's has none, and the 26-key 分词 key drops
the invented corner mark and popup its class never declares.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@Slacker-LLC Slacker-LLC changed the title Align letter hints, key weights, the nine-key rail, the candidate order and the expanded page with the reference skin Align letter hints, key weights, the bottom row, the nine-key rail and the expanded page with the reference skin Oct 14, 2026
Slacker-LLC and others added 5 commits October 15, 2026 02:09
The open Pinyin string now paints the syllables the user has fixed in
the accent and the unconverted tail in the key text colour, as the
reference's cls_comp_half / cls_comp_right pair does, and it takes
comp_font's own size. A decoder-inserted divider stays in the plain
colour: only a syllable the user chose is fixed.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The strip now insets the candidate list at both ends, draws the
reference's hairline split between the list and the expand key, keeps
a 3-unit gap before it, and gives the expand key the reference's pill
press shape. Its 48dp touch target stays: that is our own rule.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Each idle-toolbar key is now the reference's own 40-unit pill with a
press tint, instead of a bare icon stretched by weight across the row,
and the slack of the strip sits in a spacer ahead of the hide key. The
48dp touch target stays our own rule.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The digits page was a 3x3 grid over a 返回 / 空格 / 0 row with
退格 / . / @ / 发送 down the right. The reference's 26-key 123 key opens
input_kbd_number_from26.xml instead: a symbol rail over the return key in
the left column, three digit columns closed by 符号 / 0 / . , and a
delete / space / send column, at the skin's own weights 66 / 75 / 75 / 75 / 66
and with send twice as tall as the keys above it. The @ key is gone with no
number page declaring one; in a phone field the symbol cell carries the
dialer's # and the period cell its +, so * / + / # stay reachable.

Co-Authored-By: Claude Code <noreply@anthropic.com>
input_kbd_stroke.xml draws three columns of three cells: 一 丨 丿 /
丶 折 通配 / @# :;, each with the skin's own corner digit and an up swipe
that types it. The last row was missing from our board, so 通配's digit 6
and the two punctuation marks had no key; the @# cell now opens the
reference's own / - _ @ # . 7 list on a long press. The bottom rows of the
stroke and nine-key boards take the reference's 45.5 / 134 / 45.5, which
fills the 225-unit centre column exactly.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@Slacker-LLC Slacker-LLC changed the title Align letter hints, key weights, the bottom row, the nine-key rail and the expanded page with the reference skin Align the keyboards, candidate surfaces and toolbar with the reference skin Oct 14, 2026
Slacker-LLC and others added 9 commits October 15, 2026 02:36
…trip

The reference's idle row runs clipboard, emoji, board selector, where ours
led with the board selector, and its icons sit in a strip that scrolls when
they do not fit, with the slack in a spacer at the strip's own end
(sliderenable, input_toolbar_idle.xml:14,34). Ours stretched the icons by
weight instead, so a narrow screen shrank them, and the spacer sat outside
the strip. 收起 stays outside it.

Co-Authored-By: Claude Code <noreply@anthropic.com>
input_kbd_symbol.xml ends the page with a row of the return key and the
category container, weighted 85 against 13.1, and scrolling. Ours put the
categories down a 74dp column at the left edge, which cost the grid a fifth
of the page's width. The strip is the chip row the emoji page and the
expanded candidate page already use, so the symbol surfaces agree now.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference's symbol row closes with its 123 key and its backspace at the
two ends (input_kbd_symbol.xml:31-41, weight 15 against the cells' 70), so
the page reaches the digits and deletes without being left first. Our cells
are five to a row, so both keys take the ends of the tab strip's row, the
row the reference gives to the page's own return key.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference fills its symbol board's toolbar with the history of what the
user has typed (input_toolbar_quickinput.xml:4-18): a 4-unit lead inset, the
scrolling list, the candidate strip's own hairline split, a 3-unit gap, a
40-unit close key and a 4-unit tail. Each cell is 36 units wide with a
radius-6 press face over candidate-sized text in the candidate text colour
(style.xml:1061-1070), and a tap commits through the same path the board's
own cells use, so the list re-reads itself after every symbol.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The tools page and the keyboard switcher are one surface in the reference:
a container 88.9% of the page wide with its own paddings, tiles filling
their cells in a 4-by-2 grid capped at 192 units (style.xml:616-619), each
tile a radius-14 card filled with setting_button_bk, its pressed colour, or
the theme colour when it is the chosen one, and its name dimmed to
keybutton_text_3 (style.xml:626-647, values/colors.xml:15,87-93).

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference fills the toolbar row with a tip while the floating
keyboard is up (`input_toolbar_tip.xml:43-50`): one pill across the
row, a 3.571% lead space, a left-aligned ellipsized label and a round
close key whose width is its own height. Each of those numbers is now
a constant with the line it came from, and the close key keeps our
48dp touch target.

Ours names our own way back — a tap on the handle — where the
reference asks for a drag to the bottom edge, because the docked
keyboard is what our handle restores. The tip settles for the stay
afloat once its close key is tapped, and the next stay shows it again.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The typing toolbar holds the pinyin block in a wrapper of at most half
the page (`maxwidth="halfMaxWidth"`, `input_toolbar_typing.xml:7`),
with a 5-unit lead of its own and the row's 3-unit top inset; the other
half goes to an empty control, the room the reference keeps beside it
(line 18). The string scrolls inside that cap, as the reference's own
composition container does, and the block carries the container's own
press tint (`comp_pushedbk`, black 8% light and white 12% dark).

Our pre-edit ran the full width of the row in portrait and stopped at a
160dp cap of our own making in landscape, and it gave no press feedback
while it was edited.

Co-Authored-By: Claude Code <noreply@anthropic.com>
A fresh copy or a text message is answered by the reference's own
quick-input toolbar (`input_toolbar_quickinput.xml`): the text at the
candidate font in `canditem_text` with the glyph that names where it
came from (lines 39-45), and the row's own close key borrowed from
`cls_history_close_button` (line 46) instead of a back control.

Our chips were filled pills at the body size behind a back chevron, a
shape no reference toolbar draws. The system's autofill chips keep
that back control, because they are the platform's own views in the
same strip; every row keeps our 48dp touch target.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The tip, pre-edit and quick-input comments quoted nearby lines for the
container, the lead space, the close key and the composition wrapper.
Each now names the line the attribute is actually on.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Slacker-LLC and others added 28 commits October 15, 2026 16:56
The reference's own settings page names the entry 智能联想 and describes
it as 根据上文提供的文字、表情、符号等推荐 (resources.arsc). Our entry was
表情联想 with the description 先给出相关表情, which no longer describes
the row: it carries next words, emoji and kaomoji alike.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Its resources.arsc carries exactly two scheme names, 小鹤双拼 and
自然码双拼, under the title 26键双拼方案; ours read 自然码 under 双拼方案.
The schemes themselves are unchanged.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference's wubi page has four switches and its own preference XML
carries their defaults: code hint, +pinyin mixing and the four-code
commit are on, the fifth-key commit is off (res/cU.xml, R.xml.
settings_preferences, read by SettingsConfigNext.<clinit>). Only the
four-code one has a counterpart here, and the schema did not set it:
speller/auto_select is what commits a four-code input whose segment
holds exactly one candidate (speller.cc:161, gated on auto_select_ and
menu->Prepare(2) == 1), so a wubi word with no rival stayed on the bar.
The reference's codes are at most four letters, so the gate cannot fire
early.

The fifth-key switch cannot be split from it -- AutoSelectAtMaxCodeLength
(speller.cc:148) runs whenever max_code_length > 0 and ignores
auto_select_ -- and the other two need a merged table and a candidate
comment this schema does not produce. Recorded in DECISIONS.md.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference's preference XML carries a default for every fuzzy pair
(res/cU.xml, R.xml.settings_preferences): its master switch is off, and
six of the fifteen pairs -- z=zh, c=ch, s=sh, an=ang, en=eng, in=ing --
are true while the other nine are false. Our pairs all started off, so
a user who turned 模糊音 on got nothing until they ticked them one by
one. FuzzyRule.defaultOn now carries those six, the shipped
openime_fuzzy.yaml is generated from them, and the page note says so.

Also records the reference's 上滑输入数字符号 switch (enable_key_fusiyin,
default false) and why our up swipe stays on by default: the native
engine owns that behaviour there, so what it does at run time cannot be
read off the artifacts, and turning ours off would strand the corner
marks the skin draws.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference's misread-word dictionary (incorrect_syllable, 6,308
records of a word plus the reading one of its characters takes there)
disagrees with our tables on 66 of them: 岑参 is Cén Shēn where we read
cen can, 五行八作 takes háng and zuō, 说客 takes shuō.

Those differences now generate pinyin_reading_fixes.tsv (64 rows), which
PinyinLexicon loads next to pinyin_phrases.tsv, so the word can be typed
under its real reading. Only the differences ship: 3,390 records already
read as the tables do, and the 2,852 the tables lack are a coverage
question rather than a wrong reading. Each row keeps the word's own
weight, so a correction never overtakes the ordinary phrases.

The generator maps ü to v before stripping tone marks. Decomposition
alone turns ǚ into u and the first run wrote luhuzi for 捋胡子, an
override of a reading that was already right.

The file's index stays out of the port: its counting space resolves to
neither the records nor the characters, and which syllable each row names
does not decode from the artifacts.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Both of the reference's 26-key layouts list the lower case first on P and
L ("p P", "l L") — in the bubble text and in the keycode list — where the
other twenty-four keys read "X x". The nine-key layout leads lowercase
everywhere, and the swipe-up lists set their own per-key order, so this is
the reference's data on those two keys rather than a rule to generalise.
The port now offers the same order there.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Unlike the letter-hint order, these two are deliberate: this port's
haptic default (its own tuned KEYBOARD_TAP with a slider) and the single
association switch against the reference's four. Everything else in the
reference's 123-row preference file is matched where the feature exists
here, and the rows for features this port has no counterpart for are
called out in the same note.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference reads "p P" and "l L" where its other twenty-four keys
read "X x", and its nine-key long press leads lowercase throughout: one
product contradicting itself. A consistent keyboard is worth more than a
two-key copy of that, so every key offers the letter in both cases upper
first and the skin's two exceptions stay in DECISIONS.md with the rest of
the evidence.

Co-Authored-By: Claude Code <noreply@anthropic.com>
0.0.10-beta.1 added onComputeInsets to stop the app behind from being
resized or panned while the keyboard floats. The report puts the card's
own bottom edge into contentTopInsets and visibleTopInsets, but the base
onComputeInsets leaves the window on TOUCHABLE_INSETS_VISIBLE, whose area
is "the area inside of the visible insets" (ViewTreeObserver's
InternalInsetsInfo). A visible top at the window's bottom edge insets the
touchable area by the whole window height: the floating card drew as
usual and answered no touch.

AOSP pairs the same full-height report with TOUCHABLE_INSETS_FRAME in its
own full-screen branch, so the floating report sets the frame mode too.
The app behind still is not resized or panned, and the docked keyboard
never enters the branch.

FloatingInsetsInstrumentedTest pins the three values, keeps the base
report for a card with no height yet, and states the mechanism next to
the fix.

Co-Authored-By: Claude Code <noreply@anthropic.com>
updateResponsiveGeometry runs from onMeasure and wrote mainDock's and the
second key row's layout parameters back unconditionally. Assigning
layoutParams always calls requestLayout(), and a request made after
performTraversals has cleared mLayoutRequested schedules another full
layout pass ("Clear this now, so that if anything requests a layout in
the rest of this function we will catch it and re-run a full layout
pass", ViewRootImpl.performTraversals) — while nothing in the chain was
conditional. The keyboard therefore re-measured, re-laid out and redrew
every frame for as long as it was shown, docked or floating.

The three writes now happen only when the value they carry differs.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The floating keyboard's long-press drag only started when the press landed
on a toolbar icon. With no child under the finger, the framework dispatches
the rest of the gesture straight to the zone's own onTouchEvent and never
asks the interceptor again ("intercepted = true" in the else branch,
ViewGroup.dispatchTouchEvent, actionMasked != ACTION_DOWN with no touch
target), and the zone's onTouchEvent returned super's false — so the
long press armed and nothing ever came of it. The zone now claims the
gesture itself once it was going to intercept the press, swallows the
DOWN, and starts the drag on the first move after the long press.

The docked height drag never armed at all. It gates on the press being
inside the toolbar row (ImeTopZone.isInsideToolbar) and compared the row
taken with getLocationInWindow — window-local — against MotionEvent.rawY,
which is "the original location of the event on the screen, before it had
been adjusted for the containing window and views". This keyboard's window
is not the screen (dumpsys window: frame=[0,1731][1080,2400], gravity
BOTTOM), so its toolbar row sits at window y 0 while a finger on the row
reports a raw y of about 1731: the check could not be true on a device.
It now measures the row on screen, the same way the candidate choice strip
tests raw touches (KeyPopupController.trackChoiceTouch).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Dragging the card wrote the window's attributes on every touch sample,
including the samples whose rounded delta is zero: at 60Hz that is a
relayout of the IME window per frame while a finger jitters on the
toolbar, and the drag path also re-read the opacity preference each
time. applyLayout now returns early when position, size and alpha all
match what the window already carries, and the opacity comes from the
value scheduleLayout last applied.

A drag that does move the card still writes, so the bounds still clamp
and the position is still remembered.

Co-Authored-By: Claude Code <noreply@anthropic.com>
hasPressedKey() answers "a key still owns this gesture" by walking the rows
for isPressed, and renderModeBody defers to a 40ms poll while it is true.
isPressed is not cleared when a view is taken out of the hierarchy, so a
gesture whose key was rebuilt away under it — or one a rebuild swallowed —
left a key that looked pressed forever: every later rebuild (a keyboard
switch, a rotation, closing a panel) silently did nothing and the poll
re-posted itself for as long as the keyboard was attached.

A touch stream is only live between a DOWN and its UP, and this view
already sees every event that reaches the keyboard, so dispatchTouchEvent
now tracks whether one is open and hasPressedKey() answers false when none
is. The deferral still protects a real gesture, and the rebuild it deferred
lands as soon as the finger lifts.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The docked height drag went through setKeyboardHeightPercent on every touch
sample: each one wrote the preference and rebuilt the whole key surface,
so a single drag persisted the height a hundred times and rebuilt the
keyboard a hundred times.

The drag now previews — the row heights and the wanted window height follow
the finger, and the rebuild is posted to the next frame so a frame with
several samples rebuilds once — while the commit happens on release, from
either the UP or a CANCEL that takes the gesture away
(KeyboardHeightDragController.onDragEnd). The keyboard reports and persists
the height exactly once, when the user lets go.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Co-Authored-By: Claude Code <noreply@anthropic.com>
The zone gives toolbarRow its own full height, so the row reaches the zone's
bottom: measuring the band below the row found 0px and the test polled until
its 30s timeout. The band is the slack around the 48dp icon strip inside the
row, which is where the gesture lands on no child.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference documents the gesture itself: the subtitle of its own switch
(`res/cU.xml`, key `enable_key_enter_send_msg`, default false) reads
「长按「发送」可换行。开关支持App范围」, and its confirm dialog repeats the
line. Only the send face is documented, so only that face takes a long
press here; a composition reads 确定 on the key and keeps its press-only
behaviour, as do the other action faces.

`enterKeyLongPressFor` sits next to the other Enter-key policy so the key
face, the dispatched action and the long press all resolve from the same
EditorInfo bits. `ImeKeyboardView.syncEnterKeyPresentation` wires the
listener and says the gesture in the key's contentDescription
(「发送,长按换行」), `onEnterNewline` commits a live composition the way
the key's own press does and otherwise sends a raw Enter.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference names all three parts of this feature itself: the clipboard
list's tip 「长按列表可进入拆词」 (`string/clipboard_history_word_seg_tip`),
the view it opens, titled 「滑动选词」 (`word_segmentation_title`), and its
one action, 「添加到常用语」 (`word_segmentation_add_to_common_phrase`),
with a colour set that separates a clickable label from an un-clickable one
and gives that action a disabled face. This port had nothing behind a long
press on a clipboard card.

`HanWordSegmenter` takes the longest word the tables carry at each
position (they hold up to eight characters, 依法追究刑事责任 being the
longest row of `pinyin_phrases.tsv`); `TextWordIndex` is their text side,
built once on the panel's own thread. `ClipboardPanelController` opens the
split on a long press, selects a run by sliding across the words — the run
stays when the finger lifts — and files it into 常用语
(`QuickPhraseRepository`), disabled until something is selected.
`WordFlowLayout` is the wrapped list the words are laid out in.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference ships a poetry grammar (sys_poem_gram, type 11010) whose
lines are keyed by the pinyin they are typed with, and its own settings
text names poetry among what 联想 recommends (关闭联想后,将不会推荐诗词、
计算结果、emoji表情。). Generate poem/lines.tsv from the decoded table and
offer a line as a candidate once the composition spells three of its
syllables, behind the engine's own candidates; a line spelled out whole
brings the line that follows it in its own poem. The layer follows the
智能联想 switch the reference groups it under.

Three syllables is the measured floor: of the 3,000 most frequent pinyin
keys, 2 syllables match 380 (12.7%) and 3 match 3 (0.1%).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Policies and routing for the 快捷格式输入 batch, whose settings rows and
service wiring follow in a separate commit (another session is editing the
same files).

数字、英文前后加空格: LetterDigitSpacePolicy (core) and a letterDigitSpace
hook in InputConnectionGateway.commitText, the single funnel every commit
goes through. The reference's own title and subtitle are the rule
(string/letter_digit_space_list_item_title/subtitle, res/xq.xml): a space
between Chinese and digits/English, on typing and on voice alike.

双击空格键输入句号: DoubleSpacePeriodPolicy keeps the reference's 200 ms
window (InputViewRoot$z.invoke, classes.dex code_off 0x4c1764, const-wide/16
0xc8) and its 31-character punctuation gate.

算式出结果: ArithmeticExpressionPolicy evaluates the expression that ends at
the cursor. The reference computes it natively (libkeyboard.so
InputModel::Impl::AssociateCalculator 0x26523c, GetCalculatorCandidate
0x255ca4, CommitCalculatorCand 0x264594) and its 智能联想 text lists
计算结果 among what that switch offers (string/associate_close_content).

使用耳机麦克风录音: VoiceInputRoutePolicy decides between a headset and the
phone's own microphone, as the reference's enable_bluetooth_input does
(string/enable_bluetooth_input_show, res/cU.xml defaultValue=false);
VoiceAudioRouteManager and LocalAudioVoiceBackend read the switch per
session.

Unit tests for all four policies and the gateway spacing, plus an
instrumented test for the service-side double space.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Second half of the batch, carrying both lanes' wiring: the settings rows for
数字、英文前后加空格 / 首字母自动大写 / 双击空格键输入句号 (the reference's
own 快捷格式输入 page, res/xq.xml), 候选栏展示 and 使用耳机麦克风录音, plus
首字母自动大写 and 按键音量 from the other session working this tree.

首字母自动大写 reads the text before the cursor at every text change and
raises Shift at a sentence start; it rides the reference's switch
(enable_first_letter_capitalization, defaultValue=false) and the Shift key
now announces the reference's own guide text (string/caps_lock_guide_text).

按键音量 scales the bundled click samples and the system click with the
reference's key_sound_volume (0-100, default 50).

算式出结果 and 候选栏展示 fill the row between words: an expression that ends
at the cursor offers its result, and with 候选栏展示 on the clipboard's
content is offered as a candidate that writes what was copied.
Candidate-bar clipboard display ships off even though the reference defaults
it on (enable_clipboard_candidate_bar_show, defaultValue=true), because this
batch's personalisation features are off until switched on; the divergence is
recorded in the changelog and the decision log.

使用耳机麦克风录音 routes voice input to a headset when one is worn and back
to the phone's own microphone when it is not, per the reference's own
subtitle 「默认使用手机录音。佩戴耳机时,使用耳机麦克风」.

User-data export and import carry all six new settings. The
FuzzyRulesInstrumentedTest expectation now matches the shipped fuzzy defaults
(z=zh among them, fcae0dd) instead of the pre-fcae0dd behaviour.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The reference enumerates the whole open digit tail on every key press and
cuts nothing. Its left-list builder (libshell.so 0x5ba1e0) pushes one
string per marked syllable id, then the letters of the open head key --
each with its `+0x20` twin (0x5ba6c4) -- and the leading digit item,
reserving only (marked + 8) at 0x5ba430; decoding syllable.dat end to end
gives 64 -> 10 items, 9426 -> 18, 5426 -> 15. Fuzzy widens the lattice at
the digit level rather than selecting a lookup: every data3 record carries
the smallest rule mask that reaches its spelling and pass2 (0x5b962c)
admits it while `mask` is a subset of the reader's fuzzy set, so node 64
carries `li` (0x0008), 426 carries `fang` (0x0050), and 9426 runs from 19
readings to 32 with every rule on.

This port listed syllables only, offered letters for a single key press
only, cut the rail at 12 and never used fuzzy while decoding, so those
readings could not appear at all. The rail now lists the head key's
syllable-capable letters for a tail of any length (i/u/v stay out as dead
ends), the rail bound goes 12 -> 48, the reading bound 12 -> 24 and the
librime reading count 6 -> 12; the decoder files one fuzzy-variant entry
per syllable under its plain spelling's digits carrying the rules it
needs, admitted only while all of them are on, while the rail keeps plain
spellings the way the reference's left builder does (mask == 0, 0x5ba378).

ENGINE-SPEC.md:222 records the 0x6c0508 family as a nine-key fuzzy path;
the disassembly places it in the wubi engine, so that line is recorded as
wrong and not followed.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Keep the input improvements while restoring the five-key bottom row, equal letter widths, nine-key zero and segmentation keys, stroke grid, numeric arrangement, and uniform key gaps. Restore the numeric return key to its original function-key color and cover the layouts and phone symbols.

Co-Authored-By: Claude Code <noreply@anthropic.com>
…fficial model packages

The keyboard and the app pages now share one accent, the launcher grids stay
centred at any window width, and the toolbar keeps our own key order. The
voice-model page lists the official download addresses, and its own import
takes the tar.bz2 packages those addresses serve.

- Unify the accent (light #1668D0, dark #2D6FD6) across the keyboard palette
  and setup_primary, with white on the accent, and give the toolbar icons the
  weight they need to read on the strip. A test pins the two together.
- Centre the tools and keyboard-switcher grids: the reference's 88.9% width
  was computed but never applied, so the rows sat left of the window centre.
  The rows also follow the window when it changes width.
- Keep our own toolbar order (切换键盘, 剪贴板, 表情, 文本编辑, 撤销, 更多),
  in the reference's scrolling strip. docs/DECISIONS.md records that this is
  deliberate against the reference's own row.
- Refresh the SMS code chip from a ContentObserver on Telephony.Sms.CONTENT_URI
  with a 200 ms debounce, so a code that arrives while the keyboard is open
  shows up; unregister it when the chip is off, the field is a password or the
  view detaches. SmsCodeExtractor also reads the layouts real messages use.
- Import the official sherpa-onnx tar.bz2 model packages as they arrive, with
  commons-compress for bzip2 (java.util.zip has zip and gzip only), the
  application/x-bzip2 MIME type in the picker, and the archive's own traversal
  and size checks kept.
- List the official model addresses on the voice-model page with copy buttons,
  and centre that page's content and cards.
- Hide the handwriting entry (HandwritingFeaturePolicy.ENTRY_OFFERED): the pad
  is still rough. The provider, the pad and the strokes table stay in the build.
- Name the release assets openIME-llm-v<VERSION>.apk (the offline voice models)
  and openIME-v<VERSION>.apk (the smaller package), and compare the draft's
  asset names against those two instead of counting them, so a tag run with
  both APKs attached can publish.

Co-Authored-By: Claude Code <noreply@anthropic.com>
Fold [Unreleased] into the 1.0.0 section and set VERSION. No code changes.

Co-Authored-By: Claude Code <noreply@anthropic.com>
# Conflicts:
#	.github/workflows/release.yml
`theDefaultModeWouldLeaveTheCardWithNoTouchableArea` insets a Rect on all four
sides. That overload is hidden in the platform source
(`frameworks/base/graphics/java/android/graphics/Rect.java`, the javadoc block
above `public void inset(int left, int top, int right, int bottom)` carries
`@hide` at android-10.0.0_r1), so the API 29 compatibility job dies with

    NoSuchMethodError: No virtual method inset(IIII)V in class android.graphics.Rect

API 26 predates hidden-API enforcement, which is why that job stayed green and
API 29 did not. The test now moves the edge it cares about, which is what the
four-argument call did here.

Co-Authored-By: Claude Code <noreply@anthropic.com>
View.onTouchEvent posts the click on ACTION_UP, and detaching the view cancels
that post. The pending-rebuild poll could be due ahead of the post, so a tap on
Space during a layout change was swallowed. The poll now yields one turn after
it sees the finger lift. The matching test also skips when the main thread ran
past the long-press deadline, where the press is a voice hold by design.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@Slacker-LLC
Slacker-LLC merged commit 5ac757a into main Oct 15, 2026
11 of 12 checks passed
@Slacker-LLC
Slacker-LLC deleted the align/letter-hints branch October 15, 2026 23:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant