Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions .github/workflows/build-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,39 @@ on:
required: false
type: number
default: 30
workflow_dispatch:
# Manual build trigger so the fork can produce installers without the
# upstream release tooling (the fork uses v*-fork.* tags that
# release-desktop.yml does not react to).
inputs:
channel:
description: "Release channel (stable or canary)"
required: true
type: choice
options:
- stable
- canary
default: stable
version_suffix:
description: "Version suffix to append (e.g., -canary). Empty for stable."
required: false
type: string
default: ""
electron_builder_config:
description: "Electron builder config file"
required: false
type: string
default: "electron-builder.ts"
artifact_prefix:
description: "Prefix for artifact names"
required: false
type: string
default: "desktop"
artifact_retention_days:
description: "Number of days to retain artifacts"
required: false
type: number
default: 14
Comment on lines +58 to +62
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

artifact_retention_days のデフォルト値が workflow_call (30) と workflow_dispatch (14) で食い違っています

意図的であれば問題ありませんが、手動実行と呼び出し実行でリテンション期間が変わるのは混乱のもとなので、揃えるかコメントで理由を明記しておくとよいです。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-desktop.yml around lines 58 - 62, The default for
the input parameter artifact_retention_days currently differs between the
workflow_call caller (30) and the workflow_dispatch manual trigger (14); update
the workflow to make these consistent or add an inline comment explaining the
intentional difference. Locate the artifact_retention_days declaration and
either change its default to match the other trigger (e.g., set default: 30 or
14) in the .github/workflows/build-desktop.yml or add a clear comment next to
artifact_retention_days referencing workflow_call and workflow_dispatch and
explaining why the retention periods intentionally differ.


