Skip to content

feat(harmony): 鸿蒙原生 ArkUI 界面,一次开发三端部署 - #1276

Open
aeoform wants to merge 26 commits into
Open-Less:betafrom
aeoform:feat/harmony-native-ui
Open

aeoform wants to merge 26 commits into
Open-Less:betafrom
aeoform:feat/harmony-native-ui

Conversation

@aeoform

@aeoform aeoform commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

摘要

Fixes #(无关联 issue:来自「按鸿蒙规范写一套原生 UI、一多覆盖电脑/手机/平板」的需求)

把鸿蒙端从 ArkWeb 外壳换成 ArkUI 原生界面,并按 HarmonyOS 的「一次开发,多端部署」组织:手机、平板、PC(2in1)跑同一份页面代码,布局由窗口尺寸决定。

顺带修掉迁移中暴露的两个真问题:

  1. 断点在平板/自由窗口下全部失配:原来用 mediaquery 的范围语法((600vp<=width<840vp) 等),在平板上三个查询都不匹配,于是永远停在同一个尺寸类——横竖屏都不重排。现在读 window.getWindowProperties() 并订阅 windowSizeChange,旋转、分屏、PC 自由窗口拉伸走同一条路径。
  2. HAP 里没有 Rust 核心:libopenless_harmony.so 的 DT_NEEDED 指向 libopenless_harmony_host.so,而 hvigor 只打包原生构建产物,entry/src/main/libs/ 不会进包,运行期 Error loading shared library ... load failed → NAPI 模块加载失败 → initBackend 走不通。已发布 2.0.2-Beta.3 的 HAP 也有同样问题,说明这条链路此前从未真正成功过。

修复 / 新增 / 改进

一多架构

尺寸类 判定 导航形态
sm 窗口 < 600vp 底部标签栏(概览/历史/词典/风格/设置 + 更多)
md 600–840vp 图标侧栏 + 单列内容
lg ≥ 840vp 带文字侧边栏;概览/历史/设置自动两栏(列表 + 详情)
  • module.json5:deviceTypes: [phone, tablet, 2in1]、orientation: auto_rotation、supportWindowMode: [fullscreen, split, floating]。
  • 设计 token(common/Tokens.ets + color.json 浅/深各一套):品牌蓝 #0A59F7 + HarmonyOS 中性色阶,8vp 栅格、12/16vp 圆角、系统 Symbol 图标(sys.symbol.*,用编译器逐个验证过可用名)。

11 个原生页面(components/AppShell.ets 里的路由表)

概览(听写主控 + 今日统计 + 12 周热力图 + 当前配置)、历史(搜索 / 详情 / 重新转写 / 删除)、词典、纠正规则、风格、风格市场、翻译、问答、速记、模型设置、设置(转写·润色·输入·隐私·同步·关于,全部经 update_setting_fields 落盘,不做本地影子状态)。

数据层:data/Api.ets → HarmonyApi.invokeAsync → libopenless_harmony.so,与 web/桌面共享同一份 camelCase 契约;ArkTS 侧继续负责录音、麦克风枚举、剪贴板、文件与凭据。ArkWeb 的 javaScriptProxy/runJavaScript 事件改为 AppStorage 状态(dictPhase/dictLevel/dictError/backendRevision)。

顺手修的坑

  • AppStorage.setOrCreate 对已存在的键静默无效,用它写路由会把导航冻在第一次的值上(表现为"点了没反应")。新增 common/Store.ets(setOrCreate + set),所有写入改走它。
  • 深链:aa start --ps route settings --ps layout sm 直达页面并把尺寸类钉住,单机即可验收三端布局(common/Boot.ets 在 UI 创建前传参,避免 onWindowStageCreate 碰 AppStorage)。

构建

  • scripts/build-harmony-frontend.sh → scripts/build-harmony-app.sh:不再跑 npm run build,也不再往 HAP 塞 40 MB web 资源(HAP 134 MB → 91 MB),并清理缓存工程里遗留的 rawfile/web。
  • CMake 把 libopenless_harmony_host.so 复制到输出目录,随库一起打包(29 MB)。

契约测试:新增 scripts/harmony-native-ui.test.mjs(无 ArkWeb / 路由必须赋值 / 三端窗口配置 / 尺寸类映射表 / 11 个页面齐全 / 页面不直连原生模块 / 浅深色 token 对齐),并接进 ci.yml 的 harmony-check;harmony-app-dictation 改为断言 AppStorage 状态,harmony-entry-startup 覆盖深链参数。

兼容

  • 不包含:不改 Rust Core 契约、不改 web/桌面端、不改签名与发版流程、不改输入法扩展。
  • 对现有用户 / 本地环境 / 构建流程的影响:
    • 鸿蒙端 UI 完全换成原生,ArkWeb 依赖从 HAP 中移除;未联网能力与云端转写依赖不变。
    • 本地构建不再需要 npm ci 参与 HAP 打包(仍是 Rust 交叉编译 + hvigor);脚本改名,CI 与文档已同步。
    • 问答与模型设置两页按宿主能力如实标注:supportsOverlay=false 没有桌面浮窗面板,supportsLocalAsr=false 没有本地模型;页面不做"假控件"。

测试计划

  • 命令:node scripts/harmony-native-ui.test.mjs、harmony-entry-startup、harmony-app-dictation、harmony-siwc、harmony-voice-configuration、harmony-panel-layout、harmony-editor-compat、harmony-clipboard-history、harmony-symbol-gesture、harmony-rime-startup、harmony-release-contract、ci-changed-areas、ci-cache-sharing、release-publication-contract、workflow-concurrency-contract、linux-egui-release-contract、repository-owner-contract、android-ipc-import-boundary、ci-workflow-behavior
  • 结果:全部通过
  • 证据路径(真机:HUAWEI MatePad XYAO-W00 / 2560×1600 / API 26,debug 签名直装):
    • 三端布局在同一台设备上逐一验过:--ps layout sm|md|lg 分别得到底部标签栏、图标侧栏、带文字侧栏(侧栏 240vp、内容从 612px 起)。
    • 11 个路由全部渲染真实数据:历史读到 1 条记录并展示参数面板;风格读到 4 个风格包(含"使用中");风格市场通过网络读到真实包;设置读到转写/润色供应商与 2 个麦克风设备;模型设置按 supportsLocalAsr=false 显示能力边界。
    • libopenless_harmony_host.so 已进包,initBackend 正常,听写流程(开始/停止/结果)可用。
    • HAP 体积:134 MB → 91 MB(去掉 web bundle),包内 .so 三件套齐全。

HAP 里只有 `libs/arm64-v8a/libopenless_harmony.so`,而它的 DT_NEEDED 指向
`libopenless_harmony_host.so`(`bridge.cpp` 以 IMPORTED SHARED 链接的 Rust 核心)。
hvigor 只打包原生构建产物,`entry/src/main/libs/` 里的文件不会进包,于是运行期报

    Error loading shared library libopenless_harmony_host.so: ... load failed

NAPI 模块加载失败后 `initBackend` 走不通,整个应用没有可用后端。CMake 侧把 Rust
库 copy 到 CMake 输出目录,让它随库一起被收集、strip 并打进 HAP(29 MB)。

顺便说明:已发布的 2.0.2-Beta.3 HAP 也缺这个库,说明这条链路此前从未成功用过。
把 ArkWeb 外壳换成一套 ArkUI 原生界面:手机、平板、PC(2in1)跑同一份代码,布局由
**窗口尺寸**决定。原来的断点用 `mediaquery` 的范围语法,在平板上三个查询全部不匹配,
永远停在同一个断点(横竖屏都不重排);现在读 `window.getWindowProperties()` 并订阅
`windowSizeChange`,旋转、分屏、PC 自由窗口拉伸都走同一条路径。

