Repository navigation
feat: promote native dynamic UI, settings refresh and chat input work for 1.12.2-ry.8 - #70
Conversation
… observe wait_agent blocked for its full timeout even when no other agent could reach the caller, so a mis-called wait just sat there until the model gave up on it. One lock snapshot now decides the result: an empty caller mailbox, no other job or spawn reservation in the root conversation and no streaming peer turn mean the wait cannot succeed, so it returns an explicit idle outcome instead. The invariant holds because finish() enqueues its message before the job leaves jobs, send() enqueues before it starts one, and the completion callback of a job that was cancelled before its body ran now enqueues the terminal notification inside the same lock region that removes the job; previously that callback dropped the job first and notified afterwards, leaving a window in which a caller could snapshot an empty tree and give up while the notification was still on its way. Reconciliation reads the caller's stored history, so the loop reconciles on its first pass and then at most once per second; pending input keeps its 100ms cadence and senders still push their own deliveries. Limits are unchanged. The tool description, the idle result text and the parity document explain the result and tell the model to call wait_agent again once a child it just created is running, because non-task tool calls run serially in call order and a wait placed before its own spawn can see the tree still empty. Validated with :app:compileDebugKotlin and the full :app:testDebugUnitTest suite (2765 tests, 0 failures).
The floating settings button was positioned from the bottom bar's full height, and that height includes the todo / prefix-warning dock sitting above the input card. As soon as the dock had a row to show, the button was pushed a whole line above the notice icon beside it, and opening the todo panel moved it again. The dock now reports its own height and the centre of its row content, so the button is placed from the input card height plus that row centre. Collapsed and expanded todo states no longer differ. With an empty dock the formula reduces to the previous one, and the first-frame fallback is unchanged.
The classic input is one card now: the draft on top, the actions underneath. The field keeps a frame of its own only when the card behind it is see-through, which is what the transparent themes need to stay readable. The action row carries a thinking strength chip. Its popup owns the dial while a finger is on it and writes the level once the gesture settles, so dragging no longer toggles the switch and rewrites the preference on every frame - that was the stutter seen right after turning thinking off. The setter is idempotent, so a drag that re-enables thinking cannot flip back mid-gesture. Dictation runs inside the composer: a live level strip, a stop button, and a separate action that fills the message box and never sends. A failed pass keeps the panel up with the reason and a retry. Also: the fullscreen button moved to the top right of the draft and the duplicate one in the action row is gone; the voice entry uses a level-bars icon; the slider thumb is round and springs when pressed; the stop captions share the thumb geometry instead of drifting with the label width. Verified with :app:compileDebugKotlin and an independent read-only audit.
…ating taps The dial was lifted clear of the composer with padding inside the popup, so the strip below the panel still belonged to the popup window: a tap landing there counted as an inside tap and the popup refused to close. The lift now moves the window itself, which leaves the panel where it was and lets any tap outside dismiss it. The slider is drawn from measurements of the reference: the pill spans the panel, its height drives the thumb slot so the thumb's centre lands on the first and last stop circles, the filled rail is slimmer than the pill and grows out of the first stop, and the stop dots sit exactly where the thumb travels. Also: the voice conversation entry uses the level-bars icon in the agent composer, the floating window row and its menu; the fullscreen button is centred with the draft again, because IconButton's 48dp minimum touch target was stretching the row and leaving the icon below the line it belongs to; and the track no longer draws its dots or its shortest rail on the wrong side in right-to-left layouts. Verified with :app:compileDebugKotlin and independent read-only audits.
Enable bounded isolated next-message predictions by default while respecting saved opt-outs. Add native ghost text with glyph-only double-tap adoption, cache restoration, contrast-safe styling, and stable scrolling/focus behavior. Production and AndroidTest Kotlin compilation and 32 focused JVM tests passed. Independent backend/UI and fresh UI/UX audits are clean. User explicitly waived cloud lint; real-device IME and accessibility checks remain outstanding.
Block new requests when a selected model is deleted, preserve model identity across list changes, and show a persistent configuration notice in chat. Also fix wrapped task counts and clipped attachment labels.
Rate-limit streaming progress logs by time. Coalesce display-only text without changing tool or revision ordering, and throttle large node snapshots while preserving structural and final updates.
The default scheme still used the previous OpenCode Rainy tones, so the light theme looked washed out next to the RainyToken/Bongocat reference: a pink primary app bar with near-white label text, flat grey cards, and error and decorative accents sharing one rose. Light mode now uses a pale sakura page, white panels with the warm inset, a deep rose primary with readable labels, and a separate divider tone. Cards draw white with the theme ink and border, so they read as cards instead of blending into the page. Forced app bar text is measured against the opaque bar it is drawn on, which the old light-on-pale combination failed; a transparent toolbar keeps the chosen tone because the image behind it is unknown. Status bar icons follow the resolved fill the same way. Dark mode keeps its existing palette and gains only the shared accent roles.
…witch The RainyToken palette made the send button, the context ring and other accents a dark berry, and the app bar barely separated from the near-white page. Light mode now defaults to softer sakura accents that still clear 3:1 as text on the pink card fills and carry white icons at about 4.3:1, and the default app bar and status bar get their own sakura band. A new "Enhanced contrast" setting (off by default) restores the deeper accents and the automatic deepening of pale custom colors and drawer accents. It is saved with character and group themes and removed on reset. The light and dark palette generators were duplicated in Theme.kt; both the live theme and the web chat snapshot now compose the scheme through one resolver.
Dynamic UI is now rich content inside the answer instead of a framed interactive card. Blocks render without a host frame, background or padding, so prose and UI interleave like Markdown paragraphs; a root Card is the explicit opt-in frame for self-contained tools. The source toggle only appears when a block has a problem, and insets became translucent so they read on any bubble color. Add a native Flow component for steps and linear flowcharts with arrows (1..8 steps, auto horizontal/vertical, validated at compile, expansion and render time). Rewrite the guide around "prose first, display-only second, interaction only when the user must act", and add display-only examples (flow_steps, flowchart, data_story) plus a mixed_branch example.
Rewrite the package description with suitable and unsuitable scenarios, add DECIDE FIRST and VISUAL RULES to read_guide, and drop decorative headers from display-only examples.
Models can write :badge[label]{tone icon} and :icon[name] in Markdown text; they render as native spans in paragraphs, headings, lists and table cells, while copy and speech keep only the badge label.
The native inline link plugin committed to LINK on ']' so ':badge[ok]{...}' and plain '[x]' became links and lost a character. It now waits for '('. Sessions gain finish() so text still pending at the end of a stream, or when it is cancelled, is emitted as plain text instead of being dropped.
The badge syntax was only described inside the dynamic_ui package summary, so models read it as a tool to activate and rarely used it, and CLI mode, disabled tools or restricted packages hid it entirely. Add a short reply-format rule to every system prompt and drop the duplicate from the package summary.
Badges and icons are part of the Dynamic UI package: the package summary states when to use them, read_guide documents the syntax and icon rules, and the global reply-format rule added in the previous commit is removed.
…ition Communication audio with noise suppression often reaches the voice detector well below full scale, so continuous dictation dropped normal speech. Apply a capped software gain (up to 8x, peak target 0.5) to local recognizer input, including preroll, while the meter keeps showing raw microphone level.
Grid rows stretched only Display tiles, so panels with a wrapping title left uneven rows. Panels in a shared grid row now stretch to the tallest cell, and h1-h4 text in narrow multi-column cells uses one 18sp size so short titles stay on one line.
… rolls over The review list keeps the newest 200 reviews across all chats, so older conversations lost the review line under their tool calls. Finished reviews are now also kept, sanitized, in a per-chat file that the badge falls back to and that is deleted with its chat. Existing stored reviews are migrated. Archived denials no longer offer the one-time approval.
…hing The package description asked the model to decide on UI before loading it, so it rarely loaded. Explaining, introducing, analyzing or comparing now loads the package and its guide first; whether to place a block stays the model's choice.
…, branch state Models now get card-1, card-2 per turn instead of a 36-character UUID that they sometimes miscopied; the UUID stays internal for live surfaces and state files. An unresolved reference keeps its original fence and renders as a one-line notice instead of writing a failure sentence into the answer. Model-facing history, summaries and role-bridged turns replace expanded card JSON with its visible text. Branching copies each chat's card state so selections and live data survive.
Memory learning now reads each card as the readable text the user saw, without the chat prompt's length cap, and keeps the text of Html cards. Cursors saved against the raw message restart that message instead of drifting.
Each permission group shows a count and its first eight tools as compact chips; the rest open in a searchable list grouped by package. Tools allowed by default fold into one line.
The remove button grows to 40dp so chips are hard to clear by accident, and every chip shares the same height.
The show-all dialog gets a header with the group color and total, a search field with clear, package sections with count badges, and grouped rows with a clear remove button. The add-tool dialog shares its width, color and search field.
Defaults sit in one card with Max first, the budget field and 200K-500K presets shown only when Max is off, and the trigger field only when auto compaction is on. Summary format settings fold into one header card named after the bound model config.
Adds shared SettingsKit components, rebuilds the settings home as grouped cards in the context page style, and adds a bottom search bar that finds pages, group names and common options inside sub pages.
Replaces the move-to-folder button list with a folder picker that marks the current place, shows parent paths and offers search; gives the message long-press menu larger rows and red delete items; fixes the white sort menu in dark theme; localizes the bulk delete confirmation.
…tion Global display, language, storage and layout pages use the shared settings cards. A search result for an option now opens its page, scrolls to the row and flashes it. Resetting display settings asks first and says what it clears. Clear cookies looks like a delete action, the context entry moves into the AI model group, and moving a chat to a folder confirms where it went.
… restyled Folder and chat drag no longer snaps back after quick moves: the lock waits for both lists to match before releasing. Rows slide with a spring while dragging, with haptics on pick up, swap and drop. The settings search bar no longer jumps above the keyboard. Component, tag market, waifu, HTTP chat, tool permission, agent profile, functional model and custom emoji pages now use the shared settings style, and search lands on more of their options.
Theme cards now share the 20dp settings card shape and the background-aware card color, section titles use SettingsSectionTitle, dividers are softer, and the bound character card gets the brand tint. Adds SettingsCardShape and rememberSettingsCardColor to SettingsKit.
… the settings style Elevated and outlined cards become settings cards with the shared shape and background-aware color, section headers use the icon badge, oversized titles shrink, and the backup hint uses the info banner. Stats keep their white cards with the shared corner radius.
…s and safer reset Theme tabs become a segmented control that opens the right tab from settings search. Saves show a floating pill, reset asks first, switch rows use the shared settings row, color rows sit label-left with a dot on the right, chosen options are clearly marked, and bubble image sliders fold under Crop and scale. Removes the unused input-style and input color branches.
Windows cannot rewrite an existing preferences file: DataStore 1.0.x renames the scratch file with File.renameTo, which fails when the target exists. The single test wrote three times, so it always failed locally while passing on Linux CI. Split it into a one-write case that runs everywhere and the two-write reset case that is skipped on Windows, and record the constraint in the class comment.
The workspace-file empty rows read resources from LocalContext.current, which lint rejects as LocalContextGetResourceValueCall. Resolve them with stringResource during composition like the rest of the screen.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
Security findingsAdvisory findings (1)ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Here are some automated security review suggestions for this pull request.
Reviewed commit: 0985ff1858
ℹ️ About Codex security reviews in GitHub
This is an experimental Codex feature. Security reviews are triggered when:
- You comment "@codex security review"
- A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review
Once complete, Codex will leave suggestions, or a comment if no findings are found.
| ) | ||
| } | ||
| return extractImageLinkTags(message).mapNotNull { tag -> | ||
| val data = ImagePoolManager.resolveImage(tag.id, tag.sourcePath) ?: return@mapNotNull null |
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Security: Reject assistant-supplied image source paths
When composer predictions are enabled—the new default—and the selected model supports vision, a model response can emit <link type="image" id="fresh" src="/known/path.jpg">. The automatic prediction replays that assistant turn, and this line passes its untrusted src to resolveImage, which opens and encodes any app-readable image before the provider serializer includes it in the next request. This bypasses tool permission review and lets a malicious or prompt-injected provider exfiltrate known device/workspace images. Rehydrate only IDs backed by a trusted attachment registry; never resolve paths parsed from assistant text.
Useful? React with 👍 / 👎.
Promote
personal/dev→personal/mainfor Operit Ry 1.12.2-ry.8promotion-checkpoint/main-20261010-3f66d67cpersonal/main3f66d67c4(ry.7)personal/dev+ 版本号提交 + 单测修复),714 个文件,+31825 / -74158versionName1.12.2-ry.7→1.12.2-ry.8;versionCode保持100205不变.dev包名与-dev版本后缀、DEV 角标图标与快捷方式、personal-dev热更新通道、tools/hotbuild的 dev 发布脚本与子模块指针、dev-quality.yml、trigger-nightly-build.yml功能域
动态 UI(原生 A2UI 卡片)
Text/RichText支持 tone,跟随自定义气泡颜色并保留对比度兜底:badge[文字]{tone=... icon=...}、:icon[名称],与卡片共用 Lucide 描边图标集dynamic_ui包内 opt-in 的 HTML 卡片(点击加载、移出视口停止渲染、无JavascriptInterface)聊天与输入
设置界面
权限与安全
系统与工具
记忆与历史
语音
工程与基础
consumer-rules.pro的提交验证
:app:compileDebugKotlin通过、:app:testDebugUnitTest通过(ComposerPredictionPreferencesTest中需要两次真实写入的重置用例在 Windows 上按假设跳过,见提交说明)、:app:lintDebug通过AUDIT: PASSFast checks为必需检查,Full Android checks仅供参考