jobs:
build-macos:
Expand Down Expand Up @@ -263,3 +296,129 @@ jobs:
path: apps/desktop/release/*-linux.yml
retention-days: ${{ inputs.artifact_retention_days }}
if-no-files-found: error

build-windows:
name: Build - Windows (x64)
runs-on: windows-latest
environment: production

defaults:
run:
# bash (Git Bash) is bundled on windows-latest runners and is used so
# the cross-platform scripts behave like they do on macOS/Linux.
shell: bash

steps:
- name: Enable git long paths
shell: pwsh
run: git config --global core.longpaths true

- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

- name: Setup Bun
id: setup-bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: .bun-version

- name: Cache dependencies
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: |
~/.bun/install/cache
key: ${{ runner.os }}-bun-${{ steps.setup-bun.outputs.bun-revision }}-${{ github.sha }}
restore-keys: |
${{ runner.os }}-bun-${{ steps.setup-bun.outputs.bun-revision }}-

- name: Install dependencies
run: bun install --frozen --ignore-scripts

- name: Install desktop native dependencies
working-directory: apps/desktop
run: bun run install:deps

- name: Set version suffix
if: inputs.version_suffix != ''
working-directory: apps/desktop
run: |
CURRENT_VERSION=$(node -p "require('./package.json').version")
NEW_VERSION="${CURRENT_VERSION}${{ inputs.version_suffix }}"
echo "Setting version to: $NEW_VERSION"
node -e "
const fs = require('fs');
const pkg = require('./package.json');
pkg.version = '$NEW_VERSION';
fs.writeFileSync('./package.json', JSON.stringify(pkg, null, '\t') + '\n');
"
echo "Updated package.json version to $NEW_VERSION"

- name: Clean dev folder
working-directory: apps/desktop
run: bun run clean:dev

- name: Generate file icons
working-directory: apps/desktop
run: bun run generate:icons

- name: Compile app with electron-vite
working-directory: apps/desktop
env:
NEXT_PUBLIC_POSTHOG_KEY: ${{ secrets.NEXT_PUBLIC_POSTHOG_KEY }}
NEXT_PUBLIC_POSTHOG_HOST: ${{ secrets.NEXT_PUBLIC_POSTHOG_HOST }}
GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }}
GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }}
NEXT_PUBLIC_WEB_URL: ${{ secrets.NEXT_PUBLIC_WEB_URL }}
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
NEXT_PUBLIC_DOCS_URL: ${{ secrets.NEXT_PUBLIC_DOCS_URL }}
NEXT_PUBLIC_STREAMS_URL: ${{ secrets.NEXT_PUBLIC_STREAMS_URL }}
NEXT_PUBLIC_ELECTRIC_URL: ${{ secrets.NEXT_PUBLIC_ELECTRIC_URL }}
SENTRY_DSN_DESKTOP: ${{ secrets.SENTRY_DSN_DESKTOP }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
RELAY_URL: ${{ secrets.RELAY_URL }}
SUPERSET_WORKSPACE_NAME: superset
run: bun run compile:app

- name: Build superset-browser-mcp binary
# Bun doesn't auto-run lifecycle hooks, so the prebuild step that emits
# Resources/resources/superset-browser-mcp/superset-browser-mcp.exe
# has to be invoked explicitly.
working-directory: apps/desktop
run: bun run build:browser-mcp

- name: Build Electron app (NSIS)
working-directory: apps/desktop
env:
CSC_IDENTITY_AUTO_DISCOVERY: "false"
run: bun run package -- --publish never --config ${{ inputs.electron_builder_config }} --win

- name: Verify Windows installer + update manifest exist
working-directory: apps/desktop
run: |
ls -la release
test -n "$(ls -1 release/*.exe 2>/dev/null)" || {
echo "::error::No NSIS .exe generated in apps/desktop/release"
exit 1
}
test -f release/latest.yml || {
echo "::error::latest.yml (auto-update manifest) missing"
exit 1
}

- name: Upload NSIS installer artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.artifact_prefix }}-win-x64-nsis
path: |
apps/desktop/release/*.exe
apps/desktop/release/*.exe.blockmap
retention-days: ${{ inputs.artifact_retention_days }}
if-no-files-found: error

- name: Upload Windows auto-update manifest
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ inputs.artifact_prefix }}-win-x64-update-manifest
path: apps/desktop/release/latest.yml
retention-days: ${{ inputs.artifact_retention_days }}
if-no-files-found: error
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ If it runs in a terminal, it runs on Superset

| Requirement | Details |
|:------------|:--------|
| **OS** | macOS (Windows/Linux untested) |
| **OS** | macOS / Windows x64 (preview, this fork) / Linux (untested) |
| **Runtime** | [Bun](https://bun.sh/) v1.0+ |
| **Version Control** | Git 2.20+ |
| **GitHub CLI** | [gh](https://cli.github.com/) |
Expand All @@ -194,7 +194,41 @@ If it runs in a terminal, it runs on Superset

### Quick Start (Pre-built)

**[Download Superset for macOS](https://github.com/superset-sh/superset/releases/latest)**
- **macOS**: [Download Superset for macOS](https://github.com/superset-sh/superset/releases/latest) (upstream)
- **Windows x64** *(preview, this fork)*: [Latest MocA-Love/superset release](https://github.com/MocA-Love/superset/releases/latest) — pick the `Superset-*-x64.exe` NSIS installer. Windows support is a rolling preview; see the [Windows known limitations](#windows-preview-known-limitations) below.

### Windows preview: known limitations

Windows builds are a rolling preview tracked in issue [#273](https://github.com/MocA-Love/superset/issues/273). The app launches and the core workspace / terminal / chat flow works, but the following fork features are not yet on par with macOS:

- **Agent wrapper (`~/.superset/bin` PATH injection)** — not yet generated on Windows. Agents (Claude Code / Codex / Gemini / cursor-agent / Copilot) still work via their global hook configs (`~/.claude/settings.json`, `~/.codex/hooks.json`, `~/.gemini/settings.json`, `~/.cursor/hooks.json`) but Superset-side PATH overrides for managed binaries are skipped.
- **GitHub Copilot CLI project hooks** — `.github/hooks/superset-notify.json` is auto-synced by the bash wrapper on Unix only. On Windows, place the hook file manually until the wrapper is ported to PowerShell.
- **Mica / Acrylic** — applied via `BrowserWindow.setBackgroundMaterial('mica')`; Windows 10 and pre-22H2 Windows 11 show the opaque fallback.
- **Auto-install on quit** — the fork checks GitHub API for updates on Windows like on macOS, but there's no NSIS auto-install-on-quit flow yet; clicking "Install" opens the release page for manual download.
- **Dev-mode deep-link** — `superset://` protocol is registered at install time via NSIS + `app.setAsDefaultProtocolClient`. Dev-mode (running `bun run dev` from source) does not register the protocol on Windows.

### Building Windows locally

```powershell
# Enable long paths (run once per machine; opens a UAC prompt).
git config --global core.longpaths true

git clone https://github.com/MocA-Love/superset.git
cd superset

# Skip lifecycle scripts; native modules are materialized by the postinstall .mjs.
bun install --frozen --ignore-scripts
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does bun install --frozen(without-lockfilesuffix) work in Bun 1.x, or is only--frozen-lockfile supported?

💡 Result:

No, bun install --frozen (without the -lockfile suffix) does not work in Bun 1.x. Only --frozen-lockfile is supported. Official Bun documentation explicitly describes --frozen-lockfile as the flag for reproducible installs that fail if package.json disagrees with bun.lock (sources 2, 4, 14, 18). CLI help and configuration reference it as --frozen-lockfile or frozenLockfile = true in bunfig.toml (sources 14, 18, 19). No official sources mention --frozen alone. All GitHub issues and discussions use --frozen-lockfile exclusively (sources 1, 3, 9, 10, 62, 63). Searches for --frozen without -lockfile yield no supporting evidence.

Citations:


bun install --frozen--frozen-lockfile に修正してください。

Bun の公式ドキュメントによると、bun install --frozen は無効です。正しいフラグは --frozen-lockfile のみが対応しています。短縮形 --frozen では lockfile に従ったインストールが機能せず、通常の install に落ちる可能性があります。README の再現性と正確性のため、line 220 を bun install --frozen-lockfile --ignore-scripts に変更してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 220, Update the README entry that currently uses the
invalid shorthand flag by replacing the command string "bun install --frozen
--ignore-scripts" with the correct flag name so it reads "bun install
--frozen-lockfile --ignore-scripts"; locate the occurrence of the exact command
text in README.md and change only the flag name to --frozen-lockfile to preserve
the rest of the line.


cd apps/desktop
$env:SUPERSET_WORKSPACE_NAME = 'superset'
bun run install:deps
bun run compile:app
bun run build:browser-mcp
bun run package -- --publish never --win
Comment on lines +219 to +227
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the scripts referenced and where browser-mcp output is staged.
fd -t f 'package.json' apps/desktop --max-depth 2 -x cat {}
echo "---"
rg -n "build:browser-mcp|browser-mcp|superset-browser-mcp" apps/desktop -g '!**/node_modules/**' -C2
echo "---"
# Inspect electron-builder config to see if browser-mcp bin is included in extraResources/files
fd -t f 'electron-builder*' apps/desktop --max-depth 2 -x cat {}

Repository: MocA-Love/superset

Length of output: 40617


🌐 Web query:

Bun install frozen-lockfile flag documentation

💡 Result:

The --frozen-lockfile flag for bun install ensures reproducible installs by using the exact package versions specified in bun.lock. If package.json dependencies disagree with the lockfile, Bun exits with an error without updating the lockfile. Usage: bun install --frozen-lockfile This is equivalent to bun ci. Commit bun.lock to version control for CI/CD use. Configuration in bunfig.toml: [install] frozenLockfile = true Note: Some users report issues in monorepos or after lockfile migrations (e.g., binary to text format in Bun 1.2+), often fixed by running bun install first and committing updates. Official docs recommend it for production reproducibility, as in Docker: RUN bun install --frozen-lockfile.

Citations:


bun install --frozen フラグを --frozen-lockfile に修正してください。

220行の bun install --frozen は無効です。正しいフラグは --frozen-lockfile です。このフラグはロックファイルに基づいた再現可能なインストールを強制し、bun.lockとpackage.jsonが不一致の場合はエラーで終了します。

修正前後
- bun install --frozen --ignore-scripts
+ bun install --frozen-lockfile --ignore-scripts

ビルド順序(compile:app → build:browser-mcp → package)は正しく設定されています。package スクリプトが実行される際、自動的に prepackage フックが先に実行され、その中で build:browser-mcp が呼び出されるため、electron-builder は正しく構築されたブラウザーMCP バイナリを入手できます。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 219 - 227, Replace the invalid bun flag usage in the
README: change the command string "bun install --frozen" to "bun install
--frozen-lockfile" so bun enforces installs against bun.lock and fails on
mismatch; update any occurrences of the "bun install --frozen" invocation
accordingly (e.g., the README command block containing "bun install --frozen
--ignore-scripts").

# Output: apps/desktop/release/*.exe + *.exe.blockmap + latest.yml
```

The Windows NSIS build is also available via GitHub Actions → **Build Desktop App** → *Run workflow* on `MocA-Love/superset`, which produces the same artifacts without needing a Windows machine.

### Build from Source

Expand Down
9 changes: 7 additions & 2 deletions apps/desktop/electron-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ const config: Configuration = {
"!**/.DS_Store",
],

// Rebuild native modules for Electron's Node.js version
npmRebuild: true,
// Rebuild native modules for Electron's Node.js version.
// Disabled on Windows — native modules are materialized by install:deps +
// copy:native-modules, and node-gyp fails without Visual Studio Build Tools.
npmRebuild: process.platform !== "win32",

// macOS DMG
// NOTE: dmgbuild 1.2.0 は size = (sum(app files) + 128MB) を割り当てるが、
Expand Down Expand Up @@ -212,6 +214,9 @@ const config: Configuration = {
nsis: {
oneClick: false,
allowToChangeInstallationDirectory: true,
createDesktopShortcut: true,
createStartMenuShortcut: true,
shortcutName: productName,
},
};

Expand Down
7 changes: 7 additions & 0 deletions apps/desktop/electron.vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
defineEnv,
devPath,
htmlEnvTransformPlugin,
stripCrossOriginPlugin,
} from "./vite/helpers";

// override: true ensures .env values take precedence over inherited env vars
Expand Down Expand Up @@ -124,6 +125,11 @@ export default defineConfig({
},
output: {
dir: resolve(devPath, "main"),
// VS Code and other Electron hosts set ELECTRON_RUN_AS_NODE=1 on
// their child process env; leaving it set puts Electron into plain
// Node mode and the app never opens a window. Clear it before any
// require("electron") call — must be the very first statement.
banner: "delete process.env.ELECTRON_RUN_AS_NODE;",
},
external: ["electron", ...mainExternalizedDependencies],
plugins: [sentryPlugin].filter(Boolean),
Expand Down Expand Up @@ -253,6 +259,7 @@ export default defineConfig({
}),
reactPlugin(),
htmlEnvTransformPlugin(),
stripCrossOriginPlugin(),
],

