刚刚结束的 Android Dev Summit 2026 活动上,我现场和 A2UI Compose 团队的 Nick、Anton 深入交流了快一小时。之前我在《GenUI 的田野调查笔记》小册里写过一篇专门拆解 Google A2UI 的文章。而上个月他们团队放出了 Agent-to-UI renderer for Jetpack Compose(A2UI)的 1.0.0-alpha02。但官方发布的 androidx.a2ui:* 和 androidx.compose.material3:material3-a2ui 现阶段只有 Android 版本,他们回答现场观众提问时多次提到“短期的规划里,暂时没有 KMP / CMP 这块的支持优先级”。我观察到现场起码碰到两三位开发者前来跟他们交流 KMP 的支持,于是我当场就主动和他们提说我来当志愿者,做这个迁移并开源出来。

不过我没有从零去写一套所谓的“第三方兼容实现”,而是直接基于官方 A2UI 源码做了迁移架构 port 到了 Kotlin Multiplatform 上,开源了 a2ui-kmp。这个多平台库在 Android 端会通过 Gradle Module Metadata 直接解析回 Google 官方 Artifact,跑的就是 Google 原版代码;在 Desktop(JVM)与 iOS 端则直接增加兼容层来编译同一份 AOSP 源码,保留原有的 androidx.a2ui.* 包名和大家熟悉的 API。

如上图所示,同一份预订表单的 A2UI 消息流处理与事件回传逻辑写在 commonMain 里,可以同时跑在 macOS Desktop 和 iOS 模拟器上。
快速分析下需求,实际上我们发现也就不到 10 个种类的迁移问题,并且 2/3 的源码完全不用动。而我也把主要精力花在了架构设计和工程边界上:怎么保证分包清楚,让脚本同步更新的部分归脚本管、需要改写或兼容的部分单独维护(AI 手写);怎么统一各个子工程的依赖版本,用 includeBuild / Composite Build 串起跨越 Kotlin 2.3 和 2.4 的两个构建,让首次打开工程即可直接联调;以及怎么用 Jetpack Compose 版本录制出来的真实数据去测试多平台兼容层。