- 尺寸类:`sm` <600vp 底部标签栏;`md` 600–840vp 图标侧栏;`lg` ≥840vp 带文字侧边栏,
  概览/历史/设置自动两栏。
- 11 个原生页面:概览(听写主控 + 今日统计 + 12 周热力图 + 配置状态)、历史(搜索、
  详情、重转写、删除)、词典、纠正规则、风格、风格市场、翻译、问答、速记、模型设置、
  设置(转写/润色/输入/隐私/同步/关于,全部经 `update_setting_fields` 落盘)。
- 数据层 `data/Api.ets` 走 `HarmonyApi.invokeAsync` → `libopenless_harmony.so`,与
  web/桌面共享同一份 camelCase 契约;ArkTS 侧继续负责录音、麦克风枚举、剪贴板、凭据。
- 设计 token:品牌蓝 #0A59F7 + HarmonyOS 中性色阶,浅/深各一套;8vp 栅格、12/16vp 圆角。
- `module.json5` 增加 2in1、`auto_rotation`、`supportWindowMode: fullscreen|split|floating`。
- 深链:`aa start --ps route settings --ps layout sm` 可直达页面并把尺寸类钉住,
  便于单机验收三端布局(`common/Boot.ets` 负责在 UI 创建前传递参数)。
- `common/Store.ets`:`AppStorage.setOrCreate` 对已存在的键静默无效,用它写路由会把
  导航冻在第一次的值上——所有写入改为 setOrCreate + set。
原生 UI 之后 `$rawfile('web/index.html')` 与 vite 产物都没有消费者了,但构建仍在跑
`npm run build` 并把 40 MB 资源塞进 HAP(134 MB → 91 MB)。删掉 web 构建与拷贝,
并把脚本名从 `build-harmony-frontend.sh` 改成 `build-harmony-app.sh`(它现在只做
Rust 交叉编译 + Rime 数据 + hvigor)。

同时清理可复用工程目录里旧构建遗留的 `rawfile/web`,否则缓存工程会一直带着死资源。

契约测试同步:`harmony-app-dictation` 改为断言 `AppStorage` 里的听写状态(不再有 DOM
事件),`harmony-entry-startup` 覆盖深链参数,新增 `harmony-native-ui` 检查
「没有 ArkWeb / 路由必须真正赋值 / 三端窗口配置 / 窗口尺寸断点 / 11 个页面齐全 /
页面不直连原生模块 / 浅深色 token 对齐」,并接进 ci.yml 的 harmony-check。
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-26T16:17:14.045855Z
We couldn’t complete this review. Comment @codex review to try again.
d20cbc2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

`@Builder` 带字符串参数时只在创建时求值一次:概览页的"当前配置"在设置快照加载完成前
渲染,之后即使 `prefs` 更新,行里仍是空字符串 → 显示"未配置",而同一屏顶部的 pill 却
写着真实供应商。改成 `@Prop` 组件 `StatusRow`(历史详情页同步替换 `metaRow`),值变化时
重新渲染。真机验证:转写引擎=火山引擎、润色模型=火山方舟。
没有凭据时鸿蒙端听写必然失败(历史里只剩错误码),所以补一个真正的凭据页:

- `pages/CredentialsPage.ets`:状态徽标(转写/润色是否已配置)、按当前渠道渲染
  App ID + Access Token(火山)或 API Key(其余),以及接入点/模型 ID;全部经
  `set_credential` 进系统密钥库,账号名与桌面端一致,桌面填过的这里能读到,反之亦然。
- 设置页加入「凭据与密钥」入口。
- 翻译页加「按当前模式改写」:`repolish` 一次性改写/翻译粘贴的文本,结果可复制。
- 历史详情加「重新润色」(走 `repolish` + 当前风格)与「复制原文」(ASR 原文与润色后
  文本不同时才有)。