worker: {
Expand Down
17 changes: 14 additions & 3 deletions apps/desktop/scripts/copy-native-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,13 @@ function copyModuleIfSymlink(
console.log(` ${moduleName}: symlink -> replacing with real files`);
console.log(` Real path: ${realPath}`);

// Remove the symlink
rmSync(modulePath);
// Windows: Bun materializes direct deps as directory junctions instead of
// file symlinks; rmSync needs { recursive: true } to remove them.
if (process.platform === "win32") {
rmSync(modulePath, { recursive: true, force: true });
} else {
rmSync(modulePath);
}

// Copy the actual files
cpSync(realPath, modulePath, { recursive: true });
Expand Down Expand Up @@ -275,7 +280,13 @@ function copyDependencyForPackage(
const nestedStats = lstatSync(nestedDependencyPath);
if (nestedStats.isSymbolicLink()) {
const realPath = realpathSync(nestedDependencyPath);
rmSync(nestedDependencyPath);
// Windows: bun materializes these as directory junctions, not
// file symlinks; rmSync needs { recursive: true } to remove them.
if (process.platform === "win32") {
rmSync(nestedDependencyPath, { recursive: true, force: true });
} else {
rmSync(nestedDependencyPath);
}
cpSync(realPath, nestedDependencyPath, {
recursive: true,
});
Expand Down
4 changes: 3 additions & 1 deletion apps/desktop/src/lib/electron-app/factories/app/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ export async function makeAppSetup(
return window;
}

PLATFORM.IS_LINUX && app.disableHardwareAcceleration();
// Disable GPU hardware acceleration on Linux and Windows to prevent black/blank
// screens caused by driver incompatibilities with Chromium's compositor.
(PLATFORM.IS_LINUX || PLATFORM.IS_WINDOWS) && app.disableHardwareAcceleration();

// macOS Sequoia+: occluded window throttling can corrupt GPU compositor layers
if (PLATFORM.IS_MAC) {
Expand Down
12 changes: 10 additions & 2 deletions apps/desktop/src/lib/window-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,17 @@ export function registerRoute(props: {
const url = `http://localhost:${env.DESKTOP_VITE_PORT}/#/`;
console.log("[window-loader] Loading development URL:", url);
props.browserWindow.loadURL(url);
} else if (process.platform === "win32") {
// Production (Windows): file:// breaks ES module dynamic imports
// (code-split route chunks) in Electron on Windows. The custom
// superset-app:// protocol — registered in main/index.ts — serves the
// same renderer files with proper module support so lazy routes load.
const url = "superset-app://app/index.html#/";
console.log("[window-loader] Loading custom protocol URL:", url);
props.browserWindow.loadURL(url);
} else {
// Production: load from file with hash routing
// TanStack Router uses hash-based routing, so we always start at #/
// Production (macOS / Linux): load from file with hash routing.
// TanStack Router uses hash-based routing, so we always start at #/.
console.log("[window-loader] Loading file:", props.htmlFile);
props.browserWindow.loadFile(props.htmlFile, { hash: "/" });
}
Expand Down
Loading
Loading