话不多说,下面我们先看项目怎么接入,再逐一聊聊这几块架构取舍。
快速接入
在 KMP 工程里引入之前,我们先看一眼版本要求。一个大前提是:当前 Jetpack Compose 版本的 A2UI 库依赖了最新 alpha 版本 Compose 和 2.4 的 Kotlin 的编译版本,而 Gradle 会把接入的宿主工程里的 Compose 和 kotlin-stdlib 升到依赖库要求的最低版本——然而低版本 Kotlin 编译器读取不了高版本编译器打出来的 iOS klib(ABI 不向下兼容)。所以我们需要做一下取舍:
- 基础的 5 个包(
a2ui-model、a2ui-engine、compose-runtime、compose-ui、compose-ui-testing)基于 Kotlin 2.3.21 和 Compose Multiplatform 1.12.2(Compose runtime 为 1.12.1)构建,因为它们实际上没有对 2.4 的强依赖,故我们可以放开 iOS 端仅要求 Kotlin 2.3 及以上。 material3-a2ui基于 Kotlin 2.4.0 和 Compose Multiplatform 1.13.0-beta01 构建,因为上游源码用到了 CMP 1.13 才合入的 Material 3 API(原因详见后文第三节)。等 CMP 1.13 正式版发布后,我们会切到正式版。- Android 端因为直接解析到 Google 官方的 1.0.0-alpha02 Artifact,遵循官方版本要求:只用
a2ui-model/a2ui-engine时需 compileSdk 37;从compose-runtime往上需 compileSdk 37.1 与 Jetpack Compose 1.13.0-alpha03;使用material3-a2ui还需 Material 3 1.5.0-alpha28。
确认版本后,在 gradle/libs.versions.toml 里加上需要的模块即可。为了和 Google 原生依赖区分开,我们在 Version Catalog 的别名末尾统一加了 -kmp 后缀,也推荐各位开发者接入时按这个方式去写:
# gradle/libs.versions.toml
[versions]
a2ui = "1.0.0-alpha02"
[libraries]
a2ui-material3-kmp = { module = "ai.littlepress.a2ui:material3-a2ui", version.ref = "a2ui" }
a2ui-compose-ui-kmp = { module = "ai.littlepress.a2ui:compose-ui", version.ref = "a2ui" }
KMP 版本发布的 6 个 Artifact 与 Android 官方 Artifact 的一一对应关系如下表所示:
ai.littlepress.a2ui:* Artifact |
用途 | Android 端解析目标 |
|---|---|---|
a2ui-model |
面向应用与组件开发者的核心数据层 | androidx.a2ui:a2ui-model |
a2ui-engine |
面向渲染器开发者的消息与数据绑定引擎(无 Compose 依赖) | androidx.a2ui:a2ui-engine |
compose-runtime |
Compose A2UI 渲染器运行时(状态与数据绑定) | androidx.a2ui.compose:compose-runtime |
compose-ui |
Compose A2UI 渲染器与 Catalog API(不含 Material 3) | androidx.a2ui.compose:compose-ui |
compose-ui-testing |
自定义 Catalog 组件的测试 API | androidx.a2ui.compose:compose-ui-testing |
material3-a2ui |
基于 Compose Material 3 的基础组件 Catalog | androidx.compose.material3:material3-a2ui |
一般项目只需要引入 a2ui-material3-kmp(直接用 Material 3 基础组件),或者引入 a2ui-compose-ui-kmp(自己画一套自定义组件库),不用太关心另外 4 个。下面这段代码摘自仓库 samples/composeApp/src/commonMain/kotlin/sample/material3/A2ui.kt 和 FormSample.kt,先看怎么在 commonMain 里初始化 A2uiMessageProcessor、接收 Agent 消息并收集用户交互事件(示例里的 Agent 不发图片、视频和音频,因此这三个回调是空的;实际 App 需要在这里传入自己的图片加载器和播放器):
import ai.littlepress.a2ui.compat.android.content.Context
import androidx.a2ui.compose.runtime.A2uiMessageParser
import androidx.a2ui.compose.ui.A2uiMessageProcessor
import androidx.a2ui.compose.ui.catalog.A2uiBasicCatalogV1
import androidx.a2ui.model.processor.A2uiMessageProcessor
import androidx.a2ui.model.processor.processInput
import androidx.compose.foundation.layout.Arrangement
...
@Composable
fun rememberA2uiProcessor(): A2uiMessageProcessor {
val processor = remember {
val catalog = materialA2uiBasicCatalogV1(
// The sample's agent sends no images, video or audio, so these three
// slots are empty; a real app plugs its image loader and players in here.
image = MaterialA2uiBasicCatalogV1Defaults.image { _, _, _, _, _ -> },
video = MaterialA2uiBasicCatalogV1Defaults.video { _, _, _ -> },
audioPlayer = MaterialA2uiBasicCatalogV1Defaults.audioPlayer { _, _, _, _ -> },
functions = A2uiBasicCatalogV1.Functions(Context()), // ①
)
A2uiMessageProcessor(catalogs = listOf(catalog))
}
LaunchedEffect(processor) { withContext(Dispatchers.Default) { processor.collectMessages() } }
return processor
}
@Composable
fun A2uiSurfaces(processor: A2uiMessageProcessor, modifier: Modifier = Modifier) {
val surfaces by processor.activeSurfaces.collectAsState()
Column(modifier, verticalArrangement = Arrangement.spacedBy(16.dp)) {
surfaces.forEach { surface -> key(surface.id) { A2uiSurface(surface) } }
}
}
@Composable
fun FormScreen(incomingJsonLines: List<String>, onSendToAgent: (String) -> Unit) {
val processor = rememberA2uiProcessor()
LaunchedEffect(processor) {
val parser = A2uiMessageParser()
incomingJsonLines.forEach { json -> processor.processInput(parser, json) }
processor.outboundEvents.collect { message -> onSendToAgent(message.toString()) }
}
A2uiSurfaces(processor)
}
如上所示,rememberA2uiProcessor() 创建了绑定 Material 3 Basic Catalog 的消息处理器,并在后台协程持续消费消息队列;FormScreen 则把 Agent 发来的 JSON 逐行喂给 processor,再从 outboundEvents 收集表单提交等回传事件(细心的朋友可能发现了,代码顶部同时导入了两个 A2uiMessageProcessor——一个是 compose.ui 下的顶层工厂函数,另一个是 model.processor 下的接口类型,这正是上游原本的设计)。
在 commonMain 中编写调用代码时,需要注意两处与直接写 Android 代码不同的 API 差异:
- 上游有两个接口方法签名里带了
java.util.Locale,在commonMain里对应的是ai.littlepress.a2ui.compat.java.util.Locale(在 JVM 和 Android 端它就是java.util.Locale的 typealias)。 - 代码块 ① 处的
A2uiBasicCatalogV1.Functions(context),在 Android 端传的是android.content.Context,在 Desktop 和 iOS 端则传ai.littlepress.a2ui.compat.android.content.Context()(内部会调宿主平台的浏览器打开 URL)。
源码迁移架构
既然上层保留了 Google 原有的包名和 API,底下就必须处理上游代码里用到的 JDK 与 Android 类。翻看上游 6 个模块共 193 个 .kt 源文件,其 Compose 渲染代码本身并不依赖多少 Android 特有机制,但各处散落着 java.util.Locale、SimpleDateFormat、NumberFormat、ICU MessageFormat、android.util.JsonReader、android.content.res.Resources 和 ReentrantLock。
在动手写代码前,我们在方案评审和前几轮迭代里先排掉了两个坑:
1. 平台 API 与兼容层
第一个是 java.* 包名的问题。在方案阶段我们曾考虑过在 iosMain source set 里直接声明 package java.util 来放自定义的 Locale,这样连上游源码里的 import 都不用改。但核对后发现多平台下还是不行:因为上游有两个公共接口签名直接暴露了 java.util.Locale,一旦放进 commonMain,在 JVM 上类加载时会直接拒绝用户自定义的 java.* 包(SecurityException: Prohibited package name),而且还会跟 JDK 自带的同名类冲突。所以,我们确定了一个使用独立包名 ai.littlepress.a2ui.compat.* + 规则改写 import 的路线。
2. 上游同步与目录分层
第二个则是源码怎么同步、项目结构怎么制定,来让上下游代码都保持干净。我们对上游开发流程的决定其实不那么关心,所以直接从 AOSP git 仓库同步最新的 release head 即可,包括了上游的测试和资源目录也一起同步。然后,我们隔离了上下游的文件夹,并且加了个规则:上游同步和固定规则改写全部由脚本执行;多平台兼容实现单独放在独立模块里,由 AI 辅助编写,再经过评审与测试。仓库的源码分层如下:
a2ui-kmp/
├── source/ # read-only upstream snapshot
│ ├── UPSTREAM # the AOSP commit it came from
│ ├── a2ui/
│ └── material3-a2ui/
├── target/
│ ├── build-logic/ # source + overlays -> commonMain
│ ├── a2ui-kmp/ # Kotlin 2.3 build: 5 core packages
│ │ ├── a2ui-compat/ # hand-written compatibility layer
│ │ ├── a2ui-model/
│ │ │ ├── overlay-generated/ # rule-based rewrites
│ │ │ └── overlay-generated.json # rules + sha256
│ │ ├── a2ui-engine/
│ │ └── compose/
│ └── material3-a2ui-kmp/ # Kotlin 2.4 build: material3-a2ui
│ ├── overlay-generated/
│ ├── overlay-manual/ # hand-written (one file today)
│ └── resources-generated/ # generated translations
├── tests/integration/ # tests against published artifacts
├── samples/
└── scripts/ # sync, rewrite, verify, record
3. Overlay 替换与变更校验
如上所示,参与 commonMain 编译的文件被明确划分成了五层,每一层只有唯一的维护方式:
source/(只读的 AOSP 源码快照,归sync-upstream.py管):按source/UPSTREAM里记录的 AOSP commit(对应androidx.a2ui1.0.0-alpha02)拉取source/a2ui/和source/material3-a2ui/,CI 中由check-upstream.py逐文件比对。193 个上游.kt文件里,有 124 个一行都不用动直接参与编译。target/*/overlay-generated/(固定规则机械改写层,归generate-overlays.py管):剩下 69 个调了平台 API 的文件里,有 68 个只需要按固定规则做机械改写——主要是把java.*/android.*的 import 改到ai.littlepress.a2ui.compat.*,外加Locale.US常量和::class.java.simpleName的替换(完整规则见仓库 DEVELOPMENT.md),并在overlay-generated.json里记录规则名与上游文件的 sha256。target/*/overlay-manual/(手写替换层,受overlay-manual.json校验):只要某个文件在固定规则之外还需要改动哪怕一行逻辑,就不准放进overlay-generated/,必须进overlay-manual/。全仓 193 个源文件里,当前只有 1 个文件被分配在这里:ShimmerIndicator.kt。原因是 Android 原版在每帧动画里直接修改同一个 Shader 的localMatrix;而 Desktop 和 iOS 底层用的 Skia Shader 创建后不可变,必须在每帧按当前偏移和半径重新创建RadialGradientShader。target/a2ui-kmp/a2ui-compat/与模块自身的src/(兼容实现层,单独维护):实现了Locale、ICUMessageFormat、CLDR 45 复数规则、日期与数字货币格式化、严格模式JsonReader以及 iOS 端的 FIFO 公平锁ReentrantLock。它们全部收在ai.littlepress.a2ui.compat.*包下,不碰上游目录。resources-generated/与录制数据,一样由脚本生成或录制,不手动修改:例如material3-a2ui的多语言字符串(85 个 Locale、11 条字符串)由脚本从上游res/values*/strings.xml自动生成到target/material3-a2ui-kmp/resources-generated/。
到了编译阶段,target/build-logic 里的 upstreamSources(...) 会把 source/ 加进 commonMain,自动排除掉两个 overlay 目录中登记过的同名相对路径,再把 overlay 目录拼进来。
这样拆完之后,将来同步上游新版本时,但凡 Google 改了某个被 overlay 覆盖的文件,清单里的 sha256 校验就会在构建时报错,直接定位到变动的文件;平时做 Review 时,我们也能把精力重点放在手写实现(overlay-manual/ 与 a2ui-compat/)和架构变化上。
构建架构
源码分包定下来后,第二块在反复打磨的是 Gradle 构建结构。
因为 material3-a2ui 和底下 5 个基础包需要的 Kotlin 与 Compose Multiplatform 版本不同,仓库必须拆成两个独立的 Gradle 构建。在第一版 Coding Agent 的实现里,依赖版本全塞在 gradle.properties 中,material3 构建通过 srcDir 去借用另一个构建的 buildSrc;更麻烦的是,当时讨论 samples/ 怎么依赖本地库时,初步方案写着“第一次用 IDE 打开前,需要先跑一遍命令行发布任务把本地包产出来”。
这个设计和体验基本上是在胡乱使用 Gradle API,完全看不下去,就算人类不怎么看代码这个维护方式对 AI 来说也比较麻烦。参考我之前在《Android 构建与架构实战》(Extending Android Builds)第 2-1 节讨论过的 Composite Build 做法,我们把构建工程重构成下面这套结构:
下面我们分成三个部分来拆解这张图里的设计。
1. 根目录单一 Version Catalog 跨工程复用
在一个仓库里同时维护多个独立构建时,把版本号写在 gradle.properties 里四处读取,或者专门搞个内部 BOM,都会让配置变得琐碎。我们在仓库根目录只放唯一一份 gradle/libs.versions.toml(注释里标明哪些版本对齐上游 1.0.0-alpha02、哪些为什么上调),让 target/a2ui-kmp、target/material3-a2ui-kmp、target/build-logic 和 tests/integration 全部通过 from(files(...)) 引用同一份文件:
// target/a2ui-kmp/settings.gradle.kts
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("../../gradle/libs.versions.toml"))
}
}
}
全仓唯一的例外是 samples/:它保留了独立的 samples/gradle/libs.versions.toml,因为 Sample 本身就是给外部开发者抄作业用的独立工程模板。
2. Kotlin 2.3 / 2.4 两个构建基座 & 用 build-logic 替代 buildSrc
不少同学看到这里可能会问:为什么不干脆把 6 个模块全升到 Kotlin 2.4 放在一个工程里编译?
原因出在 iOS 端的 klib ABI 版本上。上游 material3-a2ui 用到了 MaterialTheme.motionScheme、新版 Slider / ExposedDropdownMenu、TimePickerState.isInputValid 等一系列新 API,它们直到 Compose Multiplatform 1.13.0-beta01 才合入,而这个版本的 iOS klib 是用 Kotlin 2.4.0 编译的。由于低版本 Kotlin 编译器无法读取高版本 ABI 的 klib,material3-a2ui 就必须上 Kotlin 2.4.0。
反过来,如果把底下 5 个基础包也一起用 Kotlin 2.4.0 编译,那所有还在用 Kotlin 2.3 稳定版 + CMP 1.12.2、且只需要基础渲染器或自定义 Catalog 的 iOS 工程,就彻底用不了这 5 个包了。因此,我们把 target/a2ui-kmp(Kotlin 2.3.21 + CMP 1.12.2)和 target/material3-a2ui-kmp(Kotlin 2.4.0 + CMP 1.13.0-beta01)拆成两个构建:命令行构建时先发基础包到本地 build/repo,再由 material3 构建消费(两边输出的元数据与字节码目标版本统一设为 Kotlin 2.2),从而保住多数基础包对 Kotlin 2.3 项目的兼容性。
两个跑在不同 Kotlin 版本下的构建,怎么共用同一套构建插件?我们没有使用 buildSrc,改用独立的 target/build-logic,并在它的 build.gradle.kts 里把插件依赖写成 compileOnly:
// target/build-logic/build.gradle.kts
plugins {
`kotlin-dsl`
}
dependencies {
compileOnly(libs.kotlin.gradlePlugin)
compileOnly(libs.mavenPublish.gradlePlugin)
}
这样 target/build-logic 只针对插件 API 编译,不往运行时 classpath 塞固定的 Kotlin 版本;两个构建各自应用自己的 Kotlin 版本,同时复用 build-logic 里的源码分层逻辑与发布脚本(发布时会在 .module 元数据中追加 Android 变体,自动转发到同版本的 Google 官方 androidx.a2ui Artifact)。
3. 根目录 Composite Build 与独立在外的 tests/integration
为了解决首次打开 IDE 的体验问题,我们在仓库根目录放了一份 settings.gradle.kts,用 includeBuild 把开发期需要联调的工程组成一个 Composite Build:
// settings.gradle.kts
rootProject.name = "a2ui-kmp-repository"
includeBuild("target/build-logic")
includeBuild("target/a2ui-kmp")
includeBuild("target/material3-a2ui-kmp")
includeBuild("samples")
这样直接用 IDE 打开仓库根目录时,Gradle 会自动把 samples 对 ai.littlepress.a2ui:* 的依赖替换成本地源码子模块。刚 clone 下来不需要跑任何发布命令就能直接运行 samples,还能从示例一路跳转进库源码打断点调试。
但这里要插一个转折:仔细看上面的根目录 settings.gradle.kts,tests/integration 并没有被 includeBuild 进来。
这是故意留出来的隔离边界,因为有很多 metadata 的东西只有真正发了 Maven Artifact 之后才存在,一旦在 Composite Build 里被替换成本地源码模块,e2e 就不方便测试了——例如 .module 元数据里的 Android 官方包转发对不对、iOS klib 里的 ABI 版本是不是预期值、传给下游 App 的传递依赖(Transitive Dependencies)有没有遗漏等等。因此,tests/integration 必须作为独立的外部工程单独运行,并通过 exclusiveContent 把 ai.littlepress.a2ui 锁定在刚发布的本地 ../../build/repo 目录:
// tests/integration/settings.gradle.kts
dependencyResolutionManagement {
repositories {
// ai.littlepress.a2ui comes from the artifacts just built only: a missing one fails.
exclusiveContent {
forRepository { maven { url = uri("../../build/repo") } }
filter { includeGroup("ai.littlepress.a2ui") }
}
google()
mavenCentral()
}
// The repository's one version catalog.
versionCatalogs { create("libs") { from(files("../../gradle/libs.versions.toml")) } }
}
如果本地 build/repo 漏发了任何一个包,构建会直接报错,不会悄悄去拉 Maven Central 上的旧包。验证脚本中,tests/integration 会跑两趟:第一趟传 -Pa2uiIntegration=minimum,用 Kotlin 2.3.21 验证 5 个基础包;第二趟传 -Pa2uiIntegration=all,切到 Kotlin 2.4.0 验证包含 material3-a2ui 在内的全部 6 个包。
测试与行为对齐:以 Android 版本的录制结果为准
当源码分层和构建边界都立住之后,AI 在项目里的角色就很好安排了:一个 Agent 负责在 a2ui-compat 边界内写多平台兼容实现,另一个 Agent 对照上游代码做独立 Code Review,人类就负责把控架构和验收标准。
这中间最核心的一个问题是:怎么确认 AI 写的 a2ui-compat 在 Desktop 和 iOS 上真的跟 Android 表现一致?
一开始做方案时,我们曾想过日期用 kotlinx-datetime、JVM 调 java.text.NumberFormat、iOS 调 Foundation.NSNumberFormatter。但在第二轮方案评审核对时发现,JDK 的 java.text 和 iOS 的 Foundation 在不少细节上本身就对不上——例如 JPY(日元,0 位小数)的小数位处理,以及日期格式符 u(在 Android 的 SimpleDateFormat 里代表周几 1..7,在 Apple 的 ICU 里却代表扩展年份)。直接纯调各自系统的默认 API,两端跑出来的结果肯定不一致。
为了避免实现和测试沿用同一个错误假设,我们在一台 Android 15 模拟器(API 35,ICU 75.1 / CLDR 45 / Unicode 15.1)上录下官方 Jetpack Compose 的执行结果,存成 tests/integration 下的一组 fixture 对照文件,作为 Desktop 和 iOS 的测试数据。
- 数字与货币格式化:比对 50 个 Locale 的数字格式,以及 JPY(0 位小数)、KWD(3 位小数)等货币格式,Desktop 和 iOS 端使用从 Android 录制的数字字符与 AM/PM 表;iOS 端和 ICU 一样按 CLDR 默认数字系统输出(例如阿拉伯语使用阿拉伯数字系统)。
- 复数规则与日期格式化:覆盖 20 个 Locale 共 836 条 ICU
MessageFormat复数与selectordinal用例,以及 19 个 Locale 的日期格式、260 条日期 pattern(52 个字母 × 宽度 1 到 5)和material3-a2ui在 29 个 Locale 下的日期与Calendar用例。 - 资源目录匹配与严格模式 JSON 解析:移植了 AOSP 的资源 Locale 匹配逻辑与 Android 15 区域映射表,验证 174 个 Locale 选出来的资源目录与 Android 15 模拟器一致;30 条合法与畸形 JSON 消息(含前导 BOM)要求 Desktop 和 iOS 连抛出的异常报错文本也与 Android 一致。
这套比对测试也帮我们找出了几处平台底层差异——例如在 ja-JP 下,iOS 输出的日元符号是半角 "¥",而 Android 是全角 "¥"。我们在测试里把这几处已知差异单独锁定到确切值,完整列表见仓库 CHANGELOG.md。
上游测试复用 & 后续自动化展望
因为 source/ 直接从 AOSP 同步了完整目录,上游自带的测试也被我们接进了构建流程:
- 上游单元测试:6 个模块共 1,446 个上游单元测试全部在 Desktop(JDK 21)原样跑通。
- 上游 Compose UI 测试:通过
tests/android-test-stand-ins提供同名的 Android 测试 runner 与注解,上游的 instrumented 测试可以直接用 Compose Multiplatform 的runComposeUiTest在 Desktop 跑,跑通了material3-a2ui的 527 个与compose-ui-testing的全部 57 个 UI 测试(共 584 个);另外scripts/verify-samples.sh还会在 Desktop 和 iOS 模拟器上构建并启动samples验证交互。 - 跳过项:Desktop 仅 8 个
material3-a2ui的上游 UI 测试未运行——其中 4 个测试要按 Android 系统返回键而跳过,另外 4 个用到了 Compose Multiplatform 在 Desktop 还未实现的StateRestorationTester而按名字排除。
在日常维护和发布检查上,目前仓库里已经配好了两道自动化脚本:
一是每次发布到本地 build/repo 后,scripts/check-requirements.py 会直接解析生成的 Gradle .module 文件和 iOS klib 里的 default/manifest,检查 README.md 里的版本要求表格跟实际打出来的产物是否一致。
二是 .github/workflows/release-watch.yml 每天夜间定时跑 scripts/check-new-releases.py。一旦发现 Google Maven 上出了新的 androidx.a2ui 或 material3-a2ui 版本,或者 material3-a2ui 等待切换的 Compose Multiplatform 1.13 正式版发布了,Workflow 就会自动在仓库里提一个 issue(会自动去重)。
如果我们再往前展望一步:现在每天的 release-watch 已经能自动检查新版本并提 issue,接下来我们准备尝试结合像 Pullfrog 这样的工具,把“提了 issue 之后自动改代码并提交 PR”也串起来——让 AI 在沙箱里自助跑完 sync-upstream.py、generate-overlays.py 和全套 verify.sh 并提好 PR。这些检查先筛出源码同步、版本和产物方面的问题;AI 提交 PR 后,人类检查 changelog 和修改背景,以及兼容实现、测试结果、架构和核心逻辑的改动。
目前 a2ui-kmp 的 1.0.0-alpha02 已经发布到 Maven Central,仓库地址是 github.com/BinaryTape/a2ui-kmp(由 LittlePress.AI 赞助支持)。我认为第三方渲染器可以用自己的组件体系替换 Material 层,主要工作可能会集中在这一层,但不只是更换 UI 主题,底层 Runtime 则可以大量使用同一套官方基座,在上面继续扩展。LittlePress.AI 正朝这个方向迭代现有的 Compose Renderer:复用这套 KMP 移植中适合的底层实现,再根据自身需求继续扩展。欢迎大家在 KMP 项目里试试看,有问题直接提 issue 交流!
欢迎扫码加入 GenUI 中文讨论组。如果群满了,可以加我微信 xs2bab 拉你入群:
