Lu Fan(SonnyCalcr)
58a27f3edb
fix(engine,server): 候选末页满页且无法再展开时置灰下一页箭头 ( #1070 )
...
下一页箭头原先在当前页满时一律可点,为首字母候选分批展开留口子;
但只有单个首字母且首字母候选恰好截在 24 条时才能展开,其余情况
末页满页的右箭头点了没反应也不会变灰。
引擎新增只判断不查库的 can_expand_initial_candidates,与实际展开
共用前置条件;Server 发布候选页快照时,满页只有在还能展开时才算
有下一页。
Refs #1069
2026-10-18 23:11:22 +08:00
Lu Fan(SonnyCalcr)
5de73de7a6
feat(engine,server,windows): 双拼末尾辅助码支持间接触发,句中辅助码单独成区 ( #1068 )
...
双拼辅助码新增「直接辅助码」开关(默认开,行为不变)。关掉后末尾辅助码改由引导键触发:
反引号、分号、大写字母可多选,触发键后接一到两个辅码(如 ulpb;xy、ulpbXy),一码重排、
两码筛选,只在输入末尾生效。触发键在 TSF 缓冲与 Server raw 里统一记成 |,与句中辅助码的
反引号段区分。
两组触发键互斥:一边勾上某个键会取消另一边的同一个键;手改配置两边都勾时归句中辅助码。
新增 Worker 消息 EndHelpcodeTriggersChanged(37),旧 DLL 忽略。
设置页把句中辅助码单独成区(仅双拼),其中间接(引导键)与直接(万象式)两种方式互斥。
新配置项同时写进出厂模板与开发配置。
2026-10-18 22:54:17 +08:00
Jia Sui and fanlusky
456b23ed94
feat(engine,server): 五笔支持 T 模式与 V 模式 ( #942 )
...
* feat(engine,server): 五笔支持 T 模式与 V 模式
五笔方案此前被排除在 Shift+T 日期时间模式与 V 模式之外。根因是方案层就把
raw 归一成小写 a–y、丢弃数字与运算符,Server 的形状判定读不到大写 T/V 前缀。
- 引擎 WubiScheme 新增 raw_input_with_cases_:保留大小写与 T/V 模式的非字母键
(数字、运算符、: /),但按码字域丢掉越域字母(默认配置下 z)。查询码仍由
normalize_wubi_code 派生,普通码下两者长度相等——Server 用原样串长度数四码。
- Server:T 触发新增独立谓词 date_time_scheme(chinese_scheme || Wubi),不并入
chinese_scheme 本身;FormatVModeWorkerPayload 对五笔返回 UppercaseOnly。
- 自动上屏 / 顶字两个谓词新增 special_mode_active 前置:T 模式关键词归一后可能与
合法四码重合(Tdate → tdat),不挡会在第四个字母上结束模式。
- 引擎与 Server 测试、README 与两份配置模板文案同步。
* fix(engine,server,ui-html): 五笔 T/V 模式由宿主标注字面输入
原实现让五笔原样串不再裁到码长,普通码下原样串可以比码长(混输关、z 通配时 wqazb 对 wqaz),
Server 读回后光标、退格和上屏都会错位;守卫也只认 T 模式的全小写主体,TdatE 仍会顶出 tdat。
- 引擎:InputSession::set_wubi_literal_input 随 pending 序列一起下发,只作用于同一次设置。
普通码的原样串就是带大小写的码,与查询码逐位对应、一起截断;字面输入原样保留,
不查码表与拼音,也不算完整四码。退格从原样串重新派生码,字母键两份视图取同一个字母。
- Server:ApplyCompositionEditKey 按 IsWubiLiteralComposition(T 模式触发标记或 V 模式形状)
标注字面输入;两个五笔上屏策略恢复原签名,不再需要 special_mode_active。
- 设置页 T/V 模式说明补上五笔。
---------
Co-authored-by: fanlusky <1730976608@qq.com >
2026-10-15 02:08:36 +08:00
Jia Sui and fanlusky
1499027f04
fix(engine,server): 光标落在拼音首单元内部不再收起候选窗 ( #940 )
...
* fix(engine,server): 光标落在拼音首单元内部不再收起候选窗
quantized_prefix_end() 把光标 floor 到完整单元边界后,prefix_end==0 同时表示「真串首」与「首单元内部」。后者被 Server 的 IsCaretPrefixEmpty 当成串首,候选窗整窗收起(全拼 nihaoya 选你好后剩 ya 左移即消失)。
floor 结果为 0 且 caret>0(首单元内部)时改返回 raw.size(),落回整串解码;prefix_end()==0 当且仅当 caret==0,只有真串首才隐藏。segment_raw_boundaries() 与 #187「前缀只切完整单元」的契约不变。
* fix(engine,server): 首单元内部的光标向上取整到首单元末尾
上一提交让首单元内部的光标落回整串解码,prefix_end() 因此报告整串已解码,
与实际光标位置不符:Server 选词时 caret_prefix_selection 为假,不复位组合
光标,造词后剩余串带着旧光标位置;首单元与后续单元的取整方向也不一致。
改为向上取整到首单元末尾(只有一个单元时即整串)。prefix_end() == 0 仍当且
仅当 caret == 0,前缀选词走正常的光标复位路径。同步更新引擎与
input_key_policy.h 中描述旧规则的注释和相应测试。
---------
Co-authored-by: fanlusky <1730976608@qq.com >
2026-10-15 01:37:58 +08:00
Lu Fan(SonnyCalcr)
53ee180ec3
feat(engine): 直接辅助码句中四码可用第二位辅码大写结束 ( #630 )
...
直接辅助码的四码在句中原来只能补 / 结束(uiab/),现在也可以把第二位辅码敲成大写(uiaB)。
设置页「直接辅助码句中四码标记」两个勾选框可多选、至少留一个,默认只勾 /。
- 引擎:拼写图新增 Aux2Upper(非缩写)。开着大写标记时大写字母只能当第二位辅码,
走不通再退回不分大小写的旧规则,第一位辅码敲大写(uiAuiQ)照常可用。/ 的收键受勾选控制。
- 协议:DirectHelpcodeChanged 载荷改为 "0"/"1"/"2"(2 = 开着但 / 不是编码键),
TSF 据此决定吃不吃 /。
- 配置:新增 helpcode.shuangpin_direct_helpcode_slash / _uppercase,同步写入出厂模板、
工作配置和两个设置宿主的读写映射。
2026-10-05 20:43:02 +08:00
Lu Fan(SonnyCalcr)
5208753ce8
feat: 句中辅助码新增大写字母触发方式 ( #620 )
...
* feat(engine): add uppercase trigger for shuangpin mid-sentence helpcode
With the uppercase trigger on, an uppercase letter right after a complete
two-key syllable opens a mid-sentence helpcode block by itself, as if the
backtick had been typed before it: ulpbXihfa is ulpb`xihfa, and a following
uppercase letter is still the second code (ulpbXYih is ulpb`xYih). The raw
input keeps the typed letters; the block rules live in
contracts/mid_sentence_helpcode.h so TSF, Server and engine parse alike,
including the semicolon final, which no longer counts uppercase blocks.
The scheme now hands the parsed layout (source index and decorations) to the
request, so selection advance and preedit no longer re-parse the raw input.
Adds the MidSentenceHelpcodeUppercaseChanged worker opcode (31).
* feat(windows): count uppercase helpcode blocks before backtick and semicolon
With MidSentenceHelpcodeUppercaseChanged on, TSF uses the shared block rules
when deciding whether '`' opens a block and whether ';' is the ing final, in
both the synchronous and the deferred key classification.
* feat(server): wire the mid-sentence helpcode uppercase trigger
New helpcode.shuangpin_mid_sentence_helpcode_uppercase (default off) in both
config templates, both settings hosts and the settings page as a third
trigger checkbox. It takes effect only with mid-sentence helpcode on and
direct helpcode off; the Server applies it to the semicolon final rule and
broadcasts MidSentenceHelpcodeUppercaseChanged to TSF.
2026-10-04 21:00:41 +08:00
Lu Fan(SonnyCalcr)
22fb1e2569
Merge pull request #619 from fanlusky/feat/shuangpin-direct-helpcode
...
feat: 双拼直接辅助码(万象式,无需引导键)
2026-10-04 19:32:10 +08:00
fanlusky
45c473158a
feat(engine,server,windows,ui-html): 双拼预编辑可显示转换后的全拼
...
外观里行内预编辑与候选窗预编辑各加一个「双拼显示转换后的全拼」开关,默认关闭。
开启后在双拼方案下:行内原始按键显示不带分词符号的全拼,拼音分词显示带分词符号的
全拼;候选窗显示带分词符号的全拼。
- 引擎新增 get_shuangpin_preedit_forms,同时给出原串切分与全拼切分,不受
shuangpin_preedit_mode 影响。
- 原始按键样式下 TSF 不等 Server 回包,开关开启且为双拼时 Server 下发的实际样式
改为 pinyin,回包里再按用户样式去掉分词符号;换方案时重新下发。
- 转换后字母数与按键不再对应,Server 在预编辑末尾附光标映射表
(engine/contracts/preedit_caret_map.h),DLL 用它定位光标和处理方向键,
无表或长度不符时回落到原有字母映射。
2026-10-02 16:14:50 +08:00
fanlusky
31241d22ed
fix(engine,server,windows): 句中辅助码支持光标在句中插入,上屏按不带约束的排序调频
...
光标用箭头或 Ctrl+箭头移回句中后敲反引号,TSF、Server、引擎三处都要求光标在串尾,
反引号被当成标点,光标前的预编辑连同「·」一起上屏。改为只看光标前的部分:光标前是
完整的两键音节就收,落在音节中间或已有段的前面不收。形状规则新增
FanyImeMidSentenceHelpcode::AcceptsMarkerAt,三处共用;引擎的光标插入路径同时接受
句中的第一码和大写第二码。
句中辅助码筛过的列表里,要的词往往已经排第一,选第一个不调频,调了也是在筛过的列表
里排位,下次不敲辅助码时排序不变。现在带句中辅助码上屏词库候选时,改用去掉约束后的
候选作排位参照,选的是第一个也照样调;空格、数字键、鼠标点选都走 ProcessSelectionKey,
引擎自己的上屏路径同样处理。
2026-10-02 12:43:16 +08:00
fanlusky
fae80660c1
feat(engine,server,windows,ui-html): 双拼句中辅助码
...
在完整音节后敲反引号,接着的字母筛这个音节上的字:第一码大小写均可,
紧跟的大写字母是第二码,之后可以接着打,一个输入里可以有多段。开关
helpcode.shuangpin_mid_sentence_helpcode 默认关闭。
约束在整句解码时生效,而不是解完再筛:
- Google 解码器在 add_char_qwerty 里送 extend_mtrx_nd 之前按约束过滤词条,
锁住的字带着上下文重新整句解码;约束变化时整段重解,不污染增量搜索。
- kenlm 词格建图时按约束筛边,受约束的跨度不截断地查词库;神经重排只在
词格 n-best 里挑,自动满足约束。
- 普通词条、云和 AI 候选按同一规则收尾校验;满足不了就不出整句。
反引号段在请求里换成手动分隔符,原串留给宿主回读与预编辑显示;选词推进
时剩余音节上的约束随原串保留。输入形状规则放在 contracts/mid_sentence_helpcode.h,
TSF、Server、引擎共用,新增 Worker opcode MidSentenceHelpcodeChanged = 28。
googlepinyinime-rev 已与上游解绑,AGENTS 与 UPSTREAM.md 同步说明。
2026-10-02 02:20:37 +08:00
fanlusky
c99fb8d585
fix(engine): 英文调频不再以中文权重为基准,并按输入记英文槽位
...
混输列表里英文调频把「全表最高权重 + 1000」写成新权重,中文候选也算在内。
在 zai 下选一次 zaire,它的权重就变成 19,196,987,超过「在」,并且对所有能查出它的输入永久生效。
- adjust_english_candidate_ranking 只用英文候选的权重当基准
- 新增 english_slots 表和 english_slot / set_english_slot /
learn_english_slot_selection / learn_english_slot_bypass:按输入记录首个英文候选在列表中的位置,
选中时按调频名次前移(可以升到首位),越过它时后退一位,最多到首页末位
- 新增 InputSession::reads_as_pinyin:整串能否读成拼音(允许简拼声母和打到一半的音节)
2026-09-30 22:49:27 +08:00
fanlusky
e365a8d1b5
perf(engine): 大模型改为只读映射并按需载入,缩短每键查询路径
...
- sc.lm 改用 kenlm 的 LAZY 载入:Windows 上默认的 POPULATE_OR_READ 会退成 READ,
把 33 MB 整份拷进私有堆且永不释放;现在是 MapViewOfFile 只读映射,可被系统回收
- dict_japanese.dat 在 Windows 上同样改为只读映射,与 POSIX 分支一致
- 神经整句模型改为开关首次生效时才载入(LazySentenceModel),默认关闭的桌面模型
不再占用约 24 MB
- 新增 reset_sentence_cache():重排结果与上文只影响词格整句,只清 series 一层,
逐前缀查库与切分缓存保持热;set_rescoring_context 也改用它
- CircularBuffer 新增 find():命中时返回指针而不复制整份候选列表,并改为真正的 LRU;
全拼、双拼每键的命中路径改用它
- Google 解码器去掉每次调用前的 im_reset_search,沿用上游的公共前缀增量搜索
- 测试:出货 sc.lm 能映射载入并正常打分;增量搜索与从头搜结果逐步一致
2026-09-30 19:58:18 +08:00
fanlusky
42fbb9541c
feat(engine): 快捷短语混入全拼 / 双拼候选并参与调频
...
Shift+K 快捷短语模式保留,仍按编码前缀查询(query_quick_phrases)。另外全拼 / 双拼下
编码精确匹配(query_quick_phrases_by_code)的快捷短语作为一组插在固定位置候选之后,由
LocalModeOptions::quick_phrase_candidates 单独开关;五笔不混。
用户日志新增 quick_phrase_slots 表记录快捷短语组前的普通候选数:选中短语让组前移,
越过组选普通候选让组后移,后移上限为页长减一。quick_phrase_frequency 关掉时组固定在
首位。与普通候选同文本时保留快捷短语那份。
2026-09-29 14:31:51 +08:00
Jia Sui
a50232f06c
fix(engine): set_wubi_mixed_pinyin 不再复位 z 通配开关
2026-09-28 18:28:42 +08:00
Jia Sui
456fdb819c
feat(engine,server,ui-html,installer): 五笔拼音混输改为同时混排并拆出独立开关
...
Signed-off-by: Jia Sui <jsfaint@gmail.com >
2026-09-28 17:55:36 +08:00
fanlusky
e7f7addd4f
feat(engine): 神经网络整句联想与词格 n-best 重排
...
- 新增 engine/neural:移植 chinese-ime-lm 的字符级 Transformer 推理,在词格解出的最多 12 条
n-best 里重排挑选整句,desktop / keyboard 两档模型各出一条首选,后台 worker 异步打分
- 词格支持 n-best 输出与重排(quanpin/lattice_rerank),全拼、双拼共用
- 新增 SentenceAssociationOptions,经 QueryRequest 传给各 provider;四个来源默认全关
- 资源契约登记两个 safetensors 与 NOTICE;模型二进制不进仓库,由
scripts/fetch-neural-model.ps1 按 neural-model/lock.json 下载并校验 SHA256
- 新增 neural_sentence 测试
2026-09-24 14:03:39 +08:00
Jia Sui
5b0be4e71d
Merge branch 'develop' into feat/caret-driven-resegmentation
2026-09-24 08:45:54 +08:00
Jia Sui
1cfba58eba
feat(engine): 光标驱动的拼音前缀解码与候选重算
...
caret 从纯显示状态升级为查询管线输入:set_caret 量化到最后一个完整
音节单元边界(只消费 segment_raw_boundaries,#187 契约),前缀候选
独立解码、preedit 保持全串拼音风格;纯移动光标同样重算候选。
IInputScheme::set_raw_input 提升为接口方法以支持不动现场组合的
独立前缀查询。runtime_isolation 两处按 R3/R4 新语义改写(caret 在
音节中间 floor 到空前缀、候选为空是预期状态);新增 caret 前缀
会话单测(engine/tests + server CI 目标,隔离词库夹具,覆盖
全拼 floor、双拼贪心配对、五笔无单元模型、boundary=0 空候选)。
2026-09-22 14:56:22 +08:00
Jia Sui
536d0a95ef
feat(windows): 五笔四码后继续输入不丢字(顶字)
...
四码(不论是否唯一)之后继续敲字母时,先上屏该码的首选候选,再把这个字母
保留为下一次组合的开头。根因是两侧对「组合里有几个字母」认知不一致:TSF 的
本地按键缓冲跟着敲键涨(raw 预编辑样式下 preedit 就是它),服务端的五笔 raw
被引擎裁在四码;空格提交时组合文本被整段替换成候选并清掉本地缓冲,多敲的
字母随之丢失。
- 新增 worker 消息 CommitCandidateAndContinue(27),payload "<consumed>\t<text>":
TSF 按服务端给出的消费数裁**自己**的组合缓冲,而不是套用服务端按旧视图算好
的剩余串——快打时用户已多敲的字母因此不会被覆盖
- TSF 处理序列全部复用既有原语:_InsertTextToComposition + _HandleCompleteCommitFirst
真提交,_StartComposition + AddVirtualKey + _HandleCompositionInputWorker(NO_REQUEST_ID)
重建余下字母的组合;无组合时退化为直写
- 服务端顶字不看自动上屏开关:开关只决定唯一码要不要多敲一键才上屏,
不决定丢不丢输入;自动上屏那条路也改用同一消息,顺带收掉快打竞态
- 引擎新增 wubi_four_code_is_complete(完整四码、码表作答、不要求唯一;
空码不算——没有候选可提交时不触发顶字)
已知限制:光标在组合中间时的改码行为维持现状;空码继续敲字母仍会被忽略。
2026-09-22 09:23:25 +08:00
Jia Sui
5d1a9a6cc2
feat(server): 五笔四码唯一自动上屏
...
五笔敲满四码且码表只答一个候选时不再需要空格:第四个字母落下即上屏,下一个
字母直接开新组合。行为无条件生效,不设配置开关:业界默认开启(微软五笔、
搜狗五笔、极点、Rime 主流五笔方案),唯一候选没有第二选择,确认空格是纯冗余;
而一条开关要牵动配置五处同步,换不来可形成预期的行为差异,不值。
- engine 只报告事实(wubi_unique_four_code:完整四码、码表作答、候选恰好一个),
是否上屏由 Server 决定;判定收在 input_key_policy.h 的纯函数里,随 portable
目标进 ASan/UBSan;组词(creating_word)激活时组合保持打开,唯一四码不会把
正在拼的词截断上屏
- 提交复用 ProcessSelectionKey,与空格上屏同一套记账;但只能走 worker 管道推送
CommitCandidate:字母键在默认 raw 预编辑样式下不读请求-回复管道,回一帧 Normal
既不会上屏,还会在 TSF 侧留下永不释放的回复帧,而 Server 已按 Normal 清掉组合
- 上屏前按当前组合同步重建候选页:候选页异步发布,提交路径读的 ui.items /
page_words 否则可能还停在第三码那一拍
- 非唯一四码不做顶字上屏:一个按键只能回一条管道消息,真顶字需要扩 IPC 协议
2026-09-22 09:23:24 +08:00
fanlusky
01c5bca325
fix(engine): 选中的整句候选落成用户词组
...
词格(CandidateSource::Generated)和 Google 解码器(CandidateSource::Fallback)
猜出来的整句在词库里没有对应的行,而调频走的是
update_weight_by_pinyin_and_word——它只改 SQLite 里已经存在的行,找不到行就什么
也不做。于是用户选中这种整句多少次,下次输入同样的拼音,它仍然是猜出来的,仍然
排在词库里那条同音短语后面,看起来就是"永远提不上来"。
选中即落库才是对的处理:存成用户词组之后,下次它以 UserDatabase 候选出现,按
quanpin::generated_sentence_insert_position 的约定排在两条整句之前,之后再选还能
走正常调频。
落库不受 frequency_adjustment_ 约束(那是候选词频策略,不是造词),也不跳过
index == 0——首位整句同样没有落库,下次仍要靠猜。长度上限对标
quanpin::WordLatticeOptions::max_phrase_syllables,再长的整句只是这一次输入的
产物,落库除了撑大用户词库没有别的作用。
2026-09-19 13:48:17 +08:00
Jia Sui
b1ec3ab1e0
feat(server): Ctrl+Backspace 按分词删除前置拼音(#187)
...
组词中 Ctrl+Backspace 删除光标前的一个分词单元:剩余拼音非空时删
最后一个字的拼音(音节段),剩余拼音为空时删除最后一段已选分词。
engine:
- InputSession::segment_raw_boundaries() 作为按段删除/移动的唯一分段来源:
全拼把纠错显示串的分隔符映回 raw 下标,双拼与 pinyin_segmentation 共用贪心谓词,
2 字辅助码自成一段;非拼音方案与本地模式返回空表示无单位模型
server:
- IsSegmentBackspaceKey(Ctrl-only)、整段丢弃 / 边界截断 / 单字符回退三分支,
截断点残留分隔符清理
- 复用 CompositionRestored 回包(含 caret),仅协商能力位且非 UiLess 时发送;
新出现「只剩已选汉字、无拼音」的中间态
windows:
- 两处 Ctrl 闸门对组词中的 Ctrl+VK_BACK 放行,新增 FUNCTION_BACKSPACE_SEGMENT
与 _HandleCompositionBackspaceSegment(读回包重建,失败回退单字符)
- 空 raw 状态渲染、终态清理 word_for_creating_word,延迟投影失效处理
Closes #187
2026-09-17 23:01:31 +08:00
Jia Sui
e64195731e
feat(server): Backspace 删空剩余拼音后退选已选分词(#35)
...
已选分词是造词中间态:删空剩余拼音时不再整条 composition 丢弃,
而是退选最后一段并恢复其原始拼音与候选。
engine:
- SelectionTransition 暴露本次选择消费掉的原始输入(保留大小写与双拼按键)
engine/contracts:
- 新增回复类型 CompositionRestored=14 与能力位 CompositionRestore;
MaxKnown 同步抬高,Server 按协商能力位门控发送,旧 DLL 不会收到未知帧
server:
- 造词中间态压 (consumed_raw, previous_creating_word) 快照,边界 Backspace 弹栈退选
- 任何清空 raw 的路径同时清 creating_word 与快照历史,避免跨 composition 泄漏
windows:
- _HandleCompositionBackspace 提示退选时读回包重建 keystroke buffer / word / 预编辑 / 光标
- NeedToCreateWord 与 CompositionRestored 共用载荷解析;旧 Server 与 UiLess 回退旧行为
Closes #35
2026-09-17 18:45:01 +08:00
fanlusky
a7779d2b05
refactor(engine): 将 engine 从 submodule 内嵌为仓内一等组件
...
`vendor/MetasequoiaImeEngine` submodule 改为 `engine/` 目录,导入的是
product-lock.json 原本钉住的提交 c810d201,逐文件核对过 blob,因此这是一次
搬迁而不是引擎升级。引擎自己的嵌套 submodule(googlepinyinime-rev、utfcpp、
miniaudio)按各自 gitlink 一并展开,来源与提交见 engine/UPSTREAM.md。
Windows 专门化裁掉了不属于本产品的部分:whisper.cpp 与本地推理 provider、
macOS 示例宿主、整个 dictionary/ 建库流水线、上游 CI 与 release-please 配置。
词库仍按 product-lock.json 在构建时取发布产物。
引擎不再进 product-lock.json:没有 gitlink 就没有可比对的东西,锁里写一个上游
commit 等于宣称这棵树仍等于上游,而第一个 Windows 专门化改动落地时这句话就假了。
verify-contracts / verify-checkout 随之移除,verify-published 保留。
CI 与工具链:引擎源码并入根 scripts/format.sh(删掉引擎自己的那份,避免两套排除
列表分叉);引擎 voice.yml 里的 silero_vad.onnx 摘要校验移到本仓 lock job;
engine-bump-triage 工作流与其测试删除;gitsubmodule 的 dependabot 频率由 daily
改为 monthly。
README、README.en、AGENTS.md、CONTRIBUTING、docs/product-release.md 及各子目录
文档同步说明新的位置与边界。
2026-09-12 03:40:48 +08:00