mirror of
https://github.com/mileswolfallen2/gelectron.git
synced 2026-09-08 12:43:16 +00:00
Compare commits
7
Commits
48a994db09
...
v0.1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
247daa6460 | ||
|
|
40f15bc45a | ||
|
|
c96d6dfc5a | ||
|
|
3ea1fb7daa | ||
|
|
04aedc53c6 | ||
|
|
28d332d210 | ||
|
|
f09c02ec30 |
@@ -0,0 +1 @@
|
|||||||
|
1db4284c-fc55-41be-a7dd-cde2036997da
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
name: Build native binaries
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build ${{ matrix.target }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
- os: windows-latest
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Build N-API addon for ${{ matrix.target }}
|
||||||
|
run: npx napi build --platform --release --target ${{ matrix.target }} --package gelectron-core
|
||||||
|
|
||||||
|
- name: Rename .node file with platform-specific name
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
case "${{ matrix.target }}" in
|
||||||
|
x86_64-apple-darwin) name="gelectron_core.darwin-x64.node" ;;
|
||||||
|
aarch64-apple-darwin) name="gelectron_core.darwin-arm64.node" ;;
|
||||||
|
x86_64-pc-windows-msvc) name="gelectron_core.win32-x64-msvc.node" ;;
|
||||||
|
aarch64-pc-windows-msvc) name="gelectron_core.win32-arm64-msvc.node" ;;
|
||||||
|
x86_64-unknown-linux-gnu) name="gelectron_core.linux-x64-gnu.node" ;;
|
||||||
|
aarch64-unknown-linux-gnu) name="gelectron_core.linux-arm64-gnu.node" ;;
|
||||||
|
esac
|
||||||
|
find crates/gelectron-core -name "*.node" -exec mv {} "$name" \;
|
||||||
|
ls -la *.node
|
||||||
|
|
||||||
|
- name: Upload .node artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: bindings-${{ matrix.target }}
|
||||||
|
path: gelectron_core.*.node
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
name: Runtime ${{ matrix.target }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
platform: darwin
|
||||||
|
arch: arm64
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
platform: darwin
|
||||||
|
arch: x64
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
platform: win32
|
||||||
|
arch: x64
|
||||||
|
- os: windows-latest
|
||||||
|
target: aarch64-pc-windows-msvc
|
||||||
|
platform: win32
|
||||||
|
arch: arm64
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
platform: linux
|
||||||
|
arch: x64
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Rust
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Install Linux system dependencies
|
||||||
|
if: matrix.platform == 'linux'
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libsoup-3.0-dev
|
||||||
|
|
||||||
|
- name: Build gelectron binary
|
||||||
|
run: cargo build --release -p gelectron --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Package release archive
|
||||||
|
run: |
|
||||||
|
bash scripts/make-release.sh \
|
||||||
|
-v "${{ github.ref_name }}" \
|
||||||
|
-p ${{ matrix.platform }} \
|
||||||
|
-a ${{ matrix.arch }} \
|
||||||
|
-b "target/${{ matrix.target }}/release/gelectron" \
|
||||||
|
--no-build
|
||||||
|
|
||||||
|
- name: Build macOS installer (DMG)
|
||||||
|
if: matrix.platform == 'darwin'
|
||||||
|
run: bash scripts/pkg/make-dmg.sh -v "${{ github.ref_name }}" -a ${{ matrix.arch }} -b "target/${{ matrix.target }}/release/gelectron" -o dist
|
||||||
|
|
||||||
|
- name: Build Windows installer (EXE)
|
||||||
|
if: matrix.platform == 'win32' && matrix.arch == 'x64'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
choco install nsis -y --no-progress
|
||||||
|
powershell -File scripts/pkg/make-exe.ps1 -Bin "target/${{ matrix.target }}/release/gelectron.exe" -Compat src/electron -Version "${{ github.ref_name }}" -Arch x64 -Out dist
|
||||||
|
|
||||||
|
- name: Build Linux installer (AppImage)
|
||||||
|
if: matrix.platform == 'linux'
|
||||||
|
run: bash scripts/pkg/make-appimage.sh -v "${{ github.ref_name }}" -b "target/${{ matrix.target }}/release/gelectron" -o dist
|
||||||
|
|
||||||
|
- name: Upload installer artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: installers-${{ matrix.target }}
|
||||||
|
path: |
|
||||||
|
dist/gelectron-*
|
||||||
|
dist/Gelectron-*
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Attach binaries to GitHub Release
|
||||||
|
needs: [build, runtime]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download all binaries
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
pattern: bindings-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Download all installers
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
path: artifacts
|
||||||
|
pattern: installers-*
|
||||||
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: List artifacts
|
||||||
|
run: ls -la artifacts/
|
||||||
|
|
||||||
|
- name: Upload assets to GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: artifacts/**
|
||||||
@@ -7,6 +7,7 @@ node_modules/
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
|
al/
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
*.node
|
*.node
|
||||||
*.dylib
|
*.dylib
|
||||||
|
|||||||
@@ -22,13 +22,78 @@ Electron bundles Chromium — ~300 MB per app with 500+ MB RSS. Gelectron uses t
|
|||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### Prerequisites
|
### Install from npm (recommended)
|
||||||
|
|
||||||
|
The built native binary and the Electron compatibility layer are published to npm. This is the main and easiest way to get Gelectron — no Rust toolchain required:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -g gelectron-core
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs the `gelectron-core` package, which bundles the pre-built native binary and the JS compatibility layer, and automatically pulls in the platform-specific addon for your operating system and CPU architecture (see the [package on npm](https://www.npmjs.com/package/gelectron-core)).
|
||||||
|
|
||||||
|
Once installed, run any Electron app with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gelectron-core /path/to/electron-app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Download a native installer (double-click, no tools needed)
|
||||||
|
|
||||||
|
Every new release tag (pushed as `v*`) is pre-compiled in CI and published as
|
||||||
|
platform-native, double-clickable installers on the [Releases page]:
|
||||||
|
|
||||||
|
| Platform | Installer | What it does |
|
||||||
|
|---|---|---|
|
||||||
|
| macOS | `Gelectron-<version>-arm64.dmg` / `-x64.dmg` | Contains `Install gelectron.pkg`, which installs the runtime + compat layer to `/usr/local/bin` |
|
||||||
|
| Windows | `Gelectron-<version>-x64.exe` | NSIS installer → `C:\Program Files\Gelectron`, adds it to the system PATH, Start Menu + uninstaller |
|
||||||
|
| Linux | `Gelectron-<version>-x86_64.AppImage` | Self-contained bundle (includes Node.js + compat), just run it |
|
||||||
|
|
||||||
|
After installing, `gelectron /path/to/electron-app` works from anywhere.
|
||||||
|
|
||||||
|
> macOS installers are ad-hoc signed (no Developer ID), so on another Mac the
|
||||||
|
> first launch shows a Gatekeeper "unidentified developer" warning — right-click
|
||||||
|
> → Open to run it.
|
||||||
|
|
||||||
|
### Install from GitHub Releases (no npm)
|
||||||
|
|
||||||
|
Every new release tag (pushed as `v*`) is pre-compiled in CI and published as
|
||||||
|
a self-contained installer archive. Install the latest pre-built runtime with
|
||||||
|
a single command — no Rust toolchain, no npm:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# macOS / Linux
|
||||||
|
curl -fsSL https://raw.githubusercontent.com/mileswolfallen2/gelectron/main/scripts/install-release.sh | bash
|
||||||
|
|
||||||
|
# Windows (PowerShell)
|
||||||
|
irm https://raw.githubusercontent.com/mileswolfallen2/gelectron/main/scripts/install-release.ps1 | iex
|
||||||
|
```
|
||||||
|
|
||||||
|
The installer downloads `gelectron-<version>-<platform>-<arch>.tar.gz` (or
|
||||||
|
`.zip`) from the latest GitHub Release and puts the `gelectron` binary plus
|
||||||
|
the JS compatibility layer into `~/.local/bin` (macOS/Linux) or
|
||||||
|
`%LOCALAPPDATA%\gelectron\bin` (Windows). Customize with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/install-release.sh --version v0.1.1 # specific tag
|
||||||
|
bash scripts/install-release.sh --prefix ~/bin # custom location
|
||||||
|
bash scripts/install-release.sh --uninstall # remove
|
||||||
|
```
|
||||||
|
|
||||||
|
Archives can also be downloaded directly from the [Releases page] and
|
||||||
|
unpacked manually — the binary just needs the `compat/` folder next to it.
|
||||||
|
|
||||||
|
> The `gelectron-core` npm package is the recommended distribution channel. Building from source (below) is only needed if you're developing Gelectron itself or want the bleeding-edge version.
|
||||||
|
|
||||||
|
[Releases page]: https://github.com/mileswolfallen2/gelectron/releases
|
||||||
|
|
||||||
|
### Prerequisites (for building from source)
|
||||||
|
|
||||||
- Rust 1.75+ (`rustup.rs`)
|
- Rust 1.75+ (`rustup.rs`)
|
||||||
- Node.js 18+
|
- Node.js 18+
|
||||||
- npm
|
- npm
|
||||||
|
|
||||||
### Build & Run
|
### Build & Run (from source)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/mileswolfallen2/gelectron.git
|
git clone https://github.com/mileswolfallen2/gelectron.git
|
||||||
@@ -316,6 +381,175 @@ cargo build --release -p gelectron-core
|
|||||||
|
|
||||||
The N-API addon compiles to a `.node` file that can be loaded directly into Node.js.
|
The N-API addon compiles to a `.node` file that can be loaded directly into Node.js.
|
||||||
|
|
||||||
|
## Publishing to npm
|
||||||
|
|
||||||
|
Gelectron uses [napi-rs](https://napi.rs/) to produce platform-specific native addons. The main `gelectron` npm package ships platform-specific optional packages so that `npm install gelectron` automatically pulls the right binary for the user's OS.
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- Rust 1.75+ (`rustup.rs`)
|
||||||
|
- Node.js 18+
|
||||||
|
- npm
|
||||||
|
- An [npm account](https://www.npmjs.com/signup) with publish access
|
||||||
|
- Each target platform needs to be built on that platform (or via CI)
|
||||||
|
|
||||||
|
### Step 1: Build the native addon for your platform
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the N-API addon (produces crates/gelectron-core/*.node)
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Or build with debug symbols for development
|
||||||
|
npm run build:debug
|
||||||
|
```
|
||||||
|
|
||||||
|
This compiles the Rust N-API addon (`gelectron-core`) into a `.node` file that Node.js can load.
|
||||||
|
|
||||||
|
### Step 2: Create platform-specific npm packages
|
||||||
|
|
||||||
|
For each platform you want to support, create a directory under `npm/` with a `package.json`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Example for macOS ARM64
|
||||||
|
mkdir -p npm/darwin-arm64
|
||||||
|
cat > npm/darwin-arm64/package.json << 'EOF'
|
||||||
|
{
|
||||||
|
"name": "gelectron-darwin-arm64",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Gelectron native addon for macOS ARM64",
|
||||||
|
"main": "index.darwin-arm64.node",
|
||||||
|
"files": ["index.darwin-arm64.node"],
|
||||||
|
"os": ["darwin"],
|
||||||
|
"cpu": ["arm64"],
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Copy the built .node file
|
||||||
|
cp crates/gelectron-core/gelectron_core.darwin-arm64.node npm/darwin-arm64/
|
||||||
|
```
|
||||||
|
|
||||||
|
Repeat for each platform:
|
||||||
|
|
||||||
|
| Directory | os | cpu |
|
||||||
|
|---|---|---|
|
||||||
|
| `npm/darwin-arm64/` | `darwin` | `arm64` |
|
||||||
|
| `npm/darwin-x64/` | `darwin` | `x64` |
|
||||||
|
| `npm/win32-x64-msvc/` | `win32` | `x64` |
|
||||||
|
| `npm/win32-arm64-msvc/` | `win32` | `arm64` |
|
||||||
|
| `npm/linux-x64-gnu/` | `linux` | `x64` |
|
||||||
|
| `npm/linux-arm64-gnu/` | `linux` | `arm64` |
|
||||||
|
|
||||||
|
### Step 3: Publish platform packages first
|
||||||
|
|
||||||
|
Each platform package must be published before the main package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Publish each platform package
|
||||||
|
npm publish npm/darwin-arm64 --access public
|
||||||
|
npm publish npm/darwin-x64 --access public
|
||||||
|
npm publish npm/win32-x64-msvc --access public
|
||||||
|
# ... etc for each platform
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Prepare and publish the main package
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run prepublish hook (generates napi artifacts metadata)
|
||||||
|
npm run prepublishOnly
|
||||||
|
|
||||||
|
# Publish the main package
|
||||||
|
npm publish --access public
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using napi-rs CLI (recommended)
|
||||||
|
|
||||||
|
The `@napi-rs/cli` handles cross-compilation and artifact management:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install napi-rs CLI globally (if not already installed)
|
||||||
|
npm install -g @napi-rs/cli
|
||||||
|
|
||||||
|
# Build for all configured targets
|
||||||
|
napi build --platform --release
|
||||||
|
|
||||||
|
# Generate artifact metadata for npm publishing
|
||||||
|
napi prepublish -t npm
|
||||||
|
|
||||||
|
# Create a GitHub release with platform binaries
|
||||||
|
napi artifacts
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI/CD Publishing (recommended)
|
||||||
|
|
||||||
|
For multi-platform publishing, use GitHub Actions to build on each OS:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/publish.yml
|
||||||
|
name: Publish to npm
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags: ['v*']
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
- os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
- os: ubuntu-latest
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
- os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: ${{ matrix.target }}
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
- run: npm ci
|
||||||
|
- run: napi build --platform --release --target ${{ matrix.target }}
|
||||||
|
- run: napi prepublish -t npm
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: bindings-${{ matrix.target }}
|
||||||
|
path: npm/
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/download-artifact@v4
|
||||||
|
- run: npm publish --access public
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Quick publish (single platform)
|
||||||
|
|
||||||
|
If you only need to publish for your current platform:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Preview what will be published
|
||||||
|
npm pack --dry-run
|
||||||
|
|
||||||
|
# Publish
|
||||||
|
npm run prepublishOnly
|
||||||
|
npm publish --access public
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Tip:** Use `npm pack` to create a tarball locally and inspect it before publishing. Run `npm pack` and then `tar -tzf gelectron-0.1.0.tgz` to verify the contents.
|
||||||
|
|
||||||
## Testing with OmniEmu2.0
|
## Testing with OmniEmu2.0
|
||||||
|
|
||||||
OmniEmu2.0 is a full Electron app used to validate Gelectron compatibility:
|
OmniEmu2.0 is a full Electron app used to validate Gelectron compatibility:
|
||||||
|
|||||||
@@ -2,5 +2,8 @@
|
|||||||
"name": "gelectron-benchmark",
|
"name": "gelectron-benchmark",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "Simple web benchmark for comparing Electron vs Gelectron",
|
"description": "Simple web benchmark for comparing Electron vs Gelectron",
|
||||||
"main": "main.js"
|
"main": "main.js",
|
||||||
|
"dependencies": {
|
||||||
|
"electron": "^43.4.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"timestamp": "2026-07-29T03:22:15Z",
|
|
||||||
"runs": 10,
|
|
||||||
"platform": "Darwin arm64",
|
|
||||||
"electron": {
|
|
||||||
"version": "v43.2.0",
|
|
||||||
"avg_startup_s": 5.671,
|
|
||||||
"min_startup_s": 5.668,
|
|
||||||
"max_startup_s": 5.674,
|
|
||||||
"avg_memory_mb": 585.9,
|
|
||||||
"min_memory_mb": 583.3,
|
|
||||||
"max_memory_mb": 587.4,
|
|
||||||
"runtime_size_mb": 296,
|
|
||||||
"raw_times": [5.670,5.672,5.674,5.672,5.671,5.668,5.670,5.668,5.673,5.672],
|
|
||||||
"raw_memory": [583.7,586.5,583.3,587.1,586.5,587.4,586.5,587.1,584.1,586.3]
|
|
||||||
},
|
|
||||||
"gelectron": {
|
|
||||||
"avg_startup_s": 5.601,
|
|
||||||
"min_startup_s": 5.598,
|
|
||||||
"max_startup_s": 5.604,
|
|
||||||
"avg_memory_mb": 131.4,
|
|
||||||
"min_memory_mb": 131.0,
|
|
||||||
"max_memory_mb": 131.7,
|
|
||||||
"runtime_size_mb": 3,
|
|
||||||
"raw_times": [5.600,5.601,5.602,5.602,5.598,5.600,5.601,5.602,5.602,5.604],
|
|
||||||
"raw_memory": [131.6,131.7,131.3,131.5,131.3,131.7,131.0,131.5,131.3,131.5]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"timestamp": "2026-08-14T21:54:50Z",
|
||||||
|
"runs": 10,
|
||||||
|
"platform": "Darwin arm64",
|
||||||
|
"electron": {
|
||||||
|
"version": "v43.2.0",
|
||||||
|
"avg_startup_s": 5.670,
|
||||||
|
"min_startup_s": 5.651,
|
||||||
|
"max_startup_s": 5.676,
|
||||||
|
"avg_memory_mb": 586.3,
|
||||||
|
"min_memory_mb": 574.0,
|
||||||
|
"max_memory_mb": 590.7,
|
||||||
|
"runtime_size_mb": 0,
|
||||||
|
"raw_times": [5.651,5.676,5.663,5.673,5.675,5.672,5.672,5.672,5.670,5.673],
|
||||||
|
"raw_memory": [584.1,590.5,574.0,589.4,590.7,589.5,589.1,586.9,588.9,580.3]
|
||||||
|
},
|
||||||
|
"gelectron": {
|
||||||
|
"avg_startup_s": 5.603,
|
||||||
|
"min_startup_s": 5.601,
|
||||||
|
"max_startup_s": 5.606,
|
||||||
|
"avg_memory_mb": 142.6,
|
||||||
|
"min_memory_mb": 142.3,
|
||||||
|
"max_memory_mb": 142.9,
|
||||||
|
"runtime_size_mb": 3,
|
||||||
|
"raw_times": [5.606,5.601,5.604,5.604,5.605,5.601,5.604,5.604,5.603,5.601],
|
||||||
|
"raw_memory": [142.8,142.8,142.5,142.5,142.9,142.7,142.6,142.3,142.8,142.5]
|
||||||
|
}
|
||||||
|
}
|
||||||
+104
-105
@@ -11,6 +11,13 @@ use std::sync::mpsc;
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
// Heartbeat interval for the event loop. We avoid ControlFlow::Poll (which
|
||||||
|
// busy-spins at 100% CPU when idle) and instead wake the loop on a fixed
|
||||||
|
// cadence to drain IPC channels, while still responding immediately to real
|
||||||
|
// window events.
|
||||||
|
const POLL_INTERVAL: Duration = Duration::from_millis(16);
|
||||||
use tao::event::{Event, StartCause, WindowEvent};
|
use tao::event::{Event, StartCause, WindowEvent};
|
||||||
use tao::event_loop::{ControlFlow, EventLoopBuilder};
|
use tao::event_loop::{ControlFlow, EventLoopBuilder};
|
||||||
use tao::window::{Fullscreen, WindowBuilder, WindowId};
|
use tao::window::{Fullscreen, WindowBuilder, WindowId};
|
||||||
@@ -236,7 +243,7 @@ struct NotificationOpts {
|
|||||||
struct WindowPair {
|
struct WindowPair {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
window: tao::window::Window,
|
window: tao::window::Window,
|
||||||
webview: WebView,
|
webview: Option<WebView>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AppState {
|
struct AppState {
|
||||||
@@ -548,7 +555,7 @@ require('{}');
|
|||||||
}
|
}
|
||||||
|
|
||||||
event_loop.run(move |event, event_loop_target, control_flow| {
|
event_loop.run(move |event, event_loop_target, control_flow| {
|
||||||
*control_flow = ControlFlow::Poll;
|
*control_flow = ControlFlow::WaitUntil(Instant::now() + POLL_INTERVAL);
|
||||||
let mut st = state.borrow_mut();
|
let mut st = state.borrow_mut();
|
||||||
|
|
||||||
if st.node_exited.load(Ordering::SeqCst) {
|
if st.node_exited.load(Ordering::SeqCst) {
|
||||||
@@ -559,7 +566,7 @@ require('{}');
|
|||||||
}
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::NewEvents(StartCause::Poll) => {
|
Event::NewEvents(StartCause::Poll | StartCause::ResumeTimeReached { .. }) => {
|
||||||
// Drain async responses from background threads (dialogs, clipboard, etc.)
|
// Drain async responses from background threads (dialogs, clipboard, etc.)
|
||||||
while let Ok((request_id, result)) = response_rx.try_recv() {
|
while let Ok((request_id, result)) = response_rx.try_recv() {
|
||||||
st.send_to_node(&ToNode::Response {
|
st.send_to_node(&ToNode::Response {
|
||||||
@@ -650,22 +657,24 @@ window.__gelectron_run_main(`{}`);
|
|||||||
}
|
}
|
||||||
|
|
||||||
event_loop.run(move |event, event_loop_target, control_flow| {
|
event_loop.run(move |event, event_loop_target, control_flow| {
|
||||||
*control_flow = ControlFlow::Poll;
|
*control_flow = ControlFlow::WaitUntil(Instant::now() + POLL_INTERVAL);
|
||||||
let mut st = state.borrow_mut();
|
let mut st = state.borrow_mut();
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::NewEvents(StartCause::Poll) => {
|
Event::NewEvents(StartCause::Poll | StartCause::ResumeTimeReached { .. }) => {
|
||||||
// Drain async responses from background threads (dialogs, clipboard, etc.)
|
// Drain async responses from background threads (dialogs, clipboard, etc.)
|
||||||
while let Ok((request_id, result)) = response_rx.try_recv() {
|
while let Ok((request_id, result)) = response_rx.try_recv() {
|
||||||
if let Some(pair) = st.windows.get(&1u32) {
|
if let Some(pair) = st.windows.get(&1u32) {
|
||||||
|
if let Some(webview) = &pair.webview {
|
||||||
let js = format!(
|
let js = format!(
|
||||||
"window.__gelectron_response('{}', {});",
|
"window.__gelectron_response('{}', {});",
|
||||||
request_id,
|
request_id,
|
||||||
serde_json::to_string(&result).unwrap_or_default()
|
serde_json::to_string(&result).unwrap_or_default()
|
||||||
);
|
);
|
||||||
let _ = pair.webview.evaluate_script(&js);
|
let _ = webview.evaluate_script(&js);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Drain IPC messages from webview → forward back into the same webview
|
// Drain IPC messages from webview → forward back into the same webview
|
||||||
while let Ok(msg) = ipc_rx.try_recv() {
|
while let Ok(msg) = ipc_rx.try_recv() {
|
||||||
@@ -690,9 +699,9 @@ window.__gelectron_run_main(`{}`);
|
|||||||
serde_json::json!({"__from_node":true,"type":"notification-event","id":id,"event":event,"action_index":action_index,"action":action,"reply":reply})
|
serde_json::json!({"__from_node":true,"type":"notification-event","id":id,"event":event,"action_index":action_index,"action":action,"reply":reply})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let _ = pair.webview.evaluate_script(
|
let _ = pair.webview.as_ref().map(|wv| wv.evaluate_script(
|
||||||
&format!("window.postMessage({},'*');", serde_json::to_string(&js_msg).unwrap())
|
&format!("window.postMessage({},'*');", serde_json::to_string(&js_msg).unwrap())
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -719,7 +728,7 @@ window.__gelectron_run_main(`{}`);
|
|||||||
let js = serde_json::to_string(&serde_json::json!({
|
let js = serde_json::to_string(&serde_json::json!({
|
||||||
"__from_node": true, "type": "window-closed", "id": id,
|
"__from_node": true, "type": "window-closed", "id": id,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
let _ = pair.webview.evaluate_script(&format!("window.postMessage({},'*');", js));
|
let _ = pair.webview.as_ref().map(|wv| wv.evaluate_script(&format!("window.postMessage({},'*');", js)));
|
||||||
}
|
}
|
||||||
st.windows.remove(&id);
|
st.windows.remove(&id);
|
||||||
st.window_wids.remove(&window_id);
|
st.window_wids.remove(&window_id);
|
||||||
@@ -733,7 +742,7 @@ window.__gelectron_run_main(`{}`);
|
|||||||
let js = serde_json::to_string(&serde_json::json!({
|
let js = serde_json::to_string(&serde_json::json!({
|
||||||
"__from_node": true, "type": "window-focus", "id": id,
|
"__from_node": true, "type": "window-focus", "id": id,
|
||||||
})).unwrap();
|
})).unwrap();
|
||||||
let _ = pair.webview.evaluate_script(&format!("window.postMessage({},'*');", js));
|
let _ = pair.webview.as_ref().map(|wv| wv.evaluate_script(&format!("window.postMessage({},'*');", js)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -742,6 +751,47 @@ window.__gelectron_run_main(`{}`);
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_webview(
|
||||||
|
window: &tao::window::Window,
|
||||||
|
url: &str,
|
||||||
|
init: &str,
|
||||||
|
id: u32,
|
||||||
|
ipc_tx: &mpsc::Sender<ToNode>,
|
||||||
|
to_rust_tx: Option<&Arc<mpsc::Sender<ToRust>>>,
|
||||||
|
) -> wry::Result<WebView> {
|
||||||
|
let ipc_tx_clone = ipc_tx.clone();
|
||||||
|
let to_rust_tx_clone = to_rust_tx.cloned();
|
||||||
|
let wid_for_ipc = id;
|
||||||
|
WebViewBuilder::new()
|
||||||
|
.with_url(url)
|
||||||
|
.with_initialization_script(init)
|
||||||
|
.with_devtools(false)
|
||||||
|
.with_ipc_handler(move |req| {
|
||||||
|
let body = req.body().to_string();
|
||||||
|
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&body) {
|
||||||
|
// Try parsing as a ToRust command (load-file, load-url, etc.)
|
||||||
|
if let Some(ref tx) = to_rust_tx_clone {
|
||||||
|
if let Ok(cmd) = serde_json::from_value::<ToRust>(val.clone()) {
|
||||||
|
let _ = tx.send(cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fall back to ipc-send handling
|
||||||
|
let msg_type = val.get("type").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
|
if msg_type == "ipc-send" {
|
||||||
|
let channel = val.get("channel").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
|
let args = val.get("args").cloned().unwrap_or(serde_json::Value::Null);
|
||||||
|
let _ = ipc_tx_clone.send(ToNode::IpcMessage { id: wid_for_ipc, channel: channel.to_string(), data: args });
|
||||||
|
} else if msg_type == "quit" {
|
||||||
|
if let Some(ref tx) = to_rust_tx_clone {
|
||||||
|
let _ = tx.send(ToRust::Quit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build(window)
|
||||||
|
}
|
||||||
|
|
||||||
fn create_initial_webview_window(
|
fn create_initial_webview_window(
|
||||||
event_loop_target: &tao::event_loop::EventLoopWindowTarget<()>,
|
event_loop_target: &tao::event_loop::EventLoopWindowTarget<()>,
|
||||||
state: &Rc<RefCell<AppState>>,
|
state: &Rc<RefCell<AppState>>,
|
||||||
@@ -783,7 +833,7 @@ fn create_initial_webview_window(
|
|||||||
Ok(webview) => {
|
Ok(webview) => {
|
||||||
let wid = window.id();
|
let wid = window.id();
|
||||||
let mut st = state.borrow_mut();
|
let mut st = state.borrow_mut();
|
||||||
st.windows.insert(window_id, WindowPair { window, webview });
|
st.windows.insert(window_id, WindowPair { window, webview: Some(webview) });
|
||||||
st.window_wids.insert(wid, window_id);
|
st.window_wids.insert(wid, window_id);
|
||||||
log::info!("Initial WebView window created (running compat layer)");
|
log::info!("Initial WebView window created (running compat layer)");
|
||||||
}
|
}
|
||||||
@@ -1459,58 +1509,32 @@ fn handle_to_rust(
|
|||||||
Ok(window) => {
|
Ok(window) => {
|
||||||
let url = options.url.unwrap_or_else(|| "about:blank".into());
|
let url = options.url.unwrap_or_else(|| "about:blank".into());
|
||||||
log::info!("Creating window {} - '{}'", id, url);
|
log::info!("Creating window {} - '{}'", id, url);
|
||||||
let ipc_tx_clone = ipc_tx.clone();
|
|
||||||
let to_rust_tx_clone = to_rust_tx.cloned();
|
|
||||||
let wid_for_ipc = id;
|
|
||||||
let init = st.bundle_js.clone().unwrap_or_else(|| preload_script());
|
let init = st.bundle_js.clone().unwrap_or_else(|| preload_script());
|
||||||
match WebViewBuilder::new()
|
// Build the WebView immediately (about:blank). Init scripts
|
||||||
.with_url(&url)
|
// and the ipc message handler are registered at build time
|
||||||
.with_initialization_script(&init)
|
// and survive subsequent in-place navigations (LoadUrl/LoadFile).
|
||||||
.with_devtools(false)
|
let webview = match create_webview(&window, &url, &init, id, ipc_tx, to_rust_tx) {
|
||||||
.with_ipc_handler(move |req| {
|
Ok(wv) => Some(wv),
|
||||||
let body = req.body().to_string();
|
Err(e) => {
|
||||||
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&body) {
|
log::error!("WebView error: {}", e);
|
||||||
// Try parsing as a ToRust command (load-file, load-url, etc.)
|
None
|
||||||
if let Some(ref tx) = to_rust_tx_clone {
|
|
||||||
if let Ok(cmd) = serde_json::from_value::<ToRust>(val.clone()) {
|
|
||||||
let _ = tx.send(cmd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Fall back to ipc-send handling
|
|
||||||
let msg_type = val.get("type").and_then(|v| v.as_str()).unwrap_or("");
|
|
||||||
if msg_type == "ipc-send" {
|
|
||||||
let channel = val.get("channel").and_then(|v| v.as_str()).unwrap_or("");
|
|
||||||
let args = val.get("args").cloned().unwrap_or(serde_json::Value::Null);
|
|
||||||
let _ = ipc_tx_clone.send(ToNode::IpcMessage { id: wid_for_ipc, channel: channel.to_string(), data: args });
|
|
||||||
} else if msg_type == "quit" {
|
|
||||||
if let Some(ref tx) = to_rust_tx_clone {
|
|
||||||
let _ = tx.send(ToRust::Quit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build(&window)
|
|
||||||
{
|
|
||||||
Ok(webview) => {
|
|
||||||
let wid = window.id();
|
|
||||||
if let Some(icon) = options
|
|
||||||
.icon
|
|
||||||
.as_deref()
|
|
||||||
.and_then(decode_base64_icon)
|
|
||||||
{
|
|
||||||
st.app_icon = Some(icon.clone());
|
|
||||||
apply_dock_icon(&icon);
|
|
||||||
apply_window_icon(&window, &icon);
|
|
||||||
} else if let Some(icon) = st.app_icon.clone() {
|
|
||||||
apply_window_icon(&window, &icon);
|
|
||||||
}
|
|
||||||
st.windows.insert(id, WindowPair { window, webview });
|
|
||||||
st.window_wids.insert(wid, id);
|
|
||||||
log::info!("Window {} ready", id);
|
|
||||||
}
|
}
|
||||||
Err(e) => log::error!("WebView error: {}", e),
|
};
|
||||||
|
let wid = window.id();
|
||||||
|
if let Some(icon) = options
|
||||||
|
.icon
|
||||||
|
.as_deref()
|
||||||
|
.and_then(decode_base64_icon)
|
||||||
|
{
|
||||||
|
st.app_icon = Some(icon.clone());
|
||||||
|
apply_dock_icon(&icon);
|
||||||
|
apply_window_icon(&window, &icon);
|
||||||
|
} else if let Some(icon) = st.app_icon.clone() {
|
||||||
|
apply_window_icon(&window, &icon);
|
||||||
}
|
}
|
||||||
|
st.windows.insert(id, WindowPair { window, webview });
|
||||||
|
st.window_wids.insert(wid, id);
|
||||||
|
log::info!("Window {} ready", id);
|
||||||
}
|
}
|
||||||
Err(e) => log::error!("Window error: {}", e),
|
Err(e) => log::error!("Window error: {}", e),
|
||||||
}
|
}
|
||||||
@@ -1518,10 +1542,12 @@ fn handle_to_rust(
|
|||||||
ToRust::LoadUrl { id, url } => {
|
ToRust::LoadUrl { id, url } => {
|
||||||
log::info!("Loading url in window {}: {}", id, url);
|
log::info!("Loading url in window {}: {}", id, url);
|
||||||
if let Some(pair) = st.windows.get(&id) {
|
if let Some(pair) = st.windows.get(&id) {
|
||||||
let _ = pair.webview.evaluate_script(&format!(
|
if let Some(webview) = &pair.webview {
|
||||||
"window.location.replace({});",
|
let _ = webview.evaluate_script(&format!(
|
||||||
serde_json::to_string(&url).unwrap()
|
"window.location.replace({});",
|
||||||
));
|
serde_json::to_string(&url).unwrap()
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToRust::LoadFile { id, path } => {
|
ToRust::LoadFile { id, path } => {
|
||||||
@@ -1529,44 +1555,17 @@ fn handle_to_rust(
|
|||||||
.map(|u| u.to_string())
|
.map(|u| u.to_string())
|
||||||
.unwrap_or_else(|_| "about:blank".into());
|
.unwrap_or_else(|_| "about:blank".into());
|
||||||
log::info!("Loading file in window {}: {}", id, url);
|
log::info!("Loading file in window {}: {}", id, url);
|
||||||
let init = st.bundle_js.clone().unwrap_or_else(|| preload_script());
|
// Navigate in-place instead of rebuilding the WebView. Rebuilding on
|
||||||
if let Some(pair) = st.windows.get_mut(&id) {
|
// macOS creates a stray 500x500 NSWindow artifact when the target
|
||||||
let window = &pair.window;
|
// window is still hidden (the vanilla app loads its splash this way).
|
||||||
let ipc_tx_clone = ipc_tx.clone();
|
// The WKUserScript init script re-runs on navigation, so window.gelectron
|
||||||
let to_rust_tx_clone = to_rust_tx.cloned();
|
// (and any bundle) is re-established on the new document.
|
||||||
let wid_for_ipc = id;
|
if let Some(pair) = st.windows.get(&id) {
|
||||||
match WebViewBuilder::new()
|
if let Some(webview) = &pair.webview {
|
||||||
.with_url(&url)
|
let _ = webview.evaluate_script(&format!(
|
||||||
.with_initialization_script(&init)
|
"window.location.replace({});",
|
||||||
.with_devtools(false)
|
serde_json::to_string(&url).unwrap()
|
||||||
.with_ipc_handler(move |req| {
|
));
|
||||||
let body = req.body().to_string();
|
|
||||||
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&body) {
|
|
||||||
if let Some(ref tx) = to_rust_tx_clone {
|
|
||||||
if let Ok(cmd) = serde_json::from_value::<ToRust>(val.clone()) {
|
|
||||||
let _ = tx.send(cmd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let msg_type = val.get("type").and_then(|v| v.as_str()).unwrap_or("");
|
|
||||||
if msg_type == "ipc-send" {
|
|
||||||
let channel = val.get("channel").and_then(|v| v.as_str()).unwrap_or("");
|
|
||||||
let args = val.get("args").cloned().unwrap_or(serde_json::Value::Null);
|
|
||||||
let _ = ipc_tx_clone.send(ToNode::IpcMessage { id: wid_for_ipc, channel: channel.to_string(), data: args });
|
|
||||||
} else if msg_type == "quit" {
|
|
||||||
if let Some(ref tx) = to_rust_tx_clone {
|
|
||||||
let _ = tx.send(ToRust::Quit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.build(window)
|
|
||||||
{
|
|
||||||
Ok(webview) => {
|
|
||||||
pair.webview = webview;
|
|
||||||
log::info!("WebView rebuilt for window {}", id);
|
|
||||||
}
|
|
||||||
Err(e) => log::error!("WebView rebuild error: {}", e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1584,12 +1583,12 @@ fn handle_to_rust(
|
|||||||
if let Some(pair) = st.windows.get(&id) {
|
if let Some(pair) = st.windows.get(&id) {
|
||||||
let msg = serde_json::json!({"__from_node":true,"channel":channel,"data":data});
|
let msg = serde_json::json!({"__from_node":true,"channel":channel,"data":data});
|
||||||
let js = format!("window.postMessage({},'*');", serde_json::to_string(&msg).unwrap());
|
let js = format!("window.postMessage({},'*');", serde_json::to_string(&msg).unwrap());
|
||||||
let _ = pair.webview.evaluate_script(&js);
|
let _ = pair.webview.as_ref().map(|wv| wv.evaluate_script(&js));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToRust::EvalJs { id, script } => {
|
ToRust::EvalJs { id, script } => {
|
||||||
if let Some(pair) = st.windows.get(&id) {
|
if let Some(pair) = st.windows.get(&id) {
|
||||||
let _ = pair.webview.evaluate_script(&script);
|
let _ = pair.webview.as_ref().map(|wv| wv.evaluate_script(&script));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToRust::Quit => {
|
ToRust::Quit => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "gelectron-darwin-arm64",
|
"name": "gelectron-darwin-arm64",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"description": "Gelectron native binary for macOS ARM64",
|
"description": "Gelectron native addon for macOS ARM64",
|
||||||
"main": "gelectron_core.darwin-arm64.node",
|
"main": "gelectron_core.darwin-arm64.node",
|
||||||
"files": [
|
"files": [
|
||||||
"gelectron_core.darwin-arm64.node"
|
"gelectron_core.darwin-arm64.node"
|
||||||
@@ -12,5 +12,6 @@
|
|||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT",
|
||||||
|
"author": "mileswa1q22"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Gelectron (macOS x64)
|
||||||
|
|
||||||
|
Gelectron native addon for **macOS on x64 (Intel)**.
|
||||||
|
|
||||||
|
This package provides the native `gelectron_core` binary for macOS x64. It is an internal dependency of [`gelectron`](https://www.npmjs.com/package/gelectron) and is installed automatically — you should not need to install it directly.
|
||||||
|
|
||||||
|
> Gelectron is a drop-in replacement for Electron using native web views (WKWebView / WebView2 / WebKitGTK) instead of Chromium.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Install the platform-specific addon for your system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install gelectron
|
||||||
|
```
|
||||||
|
|
||||||
|
The correct platform binary is selected automatically via `optionalDependencies`.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "gelectron-darwin-x64",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"description": "Gelectron native addon for macOS x64",
|
||||||
|
"main": "gelectron_core.darwin-x64.node",
|
||||||
|
"files": [
|
||||||
|
"gelectron_core.darwin-x64.node"
|
||||||
|
],
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "mileswa1q22"
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Gelectron (Linux ARM64)
|
||||||
|
|
||||||
|
Gelectron native addon for **Linux on ARM64 (GNU)**.
|
||||||
|
|
||||||
|
This package provides the native `gelectron_core` binary for Linux ARM64. It is an internal dependency of [`gelectron`](https://www.npmjs.com/package/gelectron) and is installed automatically — you should not need to install it directly.
|
||||||
|
|
||||||
|
> Gelectron is a drop-in replacement for Electron using native web views (WKWebView / WebView2 / WebKitGTK) instead of Chromium.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Install the platform-specific addon for your system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install gelectron
|
||||||
|
```
|
||||||
|
|
||||||
|
The correct platform binary is selected automatically via `optionalDependencies`.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "gelectron-linux-arm64-gnu",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"description": "Gelectron native addon for Linux ARM64 (GNU)",
|
||||||
|
"main": "gelectron_core.linux-arm64-gnu.node",
|
||||||
|
"files": [
|
||||||
|
"gelectron_core.linux-arm64-gnu.node"
|
||||||
|
],
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "mileswa1q22"
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Gelectron (Linux x64)
|
||||||
|
|
||||||
|
Gelectron native addon for **Linux on x64 (GNU)**.
|
||||||
|
|
||||||
|
This package provides the native `gelectron_core` binary for Linux x64. It is an internal dependency of [`gelectron`](https://www.npmjs.com/package/gelectron) and is installed automatically — you should not need to install it directly.
|
||||||
|
|
||||||
|
> Gelectron is a drop-in replacement for Electron using native web views (WKWebView / WebView2 / WebKitGTK) instead of Chromium.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Install the platform-specific addon for your system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install gelectron
|
||||||
|
```
|
||||||
|
|
||||||
|
The correct platform binary is selected automatically via `optionalDependencies`.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "gelectron-linux-x64-gnu",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"description": "Gelectron native addon for Linux x64 (GNU)",
|
||||||
|
"main": "gelectron_core.linux-x64-gnu.node",
|
||||||
|
"files": [
|
||||||
|
"gelectron_core.linux-x64-gnu.node"
|
||||||
|
],
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "mileswa1q22"
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Gelectron (Windows ARM64)
|
||||||
|
|
||||||
|
Gelectron native addon for **Windows on ARM64**.
|
||||||
|
|
||||||
|
This package provides the native `gelectron_core` binary for Windows ARM64. It is an internal dependency of [`gelectron`](https://www.npmjs.com/package/gelectron) and is installed automatically — you should not need to install it directly.
|
||||||
|
|
||||||
|
> Gelectron is a drop-in replacement for Electron using native web views (WKWebView / WebView2 / WebKitGTK) instead of Chromium.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Install the platform-specific addon for your system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install gelectron
|
||||||
|
```
|
||||||
|
|
||||||
|
The correct platform binary is selected automatically via `optionalDependencies`.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "gelectron-win32-arm64-msvc",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"description": "Gelectron native addon for Windows ARM64",
|
||||||
|
"main": "gelectron_core.win32-arm64-msvc.node",
|
||||||
|
"files": [
|
||||||
|
"gelectron_core.win32-arm64-msvc.node"
|
||||||
|
],
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "mileswa1q22"
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Gelectron (Windows x64)
|
||||||
|
|
||||||
|
Gelectron native addon for **Windows on x64**.
|
||||||
|
|
||||||
|
This package provides the native `gelectron_core` binary for Windows x64. It is an internal dependency of [`gelectron`](https://www.npmjs.com/package/gelectron) and is installed automatically — you should not need to install it directly.
|
||||||
|
|
||||||
|
> Gelectron is a drop-in replacement for Electron using native web views (WKWebView / WebView2 / WebKitGTK) instead of Chromium.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Install the platform-specific addon for your system:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install gelectron
|
||||||
|
```
|
||||||
|
|
||||||
|
The correct platform binary is selected automatically via `optionalDependencies`.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "gelectron-win32-x64-msvc",
|
||||||
|
"version": "0.1.1",
|
||||||
|
"description": "Gelectron native addon for Windows x64",
|
||||||
|
"main": "gelectron_core.win32-x64-msvc.node",
|
||||||
|
"files": [
|
||||||
|
"gelectron_core.win32-x64-msvc.node"
|
||||||
|
],
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"author": "mileswa1q22"
|
||||||
|
}
|
||||||
Generated
+13
@@ -1875,6 +1875,19 @@
|
|||||||
"fast-string-width": "^3.0.2"
|
"fast-string-width": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gelectron-darwin-arm64": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/gelectron-darwin-arm64/-/gelectron-darwin-arm64-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-K0bmA9pfSNlA7CLPD+BOBNdvb0Cb8qMWcYry9BYuP3DC0gcL8Gfq1iM2h3CHNKJB/YEZdBA0uCgTd3+0SMvQ3w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/graceful-fs": {
|
"node_modules/graceful-fs": {
|
||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
|
|||||||
+16
-10
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "gelectron",
|
"name": "gelectron-core",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"description": "Firefox-engine alternative to Electron, powered by Servo",
|
"description": "Firefox-engine alternative to Electron, powered by Servo",
|
||||||
"main": "src/electron/index.js",
|
"main": "src/electron/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
"gelectron": "./cli/gelectron.js"
|
"gelectron-core": "./cli/gelectron.js"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "napi build --platform --release --package gelectron-core",
|
"build": "napi build --platform --release --package gelectron-core",
|
||||||
@@ -58,11 +58,17 @@
|
|||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"gelectron-darwin-arm64": "0.1.0",
|
"gelectron-darwin-arm64": "0.1.1",
|
||||||
"gelectron-darwin-x64": "0.1.0",
|
"gelectron-darwin-x64": "0.1.1",
|
||||||
"gelectron-linux-arm64-gnu": "0.1.0",
|
"gelectron-linux-arm64-gnu": "0.1.1",
|
||||||
"gelectron-linux-x64-gnu": "0.1.0",
|
"gelectron-linux-x64-gnu": "0.1.1",
|
||||||
"gelectron-win32-arm64-msvc": "0.1.0",
|
"gelectron-win32-arm64-msvc": "0.1.1",
|
||||||
"gelectron-win32-x64-msvc": "0.1.0"
|
"gelectron-win32-x64-msvc": "0.1.1",
|
||||||
|
"gelectron-core-darwin-x64": "0.1.1",
|
||||||
|
"gelectron-core-darwin-arm64": "0.1.1",
|
||||||
|
"gelectron-core-win32-x64-msvc": "0.1.1",
|
||||||
|
"gelectron-core-win32-arm64-msvc": "0.1.1",
|
||||||
|
"gelectron-core-linux-x64-gnu": "0.1.1",
|
||||||
|
"gelectron-core-linux-arm64-gnu": "0.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Executable
+102
@@ -0,0 +1,102 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# build-npm.sh
|
||||||
|
#
|
||||||
|
# Builds the gelectron-core N-API addon for every supported platform target
|
||||||
|
# and copies the resulting .node files into the corresponding npm/ package
|
||||||
|
# folders.
|
||||||
|
#
|
||||||
|
# Each target is attempted. If the toolchain or linker isn't available,
|
||||||
|
# it's skipped with a warning. To build more targets:
|
||||||
|
# - Linux: brew install mingw-w64 (for Windows), or use Docker/cross
|
||||||
|
# - Run this script on each target OS for best results
|
||||||
|
#
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
|
HOST_TARGET="$(rustc -vV | grep '^host:' | awk '{print $2}')"
|
||||||
|
HAS_RUSTUP=false
|
||||||
|
command -v rustup &>/dev/null && HAS_RUSTUP=true
|
||||||
|
|
||||||
|
NAPI="$REPO_DIR/node_modules/.bin/napi"
|
||||||
|
if [ ! -x "$NAPI" ]; then
|
||||||
|
echo "Error: napi not found. Run 'npm install' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Each line: target_triple|npm_folder|node_filename
|
||||||
|
TARGETS=(
|
||||||
|
"aarch64-apple-darwin|npm/darwin-arm64|gelectron_core.darwin-arm64.node"
|
||||||
|
"x86_64-apple-darwin|npm/darwin-x64|gelectron_core.darwin-x64.node"
|
||||||
|
"x86_64-pc-windows-msvc|npm/win32-x64-msvc|gelectron_core.win32-x64-msvc.node"
|
||||||
|
"aarch64-pc-windows-msvc|npm/win32-arm64-msvc|gelectron_core.win32-arm64-msvc.node"
|
||||||
|
"x86_64-unknown-linux-gnu|npm/linux-x64-gnu|gelectron_core.linux-x64-gnu.node"
|
||||||
|
"aarch64-unknown-linux-gnu|npm/linux-arm64-gnu|gelectron_core.linux-arm64-gnu.node"
|
||||||
|
)
|
||||||
|
|
||||||
|
built=0
|
||||||
|
skipped=0
|
||||||
|
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
|
echo " Gelectron NPM Builder"
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
|
echo " Host target: $HOST_TARGET"
|
||||||
|
echo " rustup: $HAS_RUSTUP"
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
for entry in "${TARGETS[@]}"; do
|
||||||
|
target="$(echo "$entry" | cut -d'|' -f1)"
|
||||||
|
dest_dir="$(echo "$entry" | cut -d'|' -f2)"
|
||||||
|
node_name="$(echo "$entry" | cut -d'|' -f3)"
|
||||||
|
|
||||||
|
echo "── $target ──"
|
||||||
|
|
||||||
|
# Add the Rust target
|
||||||
|
if $HAS_RUSTUP; then
|
||||||
|
if ! rustup target add "$target" 2>/dev/null; then
|
||||||
|
echo " ⚠ Could not install Rust target — skipping"
|
||||||
|
skipped=$((skipped + 1))
|
||||||
|
echo ""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
elif [ "$target" != "$HOST_TARGET" ]; then
|
||||||
|
echo " ⚠ No rustup and not host target — skipping"
|
||||||
|
echo " Install rustup: brew install rustup && rustup-init"
|
||||||
|
skipped=$((skipped + 1))
|
||||||
|
echo ""
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build
|
||||||
|
echo " Building..."
|
||||||
|
if $NAPI build --platform --release --target "$target" --package gelectron-core 2>/dev/null; then
|
||||||
|
node_file="crates/gelectron-core/${node_name}"
|
||||||
|
|
||||||
|
if [ ! -f "$node_file" ]; then
|
||||||
|
node_file=$(ls crates/gelectron-core/gelectron_core.*.node 2>/dev/null | head -1 || true)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$node_file" ] && [ -f "$node_file" ]; then
|
||||||
|
mkdir -p "$dest_dir"
|
||||||
|
cp "$node_file" "$dest_dir/$node_name"
|
||||||
|
size=$(ls -lh "$dest_dir/$node_name" | awk '{print $5}')
|
||||||
|
echo " ✓ Built ($size) → $dest_dir/$node_name"
|
||||||
|
built=$((built + 1))
|
||||||
|
else
|
||||||
|
echo " ✗ Build succeeded but .node file not found"
|
||||||
|
skipped=$((skipped + 1))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ✗ Build failed (missing linker or toolchain)"
|
||||||
|
skipped=$((skipped + 1))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
|
echo " Done: $built built, $skipped skipped"
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
# gelectron installer (PowerShell / Windows)
|
||||||
|
#
|
||||||
|
# Downloads the pre-built gelectron binary + Electron compatibility layer from
|
||||||
|
# GitHub Releases and installs it into PREFIX so `gelectron <app>` works from
|
||||||
|
# anywhere. No Rust toolchain or npm required.
|
||||||
|
#
|
||||||
|
# Install layout:
|
||||||
|
# <PREFIX>/gelectron.exe native binary
|
||||||
|
# <PREFIX>/compat/*.js Electron API compat layer
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# .\scripts\install-release.ps1 latest release
|
||||||
|
# .\scripts\install-release.ps1 -Version v0.1.1
|
||||||
|
# .\scripts\install-release.ps1 -File C:\path\to\gelectron-0.1.1-win32-x64.zip
|
||||||
|
# .\scripts\install-release.ps1 -Prefix "$HOME\bin"
|
||||||
|
# .\scripts\install-release.ps1 -Uninstall
|
||||||
|
|
||||||
|
param(
|
||||||
|
[string]$Repo = "mileswolfallen2/gelectron",
|
||||||
|
[string]$Prefix = "",
|
||||||
|
[string]$Version = "",
|
||||||
|
[string]$File = "",
|
||||||
|
[switch]$Uninstall,
|
||||||
|
[switch]$Help
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
function Show-Help {
|
||||||
|
@"
|
||||||
|
gelectron installer - install pre-built gelectron from GitHub Releases
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
install-release.ps1 [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-Repo owner/repo GitHub repo (default: mileswolfallen2/gelectron)
|
||||||
|
-Prefix DIR Install directory (default: %LOCALAPPDATA%\gelectron\bin)
|
||||||
|
-Version TAG Install a specific release tag (default: latest)
|
||||||
|
-File PATH Install from a local archive instead of downloading
|
||||||
|
-Uninstall Remove previously installed files
|
||||||
|
-Help Show this help
|
||||||
|
"@ | Write-Host
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Help) { Show-Help; exit 0 }
|
||||||
|
if (-not $Prefix) { $Prefix = Join-Path $env:LOCALAPPDATA "gelectron\bin" }
|
||||||
|
|
||||||
|
# ── Detect platform / arch ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
$Platform = "win32"
|
||||||
|
switch ($env:PROCESSOR_ARCHITECTURE) {
|
||||||
|
"ARM64" { $Arch = "arm64" }
|
||||||
|
default { $Arch = "x64" }
|
||||||
|
}
|
||||||
|
$ArchiveExt = "zip"
|
||||||
|
|
||||||
|
# ── Uninstall ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if ($Uninstall) {
|
||||||
|
foreach ($p in @((Join-Path $Prefix "gelectron.exe"), (Join-Path $Prefix "compat"))) {
|
||||||
|
if (Test-Path $p) { Remove-Item -Recurse -Force $p }
|
||||||
|
}
|
||||||
|
Write-Host "Removed $Prefix\gelectron.exe and $Prefix\compat"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Resolve version + archive ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
$ArchiveFile = $File
|
||||||
|
if (-not $ArchiveFile) {
|
||||||
|
if ($Version) {
|
||||||
|
$Tag = "v" + ($Version.TrimStart("v"))
|
||||||
|
} else {
|
||||||
|
Write-Host "==> Resolving latest release from $Repo..."
|
||||||
|
$latest = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest" -Headers @{ "User-Agent" = "gelectron-installer" }
|
||||||
|
$Tag = $latest.tag_name
|
||||||
|
}
|
||||||
|
$Ver = $Tag.TrimStart("v")
|
||||||
|
$Asset = "gelectron-$Ver-$Platform-$Arch.$ArchiveExt"
|
||||||
|
$Url = "https://github.com/$Repo/releases/download/$Tag/$Asset"
|
||||||
|
Write-Host "==> Downloading $Asset ..."
|
||||||
|
$ArchiveFile = Join-Path $env:TEMP "gelectron-install-$([Guid]::NewGuid().ToString('N')).$ArchiveExt"
|
||||||
|
Invoke-WebRequest -Uri $Url -OutFile $ArchiveFile -UseBasicParsing
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Extract ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
$Stage = Join-Path $env:TEMP ("gelectron-install-stage-" + [Guid]::NewGuid().ToString("N"))
|
||||||
|
New-Item -ItemType Directory -Path $Stage | Out-Null
|
||||||
|
try {
|
||||||
|
Write-Host "==> Extracting archive..."
|
||||||
|
Expand-Archive -Path $ArchiveFile -DestinationPath $Stage -Force
|
||||||
|
|
||||||
|
$Bin = Join-Path $Stage "gelectron.exe"
|
||||||
|
if (-not (Test-Path $Bin)) {
|
||||||
|
throw "archive does not contain gelectron.exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Install ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Write-Host "==> Installing to $Prefix"
|
||||||
|
New-Item -ItemType Directory -Path $Prefix -Force | Out-Null
|
||||||
|
Copy-Item $Bin (Join-Path $Prefix "gelectron.exe") -Force
|
||||||
|
if (Test-Path (Join-Path $Stage "compat")) {
|
||||||
|
Copy-Item -Recurse -Force (Join-Path $Stage "compat") $Prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " OK Installed: $Prefix\gelectron.exe"
|
||||||
|
Write-Host " OK Installed: $Prefix\compat\"
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host " Add $Prefix to your PATH, then run:"
|
||||||
|
Write-Host ' gelectron C:\path\to\electron-app'
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Remove-Item -Recurse -Force $Stage -ErrorAction SilentlyContinue
|
||||||
|
if (-not $File) { Remove-Item -Force $ArchiveFile -ErrorAction SilentlyContinue }
|
||||||
|
}
|
||||||
Executable
+174
@@ -0,0 +1,174 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# gelectron installer
|
||||||
|
#
|
||||||
|
# Downloads the pre-built gelectron binary + Electron compatibility layer from
|
||||||
|
# GitHub Releases and installs it into PREFIX so `gelectron <app>` works from
|
||||||
|
# anywhere. No Rust toolchain or Node dependency manager required.
|
||||||
|
#
|
||||||
|
# Install layout:
|
||||||
|
# <PREFIX>/gelectron native binary
|
||||||
|
# <PREFIX>/compat/*.js Electron API compat layer
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# curl -fsSL https://raw.githubusercontent.com/mileswolfallen2/gelectron/main/scripts/install-release.sh | bash
|
||||||
|
# ./scripts/install-release.sh latest release
|
||||||
|
# ./scripts/install-release.sh --version v0.1.1
|
||||||
|
# ./scripts/install-release.sh --file /path/to/gelectron-0.1.1-darwin-arm64.tar.gz
|
||||||
|
# PREFIX=~/bin ./scripts/install-release.sh custom prefix
|
||||||
|
# ./scripts/install-release.sh --uninstall remove installed files
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DEFAULT_REPO="mileswolfallen2/gelectron"
|
||||||
|
DEFAULT_PREFIX="${HOME}/.local/bin"
|
||||||
|
|
||||||
|
REPO="$DEFAULT_REPO"
|
||||||
|
PREFIX="${PREFIX:-$DEFAULT_PREFIX}"
|
||||||
|
VERSION=""
|
||||||
|
FILE=""
|
||||||
|
UNINSTALL=0
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
gelectron installer — install pre-built gelectron from GitHub Releases
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
install-release.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--repo owner/repo GitHub repo to fetch releases from (default: mileswolfallen2/gelectron)
|
||||||
|
--prefix DIR Install directory (env: PREFIX, default: ~/.local/bin)
|
||||||
|
--version TAG Install a specific release tag (default: latest)
|
||||||
|
--file PATH Install from a local release archive instead of downloading
|
||||||
|
--uninstall Remove previously installed files
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--repo) REPO="${2:-}"; shift 2 ;;
|
||||||
|
--prefix) PREFIX="${2:-}"; shift 2 ;;
|
||||||
|
--version) VERSION="${2:-}"; shift 2 ;;
|
||||||
|
--file) FILE="${2:-}"; shift 2 ;;
|
||||||
|
--uninstall) UNINSTALL=1; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "error: unknown option: $1" >&2; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Detect platform / arch ──────────────────────────────────────────────────
|
||||||
|
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Darwin) PLATFORM="darwin" ;;
|
||||||
|
MINGW*|MSYS*|CYGWIN*) PLATFORM="win32" ;;
|
||||||
|
Linux) PLATFORM="linux" ;;
|
||||||
|
*) echo "error: unsupported platform: $(uname -s)" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$(uname -m)" in
|
||||||
|
arm64|aarch64) ARCH="arm64" ;;
|
||||||
|
x86_64|amd64) ARCH="x64" ;;
|
||||||
|
*) echo "error: unsupported arch: $(uname -m)" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
[[ "$PLATFORM" == "win32" ]] && ARCHIVE_EXT="zip" || ARCHIVE_EXT="tar.gz"
|
||||||
|
|
||||||
|
# ── Uninstall ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if [[ "$UNINSTALL" == "1" ]]; then
|
||||||
|
rm -f "$PREFIX/gelectron"
|
||||||
|
rm -f "$PREFIX/gelectron.exe"
|
||||||
|
rm -rf "$PREFIX/compat"
|
||||||
|
echo "Removed $PREFIX/gelectron and $PREFIX/compat"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Resolve version + archive ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
if [[ -n "$FILE" ]]; then
|
||||||
|
ARCHIVE_FILE="$FILE"
|
||||||
|
else
|
||||||
|
if [[ -n "$VERSION" ]]; then
|
||||||
|
TAG="${VERSION#v}"
|
||||||
|
TAG="v${TAG}"
|
||||||
|
else
|
||||||
|
echo "==> Resolving latest release from $REPO..."
|
||||||
|
if ! command -v curl >/dev/null 2>&1; then
|
||||||
|
echo "error: curl is required" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
API_URL="https://api.github.com/repos/$REPO/releases/latest"
|
||||||
|
TAG="$(curl -fsSL "$API_URL" | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1)"
|
||||||
|
if [[ -z "$TAG" ]]; then
|
||||||
|
echo "error: could not resolve latest release from $API_URL" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
VER="${TAG#v}"
|
||||||
|
ASSET="gelectron-$VER-$PLATFORM-$ARCH.$ARCHIVE_EXT"
|
||||||
|
URL="https://github.com/$REPO/releases/download/$TAG/$ASSET"
|
||||||
|
echo "==> Downloading $ASSET ..."
|
||||||
|
ARCHIVE_FILE="$(mktemp /tmp/gelectron-install.XXXXXX.$ARCHIVE_EXT)"
|
||||||
|
trap 'rm -f "$ARCHIVE_FILE"' EXIT
|
||||||
|
curl -fsSL -L "$URL" -o "$ARCHIVE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Extract ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d "${TMPDIR:-/tmp}/gelectron-install-stage.XXXXXX")"
|
||||||
|
trap 'rm -rf "$STAGE" "${ARCHIVE_FILE:-}"' EXIT
|
||||||
|
|
||||||
|
echo "==> Extracting archive..."
|
||||||
|
case "$ARCHIVE_EXT" in
|
||||||
|
zip) unzip -qo "$ARCHIVE_FILE" -d "$STAGE" ;;
|
||||||
|
*) tar -xzf "$ARCHIVE_FILE" -C "$STAGE" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
BIN_ABS="$STAGE/gelectron"
|
||||||
|
[[ -f "$STAGE/gelectron.exe" ]] && BIN_ABS="$STAGE/gelectron.exe"
|
||||||
|
if [[ ! -f "$BIN_ABS" ]]; then
|
||||||
|
echo "error: archive does not contain gelectron" >&2
|
||||||
|
ls -la "$STAGE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Install ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
echo "==> Installing to $PREFIX"
|
||||||
|
mkdir -p "$PREFIX"
|
||||||
|
|
||||||
|
install -m 755 "$BIN_ABS" "$PREFIX/$(basename "$BIN_ABS")"
|
||||||
|
if [[ -d "$STAGE/compat" ]]; then
|
||||||
|
mkdir -p "$PREFIX/compat"
|
||||||
|
install -m 644 "$STAGE"/compat/*.js "$PREFIX/compat/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Downloaded-from-GitHub binaries carry a quarantine attribute on macOS and
|
||||||
|
# Gatekeeper will block the first launch. Clear it — the binary is ad-hoc
|
||||||
|
# signed, so this is the only thing standing in the way.
|
||||||
|
if [[ "$PLATFORM" == "darwin" ]]; then
|
||||||
|
xattr -dr com.apple.quarantine "$PREFIX/$(basename "$BIN_ABS")" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo " ✓ Installed: $PREFIX/$(basename "$BIN_ABS")"
|
||||||
|
echo " ✓ Installed: $PREFIX/compat/"
|
||||||
|
|
||||||
|
if ! command -v gelectron >/dev/null 2>&1 || [[ "$(command -v gelectron)" != "$PREFIX/gelectron" ]]; then
|
||||||
|
echo
|
||||||
|
echo " warning: $PREFIX is not on your PATH."
|
||||||
|
case "$SHELL" in
|
||||||
|
*zsh) RC="$HOME/.zshrc" ;;
|
||||||
|
*bash) RC="$HOME/.bashrc" ;;
|
||||||
|
*) RC="$HOME/.profile" ;;
|
||||||
|
esac
|
||||||
|
echo " Add this line to $RC:"
|
||||||
|
echo " export PATH=\"$PREFIX:\$PATH\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
"$PREFIX/$(basename "$BIN_ABS")" --version >/dev/null 2>&1 && \
|
||||||
|
echo " Run: gelectron /path/to/electron-app" || \
|
||||||
|
echo " Hint: $PREFIX/$(basename "$BIN_ABS") --version"
|
||||||
Executable
+180
@@ -0,0 +1,180 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# gelectron release builder
|
||||||
|
#
|
||||||
|
# Pre-compiles the gelectron native binary and bundles it with the Electron
|
||||||
|
# compatibility layer (src/electron/*.js) into a single, self-contained
|
||||||
|
# installer archive per platform/arch:
|
||||||
|
#
|
||||||
|
# gelectron-<version>-darwin-arm64.tar.gz
|
||||||
|
# gelectron-<version>-darwin-x64.tar.gz
|
||||||
|
# gelectron-<version>-win32-arm64.zip
|
||||||
|
# gelectron-<version>-win32-x64.zip
|
||||||
|
# gelectron-<version>-linux-arm64.tar.gz
|
||||||
|
# gelectron-<version>-linux-x64.tar.gz
|
||||||
|
#
|
||||||
|
# Archive layout (the install scripts unpack this into PREFIX):
|
||||||
|
# gelectron | gelectron.exe native binary
|
||||||
|
# compat/*.js Electron API compatibility layer
|
||||||
|
#
|
||||||
|
# This script is what the GitHub Actions workflow runs on every new release
|
||||||
|
# tag; it can also be run locally for testing or manual distributions.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
|
||||||
|
VERSION=""
|
||||||
|
PLATFORM=""
|
||||||
|
ARCH=""
|
||||||
|
BINARY=""
|
||||||
|
OUT_DIR="$REPO_DIR/dist"
|
||||||
|
BUILD=1
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
gelectron release builder
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
scripts/make-release.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-v, --version VER Release version (default: from package.json, minus v)
|
||||||
|
-p, --platform P Target OS: darwin, win32, linux (default: current)
|
||||||
|
-a, --arch A Target arch: x64, arm64 (default: current)
|
||||||
|
-b, --binary PATH Use an already-built gelectron binary (skips cargo build)
|
||||||
|
-o, --out DIR Output directory (default: <repo>/dist)
|
||||||
|
-n, --no-build Do not compile (requires --binary or an existing build)
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-v|--version) VERSION="${2:-}"; shift 2 ;;
|
||||||
|
-p|--platform) PLATFORM="${2:-}"; shift 2 ;;
|
||||||
|
-a|--arch) ARCH="${2:-}"; shift 2 ;;
|
||||||
|
-b|--binary) BINARY="${2:-}"; shift 2 ;;
|
||||||
|
-o|--out) OUT_DIR="${2:-}"; shift 2 ;;
|
||||||
|
-n|--no-build) BUILD=0; shift ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "error: unknown option: $1" >&2; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Resolve defaults ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
if [[ -z "$VERSION" ]]; then
|
||||||
|
VERSION="$(node -p "require('$REPO_DIR/package.json').version" 2>/dev/null || echo '0.0.0')"
|
||||||
|
fi
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
|
||||||
|
if [[ -z "$PLATFORM" ]]; then
|
||||||
|
case "$(uname -s)" in
|
||||||
|
Darwin) PLATFORM="darwin" ;;
|
||||||
|
MINGW*|MSYS*|CYGWIN*) PLATFORM="win32" ;;
|
||||||
|
Linux) PLATFORM="linux" ;;
|
||||||
|
*) echo "error: cannot detect platform" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$ARCH" ]]; then
|
||||||
|
case "$(uname -m)" in
|
||||||
|
arm64|aarch64) ARCH="arm64" ;;
|
||||||
|
x86_64|amd64) ARCH="x64" ;;
|
||||||
|
*) echo "error: cannot detect arch" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$PLATFORM-$ARCH" in
|
||||||
|
darwin-arm64|darwin-x64|win32-arm64|win32-x64|linux-arm64|linux-x64) ;;
|
||||||
|
*) echo "error: unsupported platform/arch: $PLATFORM/$ARCH" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
EXE_SUFFIX=""
|
||||||
|
[[ "$PLATFORM" == "win32" ]] && EXE_SUFFIX=".exe"
|
||||||
|
[[ "$PLATFORM" == "win32" ]] && ARCHIVE_EXT="zip" || ARCHIVE_EXT="tar.gz"
|
||||||
|
|
||||||
|
log() { echo "==> $*"; }
|
||||||
|
|
||||||
|
# ── Locate or build the gelectron binary ────────────────────────────────────
|
||||||
|
|
||||||
|
BIN="$BINARY"
|
||||||
|
if [[ -z "$BIN" && "$BUILD" == "1" ]]; then
|
||||||
|
CANDIDATES=(
|
||||||
|
"$REPO_DIR/target/release/gelectron$EXE_SUFFIX"
|
||||||
|
"$REPO_DIR/target/debug/gelectron$EXE_SUFFIX"
|
||||||
|
)
|
||||||
|
for c in "${CANDIDATES[@]}"; do
|
||||||
|
if [[ -f "$c" ]]; then BIN="$c"; break; fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$BIN" && "$BUILD" == "1" ]]; then
|
||||||
|
log "Building gelectron (release)..."
|
||||||
|
cargo build --release --manifest-path "$REPO_DIR/Cargo.toml" -p gelectron
|
||||||
|
BIN="$REPO_DIR/target/release/gelectron$EXE_SUFFIX"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A --binary path may omit the platform extension (e.g. CI passes the target
|
||||||
|
# path before the .exe suffix is appended).
|
||||||
|
if [[ -n "$BIN" && ! -f "$BIN" && -n "$EXE_SUFFIX" && -f "$BIN$EXE_SUFFIX" ]]; then
|
||||||
|
BIN="$BIN$EXE_SUFFIX"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$BIN" || ! -f "$BIN" ]]; then
|
||||||
|
echo "error: gelectron binary not found. Build it first (cargo build --release -p gelectron) or pass --binary." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BIN="$(cd "$(dirname "$BIN")" && pwd)/$(basename "$BIN")"
|
||||||
|
|
||||||
|
# ── Stage the payload ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d "${TMPDIR:-/tmp}/gelectron-release.XXXXXX")"
|
||||||
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
|
|
||||||
|
PAYLOAD="$STAGE/payload"
|
||||||
|
mkdir -p "$PAYLOAD"
|
||||||
|
|
||||||
|
log "Copying binary ($(basename "$BIN"))..."
|
||||||
|
install -m 755 "$BIN" "$PAYLOAD/gelectron$EXE_SUFFIX"
|
||||||
|
|
||||||
|
COMPAT_SRC="$REPO_DIR/src/electron"
|
||||||
|
if [[ ! -d "$COMPAT_SRC" || -z "$(ls "$COMPAT_SRC"/*.js 2>/dev/null)" ]]; then
|
||||||
|
echo "error: compat layer not found in $COMPAT_SRC" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log "Copying compat layer..."
|
||||||
|
mkdir -p "$PAYLOAD/compat"
|
||||||
|
install -m 644 "$COMPAT_SRC"/*.js "$PAYLOAD/compat/"
|
||||||
|
|
||||||
|
# Ad-hoc sign so the binary actually runs on arm64 Macs (unsigned binaries
|
||||||
|
# are killed by the kernel). No certificate required.
|
||||||
|
if [[ "$PLATFORM" == "darwin" ]]; then
|
||||||
|
if command -v codesign >/dev/null 2>&1; then
|
||||||
|
log "Ad-hoc signing binary..."
|
||||||
|
codesign --force --sign - "$PAYLOAD/gelectron" 2>/dev/null || echo " (warning: codesign failed)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Archive ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
ARCHIVE_NAME="gelectron-$VERSION-$PLATFORM-$ARCH.$ARCHIVE_EXT"
|
||||||
|
ARCHIVE_PATH="$OUT_DIR/$ARCHIVE_NAME"
|
||||||
|
|
||||||
|
if [[ "$ARCHIVE_EXT" == "zip" ]]; then
|
||||||
|
log "Creating $ARCHIVE_NAME..."
|
||||||
|
if command -v powershell >/dev/null 2>&1; then
|
||||||
|
powershell -NoProfile -Command "Compress-Archive -Path '$PAYLOAD/*' -DestinationPath '$ARCHIVE_PATH' -Force"
|
||||||
|
else
|
||||||
|
(cd "$PAYLOAD" && zip -qr "$ARCHIVE_PATH" .)
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "Creating $ARCHIVE_NAME..."
|
||||||
|
(cd "$PAYLOAD" && tar -czf "$ARCHIVE_PATH" .)
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
log "Created installer: $ARCHIVE_PATH"
|
||||||
|
sha256sum "$ARCHIVE_PATH" 2>/dev/null || shasum -a 256 "$ARCHIVE_PATH" 2>/dev/null || true
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
; Gelectron Windows installer (NSIS)
|
||||||
|
;
|
||||||
|
; Build with makensis from a staging directory containing:
|
||||||
|
; gelectron.exe
|
||||||
|
; compat\*.js
|
||||||
|
;
|
||||||
|
; makensis /DVERSION=0.1.1 /DARCH=x64 Gelectron.nsi
|
||||||
|
;
|
||||||
|
; Installs to $PROGRAMFILES64\Gelectron, adds it to the machine PATH, creates
|
||||||
|
; Start Menu + Add/Remove Programs entries, and writes an uninstaller.
|
||||||
|
|
||||||
|
!include "MUI2.nsh"
|
||||||
|
!include "WordFunc.nsh"
|
||||||
|
|
||||||
|
!ifndef VERSION
|
||||||
|
!define VERSION "0.0.0"
|
||||||
|
!endif
|
||||||
|
!ifndef ARCH
|
||||||
|
!define ARCH "x64"
|
||||||
|
!endif
|
||||||
|
|
||||||
|
!define APPNAME "Gelectron"
|
||||||
|
!define COMPANY "gelectron"
|
||||||
|
!define ENV_KEY 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||||
|
!define UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
|
||||||
|
|
||||||
|
Name "${APPNAME}"
|
||||||
|
OutFile "Gelectron-${VERSION}-${ARCH}.exe"
|
||||||
|
InstallDir "$PROGRAMFILES64\${APPNAME}"
|
||||||
|
InstallDirRegKey HKLM "Software\${APPNAME}" "InstallDir"
|
||||||
|
RequestExecutionLevel admin
|
||||||
|
|
||||||
|
; ── Modern UI ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
|
|
||||||
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
|
|
||||||
|
; ── Install section ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Section "Install"
|
||||||
|
SetShellVarContext all
|
||||||
|
SetOutPath "$INSTDIR"
|
||||||
|
|
||||||
|
File "gelectron.exe"
|
||||||
|
File /r "compat"
|
||||||
|
|
||||||
|
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||||
|
|
||||||
|
CreateDirectory "$SMPROGRAMS\${APPNAME}"
|
||||||
|
CreateShortcut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\gelectron.exe"
|
||||||
|
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "${APPNAME}"
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "DisplayVersion" "${VERSION}"
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "Publisher" "${COMPANY}"
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "InstallLocation" "$INSTDIR"
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "UninstallString" '"$INSTDIR\Uninstall.exe"'
|
||||||
|
WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$INSTDIR\gelectron.exe"
|
||||||
|
|
||||||
|
; Add $INSTDIR to the machine PATH (add-if-not-present via WordFunc)
|
||||||
|
ReadRegStr $0 ${ENV_KEY} "Path"
|
||||||
|
${WordAdd} $0 ";" "+$INSTDIR" $1
|
||||||
|
WriteRegExpandStr ${ENV_KEY} "Path" $1
|
||||||
|
SendMessage ${HWND_BROADCAST} ${WM_SETTINGCHANGE} 0 "STR:Environment"
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
; ── Uninstall section ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Section "Uninstall"
|
||||||
|
SetShellVarContext all
|
||||||
|
; Remove $INSTDIR from the machine PATH
|
||||||
|
ReadRegStr $0 ${ENV_KEY} "Path"
|
||||||
|
${WordAdd} $0 ";" "-$INSTDIR" $1
|
||||||
|
WriteRegExpandStr ${ENV_KEY} "Path" $1
|
||||||
|
SendMessage ${HWND_BROADCAST} ${WM_SETTINGCHANGE} 0 "STR:Environment"
|
||||||
|
|
||||||
|
Delete "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk"
|
||||||
|
RMDir "$SMPROGRAMS\${APPNAME}"
|
||||||
|
|
||||||
|
DeleteRegKey HKLM "${UNINST_KEY}"
|
||||||
|
DeleteRegKey HKLM "Software\${APPNAME}"
|
||||||
|
|
||||||
|
RMDir /r "$INSTDIR"
|
||||||
|
SectionEnd
|
||||||
Executable
+142
@@ -0,0 +1,142 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Build the Linux AppImage: bundles the gelectron binary, the compat layer
|
||||||
|
# and a Node.js runtime into a single self-contained, downloadable AppImage.
|
||||||
|
#
|
||||||
|
# Gelectron-<version>-x86_64.AppImage
|
||||||
|
#
|
||||||
|
# Requires: the gelectron binary (built against webkit2gtk-4.1), compat layer,
|
||||||
|
# and network access (downloads Node.js + appimagetool).
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# scripts/pkg/make-appimage.sh -v VERSION [--binary PATH] [--compat DIR] [-o DIR]
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
NODE_VERSION="20.18.1"
|
||||||
|
|
||||||
|
VERSION=""
|
||||||
|
BINARY=""
|
||||||
|
COMPAT="$REPO_DIR/src/electron"
|
||||||
|
OUT_DIR="$REPO_DIR/dist"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Linux AppImage builder
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
scripts/pkg/make-appimage.sh -v VERSION [--binary PATH] [--compat DIR] [-o DIR]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-v, --version VER Version string (e.g. 0.1.1)
|
||||||
|
-b, --binary PATH Path to the gelectron binary
|
||||||
|
-c, --compat DIR Path to the compat layer (default: <repo>/src/electron)
|
||||||
|
-o, --out DIR Output directory (default: <repo>/dist)
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-v|--version) VERSION="${2:-}"; shift 2 ;;
|
||||||
|
-b|--binary) BINARY="${2:-}"; shift 2 ;;
|
||||||
|
-c|--compat) COMPAT="${2:-}"; shift 2 ;;
|
||||||
|
-o|--out) OUT_DIR="${2:-}"; shift 2 ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "error: unknown option: $1" >&2; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
[[ -n "$VERSION" ]] || { echo "error: --version required" >&2; exit 1; }
|
||||||
|
if [[ -z "$BINARY" ]]; then
|
||||||
|
BINARY="$REPO_DIR/target/release/gelectron"
|
||||||
|
fi
|
||||||
|
[[ -f "$BINARY" ]] || { echo "error: binary not found: $BINARY" >&2; exit 1; }
|
||||||
|
[[ -d "$COMPAT" ]] || { echo "error: compat dir not found: $COMPAT" >&2; exit 1; }
|
||||||
|
|
||||||
|
log() { echo "==> $*"; }
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d "${TMPDIR:-/tmp}/gelectron-appimage.XXXXXX")"
|
||||||
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
|
|
||||||
|
APPDIR="$STAGE/Gelectron.AppDir"
|
||||||
|
mkdir -p "$APPDIR/usr/bin"
|
||||||
|
|
||||||
|
# ── Binaries ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
log "Staging binary..."
|
||||||
|
install -m 755 "$BINARY" "$APPDIR/usr/bin/gelectron"
|
||||||
|
|
||||||
|
log "Staging compat layer (usr/bin/compat, resolved next to the binary)..."
|
||||||
|
mkdir -p "$APPDIR/usr/bin/compat"
|
||||||
|
install -m 644 "$COMPAT"/*.js "$APPDIR/usr/bin/compat/"
|
||||||
|
|
||||||
|
# Node.js runtime (gelectron's Node mode requires node on PATH)
|
||||||
|
NODE_DIR="$STAGE/node"
|
||||||
|
if [[ ! -d "$NODE_DIR" ]]; then
|
||||||
|
log "Downloading Node.js v$NODE_VERSION..."
|
||||||
|
mkdir -p "$NODE_DIR"
|
||||||
|
NODE_ARCHIVE="$STAGE/node.tar.xz"
|
||||||
|
curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" -o "$NODE_ARCHIVE"
|
||||||
|
tar -xJf "$NODE_ARCHIVE" -C "$NODE_DIR"
|
||||||
|
fi
|
||||||
|
install -m 755 "$NODE_DIR"/node-v${NODE_VERSION}-linux-x64/bin/node "$APPDIR/usr/bin/node"
|
||||||
|
install -m 644 "$NODE_DIR"/node-v${NODE_VERSION}-linux-x64/lib/libnode.so* "$APPDIR/usr/lib/" 2>/dev/null || true
|
||||||
|
|
||||||
|
mkdir -p "$APPDIR/usr/lib"
|
||||||
|
cp "$NODE_DIR"/node-v${NODE_VERSION}-linux-x64/lib/libnode.so* "$APPDIR/usr/lib/" 2>/dev/null || true
|
||||||
|
|
||||||
|
# ── AppImage metadata ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
cat > "$APPDIR/AppRun" <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
SELF="$(dirname "$(readlink -f "$0")")"
|
||||||
|
export PATH="$SELF/usr/bin:$PATH"
|
||||||
|
export LD_LIBRARY_PATH="$SELF/usr/lib:${LD_LIBRARY_PATH:-}"
|
||||||
|
export GELECTRON_NATIVE=1
|
||||||
|
exec "$SELF/usr/bin/gelectron" "$@"
|
||||||
|
EOF
|
||||||
|
chmod +x "$APPDIR/AppRun"
|
||||||
|
|
||||||
|
cat > "$APPDIR/gelectron.desktop" <<EOF
|
||||||
|
[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name=Gelectron
|
||||||
|
Comment=Firefox-engine alternative to Electron, powered by Servo
|
||||||
|
Exec=gelectron
|
||||||
|
Icon=gelectron
|
||||||
|
Terminal=false
|
||||||
|
Categories=Development;
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [[ -f "$REPO_DIR/logo.png" ]]; then
|
||||||
|
install -m 644 "$REPO_DIR/logo.png" "$APPDIR/gelectron.png"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── appimagetool ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
TOOL="$STAGE/appimagetool"
|
||||||
|
if [[ ! -f "$TOOL" ]]; then
|
||||||
|
log "Downloading appimagetool..."
|
||||||
|
curl -fsSL "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage" -o "$TOOL"
|
||||||
|
fi
|
||||||
|
chmod +x "$TOOL"
|
||||||
|
|
||||||
|
# ── Build ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
APPIMAGE_NAME="Gelectron-$VERSION-x86_64.AppImage"
|
||||||
|
APPIMAGE_PATH="$OUT_DIR/$APPIMAGE_NAME"
|
||||||
|
|
||||||
|
export VERSION="$VERSION"
|
||||||
|
export ARCH="x86_64"
|
||||||
|
# Run appimagetool without FUSE (Ubuntu 24.04+ runners don't ship libfuse2)
|
||||||
|
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||||
|
log "Creating $APPIMAGE_NAME ..."
|
||||||
|
"$TOOL" "$APPDIR" "$APPIMAGE_PATH" >/dev/null
|
||||||
|
|
||||||
|
echo
|
||||||
|
log "Created installer: $APPIMAGE_PATH"
|
||||||
|
sha256sum "$APPIMAGE_PATH" 2>/dev/null || shasum -a 256 "$APPIMAGE_PATH" 2>/dev/null || true
|
||||||
Executable
+114
@@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Build the macOS installer: a .pkg (installs the gelectron runtime +
|
||||||
|
# compat layer to /usr/local/bin) wrapped in a .dmg for distribution.
|
||||||
|
#
|
||||||
|
# Gelectron-<version>-<arch>.dmg
|
||||||
|
# └── Install gelectron.pkg (double-click, runs macOS Installer as root)
|
||||||
|
#
|
||||||
|
# Requires macOS (pkgbuild + hdiutil). Ad-hoc signing only — no Developer ID
|
||||||
|
# certificate, so first launch shows a Gatekeeper warning on other machines.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
|
||||||
|
VERSION=""
|
||||||
|
ARCH=""
|
||||||
|
BINARY=""
|
||||||
|
COMPAT="$REPO_DIR/src/electron"
|
||||||
|
OUT_DIR="$REPO_DIR/dist"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
macOS installer builder (pkg inside DMG)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
scripts/pkg/make-dmg.sh -v VERSION -a ARCH [--binary PATH] [--compat DIR] [-o DIR]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-v, --version VER Version string (e.g. 0.1.1)
|
||||||
|
-a, --arch A arm64 | x64
|
||||||
|
-b, --binary PATH Path to the gelectron binary
|
||||||
|
-c, --compat DIR Path to the compat layer (default: <repo>/src/electron)
|
||||||
|
-o, --out DIR Output directory (default: <repo>/dist)
|
||||||
|
-h, --help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-v|--version) VERSION="${2:-}"; shift 2 ;;
|
||||||
|
-a|--arch) ARCH="${2:-}"; shift 2 ;;
|
||||||
|
-b|--binary) BINARY="${2:-}"; shift 2 ;;
|
||||||
|
-c|--compat) COMPAT="${2:-}"; shift 2 ;;
|
||||||
|
-o|--out) OUT_DIR="${2:-}"; shift 2 ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "error: unknown option: $1" >&2; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -n "$VERSION" ]] || { echo "error: --version required" >&2; exit 1; }
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
[[ -n "$ARCH" ]] || { echo "error: --arch required" >&2; exit 1; }
|
||||||
|
case "$ARCH" in arm64|x64) ;; *) echo "error: unsupported arch: $ARCH" >&2; exit 1 ;; esac
|
||||||
|
|
||||||
|
if [[ -z "$BINARY" ]]; then
|
||||||
|
BINARY="$REPO_DIR/target/release/gelectron"
|
||||||
|
fi
|
||||||
|
[[ -f "$BINARY" ]] || { echo "error: binary not found: $BINARY" >&2; exit 1; }
|
||||||
|
[[ -d "$COMPAT" ]] || { echo "error: compat dir not found: $COMPAT" >&2; exit 1; }
|
||||||
|
|
||||||
|
log() { echo "==> $*"; }
|
||||||
|
|
||||||
|
STAGE="$(mktemp -d "${TMPDIR:-/tmp}/gelectron-dmg.XXXXXX")"
|
||||||
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
|
|
||||||
|
# ── Payload root (what pkgbuild installs into /usr/local/bin) ─────────────
|
||||||
|
|
||||||
|
PAYLOAD="$STAGE/gelectron"
|
||||||
|
mkdir -p "$PAYLOAD"
|
||||||
|
|
||||||
|
log "Staging payload..."
|
||||||
|
install -m 755 "$BINARY" "$PAYLOAD/gelectron"
|
||||||
|
mkdir -p "$PAYLOAD/compat"
|
||||||
|
install -m 644 "$COMPAT"/*.js "$PAYLOAD/compat/"
|
||||||
|
|
||||||
|
if command -v codesign >/dev/null 2>&1; then
|
||||||
|
log "Ad-hoc signing binary..."
|
||||||
|
codesign --force --sign - "$PAYLOAD/gelectron" 2>/dev/null || echo " (warning: codesign failed)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Component package ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
PKG="$STAGE/Install gelectron.pkg"
|
||||||
|
log "Building package (installs to /usr/local/bin)..."
|
||||||
|
pkgbuild \
|
||||||
|
--root "$PAYLOAD" \
|
||||||
|
--identifier "com.gelectron.runtime.$ARCH" \
|
||||||
|
--version "$VERSION" \
|
||||||
|
--ownership recommended \
|
||||||
|
--install-location /usr/local/bin \
|
||||||
|
"$PKG"
|
||||||
|
|
||||||
|
# ── DMG ────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
DMG_NAME="Gelectron-$VERSION-$ARCH.dmg"
|
||||||
|
DMG_PATH="$OUT_DIR/$DMG_NAME"
|
||||||
|
|
||||||
|
DMG_STAGE="$STAGE/dmg"
|
||||||
|
mkdir -p "$DMG_STAGE"
|
||||||
|
cp "$PKG" "$DMG_STAGE/"
|
||||||
|
|
||||||
|
log "Creating $DMG_NAME ..."
|
||||||
|
hdiutil create \
|
||||||
|
-volname "Gelectron $VERSION" \
|
||||||
|
-srcfolder "$DMG_STAGE" \
|
||||||
|
-format UDZO \
|
||||||
|
-ov \
|
||||||
|
"$DMG_PATH" >/dev/null
|
||||||
|
|
||||||
|
echo
|
||||||
|
log "Created installer: $DMG_PATH"
|
||||||
|
shasum -a 256 "$DMG_PATH"
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
# Gelectron Windows installer builder (NSIS)
|
||||||
|
#
|
||||||
|
# Stages the payload and compiles the installer EXE with makensis:
|
||||||
|
# Gelectron-<version>-<arch>.exe
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# powershell -File scripts/pkg/make-exe.ps1 `
|
||||||
|
# -Bin target\release\gelectron.exe `
|
||||||
|
# -Compat src\electron `
|
||||||
|
# -Version 0.1.1 -Arch x64 -Out dist
|
||||||
|
#
|
||||||
|
# Requires makensis on PATH (install via: choco install nsis -y)
|
||||||
|
|
||||||
|
param(
|
||||||
|
[Parameter(Mandatory = $true)][string]$Bin,
|
||||||
|
[Parameter(Mandatory = $true)][string]$Compat,
|
||||||
|
[Parameter(Mandatory = $true)][string]$Version,
|
||||||
|
[string]$Arch = "x64",
|
||||||
|
[string]$Out = "dist",
|
||||||
|
[string]$NsiPath = "scripts/pkg/gelectron.nsi"
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
if (-not (Get-Command makensis -ErrorAction SilentlyContinue)) {
|
||||||
|
throw "makensis not found. Install NSIS first (choco install nsis -y)."
|
||||||
|
}
|
||||||
|
|
||||||
|
$RealityVersion = $Version.TrimStart("v")
|
||||||
|
|
||||||
|
$Stage = Join-Path $env:TEMP ("gelectron-nsis-" + [Guid]::NewGuid().ToString("N"))
|
||||||
|
New-Item -ItemType Directory -Path $Stage -Force | Out-Null
|
||||||
|
try {
|
||||||
|
Copy-Item $Bin (Join-Path $Stage "gelectron.exe") -Force
|
||||||
|
Copy-Item -Recurse -Force $Compat (Join-Path $Stage "compat")
|
||||||
|
Copy-Item $NsiPath (Join-Path $Stage "gelectron.nsi") -Force
|
||||||
|
|
||||||
|
Write-Host "==> Compiling installer (makensis)..."
|
||||||
|
Push-Location $Stage
|
||||||
|
try {
|
||||||
|
& makensis "-DVERSION=$RealityVersion" "-DARCH=$Arch" gelectron.nsi
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "makensis failed with exit code $LASTEXITCODE" }
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
|
|
||||||
|
$Installer = Get-ChildItem $Stage -Filter "Gelectron-*.exe" | Select-Object -First 1
|
||||||
|
if (-not $Installer) { throw "makensis produced no installer" }
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Path $Out -Force | Out-Null
|
||||||
|
Copy-Item $Installer.FullName (Join-Path $Out $Installer.Name) -Force
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "==> Created installer: $(Join-Path $Out $Installer.Name)"
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Remove-Item -Recurse -Force $Stage -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
Executable
+68
@@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# publish-npm.sh
|
||||||
|
#
|
||||||
|
# Publishes all platform packages and the main gelectron package to npm.
|
||||||
|
# Run this after build-npm.sh has placed .node files in the npm/ folders.
|
||||||
|
#
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$REPO_DIR"
|
||||||
|
|
||||||
|
PLATFORMS=(
|
||||||
|
"npm/darwin-arm64"
|
||||||
|
"npm/darwin-x64"
|
||||||
|
"npm/win32-x64-msvc"
|
||||||
|
"npm/win32-arm64-msvc"
|
||||||
|
"npm/linux-x64-gnu"
|
||||||
|
"npm/linux-arm64-gnu"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
|
echo " Gelectron NPM Publisher"
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if logged in
|
||||||
|
if ! npm whoami &>/dev/null; then
|
||||||
|
echo "Not logged in to npm. Run 'npm login' first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
published=0
|
||||||
|
skipped=0
|
||||||
|
|
||||||
|
for dir in "${PLATFORMS[@]}"; do
|
||||||
|
pkg_name=$(node -e "console.log(require('./$dir/package.json').name)")
|
||||||
|
has_node=false
|
||||||
|
|
||||||
|
# Check if the .node file exists
|
||||||
|
for f in "$dir"/*.node; do
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
has_node=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if $has_node; then
|
||||||
|
echo "Publishing $pkg_name..."
|
||||||
|
(cd "$dir" && npm publish --access public)
|
||||||
|
published=$((published + 1))
|
||||||
|
else
|
||||||
|
echo "Skipping $pkg_name (no .node file)"
|
||||||
|
skipped=$((skipped + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Publishing main gelectron package..."
|
||||||
|
npm run prepublishOnly
|
||||||
|
npm publish --access public
|
||||||
|
published=$((published + 1))
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
|
echo " Done: $published published, $skipped skipped"
|
||||||
|
echo "═══════════════════════════════════════════════════"
|
||||||
Reference in New Issue
Block a user