- `verify-harmony-hap.sh`:去掉 ArkWeb bundle 要求,改为必须带 Rust host 库、必须不带
  web bundle、必须有 Rime 数据(CI 的 harmony job 之前就是卡在这条旧断言上)。
- 契约测试同步:凭据页必须走 `set_credential`,密钥不得写进偏好文档。
- 凭据页补渠道 CRUD:列出 ASR/LLM 渠道(当前使用/启用/删除)、按供应商新建渠道、
  「测试」走 validate_provider_credentials + record_channel_test 并把延迟写回渠道。
- 风格页:编辑系统提示词(save_style_pack)、新建风格、删除风格;编辑区用 TextArea。
- 风格市场:按热度/最新排序、条目点赞(marketplace_like)。
- 设置页新增「外观」:跟随系统/浅色/深色(写 /theme)。
- Api 新增 validateChannel / listModels / recordChannelTest / deleteStylePack /
  resetBuiltinStylePack / likePack。
按"全部对齐"继续补:

- 历史:播放录音(`read_audio_recording` → 缓存 wav → AVPlayer `fdSrc`),
  播放状态随条目切换自动停止。
- 概览:未配置转写密钥时显示首启引导卡(去配凭据 / 查看引擎),不再让人对着必失败的
  录音按钮发呆。
- 设置:麦克风电平测试(`start_microphone_level_monitor` + AppStorage 电平条)、
  低延迟/稳定转写/音频增强开关、导出诊断日志(`export_harmony_error_log`)。
- 风格市场:GitHub 设备码登录(`github_device_flow_start` / `_poll`,自动复制 user code,
  授权后刷新列表)。
- 凭据页:拉取可用模型(`list_provider_models`),点模型直接写入 `asr.model` / `ark.model_id`。
- 契约测试覆盖上述接线(回放必须用 AVPlayer + fdSrc,页面只能经 Api 调核心)。
@H-Chris233 H-Chris233 self-assigned this Oct 27, 2026
@H-Chris233 H-Chris233 added area:ui UI area area:ux UX area enhancement New feature or request large-change Large or risky change needs-review Needs review needs-tests Needs tests P2 Medium priority labels Oct 27, 2026
按平板实机反馈调整:

- **导航**:一级只剩 概览/历史/风格/词典/设置 五项。风格市场、翻译、问答、速记、
  纠正规则、模型设置、凭据全部收进设置的「内容与工具」,并在归属页做上下文入口
  (风格页 → 风格市场,词典页 → 纠正规则)。删掉 MorePage。
- **竖屏**:只有 ≥840vp(横屏平板/PC 窗口)才用侧边栏,其余一律底部标签栏——手机与
  平板竖屏同一形态。
- **光效**:上一版的三个大模糊彩色圆在平板上是肉眼可见的色块/污渍。改成整屏一层低透明度
  品牌斜向渐变(`linearGradient` + glow token),玻璃卡与导航条继续用
  `backgroundBlurStyle` 折射它;麦克风主按钮用两层同心柔光圈 + 品牌阴影做局部光。
- **2in1 提示**:`deviceTypes` 回到 phone/tablet,`supportWindowMode` 去掉 `floating`。
  AGC 的 Profile 是按设备类型签发的,HAP 声明了 Profile 没授权的 2in1,系统就报
  「不支持 PC 和 2in1 设备」,且自由窗口永远建不出来。契约测试加断言防止再犯。
读了华为 UX 设计文档(doccenter-ux-design)并整理成
`/data-win/download/harmonyos-ux-design.md`(含规范数值 + OpenLess 落地对照)。据此重设:

