| Merge branch 'pr_132' into release/v4.22.0
Brings in the 16-commit i18n migration (Locale enum, i18n::t() lookup,
Config.language, --lang flag, /language slash command, LanguagePicker
modal, full migration of user-visible TUI strings) on top of the v4.22.0
work (image attach + preview rows, sync_recalled_attachments retain-on-edit
fix, body-echo reorder so marker numbers match, OH login URL omission,
attachment field on UiLine::InputPrompt/StreamingBox, MenuPayload.kind).
Eight conflicts resolved:
* atomcode-cli/src/main.rs, atomcode-daemon/src/api_config.rs,
atomcode-core/src/{coding_plan/setup,turn/tests}.rs — Config struct
initializers: both sides added new fields; merged to keep all
(subagent + vision_preprocessor_provider + language).
* atomcode-core/src/config/mod.rs — struct gets all three new fields;
HEAD's impl Config { can_handle_attached_images } retained; pr_132's
language round-trip tests added (with HEAD's mandatory fields filled
in and the legacy reflection_cadence field dropped — it's silently
ignored on load now).
* atomcode-tuix/src/event_loop/mod.rs — three conflicts. Kept HEAD's
universal \<Enter> fallback hint and JediTerm banner (newer
behaviour from 1f7f694c / 44a8eaea — deliberately dropped the
legacy-conhost banner once alt-screen shipped; not resurrected from
pr_132). Used pr_132's i18n version for the session-replay hint
since the string semantics match.
* atomcode-tuix/src/event_loop/commands.rs — /init flow: kept HEAD's
render_instruction_status_block confirmation step, used pr_132's
i18n Msg::InitWrote for the "Wrote N bytes" line.
* atomcode-tuix/src/modals/session_picker.rs — kept HEAD's load-failure
state cleanup (current_session_id = None, etc.) and rename_editing
rendering branch; folded in pr_132's i18n SessionLoadFailed +
SessionMsgCount strings.
Three follow-up fixes to make the workspace compile cleanly:
* language_picker.rs — added MenuPayload.kind and
UiLine::InputPrompt.attachments fields the pr_132 modal didn't know
about.
* config/mod.rs, tool/parallel_edit.rs, vision_preprocessor.rs —
three more test/helper Config initializers got language: None.
cargo check --workspace --tests passes. atomcode-tuix tests 540/540
green. The 4 atomcode-core test failures (auth_token_path_consistency,
sync_marker round_trips, load_plugin_layer_namespaces, datalog
resolve_log_dir) are pre-existing parallel-execution HOME-env-bleed
flakes — all pass individually, unrelated to this merge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 24 天前 |
| Merge branch 'pr_132' into release/v4.22.0
Brings in the 16-commit i18n migration (Locale enum, i18n::t() lookup,
Config.language, --lang flag, /language slash command, LanguagePicker
modal, full migration of user-visible TUI strings) on top of the v4.22.0
work (image attach + preview rows, sync_recalled_attachments retain-on-edit
fix, body-echo reorder so marker numbers match, OH login URL omission,
attachment field on UiLine::InputPrompt/StreamingBox, MenuPayload.kind).
Eight conflicts resolved:
* atomcode-cli/src/main.rs, atomcode-daemon/src/api_config.rs,
atomcode-core/src/{coding_plan/setup,turn/tests}.rs — Config struct
initializers: both sides added new fields; merged to keep all
(subagent + vision_preprocessor_provider + language).
* atomcode-core/src/config/mod.rs — struct gets all three new fields;
HEAD's impl Config { can_handle_attached_images } retained; pr_132's
language round-trip tests added (with HEAD's mandatory fields filled
in and the legacy reflection_cadence field dropped — it's silently
ignored on load now).
* atomcode-tuix/src/event_loop/mod.rs — three conflicts. Kept HEAD's
universal \<Enter> fallback hint and JediTerm banner (newer
behaviour from 1f7f694c / 44a8eaea — deliberately dropped the
legacy-conhost banner once alt-screen shipped; not resurrected from
pr_132). Used pr_132's i18n version for the session-replay hint
since the string semantics match.
* atomcode-tuix/src/event_loop/commands.rs — /init flow: kept HEAD's
render_instruction_status_block confirmation step, used pr_132's
i18n Msg::InitWrote for the "Wrote N bytes" line.
* atomcode-tuix/src/modals/session_picker.rs — kept HEAD's load-failure
state cleanup (current_session_id = None, etc.) and rename_editing
rendering branch; folded in pr_132's i18n SessionLoadFailed +
SessionMsgCount strings.
Three follow-up fixes to make the workspace compile cleanly:
* language_picker.rs — added MenuPayload.kind and
UiLine::InputPrompt.attachments fields the pr_132 modal didn't know
about.
* config/mod.rs, tool/parallel_edit.rs, vision_preprocessor.rs —
three more test/helper Config initializers got language: None.
cargo check --workspace --tests passes. atomcode-tuix tests 540/540
green. The 4 atomcode-core test failures (auth_token_path_consistency,
sync_marker round_trips, load_plugin_layer_namespaces, datalog
resolve_log_dir) are pre-existing parallel-execution HOME-env-bleed
flakes — all pass individually, unrelated to this merge.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 24 天前 |
| refactor(i18n): move source files to core + polish wave
The i18n module previously lived in atomcode-tuix/src/i18n/ so all
core renderers wanting localised output had to either reach into the
tuix crate (a dependency inversion) or hardcode strings. Now that
coding_plan::setup::SetupReport::render and similar core paths
need the same t(Msg::*) lookup, the natural home for the i18n
module is atomcode-core. Tuix keeps a one-line re-export shim so
every existing crate::i18n::* call site compiles unchanged.
Bundled in the same wave because they depend on the move or land
naturally alongside it:
* coding_plan/setup.rs — JediTerm SGR-9 fallback. When the
TERMINAL_EMULATOR env var marks the host as JetBrains JediTerm
(Android Studio / IntelliJ / etc.), strikethrough renders
inconsistently, so locked-model rows fall back to ASCII ✗ +
"(Locked: require plan upgrade)" text marker. Threaded through a
testable render_with_terminal_caps(is_jediterm: bool) so both
shapes are unit-coverable without env mutation.
* event_loop/commands.rs — /language slash command now emits the
unified Msg::LanguageSwitched { label, locale } line (matches the
LanguagePicker's confirmation shape, not the older bare-locale-code
one). /status instruction-files block migrated to
Msg::StatusInstructionFilesHeader/Present/Missing.
* modals/language_picker.rs — already on LanguageSwitched, kept
in sync.
* render/alt_screen.rs — adds regression test
command_output_survives_subsequent_input_prompt_redraw: pins the
/language flow where the picker emits a CommandOutput then the
event loop immediately redraws the input prompt (no menu). The
confirmation must stay in body_lines AND in painted output past
that second redraw, otherwise the user sees no feedback that the
locale switch took effect.
All existing i18n tests (13 in core, multiple in tuix), coding_plan
suite (51), and the new alt_screen regression pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 24 天前 |
| feat(plugin): interactive /plugin manager modal
Bare /plugin (no subcommand) now opens a full interactive manager
instead of printing usage. A single modal drives a screen state machine:
browse marketplaces -> list a marketplace's plugins (✓ marks installed,
Enter toggles install/uninstall); add marketplace by git URL; remove
marketplace; list+uninstall installed plugins. No more memorizing
name@marketplace. Existing /plugin marketplace …, install x@mp,
etc. subcommands are unchanged.
Slow ops (clone/install) dispatch through the existing plugin_job_tx
pipeline; the new Modal::on_plugin_event hook lets the open modal refresh
its lists when a job completes (the plugin_job_rx select arm now redraws
the modal instead of painting over it). Fast ops (uninstall, remove)
run inline and refresh immediately.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 6 天前 |
| feat(live): webui↔TUI 模型双向实时同步 + 修复同步模式 TUI 审批不生效
模型同步:此前 webui 下拉框只改本地态、且仅在发消息时才把 provider 带给后端
(LIVE_PROVIDER),TUI 的活动模型与头部完全收不到通知。新增一条贯穿四层的
模型变更广播:
- core:LiveEvent::ProviderChanged + LiveSession::notify_provider_changed;
AgentEvent::ProviderChanged。
- daemon:live_set_provider(设 LIVE_PROVIDER + 广播);POST /live/provider
端点(持久化 default_provider 到 config.toml + 广播,下拉框一变即生效,不必先
发消息);/live 快照新增 provider 字段,新 tab 连上即回显正确模型。
- tuix:live_sync 转发 ProviderChanged;handle_agent_event 据此更新
default_provider/model_name + 通知 agent + 刷新头部(已是该 provider 则跳过,
避免自身 /model 切换的回声);/model 选择器切换后 live_set_provider 广播给
webui(反向)。
- webui:postLiveProvider;下拉框变更即在 sync 下上报;收到 provider/snapshot
即 setProvider(不回调 onChange,避免回环)。
切换发起方落盘一次(webui→端点;TUI→选择器 save_and_reload),TUI 收到广播只
做内存态同步,不二次落盘。
审批修复:同步模式下,工具这一轮跑在进程内 LiveSession 协调器(DaemonTurnExecutor)
里,审批只能经 LiveSession::approve 投递(webui /live/permission 即如此)。但 TUI
的 handle_approval_key 无条件把决定发给 TUI 自己的 agent——而同步模式下该 agent
没跑这一轮,决定石沉大海:工具一直 Running 卡死、webui 审批卡片也不关。新增
deliver_approval:sync_forwarder 存在时把决定投给 current_live_session().approve
(A 降级为 Allow,与 webui 一致),否则照旧发 TUI agent;Ctrl+C 拒绝同样改走它。
工具随之继续并广播 ToolCallResult,触发 webui 侧清卡片逻辑,两个症状一并解决。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| refactor(commands): fold /codingplan into /login
/login now runs the full OAuth + CodingPlan setup flow (claim →
fetch models → register providers → status); the standalone
/codingplan slash command is removed. atomcode codingplan is
kept as a hidden CLI alias so existing scripts and muscle memory
keep working. /login is promoted to the top of the slash-menu
since it's the primary onboarding entry now.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 7 天前 |
| style(tui): optimize plugin manager installation progress interface
| 1 天前 |
| fix(tuix): /provider 识别行显示 base_url,手动 Base URL 改为必填前置步
1. "已识别"行改为展示 base_url · type · model(原来误显示 name)
2. 手动路径:Base URL 成为必填的前置步(不显示 (x/y)、去掉"留空用默认值"),
据其推断类型并回显"已识别类型:…",随后才是计数的 (1/3) API 密钥 → Model →
名称。手动不再单独问类型(由 Base URL 推断)。
新增 ProviderBaseUrlEmpty、恢复 ProviderTypeInferred 文案。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 6 天前 |
| feat(tuix): QR rendering helper for onboarding redesign
Building block for the upcoming single-page onboarding rewrite —
isolated so it can land + be unit-tested independently of the
wizard state-machine refactor that comes next.
Wraps qrcode 0.14's Dense1x2 (half-block) renderer:
- One terminal cell carries 2 QR modules vertically, so a 33-module
QR (typical short URL) lands in ~17 rows — fits inside the wizard
panel without overflowing typical terminal heights.
- Returns Option<Vec<String>> per-row so callers can centre rows
inside the panel without re-splitting.
- ASCII-only / dumb terminals return None — caller MUST fall back to
plain text URL. Half-block glyphs render as tofu on Windows
conhost / TERM=dumb / LANG=C and a tofu QR would silently break
the only thing the screen exists to do (let the user scan).
3 tests pin: ASCII gate returns None; short-URL renders >= 8
non-empty rows; all rows have uniform char width (so the QR doesn't
render as a parallelogram and break phone scanning).
PR 1 follow-up will rewrite OnboardingWizard's Step machinery to
use this — currently 3 steps (Intro / Language / Setup), target is
a single Confirm + QrLogin page with auto-detect language.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 14 天前 |
| feat(session): 恢复对话时还原轮次分隔线与每轮 token/工具统计
会话只持久化 messages,每轮的 token/时长/轮数统计来自实时 TurnComplete 事件、从不
落盘;replay_session 又只在开头/结尾画分隔线,轮与轮之间一条没有。于是恢复后上一轮
模型输出和下一条用户输入贴死,统计分隔线也丢了。
- session.rs:新增 TurnStat(after_message 锚点 + 轮数/工具数/时长/tokens/errored)
和 Session.turn_stats(#[serde(default)],老会话加载为空)。
- event_loop/mod.rs:TurnComplete 时把本轮统计按"轮结束时的消息数"锚定推入
current_session.turn_stats 再落盘(对取消/缺失的轮也稳)。
- session_picker.rs:replay_session 在每个轮边界(每条非首条用户消息前 + 末轮)画
分隔线——有统计就画与实时一致的 ✓ Done · N 轮 · M 工具 · Xs · K tokens(错误轮 ✗),
没有就画纯横线。新会话 resume 完整还原统计;老会话也能拿回轮间间隔(纯线)。
新增测试:turn_stats 落盘往返 + 老会话缺字段降级空;turn_divider_label 三态。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |