From 9f1f62a54819bd730fd66c8364e37f9f524e48c7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 17 Sep 2026 18:04:32 +0000 Subject: [PATCH 1/2] Add an in-app Organization browser so new pages stay as tabs. Embed a tabbed iframe browser with an address bar on Organization, open local openXYOS on enter, and trap target=_blank / window.open into in-app tabs via openXYOS embed postMessage plus desktop host hooks. Remove the system new-window action and keep sidecar recover, CORS self-origin, layout heal, and download-source on the top bar. Co-authored-by: XYAI Labs --- CHANGELOG.md | 1 + dashboard/src/locales/en.json | 4 + dashboard/src/locales/zh.json | 4 + .../src/pages/Organization/OrgMiniBrowser.tsx | 131 ++++++++++ .../Organization/Organization.module.less | 28 ++ .../src/pages/Organization/index.test.tsx | 100 ++++++- dashboard/src/pages/Organization/index.tsx | 247 ++++++++++++------ .../src/pages/Organization/orgBrowser.test.ts | 145 ++++++++++ .../src/pages/Organization/orgBrowser.ts | 221 ++++++++++++++++ .../pages/Organization/orgPageWindowTrap.ts | 53 ++++ dashboard/src/utils/desktopExternalLinks.ts | 7 +- dashboard/src/utils/orgBrowserHost.test.ts | 21 ++ dashboard/src/utils/orgBrowserHost.ts | 29 ++ desktop/src/openurl.go | 3 + .../frontend/src/embed/trapWindows.ts | 148 +++++++++++ modules/openxyos/frontend/src/main.tsx | 4 +- modules/openxyos/package.json | 3 +- modules/openxyos/scripts/test-embed-trap.ts | 36 +++ tests/unit/desktop/test_nsis_uninstall.py | 23 +- 19 files changed, 1112 insertions(+), 96 deletions(-) create mode 100644 dashboard/src/pages/Organization/OrgMiniBrowser.tsx create mode 100644 dashboard/src/pages/Organization/orgBrowser.test.ts create mode 100644 dashboard/src/pages/Organization/orgBrowser.ts create mode 100644 dashboard/src/pages/Organization/orgPageWindowTrap.ts create mode 100644 dashboard/src/utils/orgBrowserHost.test.ts create mode 100644 dashboard/src/utils/orgBrowserHost.ts create mode 100644 modules/openxyos/frontend/src/embed/trapWindows.ts create mode 100644 modules/openxyos/scripts/test-embed-trap.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ab3f424e..86752ce7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ ### 变更 +- 组织页改为应用内多标签浏览器(标签栏 + 地址栏 + iframe),进入时打开本机 openXYOS。`target=_blank` / `window.open` 收入应用内标签,不再弹出系统新窗口;去掉「在新窗口打开」。 - 组织页顶部蓝色状态栏增加「下载最新源码」主按钮:点击后先打开系统文件夹选择器,再下载最新 openXYOS 主分支源码。不必先打开「组织OS管理」。成功提示保存路径;管理抽屉仍保留同一操作作为次要入口。 - 对话与工作区共用一份列表:侧栏「对话」进入工作区「对话」标签;点开一条才进入聊天画布。工作区侧栏默认打开「项目」标签。 - 用户可见用词对齐:员工 = 已入编部门;同事 = 同一组织里一起聊的人;专家 / 智能助手 = 尚未入编的帮手。个性化「子智能体」改为「专项助手」。群聊选择「同事或智能助手」。 diff --git a/dashboard/src/locales/en.json b/dashboard/src/locales/en.json index bb0a03fa..bc760a37 100644 --- a/dashboard/src/locales/en.json +++ b/dashboard/src/locales/en.json @@ -97,6 +97,10 @@ "optional": "Optional", "embedTitle": "Organization console", "openSidecar": "Open in new tab", + "browserNewTab": "New tab", + "addressPlaceholder": "Enter a URL", + "addressGo": "Go", + "homeTabTitle": "openXYOS", "loadFailed": "Could not load organization module status", "saveFailed": "Could not update organization module", "actionFailed": "Action failed", diff --git a/dashboard/src/locales/zh.json b/dashboard/src/locales/zh.json index 7f626bc4..49547e91 100644 --- a/dashboard/src/locales/zh.json +++ b/dashboard/src/locales/zh.json @@ -97,6 +97,10 @@ "optional": "可选", "embedTitle": "组织控制台", "openSidecar": "新窗口打开", + "browserNewTab": "新标签", + "addressPlaceholder": "输入网址", + "addressGo": "前往", + "homeTabTitle": "openXYOS", "loadFailed": "无法加载组织模块状态", "saveFailed": "无法更新组织模块", "actionFailed": "操作失败", diff --git a/dashboard/src/pages/Organization/OrgMiniBrowser.tsx b/dashboard/src/pages/Organization/OrgMiniBrowser.tsx new file mode 100644 index 00000000..1f7e97af --- /dev/null +++ b/dashboard/src/pages/Organization/OrgMiniBrowser.tsx @@ -0,0 +1,131 @@ +import { useMemo, type MutableRefObject } from "react"; +import { Button, Input } from "antd"; +import { useTranslation } from "react-i18next"; +import { ChromeTabBar } from "../../components/ChromeTabBar"; +import { + buildEmbedSrc, + iframeSandboxFor, + type OrgBrowserTab, +} from "./orgBrowser"; +import styles from "./Organization.module.less"; + +type OrgMiniBrowserProps = { + tabs: OrgBrowserTab[]; + activeId: string; + addressValue: string; + sidecarOrigin: string; + disabledKeys: string[]; + previewNonce: string; + sidecarUp: boolean; + previewBlank: boolean; + iframeRefs: MutableRefObject>; + onAddressChange: (value: string) => void; + onAddressSubmit: () => void; + onSelectTab: (id: string) => void; + onCloseTab: (id: string) => void; + onNewTab: () => void; + onFrameLoad: () => void; +}; + +export default function OrgMiniBrowser({ + tabs, + activeId, + addressValue, + sidecarOrigin, + disabledKeys, + previewNonce, + sidecarUp, + previewBlank, + iframeRefs, + onAddressChange, + onAddressSubmit, + onSelectTab, + onCloseTab, + onNewTab, + onFrameLoad, +}: OrgMiniBrowserProps) { + const { t } = useTranslation(); + const chromeTabs = useMemo( + () => + tabs.map((tab) => ({ + key: tab.id, + label: tab.title, + tooltip: tab.url, + closable: tabs.length > 1, + })), + [tabs], + ); + + return ( +
+ { + event.preventDefault(); + onCloseTab(key); + }} + onNewTab={onNewTab} + newTabTitle={t("organization.browserNewTab")} + /> +
{ + event.preventDefault(); + onAddressSubmit(); + }} + > + onAddressChange(event.target.value)} + placeholder={t("organization.addressPlaceholder")} + aria-label={t("organization.addressPlaceholder")} + /> + +
+
+ {!sidecarUp && ( +
+ {t("organization.previewOffline")} +
+ )} + {previewBlank && ( +
+ {t("organization.previewBlank")} +
+ )} + {tabs.map((tab) => { + const src = buildEmbedSrc(tab.srcUrl, { + sidecarOrigin, + disabledKeys, + nonce: previewNonce, + }); + const active = tab.id === activeId; + return ( +