- **断点补 XL**:320 / 600 / 840 / 1080vp 四段;侧栏只在 LG/XL,XL 用 288vp 侧栏 + 32vp 页边距。
- **间距回到 8vp 栅格**:4/8/12/16/24/32/48/64,新增 touch=44、rowSingle=48、rowDouble=64。
- **字号按规范重排**:Display 48 / Headline L 32 / S 24 / Title L 20 / M 17 / Body S 14 / Caption 12,
  另给行高常量 `Lh`(60/40/32/28/24/20/16)。
- **圆角按规范**:S 8(输入/标签)、M 16、L 24(卡片)、XL 32(页面容器)、pill。
- **语义色改为宇宙蓝体系**:brand #007DFF、success #00C566、warning #FF8800、error #E84026;
  文本用 #E5000000 / #99000000 / #4D000000 三档;分隔线 #0D000000;深色 #000000 底 + #1C1C1E 卡。
- **阴影按规范 Level 2**(blur 8vp, 15%)与 Level 3(弹层)。
- **2in1 走 release 渠道**:仓库仍声明 phone/tablet(本地 debug Profile 只授权这些);打 release/test
  包时构建脚本把 `2in1` + `floating` 补进 module.json5 —— 与 AGC 发布 Profile 对齐,
  避免"声明了但 Profile 不授权"导致系统拒绝启动。
- 契约测试同步(XL 断点、8vp 栅格下限、字号/热区/页边距断言)。
按华为 UX 规范做视觉重排的第一遍(数值上一版已对齐,这遍是结构与层级):

- 概览页改成「问候语(14) + 页面大标题(32)」的双层标题;今日三项统计用 **Display 48fp /
  行高 60**,标签降为 Body S,视线先落在数字上。
- 新增 `ListRow`:40vp 图标砖 + 标题(16) + 副标题(14) + 尾部值/chevron,单行 48vp /
  双行 64+8vp,分隔线按规范内缩(有图标时左缩 64vp)。
- 设置页的「内容与工具」、转写引擎、润色模型改用 `ListRow`,从纯文字列表变成
  图标 + 标题 + 值的规范列表形态。
- 卡片内边距回到 16vp、圆角 24vp(规范 L 级),与 L2 阴影配套。
…rketplace_auth_status

按 remaining-work.md 的 B 组逐条落地,先做零风险的三条:

- 概览首帧调用 `take_splash_playback`(启动动画只播一次;未实现的后端不抛错,只记日志)。
- 设置的模式胶囊改走 `set_default_polish_mode`,切换即更新管线,不再只改偏好字段。
- 风格市场进入时读 `marketplace_auth_status`,登录态直接显示在按钮上,而不是只在点登录后才知道。
B1 完成。设置 → 云同步现在是一条真正可用的链路:

- 进入时读 `cloud_sync_status`,显示登录态、上次同步时间与云端修订号。
- 「上传本机偏好」:用当前偏好快照 + `baseRevision` 调 `cloud_sync_upload`,完成后重读状态。
- 「从云端恢复」:`cloud_sync_restore`,完成后刷新设置页。
- 「删除云端」:`cloud_sync_delete(baseRevision)`。
- 三条操作共用 syncing 态与结果提示(成功/失败原因),并说明端到端加密、口令与密钥不出设备。

按约定没做口令输入:Host 侧 `cloud_sync_upload/delete` 只接受 baseRevision + 偏好,
E2EE 口令走的是密钥库条目而非 IPC 参数,所以这一页只负责"用哪个修订号同步"。
- `data/Api.ets` 新增 pendingCorrections / acceptCorrection / rejectCorrection / dismissSuggestions。
  数据源用 `get_startup_snapshot` 的 backend 快照:桌面端靠事件推送 pendingCorrection,
  而鸿蒙 Host 是请求/响应,没有事件通道,快照是唯一可读来源(读不到就返回空数组)。
- 纠正规则页顶部新增「学习建议」卡:每条显示 `pattern → replacement`,接受走
  `accept_pending_correction`,忽略走 `reject_pending_correction`,操作后重新拉取。
