Repository navigation
feat(web-shell): enrich browser notifications and open target sessions #11447
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
doudouOUC
merged 8 commits into
QwenLM:main
from
doudouOUC:codex/browser-notification-details
Sep 11, 2026
Merged
Changes from 3 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
2034a0a
feat(web-shell): enrich browser notifications and open target sessions
doudouOUC 462ee98
feat(web-shell): include current prompt in browser notifications
doudouOUC b02ab50
feat(web-shell): configure browser notification branding and defaults
doudouOUC 9056ceb
Merge branch 'main' into codex/browser-notification-details
qwen-code-dev-bot 567912e
fix(web-shell): address browser notification review feedback
doudouOUC bb8ccdb
chore: merge main into browser notification follow-up
doudouOUC 0cf3094
fix(web-shell): address notification follow-up review cases
doudouOUC 321f53b
Merge remote-tracking branch 'origin/main' into codex/browser-notific…
doudouOUC File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
25 changes: 25 additions & 0 deletions
25
docs/design/web-shell/web-shell-browser-notification-branding.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # Configurable browser notification branding | ||
|
|
||
| [English](web-shell-browser-notification-branding.md) | [简体中文](web-shell-browser-notification-branding.zh-CN.md) | ||
|
|
||
| ## Problem and scope | ||
|
|
||
| Browser notifications currently use a fixed QwenCode name and bundled icon. Hosts embedding Web Shell need to supply their own application name and image URL, including an HTTPS CDN URL. This changes notification branding only, not sidebar branding or browser-controlled site attribution. | ||
|
|
||
| ## Configuration and integration | ||
|
|
||
| Expose `browserNotifications?: WebShellBrowserNotificationsOptions` on `WebShellWithProviders` and its `StandaloneWebShell` alias. The options contain optional `appName` and `iconUrl` strings, plus `defaultEnabled?: boolean` (false by default). Missing or whitespace-only branding values fall back independently to QwenCode and the bundled PNG. The title remains application name · session title, or only the application name when there is no session title. Pass the trimmed icon URL directly to Notification.icon; no proxy, preload, authentication headers, or automatic retry is added. The browser fetches the image under its security policies; a failed custom image does not guarantee the default icon is shown. | ||
|
|
||
| Omitting the options preserves the embedded entry's existing behavior without automatic browser notifications. Passing an object, including an empty object, connects the notification provider above the daemon session provider and exposes the existing UI setting. The initial browser-local preference uses defaultEnabled only when no stored choice exists. Explicit stored true or false always wins, including after reload; removing the stored choice restores that instance's initial default. Changing defaultEnabled after mounting does not overwrite the current choice. Permission is never requested automatically, even when the default is on. Changing branding values preserves the mounted session and notification preference. The built-in main.tsx explicitly sets defaultEnabled to true while keeping the default name and icon. Thus the built-in development and installed qwen serve pages start enabled for a fresh browser site, but delivery still requires browser permission and the existing background/unfocused conditions. | ||
|
|
||
| The low-level WebShell component does not gain this prop: its daemon providers are owned by the caller and sit outside its subtree, so an internal notifier could not observe their events. No new CLI option, daemon setting, public daemon protocol, or low-level notification provider API is introduced. Iframe and server-rendered paths continue without browser notifications. | ||
|
|
||
| Notification status and excerpt labels follow the App's resolved UI language, including settings changes. The existing internal settings context synchronizes that language without adding a second locale selection policy. | ||
|
|
||
| ## Click ownership | ||
|
|
||
| Each mounted notification provider owns an internal EventTarget shared with its descendant App through React context. Notification clicks use this target instead of a window-wide broadcast, so only the owning Web Shell navigates. Existing window qwen:open-session events remain available for Markdown links. A notification target that conflicts with the App's current locked workspace is ignored, including a stale notification after the host changes the lock. A target whose owner has unmounted has no active navigation listener. | ||
|
|
||
| ## Validation | ||
|
|
||
| Tests cover public-entry provider order, opt-in versus omitted configuration, default and partial/blank options, CDN URL forwarding, changed options without remounting, preserved permission/off behavior, per-instance notification navigation, locked-workspace rejection, and the existing standalone navigation path. Library builds must include the default image in the published output. Browser verification uses a host fixture importing the public entry with custom branding and a real daemon turn; API capture verifies the name, prompt/reply, and image URL, not OS icon placement or remote CDN availability. Existing user preview services are left running. | ||
25 changes: 25 additions & 0 deletions
25
docs/design/web-shell/web-shell-browser-notification-branding.zh-CN.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| # 浏览器通知品牌配置 | ||
|
|
||
| [English](web-shell-browser-notification-branding.md) | [简体中文](web-shell-browser-notification-branding.zh-CN.md) | ||
|
|
||
| ## 问题与范围 | ||
|
|
||
| 浏览器通知目前固定使用 QwenCode 名称和随包图标。嵌入 Web Shell 的宿主需要传入自己的应用名称和图片 URL,包括 HTTPS CDN 地址。本次只调整通知品牌,不修改侧边栏品牌或浏览器控制的站点来源。 | ||
|
|
||
| ## 配置与接入 | ||
|
|
||
| 在 WebShellWithProviders 及其 StandaloneWebShell 别名上提供 `browserNotifications?: WebShellBrowserNotificationsOptions`。配置包含可选字符串 appName 和 iconUrl,以及默认值为 false 的可选布尔值 defaultEnabled。名称和图标缺失或仅含空白时分别回退到 QwenCode 和随包 PNG。标题保持“应用名称 · 会话标题”,没有会话标题时只显示应用名称。图片 URL 去掉首尾空白后直接传给 Notification.icon,不增加代理、预加载、认证请求头或自动重试。浏览器按自身安全策略获取图片,自定义图片加载失败不保证自动显示默认图标。 | ||
|
|
||
| 不传配置时保留嵌入入口原有行为,不自动接入浏览器通知。传入对象(包括空对象)时,在 daemon session provider 上方接入通知 provider,并展示已有的 UI 设置。浏览器本地偏好仅在没有保存选择时使用 defaultEnabled。已保存的 true 或 false 始终优先,刷新后也保留;删除保存值时恢复该实例的初始默认值。挂载后修改 defaultEnabled 不覆盖当前选择。即使默认开启,也不自动申请浏览器权限。修改品牌配置时保留已挂载会话和通知偏好。内置 main.tsx 显式传入 defaultEnabled 为 true,并继续使用默认名称和图标。因此内置开发页面和安装后的 qwen serve 页面在新的浏览器站点上默认开启,但实际通知仍需要浏览器授权,并符合已有后台或失焦条件。 | ||
|
|
||
| 低层 WebShell 组件不增加该属性:它的 daemon providers 由调用方管理且在组件子树之外,内部通知组件无法观察那些事件。不增加 CLI 参数、daemon 设置、公共 daemon 协议或低层通知 provider API。iframe 和服务端渲染路径继续不使用浏览器通知。 | ||
|
|
||
| 通知状态和摘要标签跟随 App 实际使用的界面语言,包括设置中的语言修改。通过已有内部设置 context 同步语言,不增加第二套语言选择规则。 | ||
|
|
||
| ## 点击归属 | ||
|
|
||
| 每个已挂载的通知 provider 拥有内部 EventTarget,通过 React context 与其后代 App 共享。通知点击使用该 target,替代 window 全局广播,因此只有所属 Web Shell 会导航。已有 window qwen:open-session 事件继续服务 Markdown 链接。通知目标与 App 当前锁定工作区冲突时会被忽略,包括宿主修改锁定工作区后点击旧通知。所属实例卸载后,旧目标不再有活动导航监听。 | ||
|
|
||
| ## 验证 | ||
|
|
||
| 测试覆盖公开入口 provider 层级、传入与省略配置、默认及部分/空白配置、CDN URL 传递、更新配置不重新挂载、权限与关闭偏好保持、实例间通知导航隔离、锁定工作区拒绝及现有独立页面导航路径。组件库构建必须在发布产物中包含默认图片。浏览器验证使用导入公开入口的宿主 fixture,传入自定义品牌并执行真实 daemon 回合;API 采集验证名称、提问/回复及图片 URL,不代表 OS 图标位置或远端 CDN 可用性已验证。已有用户预览服务保持运行。 |
31 changes: 31 additions & 0 deletions
31
docs/design/web-shell/web-shell-browser-notification-details.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # Browser notification details and session navigation | ||
|
|
||
| [English](web-shell-browser-notification-details.md) | [简体中文](web-shell-browser-notification-details.zh-CN.md) | ||
|
|
||
| Following the base implementation in #11398, this enhancement lets users identify the session behind a notification, preview this turn's reply, and click to return to that session. The browser switch, permissions, background trigger, and deduplication rules remain those of the base implementation. No daemon route or public SDK field is added. | ||
|
doudouOUC marked this conversation as resolved.
Resolved
|
||
|
|
||
| ## Content and icon | ||
|
|
||
| The title is QwenCode · session title. If a new session has no title yet or its title has been cleared, use the first line of this turn's request; if neither exists, display QwenCode. The body retains the turn status and includes a plain-text excerpt from the last main-assistant reply for a completed turn, without another model call to summarize it. Extraction matches the terminal promptId exactly and excludes tools, thoughts, background agents, and internal insight messages. Missing text does not fall back to an older turn, and failures do not expose partial replies or error details. | ||
|
|
||
| Extraction runs after the live/replay transcript projection. Titles use the owning session's state and owner-matched connection metadata. The main chat suppresses the submitting user's SSE echo, and the local optimistic user block may lack a promptId. The observer therefore retains a title fallback through the existing admission promptId/label; live pending-start events can also supply it. Labels remain only in the pending-turn memory Map and are removed on consumption, removal, or release of the last observer. | ||
|
|
||
| The body also includes this turn's user prompt, with localized Prompt and Reply labels after the status. Extract the first non-empty main-user block matching the terminal promptId, independently of the session title. When the optimistic block has no promptId, retain the complete existing admission label (or live pending-event text) in the pending Map as a fallback; only the title fallback takes its first line. Do not borrow another turn's prompt or use the session title as the prompt. Failures may include the prompt but never partial replies or error details. Missing prompt or reply text omits that line. | ||
|
|
||
| Titles retain at most 60 Unicode code points, prompt excerpts at most 80, and reply excerpts at most 120, ending in an ellipsis when truncated. Excerpts remove common Markdown formatting, link destinations, and HTML tags, collapse whitespace, and avoid splitting Unicode code points. No extra configuration or model call is introduced. Browser/OS layout may truncate the combined body further. | ||
|
|
||
| Notifications include a package-local copy of the project's existing 128×128 PNG mark, with Vite resolving and bundling its URL. Business text does not append IP addresses, ports, or navigation URLs. Chrome's site attribution and its own macOS logo are controlled by the browser and OS and cannot be replaced by the business icon. Content may appear in the notification center or on the lock screen; the setting description explains that titles, prompts, and reply excerpts are shown. Shared storage continues to record only deduplication hashes, never bodies, titles, or navigation targets. | ||
|
|
||
| ## Clicking to open a session | ||
|
|
||
| The internal owner binding captures sessionId and product context: workspace uses the resolved owner.workspaceCwd, while standalone/live omit internal working directories. The target remains in the notification's in-memory closure. Clicking attempts to focus the original window, passes the explicit target to the existing qwen:open-session entry point, and closes the notification. Navigation is still attempted if the browser refuses focus. | ||
|
|
||
| App reuses the existing session-loading flow for settings, split view, draft context, asynchronous navigation, and failure reporting. Existing session links without explicit context keep their behavior. Malformed or conflicting explicit contexts are rejected instead of falling back to the current or primary workspace. If the target is already the healthy current session, its context matches, and there is no pending draft intent, only reveal chat and close panels to avoid disrupting the next turn with a duplicate load. Split-view notifications open the main chat, and the existing callback updates the address bar after a successful load. | ||
|
|
||
| The original page and main App navigation listener must still be alive. Expired sessions, removed workspaces, and load failures use existing error feedback without creating a session or switching to another workspace. Closed-page delivery, Channel, Service Worker, and Web Push are outside this enhancement. | ||
|
|
||
| ## Validation scope | ||
|
|
||
| Unit tests cover live/replay content projection, admission title fallback, exact-turn and main-assistant filtering, Unicode truncation, failure and empty-content fallbacks, icon parameters, captured targets, cross-workspace and standalone/live navigation, exiting settings and split view, avoiding duplicate current-session loads, legacy links, and focus failure. | ||
|
|
||
| Page verification uses an isolated runtime and Chrome: capture the notification produced after a real submission settles, switch to another session and open settings, then invoke the captured notification's click callback. Check that the original session and URL return, settings exits, and the notification closes. A Notification stub and simulated loss of focus demonstrate browser parameters and navigation, not actual OS banner or icon display. The icon is separately checked for production HTTP availability, PNG signature, and dimensions. Detailed test plans and results are kept locally under .qwen/e2e-tests/. | ||
31 changes: 31 additions & 0 deletions
31
docs/design/web-shell/web-shell-browser-notification-details.zh-CN.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # 浏览器通知内容与会话导航 | ||
|
|
||
| [English](web-shell-browser-notification-details.md) | [简体中文](web-shell-browser-notification-details.zh-CN.md) | ||
|
|
||
| 基础版合入 #11398 后,本次增强让用户直接辨认通知对应的会话、查看本轮回复摘录,并点击返回对应会话。浏览器开关、权限、后台触发和去重规则沿用基础版,不增加 daemon 路由或公共 SDK 字段。 | ||
|
|
||
| ## 内容与图标 | ||
|
|
||
| 标题显示 QwenCode · 会话标题。新会话标题尚未生成或已清空时,使用本轮问题首行;两者均缺失时显示 QwenCode。正文保留回合状态,完成时附本轮最后一段主助手回复的纯文本摘录,不额外调用模型生成摘要。摘录严格匹配终态 promptId,排除工具、思考、后台子代理和内部 insight 消息;无匹配文本时不借用旧轮回复,失败时不展示部分回复或错误详情。 | ||
|
|
||
| 提取发生在 live/replay transcript 投影完成后。标题使用所属 session 的状态及 owner 匹配的 connection 元数据。主聊天会抑制本人 user SSE 回显,本地 optimistic user block 不一定带 promptId,因此观察器通过已有 admission 的 promptId/label 保存标题兜底,live pending 启动事件也可提供该文本。标签仅保留在未结束回合的内存 Map,消费、移除或最后观察者退出时清理。 | ||
|
|
||
| 正文同时包含本轮用户提问,在状态之后使用本地化的“提问”和“回复”标签。独立于会话标题,提取与终态 promptId 匹配的第一段非空主用户文本。本地 optimistic block 没有 promptId 时,在 pending Map 中保留完整的已有 admission label(或 live pending 事件文本)作为兜底,只有标题兜底取其首行。不借用其他轮次的提问,也不将会话标题当作提问。失败时可附提问,但不包含部分回复或错误详情。提问或回复缺失时省略对应行。 | ||
|
|
||
| 标题最多保留 60 个 Unicode 码点,提问摘录最多 80 个,回复摘录最多 120 个,超长时以省略号结尾。摘录移除常见 Markdown 格式、链接地址和 HTML 标记并合并空白,截断不拆开 Unicode 码点。不增加配置或模型调用。浏览器或系统布局可能进一步截短整体正文。 | ||
|
|
||
| 通知携带项目现有 128×128 PNG 标志的本包副本,由 Vite 解析和打包图片 URL。标题与正文不额外拼接 IP、端口或跳转地址;Chrome 自带的站点来源和 macOS 上的 Chrome 标志由浏览器及系统控制,业务图标不能替换它们。通知内容可能显示在通知中心或锁屏上,设置说明告知会展示标题、提问和回复摘录。共享存储仍仅记录去重哈希,不保存正文、标题或导航目标。 | ||
|
|
||
| ## 点击打开会话 | ||
|
|
||
| 内部 owner 绑定时捕获 sessionId 和产品上下文:workspace 使用已解析 owner.workspaceCwd,standalone/live 不携带内部工作目录。目标仅保留在通知的内存闭包。点击时尝试聚焦原窗口,将显式目标传给已有 qwen:open-session 入口,再关闭通知;浏览器拒绝聚焦时仍尝试导航。 | ||
|
|
||
| App 复用现有会话加载流程处理设置页、分屏、草稿上下文、异步导航与失败报告。没有显式上下文的旧会话链接保持原行为;畸形或冲突的显式上下文直接拒绝,不回落到当前或主工作区。目标已经是当前健康会话、上下文一致且没有待分配草稿意图时,只显示聊天并关闭面板,避免重复加载干扰下一轮。分屏通知统一打开主聊天,加载成功后既有回调同步更新地址栏。 | ||
|
|
||
| 范围限于原页面仍打开且主 App 导航监听仍挂载的情形。过期会话、已移除工作区和加载失败复用既有错误反馈,不自动创建会话或切换到另一工作区。关闭页面后的通知、Channel、Service Worker 与 Web Push 不属于本次增强。 | ||
|
|
||
| ## 验证范围 | ||
|
|
||
| 单元测试覆盖 live/replay 内容投影、admission 标题兜底、精确回合及主助手过滤、Unicode 截断、失败与空内容兜底、图标参数、捕获目标、跨工作区及 standalone/live 导航、设置页与分屏退出、同会话避免重复加载、旧链接兼容和聚焦失败。 | ||
|
|
||
| 页面验证使用独立 runtime 与 Chrome:真实提交产生终态后捕获通知,切换到另一会话并打开设置,再调用捕获通知的点击回调,检查原会话及 URL 恢复、设置退出与通知关闭。Notification stub 和模拟失焦只证明浏览器参数与导航链路,不代表 OS 横幅或图标实际展示。图标另外核对生产资源的 HTTP 状态、PNG 签名和尺寸。详细测试计划及结果记录在本地 .qwen/e2e-tests/。 |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.