diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml new file mode 100644 index 00000000..f6080fdd --- /dev/null +++ b/.github/workflows/linux-build.yml @@ -0,0 +1,61 @@ +name: Linux x64 build + +on: + push: + branches: + - linux + pull_request: + branches: + - linux + workflow_dispatch: + +permissions: + contents: write + +concurrency: + group: linux-build-${{ github.ref }} + cancel-in-progress: true + +jobs: + linux-x64: + name: Linux x64 AppImage + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - name: Install dependencies + env: + # Upstream lockfile resolved URLs may point at registry.npmmirror.com, + # which newer npm treats as a remote fetch. + npm_config_allow_remote: all + run: npm ci + - run: npm test + - run: npm run typecheck + - name: Package Linux x64 AppImage + run: npm run package:linux:x64 + - name: Verify packaged Harness resources + run: | + test -x dist/linux-unpacked/resources/app/node_modules/node/bin/node + dist/linux-unpacked/resources/app/node_modules/node/bin/node -p 'process.versions.node' + - uses: actions/upload-artifact@v4 + with: + name: dsh-desktop-linux-x64 + path: dist/dsh-desktop-linux-x86_64.AppImage + if-no-files-found: error + - name: Name the release asset for sharing + if: github.event_name == 'push' + run: cp dist/dsh-desktop-linux-x86_64.AppImage dist/dsh-desktop-linux-x64.AppImage + - name: Refresh the rolling pre-release asset + if: github.event_name == 'push' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create linux-rolling --repo "$GITHUB_REPOSITORY" --prerelease \ + --target linux --title "DSH Desktop Linux x64 (rolling)" \ + --notes "Auto-refreshed on every push to the linux branch. Unsigned community build; needs FUSE (or run with --appimage-extract-and-run). Updates arrive whenever the linux branch is synced upstream." 2>/dev/null || true + gh release upload linux-rolling --repo "$GITHUB_REPOSITORY" --clobber dist/dsh-desktop-linux-x64.AppImage diff --git a/.github/workflows/upstream-sync.yml b/.github/workflows/upstream-sync.yml new file mode 100644 index 00000000..e4a9d049 --- /dev/null +++ b/.github/workflows/upstream-sync.yml @@ -0,0 +1,74 @@ +name: Upstream sync check + +# Keeps the `linux` branch tracking dataelement/dsh-desktop:main. When upstream +# moves, the port commits are rebased and re-validated; a clean result is pushed +# and surfaced as a PR in this fork. A failing rebase or test run is the signal +# that new upstream platform work needs porting attention. + +on: + schedule: + - cron: "23 4 * * 2" # Tue 04:23 UTC, weekly + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: upstream-sync + cancel-in-progress: false + +jobs: + sync: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: linux + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Resolve upstream state + id: upstream + run: | + git remote add upstream https://github.com/dataelement/dsh-desktop.git + git fetch upstream main + echo "sha=$(git rev-parse upstream/main)" >> "$GITHUB_OUTPUT" + echo "behind=$(git rev-list --count HEAD..upstream/main)" >> "$GITHUB_OUTPUT" + + - name: Rebase port commits onto upstream/main + if: steps.upstream.outputs.behind != '0' + run: | + git config user.name "upstream-sync[bot]" + git config user.email "upstream-sync[bot]@users.noreply.github.com" + git rebase upstream/main + + - uses: actions/setup-node@v4 + if: steps.upstream.outputs.behind != '0' + with: + node-version: 22 + cache: npm + + - name: Validate after rebase + if: steps.upstream.outputs.behind != '0' + env: + npm_config_allow_remote: all + run: | + npm ci + npm test + npm run typecheck + npm run package:linux:x64 + + - name: Push rebased branch + if: steps.upstream.outputs.behind != '0' + run: git push --force-with-lease origin linux + + - name: Open or refresh the sync PR + if: steps.upstream.outputs.behind != '0' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr list --head linux --base main --state open --json number | grep -q . \ + || gh pr create --head linux --base main \ + --title "upstream sync: rebase linux onto dataelement/dsh-desktop" \ + --body "Automated rebase pass (upstream/main at ${{ steps.upstream.outputs.sha }}). The linux-x64 build workflow carries the artifact." diff --git a/README.es.md b/README.es.md index b0f1e84c..1e4d4fd8 100644 --- a/README.es.md +++ b/README.es.md @@ -91,9 +91,9 @@ open -a "DSH Desktop" --args --safe-mode | macOS Intel | DMG/ZIP firmados y notarizados | Compatible | | Windows x64 | Instalador NSIS firmado | Compatible | | Windows ARM64 | — | No compatible actualmente | -| Linux | — | No compatible actualmente | +| Linux x64 | AppImage comunitaria (compilada desde el código) | Compatible | -Harness incluye dependencias nativas, por lo que cada artefacto se compila en el sistema operativo y la arquitectura correspondientes. +Harness incluye dependencias nativas, por lo que cada artefacto se compila en el sistema operativo y la arquitectura correspondientes. En Linux, compila la AppImage comunitaria sin firmar con `npm run package:linux:x64`; el actualizador integrado sigue siendo exclusivo de macOS/Windows y en Linux se recompila desde un árbol sincronizado aguas arriba. ## Desarrollo y arquitectura diff --git a/README.ja.md b/README.ja.md index d6e87e85..c353231b 100644 --- a/README.ja.md +++ b/README.ja.md @@ -90,9 +90,9 @@ open -a "DSH Desktop" --args --safe-mode | macOS Intel | 署名・公証済み DMG/ZIP | 対応 | | Windows x64 | コード署名済み NSIS インストーラー | 対応 | | Windows ARM64 | — | 未対応 | -| Linux | — | 未対応 | +| Linux x64 | ソースからビルドするコミュニティ版 AppImage | 対応 | -Harness にはターゲット固有のネイティブ依存関係が含まれるため、各リリースは対応する OS とアーキテクチャ上でビルドされます。 +Harness にはターゲット固有のネイティブ依存関係が含まれるため、各リリースは対応する OS とアーキテクチャ上でビルドされます。Linux では `npm run package:linux:x64` で未署名のコミュニティ版 AppImage をソースからビルドできます。アプリ内アップデートは引き続き macOS/Windows のみで、Linux の場合は同期した上流ツリーから再ビルドします。 ## 開発とアーキテクチャ diff --git a/README.md b/README.md index ea88f99a..359f6ff5 100644 --- a/README.md +++ b/README.md @@ -92,9 +92,9 @@ open -a "DSH Desktop" --args --safe-mode | macOS Intel | Signed and notarized DMG/ZIP | Supported | | Windows x64 | Code-signed NSIS installer | Supported | | Windows ARM64 | — | Not currently supported | -| Linux | — | Not currently supported | +| Linux x64 | Community AppImage build (built from source) | Supported | -Harness includes target-native dependencies, so every release artifact is built on the matching operating system and architecture. +Harness includes target-native dependencies, so every release artifact is built on the matching operating system and architecture. On Linux, build the unsigned community AppImage from source with `npm run package:linux:x64`; the in-app updater stays macOS/Windows-only and Linux users rebuild from a synced upstream checkout instead. ## Development and architecture diff --git a/README.pt.md b/README.pt.md index 0e63d38d..49019cab 100644 --- a/README.pt.md +++ b/README.pt.md @@ -91,9 +91,9 @@ open -a "DSH Desktop" --args --safe-mode | macOS Intel | DMG/ZIP assinados e notarizados | Compatível | | Windows x64 | Instalador NSIS assinado | Compatível | | Windows ARM64 | — | Não compatível atualmente | -| Linux | — | Não compatível atualmente | +| Linux x64 | AppImage da comunidade (compilada do código) | Compatível | -O Harness inclui dependências nativas, portanto cada artefato é compilado no sistema operacional e na arquitetura correspondentes. +O Harness inclui dependências nativas, portanto cada artefato é compilado no sistema operacional e na arquitetura correspondentes. No Linux, compile a AppImage da comunidade não assinada com `npm run package:linux:x64`; o atualizador integrado continua exclusivo do macOS/Windows e no Linux rebuilda-se a partir de uma árvore sincronizada de upstream. ## Desenvolvimento e arquitetura diff --git a/README.ru.md b/README.ru.md index c2c520bb..c0f28322 100644 --- a/README.ru.md +++ b/README.ru.md @@ -91,9 +91,9 @@ open -a "DSH Desktop" --args --safe-mode | macOS Intel | Подписанные и нотариально заверенные DMG/ZIP | Поддерживается | | Windows x64 | Подписанный установщик NSIS | Поддерживается | | Windows ARM64 | — | Не поддерживается | -| Linux | — | Не поддерживается | +| Linux x64 | AppImage от сообщества (сборка из исходников) | Поддерживается | -Harness содержит нативные зависимости, поэтому каждый выпуск собирается на соответствующей операционной системе и архитектуре. +Harness содержит нативные зависимости, поэтому каждый выпуск собирается на соответствующей операционной системе и архитектуре. В Linux соберите неподписанное AppImage сообщества из исходников командой `npm run package:linux:x64`; встроенный обновлятор остаётся доступным только для macOS/Windows — в Linux дерево сборки переводится из синхронизированного апстрима. ## Разработка и архитектура diff --git a/README.zh.md b/README.zh.md index a8984976..03250021 100644 --- a/README.zh.md +++ b/README.zh.md @@ -91,9 +91,9 @@ open -a "DSH Desktop" --args --safe-mode | macOS Intel | 已签名并通过公证的 DMG/ZIP | 支持 | | Windows x64 | 已完成代码签名的 NSIS 安装包 | 支持 | | Windows ARM64 | — | 当前不支持 | -| Linux | — | 当前不支持 | +| Linux x64 | 由源码构建的社区 AppImage | 支持 | -Harness 包含目标平台原生依赖,因此每一种正式安装包都在对应操作系统与架构上构建。 +Harness 包含目标平台原生依赖,因此每一种正式安装包都在对应操作系统与架构上构建。在 Linux 上,可以使用 `npm run package:linux:x64` 从源码构建未签名的社区版 AppImage;应用内更新仍仅限 macOS/Windows,Linux 用户改为从同步的上游代码重新构建。 ## 开发与架构 diff --git a/build/app-icon-512.png b/build/app-icon-512.png new file mode 100644 index 00000000..777224e7 Binary files /dev/null and b/build/app-icon-512.png differ diff --git a/docs/development.md b/docs/development.md index 82581af6..2cfa489f 100644 --- a/docs/development.md +++ b/docs/development.md @@ -87,10 +87,31 @@ npm run package:mac:x64 # Windows x64 NSIS installer, on a Windows x64 machine or runner npm run package:win + +# Linux x64 AppImage, on a Linux x64 machine or runner +npm run package:linux:x64 ``` Do not invoke `electron-builder --win` from macOS or Linux for a distributable Windows package. The target verification scripts intentionally reject host/target mismatches. +On Linux, the practical port surface is small: the Harness host already runs from +the bundled Node.js on every non-macOS platform (see `build/harness-node-entry.mjs`), +the POSIX pnpm shim and hostname-profile paths have live Linux branches, and the +mobile tunnel asset map covers linux-x64/linux-arm64. Packaging needs a 512×512 +8-bit RGBA PNG (`build/app-icon-512.png`, committed); regenerating icns/ico with +`scripts/generate-app-icons.mjs` stays macOS-only (`sips`/`iconutil`). AppImage +window association under Wayland uses the packaged `desktopName`. + +Under npm ≥ 12 (Node ≥ 24 toolchains), dependency lifecycle scripts are blocked +until reviewed. This repository carries the reviewed `allowScripts` approvals in +`package.json`; the vendored `@deepseek-ai/dsh-subprocess-local` helper script +(only chmods a macOS-only spawn helper) cannot take a portable `allowScripts` +key because npm keys `file:` dependencies by their resolved absolute URL, so its +script is skipped — harmless on Linux/Windows alike. Lockfile `resolved` URLs +may point at `registry.npmmirror.com`; run installs with +`npm_config_allow_remote=all` (or a project `.npmrc`) instead of rewriting the +lockfile, and prefer reusing the registry that upstream pinned. + For local unsigned development packages, use the corresponding `package:dev:*` command. Before handing off a Windows installer, verify that `resources/app/node_modules/node/bin/node.exe` exists in `win-unpacked` and require the packaged Windows Harness smoke test to pass. Formal release artifacts are built, signed, and published by the tag workflow. A local build or pull-request check is not formal release evidence. diff --git a/package.json b/package.json index e1e39b4a..981c441a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "dsh-desktop", "version": "0.1.1", + "desktopName": "dsh-desktop.desktop", "description": "A cross-platform desktop shell for DeepSeek Harness.", "private": true, "type": "module", @@ -40,7 +41,9 @@ "package:mac": "npm run build && electron-builder --mac --publish never", "package:mac:arm64": "node scripts/verify-target.mjs darwin arm64 && npm run build && electron-builder --mac --arm64 --publish never", "package:mac:x64": "node scripts/verify-target.mjs darwin x64 && npm run build && electron-builder --mac --x64 --publish never", - "package:win": "node scripts/verify-target.mjs win32 x64 && npm run build && electron-builder --win --x64 --publish never" + "package:win": "node scripts/verify-target.mjs win32 x64 && npm run build && electron-builder --win --x64 --publish never", + "package:dev:linux:x64": "node scripts/verify-target.mjs linux x64 && npm run build && electron-builder --linux --x64 --publish never --config electron-builder.dev.cjs", + "package:linux:x64": "node scripts/verify-target.mjs linux x64 && npm run build && electron-builder --linux --x64 --publish never" }, "dependencies": { "@deepseek-ai/cordis": "file:packages/harness-0.1.2-rc.1/npm-vendor/deepseek-ai-cordis-4.0.2.tgz", @@ -409,6 +412,28 @@ "allowToChangeInstallationDirectory": true, "createDesktopShortcut": true, "createStartMenuShortcut": true + }, + "linux": { + "icon": "build/app-icon-512.png", + "category": "Development", + "syncDesktopName": true, + "target": [ + { + "target": "AppImage", + "arch": [ + "x64" + ] + } + ] } + }, + "allowScripts": { + "node-pty@1.2.0-beta.15": true, + "koffi@3.1.5": true, + "esbuild": true, + "node": true, + "electron-winstaller": false, + "protobufjs": false, + "@google/genai": false } } diff --git a/patches/@deepseek-ai+dsh-client-ui-settings-general+0.1.2-rc.1.patch b/patches/@deepseek-ai+dsh-client-ui-settings-general+0.1.2-rc.1.patch new file mode 100644 index 00000000..b8bff359 --- /dev/null +++ b/patches/@deepseek-ai+dsh-client-ui-settings-general+0.1.2-rc.1.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js b/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js +index 0457ae0..dee6238 100644 +--- a/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js ++++ b/node_modules/@deepseek-ai/dsh-client-ui-settings-general/lib/client.js +@@ -25,7 +25,7 @@ + } + //#endregion + //#region \0dsh-css:/private/tmp/dsh-harness-v0.1.2-rc.1/packages/client/ui-settings-general/src/client/SettingsRoot.module.css.mjs +- const css$3 = ".tqoa8q_triggerRow{flex:none;align-items:center;gap:8px;width:calc(100% + 4px);margin:4px -2px;display:flex}.tqoa8q_triggerRow.tqoa8q_railRow{width:36px;margin:8px 0 10px}.tqoa8q_trigger{box-sizing:border-box;cursor:pointer;width:auto;min-width:0;height:42px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:12px;flex:1;align-items:center;gap:8px;margin:0;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.tqoa8q_trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.tqoa8q_trigger.tqoa8q_rail{corner-shape:round;border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.tqoa8q_triggerLabel{white-space:nowrap;overflow:hidden}.tqoa8q_overlay{z-index:1000;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.tqoa8q_mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}.tqoa8q_panel{z-index:1;background:var(--dsw-alias-bg-layer-2);width:800px;max-width:calc(100vw - 48px);height:min(800px,100vh - 48px);box-shadow:var(--dsw-elevation-prominent);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:32px;display:flex;position:relative;overflow:hidden}.tqoa8q_nav{box-sizing:border-box;flex-direction:column;flex:none;gap:18px;width:188px;padding:22px 12px 0;display:flex}.tqoa8q_navTitle{color:var(--dsw-alias-label-primary);padding:0 12px;font-size:16px;font-weight:500;line-height:24px}.tqoa8q_navList{flex-direction:column;gap:4px;display:flex}.tqoa8q_navCell{box-sizing:border-box;cursor:pointer;height:40px;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:9px 16px 9px 12px;font-family:inherit;font-size:14px;font-weight:400;line-height:22px;display:flex}.tqoa8q_navCell:hover{background:var(--dsw-specific-sidebar-nav-item-hover)}.tqoa8q_navCell.tqoa8q_active{background:var(--dsw-specific-sidebar-nav-item-active)}.tqoa8q_navIcon{flex:none}.tqoa8q_navLabel{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.tqoa8q_content{flex-direction:column;flex:1;min-width:0;display:flex}.tqoa8q_header{box-sizing:border-box;flex:none;justify-content:space-between;align-items:flex-start;gap:8px;height:54px;padding:20px 14px 8px 10px;display:flex}.tqoa8q_actions{justify-content:flex-end;align-items:center;gap:8px;min-width:0;margin-left:auto;display:flex}.tqoa8q_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.tqoa8q_close:hover{background:var(--dsw-alias-interactive-bg-hover)}.tqoa8q_options{flex:1;min-height:0;padding:0 24px 24px;overflow-y:auto}.tqoa8q_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}"; ++ const css$3 = ".tqoa8q_triggerRow{flex:none;align-items:center;gap:8px;width:calc(100% + 4px);margin:4px -2px;display:flex}.tqoa8q_triggerRow.tqoa8q_railRow{width:36px;margin:8px 0 10px}.tqoa8q_trigger{box-sizing:border-box;cursor:pointer;width:auto;min-width:0;height:42px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:12px;flex:1;align-items:center;gap:8px;margin:0;padding:0 10px 0 8px;font-family:inherit;font-size:14px;line-height:22px;display:flex;overflow:hidden}.tqoa8q_trigger:hover{background:var(--dsw-alias-interactive-bg-hover)}.tqoa8q_trigger.tqoa8q_rail{corner-shape:round;border-radius:50%;flex:none;justify-content:center;gap:0;width:36px;height:36px;margin:0;padding:0}.tqoa8q_triggerLabel{white-space:nowrap;overflow:hidden}.tqoa8q_overlay{z-index:1000;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.tqoa8q_mask{background:var(--dsw-alias-bg-mask-1);backdrop-filter:var(--dsw-mask-blur);position:absolute;inset:0}.tqoa8q_panel{z-index:1;background:var(--dsw-alias-bg-layer-2);width:800px;max-width:calc(100vw - 48px);height:min(800px,100vh - 48px);height:min(800px,100dvh - 48px);box-shadow:var(--dsw-elevation-prominent);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:32px;display:flex;position:relative;overflow:hidden}.tqoa8q_nav{box-sizing:border-box;flex-direction:column;flex:none;gap:18px;width:188px;min-height:0;padding:22px 12px 0;display:flex;overflow-y:auto;overscroll-behavior:contain}.tqoa8q_navTitle{color:var(--dsw-alias-label-primary);padding:0 12px;font-size:16px;font-weight:500;line-height:24px}.tqoa8q_navList{flex-direction:column;gap:4px;display:flex}.tqoa8q_navCell{box-sizing:border-box;cursor:pointer;height:40px;color:var(--dsw-alias-label-primary);text-align:left;background:0 0;border:none;border-radius:12px;align-items:center;gap:8px;padding:9px 16px 9px 12px;font-family:inherit;font-size:14px;font-weight:400;line-height:22px;display:flex}.tqoa8q_navCell:hover{background:var(--dsw-specific-sidebar-nav-item-hover)}.tqoa8q_navCell.tqoa8q_active{background:var(--dsw-specific-sidebar-nav-item-active)}.tqoa8q_navIcon{flex:none}.tqoa8q_navLabel{white-space:nowrap;text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.tqoa8q_content{flex-direction:column;flex:1;min-width:0;display:flex}.tqoa8q_header{box-sizing:border-box;flex:none;justify-content:space-between;align-items:flex-start;gap:8px;height:54px;padding:20px 14px 8px 10px;display:flex}.tqoa8q_actions{justify-content:flex-end;align-items:center;gap:8px;min-width:0;margin-left:auto;display:flex}.tqoa8q_close{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-primary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:0;display:inline-flex}.tqoa8q_close:hover{background:var(--dsw-alias-interactive-bg-hover)}.tqoa8q_options{flex:1;min-height:0;padding:0 24px 24px;overflow-y:auto}.tqoa8q_hiddenLabel{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}"; + const tagId$3 = "@deepseek-ai/dsh-client-ui-settings-general/SettingsRoot.module.css"; + if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) { + const tag = document.createElement("style"); diff --git a/patches/@deepseek-ai+dsh-client-ui-sidebar+0.1.2-rc.1.patch b/patches/@deepseek-ai+dsh-client-ui-sidebar+0.1.2-rc.1.patch index 019b747c..11bb41d0 100644 --- a/patches/@deepseek-ai+dsh-client-ui-sidebar+0.1.2-rc.1.patch +++ b/patches/@deepseek-ai+dsh-client-ui-sidebar+0.1.2-rc.1.patch @@ -1,17 +1,17 @@ diff --git a/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js b/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js -index 696591c..90c1520 100644 +index 557185e..3206076 100644 --- a/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js +++ b/node_modules/@deepseek-ai/dsh-client-ui-sidebar/lib/client.js -@@ -29,7 +29,7 @@ window.__ModuleLoader__.load({ +@@ -29,7 +29,7 @@ const tag = document.createElement("style"); tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-sidebar"; tag.dataset.pluginCss = tagId; - tag.textContent = css; -+ tag.textContent = css + "[data-dsh-sidebar-root][data-dsh-sidebar-wide=\"true\"]{padding-top:32px}" + (navigator.userAgent.includes("Macintosh") ? "[data-dsh-sidebar-root][data-dsh-sidebar-wide=\"false\"]{padding:46px 22px 6px}" : ""); ++ tag.textContent = css + "[data-dsh-sidebar-root][data-dsh-sidebar-wide=\"true\"]{padding-top:32px}" + "[data-dsh-sidebar-region]{overflow-y:auto;overscroll-behavior:contain}" + (navigator.userAgent.includes("Macintosh") ? "[data-dsh-sidebar-root][data-dsh-sidebar-wide=\"false\"]{padding:46px 22px 6px}" : ""); document.head.appendChild(tag); } var SidebarRoot_module_css_default = { -@@ -150,6 +150,8 @@ window.__ModuleLoader__.load({ +@@ -150,6 +150,8 @@ const buildVersion = localBuildVersion(); return (0, react_jsx_runtime.jsxs)("div", { ref: column, @@ -20,7 +20,16 @@ index 696591c..90c1520 100644 className: clsx(SidebarRoot_module_css_default.root, !wide && SidebarRoot_module_css_default.collapsed, !wide && everWide.current && SidebarRoot_module_css_default.railIn, collapsed && wide && SidebarRoot_module_css_default.fading, !pointerInside && SidebarRoot_module_css_default.quietBars), style: wide ? { width: collapsed ? lastWideWidth.current : width } : void 0, onPointerEnter: () => { -@@ -246,5 +248,6 @@ window.__ModuleLoader__.load({ +@@ -231,6 +233,7 @@ + }) + }), + (0, react_jsx_runtime.jsx)("div", { ++ "data-dsh-sidebar-region": "", + className: SidebarRoot_module_css_default.regionArea, + children: renderSlot("sidebar.workspaces", { + wide, +@@ -245,6 +248,7 @@ + className: SidebarRoot_module_css_default.footerActions, children: renderSlot("sidebar.footer.action", { wide }) }), (0, react_jsx_runtime.jsx)("div", { + "data-dsh-sidebar-settings": "", diff --git a/src/main/index.ts b/src/main/index.ts index d4ea8115..299df381 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -931,7 +931,8 @@ function createWindow(): BrowserWindow { if (process.platform === 'darwin') { window.setWindowButtonVisibility(true) window.setWindowButtonPosition({ x: 12, y: 9 }) - } else if (isWindows) { + } else { + window.setAutoHideMenuBar(true) window.setMenuBarVisibility(false) } window.on('close', (event) => { diff --git a/src/main/runtime/harness-runtime.ts b/src/main/runtime/harness-runtime.ts index 0ea3ca15..60c54d3b 100644 --- a/src/main/runtime/harness-runtime.ts +++ b/src/main/runtime/harness-runtime.ts @@ -383,7 +383,7 @@ export class HarnessRuntime { profile ) const startupTimeoutMs = - this.options.startupTimeoutMs ?? (process.platform === 'win32' ? 120_000 : 45_000) + this.options.startupTimeoutMs ?? (process.platform === 'win32' || process.platform === 'linux' ? 120_000 : 45_000) this.writeLog(`\n[desktop] starting ${new Date().toISOString()}`) this.writeLog(`[desktop] launch directory ${launchDirectory}`) diff --git a/test/lan-mobile-bridge.test.ts b/test/lan-mobile-bridge.test.ts index 0ff951a5..c902875c 100644 --- a/test/lan-mobile-bridge.test.ts +++ b/test/lan-mobile-bridge.test.ts @@ -667,7 +667,7 @@ describe('LAN mobile bridge pairing surface', () => { // cleanup() has removed the real listener. Without a permanent sink that // self-inflicted error becomes an unhandled 'error' event and takes the // whole process down (consumeMux documents the identical crash path). - const upgraded: import('node:net').Socket[] = [] + const upgraded: import('node:stream').Duplex[] = [] const harness = createServer((_request, response) => { response.statusCode = 404 response.end()