- 读不到建议时不显示卡片,不假装有数据。
- 导出:`pick_style_pack_zip_target` 打开系统保存对话框(默认文件名
  `OpenLess-<风格名>.zip`),用户可另存到下载/文档;确认后 `export_style_pack_to_zip`
  把核心生成的 ZIP 写进所选 URI。
  说明:第三方应用不能直接写公共「下载」目录(需要系统级
  `ohos.permission.READ_WRITE_DOWNLOAD_DIRECTORY`),所以走系统保存对话框是唯一合规路径,
  对话框里选「下载」即可。
- 导入:`pick_style_pack_zip` 文档选择器(后缀过滤 .zip)→ `import_style_pack_from_zip`,
  Host 侧仍限制 512 KiB。
- 风格页头部加「从 ZIP 导入」,每个包加「导出 ZIP」,结果/失败原因以一行提示显示。
`list_vocab_presets` 读的是加密文档库里的 `VocabularyPresetRecord[]`
(id / origin / name? / phrases[] / enabled / order),随云同步一起更新。

这轮只做**只读展示**:词典页顶部「预设词组」卡,逐条显示名称、词条数与启用状态。
不做开关的原因:写回需要整份 store 契约(`save_vocab_presets` 收 `store`),
只回写部分字段有丢掉 origin 等字段、污染云同步文档的风险;
等 B1 云同步的文档结构确认后再补可编辑版本。
按规范把"内联展开的供应商列表"换成从底部升起的半模态:

- 新增 `asrSheet` / `llmSheet` 两个 `@Builder`,带标题与当前选择提示。
- 卡片内放 0×0 的锚点组件挂 `bindSheet($$this.asrOpen, …)`,避开属性链落点问题;
  `height: SheetSize.MEDIUM`、`dragBar`、`showClose`,`onDisappear` 复位状态。
- 点击「转写引擎 / 润色模型」置 true 打开,不再把列表撑在卡片里。
- 新增 `SkeletonList`:3–4 行 40vp 图标砖 + 两行占位条,玻璃底、0.7 透明度,
  用于首屏加载而不是"转圈 + 空白"。
- 新增 `ErrorState`:图标 + 原因 + 「重试」按钮,危险色浅底,接 `onRetry`。
- 历史页接入:首次加载显示骨架屏;失败时整块换成 ErrorState 并可直接重试;
  空结果仍走 EmptyState(三种状态不再混在一起)。
侧栏项与底部标签加 `hoverEffect(HoverEffect.Highlight)` + `focusable(true)`:
鼠标悬停有系统高亮、Tab 键能停到导航项上,不用先点一下才知道能不能点。

`Ctrl+1..5` 快捷键没做:需要在 AppShell 根部接 `onKeyEvent` 并判断修饰键,
当前 SDK 的修饰键判定 API 在宿主上行为未验证,先不引入不确定路径;
等 A2 把导航换成 `Navigation` 时一起做(那时有统一的 key handling 入口)。
- Api 新增 `marketplaceLogout`(`marketplace_logout`)。
- 风格市场顶部按钮变成两态:未登录 → 走 GitHub 设备码登录;已登录 → 显示登录名,
  再点一次即退出并刷新列表。
- `AppShell` 根部接 `onKeyEvent`,`KeyType.Down` + 修饰键(Ctrl/Meta)时按 1..5
  跳到概览/历史/风格/词典/设置。
- `getModifierKeyState` 在 KeyEvent 上是可选方法,调用前判空并 try/catch,
  宿主不支持时快捷键自动失效而不是抛错(不阻断鼠标/触屏使用)。
- `KeyCode` 来自 `@kit.InputKit`。

A2 剩下的 `Navigation` 三栏容器没在本轮做:那要重写历史/设置的页面结构,
需要一次完整的构建 + 真机 lg/xl 验证,不适合和快捷键混在一次提交里。
按 `harmonyos-design-notes/a2-navigation-plan.md` 执行第一步:

- 宽屏(lg/xl)不再手写 Row + 两个 Column,改用 `Navigation(this.stack)` +
  `mode(NavigationMode.Split)`、`navBarWidth(360)`、`navBarWidthRange([300,460])`、
  `hideTitleBar(true)`;详情作为 `NavDestination` 推入。
- `NavPathStack` 放在组件字段上(放 build() 里会每次重建丢栈)。
- 选中统一走 `setDetail(id)`:窄屏只记 `detailId`(行为不变),宽屏在栈为空时
  push 一次,避免旋转屏幕重复压栈。
- `NavDestination.title('详情')`:`HistoryEntry` 没有 title 字段,标题用固定文案,
  不做假字段。

竖屏路径与之前完全一致,只替换了宽屏分支。
上一版用一条 linearGradient 色洗当"光效",不符合规范。改成 HDS 真正的沉浸光感:

- 新增 `components/Material.ets` 导出 `Glass`:
  - `available()`:`deviceInfo.apiAvailable('26.0.0')` 判定 + 结果缓存;
  - `surface()` / `bar()`:返回 `uiMaterial.ImmersiveMaterial` 实例,不支持时返回 undefined
    (`.systemMaterial(undefined)` 就是关闭,卡片回落到原有毛玻璃)。
- `Common.Card` 与 `AppShell` 导航容器接 `.systemMaterial(...)`,拿到系统渲染的
  毛玻璃、按压弹性、触点光晕与智能反色,而不是我们伪造的半透明色块。

坑(已记录到 harmonyos-design-notes):
- `ImmersiveMaterial` 构造参数不能传裸对象字面量(`arkts-no-untyped-obj-literals`),用无参构造;
- 类名不能叫 `Material`,与 ArkUI 内建类型冲突("only refers to a type")。
真机竖屏 1600×2560px(density 2 → 800vp,断点 md)实测:侧栏正确不出现、底部标签栏正常,
但页面内容从 x=54 铺到 x=1473(约 750vp),表单两端拉开,读起来是平板而不是手机。

- `Tokens.contentMax(bp)`:md 返回 560vp,其它断点不限制。
- `AppShell` 的内容列加 `constraintSize({ maxWidth })`;Column 默认居中,
  所以上限生效后两侧留白相等,页面成为居中的单列。
- 同时撤掉卡片上的 `.systemMaterial`:真机日志明确
  "Material inactive: out of scope. Use component in navigation title bar or Tabbar",
  普通容器上不生效,留着属于"以为做了"。材质等底部导航换成 HdsTabs 时再接。
- 合并 origin/beta(23 个提交:Android IME 学习/英文输入、更新清单走 CDN、CI 平台范围收敛等),
  无冲突。
- 新增 `Nav.isSecondary(id)`;`AppShell` 在二级页(凭据/市场/纠正/翻译/问答/速记/模型设置)
  顶部渲染「‹ 所属页」返回条,44vp 高、可点、带 hover 高亮。
  之前这些页面只能靠底部标签跳走,没有任何返回路径。
- `PanelLayout` 新增 `voice` 模式:面板 `changeFlag(FLG_FLOATING)` 后
  缩成 min(屏宽 86%, 360vp) × min(148vp, 屏高 24%) 的小窗,按保存的 x/y
  拖放位置 `moveTo`,重启后仍记得位置。
- 面板菜单新增「悬浮语音」:切到 voice 布局并直接开麦(空闲且已连接、非密码框时)。
- 新增 `voiceCapsule()`:圆形麦克风(录音中变停止键)、状态行、实时文字/音量、
  「键盘」一键回到标准布局;识别完成出现「重新输入 / 写入」。
- 键盘本体在 voice 布局下整体不渲染,避免小窗里塞进整套键盘。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ui UI area area:ux UX area enhancement New feature or request large-change Large or risky change needs-review Needs review needs-tests Needs tests P2 Medium priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants