Skip to content

feat!: Navigator 的 push / pop 带上调用方 PageContext,pop 定为关闭调用方 - #47

Merged
HarlonWang merged 3 commits into
mainfrom
feat/navigator-caller
Oct 28, 2026
Merged

HarlonWang merged 3 commits into
mainfrom
feat/navigator-caller

Conversation

@HarlonWang

@HarlonWang HarlonWang commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Navigator.push / pop 原来不知道是哪一页在调用,宿主只能对栈顶操作。两种情况会出错:

改动:

  • push(page, paramsJson, from: PageContext) / pop(resultJson, from: PageContext),PageHost 传入自己的 context。与 HostCapability、AnalyticsSink、LinkOpener 一致,调用方身份由框架带上,页面改不了
  • 语义:pop = 关闭调用方自己(同 Android finish()、SwiftUI dismiss),结果交给它下面那一页;非栈顶页面发起的 push 由宿主决定,通常忽略并上报
  • 宿主怎么认出调用方:挂载时经 locals 给页面一个指向自己导航条目的句柄,from[local] 取回。写法见 native-api.md §3
  • tinyui-native 的 JS API 不变

破坏性:实现了 Navigator 的宿主要加参数。0.14.0 还没发布,随它一起出。

测试:

  • PageHostTest.navigationTellsTheNavigatorWhichMountCalled:push / pop 到达 Navigator 时 from 是这次挂载,带着挂载时的 locals
  • :compose:iosSimulatorArm64Test 通过、apiCheck 通过(ABI 文件已更新);本机 Android host test 缺宿主 JNI 库没跑起来,看 CI

🤖 Generated with Claude Code

Summary by Sourcery

Make Navigator operations caller-aware so hosts can close the originating page and route results correctly across persistent and multiple navigation stacks.

New Features:

  • Pass the calling PageContext through Navigator push and pop operations so hosts can identify and operate on the originating navigation entry.
  • Define pop as closing the calling page and delivering its result to the page below it, with host-controlled handling for pushes from covered pages.

Bug Fixes:

  • Ensure navigation results emitted before a page mounts are delivered after mounting, while results targeting failed or closed pages are discarded.

Enhancements:

  • Document caller-aware navigation semantics and the PageLocal-based navigation entry lookup pattern for hosts.

Documentation:

  • Update native API documentation for Navigator caller context, pop behavior, and multi-stack host integration.

Tests:

  • Add coverage verifying navigation callbacks carry the originating mount context and locals, including queued results and failed mounts.

Chores:

  • Update the public API/ABI definitions for the breaking Navigator signature change.

留在返回栈里的页面照常运行,页面也可能分在几个栈里,宿主只凭「栈顶」会弹错页、动错栈。
两个方法都加 from: PageContext(与 HostCapability、AnalyticsSink、LinkOpener 一致),
宿主经挂载时的 locals 认出调用方自己的条目;pop 关闭调用方并把结果交给它下面那一页,
非栈顶页面发起的 push 由宿主决定,通常忽略。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

该 PR 让 Navigator 的 push/pop 携带框架注入的 PageContext,由 PageHost 自动传递调用方身份;宿主据此按具体挂载和导航栈执行操作,pop 关闭调用方并向下方页面传递结果,同时更新文档、测试和 ABI。

Sequence diagram for caller-aware navigation

sequenceDiagram
    participant Page as PageHost
    participant Navigator
    participant Stack as Host navigation stack
    participant Below as PageHost below caller

    Page->>Navigator: push(page, paramsJson, context)
    Navigator->>Stack: from[NavEntryLocal]
    alt caller is not topmost
        Stack-->>Navigator: ignore or report push
    else caller is topmost
        Navigator->>Stack: push page
    end
    Page->>Navigator: pop(resultJson, context)
    Navigator->>Stack: remove caller entry from[NavEntryLocal]
    Stack->>Below: emit(navigation.result, resultJson)
Loading

File-Level Changes

Change Details Files
将 Navigator 调用绑定到具体页面挂载,并明确 pop 的关闭语义。
  • 为 push/pop 增加不可由页面伪造的 PageContext 参数。
  • PageHost 转发自身 context,使宿主可定位调用方所在导航条目和栈。
  • 规定 pop 关闭调用方并向其下方页面发送结果;非栈顶 push 由宿主决定,通常忽略并上报。
compose/src/commonMain/kotlin/app/tinyui/HostInterfaces.kt
compose/src/commonMain/kotlin/app/tinyui/PageHost.kt
docs/native-api.md
更新测试以验证导航请求携带正确挂载身份及 locals。
  • 扩展导航测试 Navigator 实现以记录 from 的页面名和 PageLocal。
  • 新增 push/pop 端到端断言,确认调用方是当前挂载且 locals 可用。
compose/src/commonTest/kotlin/app/tinyui/PageHostTest.kt
同步公共 API/ABI 变更。
  • 更新 klib API 签名,反映 Navigator 方法参数变化。
compose/api/compose.klib.api

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 60a12d0c-ba42-4651-ad8f-71793b1d247a
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codeseerbot codeseerbot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeSeer 审查

结论:批准

摘要

本次改动将 Navigator.push 和 Navigator.pop 的调用方 PageContext 传递到宿主,并在 PageHost 的导航 J4 分发处使用当前挂载的 context。ABI 快照、测试及 native-api.md 也同步了接口变化和新的 pop 语义。给出的代码中未发现由该增量引入的确定性逻辑或资源管理问题。

整个 PR 至 1d9e62b · gpt-5.6-terra

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="compose/src/commonMain/kotlin/app/tinyui/PageHost.kt" line_range="441" />
<code_context>
             "timer.cancel" -> a["cbId"]?.jsonPrimitive?.intOrNull?.let { timers.remove(it)?.cancel() }
-            "navigation.push" -> host.navigator.push(a.str("page") ?: return, a["params"]?.toString() ?: "{}")
-            "navigation.pop" -> host.navigator.pop(a["result"]?.toString())
+            "navigation.push" -> host.navigator.push(a.str("page") ?: return, a["params"]?.toString() ?: "{}", context)
+            "navigation.pop" -> host.navigator.pop(a["result"]?.toString(), context)
             "store.set" -> { val key = a.str("key") ?: return; host.store.setJson(key, a.str("value") ?: "null") }
</code_context>
<issue_to_address>
**Deferred navigation targets the wrong entry**

When a host defers handling a navigation call while the same retained page is recomposed with a different entry in `locals`, `PageHost.send` passes a mutable `PageContext`; if a host defers handling until after `TinyUIPage` updates `context.locals`, `from[NavEntryLocal]` resolves the replacement entry and navigation acts on the wrong page or stack.

Have the host capture the entry from `from` synchronously before deferring navigation, or provide an immutable caller-context snapshot.

Also at `compose/src/commonMain/kotlin/app/tinyui/PageHost.kt:442`.
</issue_to_address>

### Comment 2
<location path="docs/native-api.md" line_range="67" />
<code_context>
+override fun pop(resultJson: String?, from: PageContext) {
+    val entry = from[NavEntryLocal] ?: return
+    val below = entry.stack.remove(entry)          // 关的是调用方,哪怕它不在栈顶
+    if (resultJson != null) below?.pageHost?.emit("navigation.result", """{"result":$resultJson,"from":"${from.name}"}""")
+}
+```
</code_context>
<issue_to_address>
**Navigation results can be dropped**

When the recipient is still mounting when the host emits the result, or it closes before its queued emit runs, `PageHost.emit` only queues an entry; `PageHost.entry` silently returns when `entries` is still null, and scope cancellation also discards queued work. The host gets no indication to retry, so the page below the caller never receives `navigation.result`.

Buffer navigation results until the recipient is mounted and ensure delivery is serialized with closing it.
</issue_to_address>

### Comment 3
<location path="docs/native-api.md" line_range="66-67" />
<code_context>
+
+override fun pop(resultJson: String?, from: PageContext) {
+    val entry = from[NavEntryLocal] ?: return
+    val below = entry.stack.remove(entry)          // 关的是调用方,哪怕它不在栈顶
+    if (resultJson != null) below?.pageHost?.emit("navigation.result", """{"result":$resultJson,"from":"${from.name}"}""")
+}
+```
</code_context>
<issue_to_address>
**Documented pop example does not compile**

When the host follows the documented example with a standard Kotlin mutable list for its stack, `entry.stack.remove(entry)` returns a Boolean, so `below?.pageHost` does not compile and hosts cannot deliver the result to the page below.

Remove the caller while retaining the entry below it, then emit the result through that entry's `PageHost`.

Also at `docs/native-api.md:69`.
</issue_to_address>

Sourcery assessment

Approval pending. 3 findings to address first.

Blocking findings: compose/src/commonMain/kotlin/app/tinyui/PageHost.kt:441, docs/native-api.md:67, docs/native-api.md:67


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread docs/native-api.md Resolved
Comment thread docs/native-api.md Resolved
- PageHost 的 entry() 遇到还没挂载的页面直接返回,宿主在页面被回收重建时 emit 的 navigation.result 会丢
  (与 app-model.md「被回收重建后照样收到」不符);现在先等挂载结束,挂载失败或页面关闭才放弃
- native-api.md §3 的 pop 示例改成能编译的写法,并写明 from[local] 要在调用里同步取(locals 跟着最近一次组合)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@codeseerbot codeseerbot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeSeer 审查

结论:批准(附 1 条建议)

摘要

本次增量为 PageHost 增加了挂载完成屏障,使挂载前到达的 K entry(如 navigation.result)等待挂载结束后再执行,并补充了相应测试和宿主文档。主要风险在于屏障等待期间页面可能已经挂载失败,但等待中的 entry 恢复后没有再次确认失败状态。

增量 1d9e62b..658364b · gpt-5.6-terra

mount 在 entries 赋值之后才抛时,先到的 emit 等挂载结束后仍会调失败页面的引擎(CodeSeer 审查意见)。
测试用桩里只认 probe topic 的 emit 回调 navigation.pop 作为可观察副作用:不修时 pop 到达 Navigator。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@codeseerbot codeseerbot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeSeer 审查

结论:批准

上轮意见:1 条,已处理 1 条,待处理 0 条

摘要

本次增量在等待 mountSettled 后再次检查 failure,避免 mount 已失败时仍执行此前排队的 entry。新增测试覆盖了 mount 在 entries 已初始化后失败、等待中的 emit 不应继续触发导航操作的场景。未发现其他由本次改动引入的问题。

增量 658364b..c042f6f · gpt-5.6-terra

@HarlonWang
HarlonWang merged commit 4691145 into main Oct 28, 2026
5 checks passed
@HarlonWang
HarlonWang deleted the feat/navigator-caller branch October 28, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant