diff --git a/CHANGELOG.md b/CHANGELOG.md index 54cd2301..94e858ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ ### 修复 +- Windows 安装把预构建 openXYOS 运行包用 `tar.exe` 解到 `$INSTDIR\openxyos` 与 `$INSTDIR\openxyos-runtime`(带空格的 `Program Files` 也能解全)。缺少 `node\node.exe` 或 `dist\index.html` 时安装失败,不再留下只有 README 的工作目录。首次启动会从完整树复制到 `%LOCALAPPDATA%\FreeOS\openxyos` 并自动拉起边车。组织页「下载最新 openXYOS 源码」走系统文件夹选择器(任意盘符),不再只用手输路径。 - 桌面 / 本地会话不再被 `setup_required` 短路:`GET /setup/status` 之后仍可调用 `POST /auth/local-session`,不会误跳进服务端密码向导。 - 组织页「启动边车」会拉起捆绑的 Node + openXYOS(Windows 用 `node.exe` / `cmd /c`,不再直接 Popen `.bat`),并在右侧预览区嵌入控制台(支持全屏)。openXYOS 在 FreeOS 桌面环境下关闭 `X-Frame-Options`,避免 iframe 空白。安装 / 桌面启动时宿主与 Wails 都会确保边车常开(需 `dist/index.html` 已打包)。 - Windows 打开时不再因 WebView2 `Navigate`/`SetURL` 在 Chromium 未就绪时触发 Go panic。主窗口等 WebView 点火后再跳转;`SetURL` 失败会重试并显示 FreeOS 提示,而不是原始堆栈。设置窗口延后创建,避免两个 WebView2 同时初始化同一用户目录。用户数据目录必须可写,并设置 `WEBVIEW2_USER_DATA_FOLDER`。 diff --git a/dashboard/src/locales/en.json b/dashboard/src/locales/en.json index 7660e674..f99907f0 100644 --- a/dashboard/src/locales/en.json +++ b/dashboard/src/locales/en.json @@ -98,10 +98,12 @@ "fullscreen": "Fullscreen", "exitFullscreen": "Exit fullscreen", "downloadSource": "Download latest openXYOS source", - "downloadSourceHint": "Download the latest openXYOS main-branch source zip from GitHub into a folder you choose, for other deployments.", + "downloadSourceHint": "Pick any folder on any drive with the system dialog. FreeOS then downloads the latest openXYOS main-branch source from GitHub.", "downloadSourceDest": "Save to folder", "downloadSourceDone": "Downloaded to {{path}}", "downloadSourceFailed": "Could not download the source", + "browseDest": "Browse…", + "pickFolderFailed": "Could not open the folder picker", "catalogEnabled": "Shown", "catalogDisabled": "Hidden", "catalogToggleHint": "When off, the module disappears from the openXYOS preview sidebar.", diff --git a/dashboard/src/locales/zh.json b/dashboard/src/locales/zh.json index 48a45841..b6487b3b 100644 --- a/dashboard/src/locales/zh.json +++ b/dashboard/src/locales/zh.json @@ -98,10 +98,12 @@ "fullscreen": "全屏", "exitFullscreen": "退出全屏", "downloadSource": "下载最新 openXYOS 源码", - "downloadSourceHint": "从 GitHub 下载 openXYOS 主分支源码 zip,保存到你选择的文件夹,便于在其他环境部署。", + "downloadSourceHint": "用系统文件夹选择器任选盘符与目录,FreeOS 会从 GitHub 下载最新 openXYOS 主分支源码。", "downloadSourceDest": "保存到文件夹", "downloadSourceDone": "已下载到 {{path}}", "downloadSourceFailed": "下载源码失败", + "browseDest": "浏览…", + "pickFolderFailed": "无法打开文件夹选择器", "catalogEnabled": "已显示", "catalogDisabled": "已隐藏", "catalogToggleHint": "关闭后,该模块会从右侧 openXYOS 预览的侧栏消失。", diff --git a/dashboard/src/pages/Organization/index.tsx b/dashboard/src/pages/Organization/index.tsx index 1b6e33b4..bc85210f 100644 --- a/dashboard/src/pages/Organization/index.tsx +++ b/dashboard/src/pages/Organization/index.tsx @@ -25,7 +25,12 @@ import { } from "../../api/modules/orgModule"; import { formatServerIsoDateTime } from "../../utils/formatMessageTime"; import { useServerTimezone } from "../../hooks/useServerTimezone"; +import { + canPickDesktopFolder, + pickDesktopFolder, +} from "../../utils/desktopFolder"; import { message } from "../../utils/antdMessage"; +import { resolveOpenxyosSourceDest } from "./pickSourceDest"; import styles from "./Organization.module.less"; type ActionKey = "assemble" | "pack" | "loop" | "sidecar" | "produce" | null; @@ -51,6 +56,7 @@ export default function OrganizationPage() { ); const [sourceDest, setSourceDest] = useState(""); const [downloading, setDownloading] = useState(false); + const [pickingFolder, setPickingFolder] = useState(false); const [produceName, setProduceName] = useState(""); const [produceIma, setProduceIma] = useState(""); const [landed, setLanded] = useState | null>(null); @@ -203,12 +209,7 @@ export default function OrganizationPage() { } }; - const downloadSource = async () => { - const dest = sourceDest.trim(); - if (!dest) { - message.error(t("organization.downloadSourceDest")); - return; - } + const saveSourceTo = async (dest: string) => { setDownloading(true); try { const result = await orgModuleApi.downloadSource(dest); @@ -226,6 +227,51 @@ export default function OrganizationPage() { } }; + const browseSourceDest = async () => { + setPickingFolder(true); + try { + const path = await pickDesktopFolder(); + if (path) { + setSourceDest(path); + } else if (!canPickDesktopFolder()) { + message.info(t("organization.pickFolderFailed")); + } + } catch (err) { + message.error( + err instanceof Error ? err.message : t("organization.pickFolderFailed"), + ); + } finally { + setPickingFolder(false); + } + }; + + const downloadSource = async () => { + setPickingFolder(true); + let dest: string | null = null; + try { + dest = await resolveOpenxyosSourceDest({ + canPickNative: canPickDesktopFolder(), + pickNative: pickDesktopFolder, + typedDest: sourceDest, + }); + } catch (err) { + message.error( + err instanceof Error ? err.message : t("organization.pickFolderFailed"), + ); + setPickingFolder(false); + return; + } + setPickingFolder(false); + if (!dest) { + if (!canPickDesktopFolder()) { + message.error(t("organization.downloadSourceDest")); + } + return; + } + setSourceDest(dest); + await saveSourceTo(dest); + }; + const assemble = () => runAction("assemble", async () => { const result: OrgAssembleResult = await orgModuleApi.assemble(); @@ -331,34 +377,8 @@ export default function OrganizationPage() { {(sidecarUp || overview?.enabled) && ( @@ -741,15 +761,23 @@ export default function OrganizationPage() { {t("organization.downloadSourceHint")}

- setSourceDest(event.target.value)} - placeholder={t("organization.downloadSourceDest")} - style={{ minWidth: 260 }} - /> + + setSourceDest(event.target.value)} + placeholder={t("organization.downloadSourceDest")} + readOnly={canPickDesktopFolder()} + /> + +