Skip to content
Closed
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
14 changes: 7 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ on:
workflow_dispatch:

push:
branches:
- '**'
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
Runner:
Expand All @@ -15,19 +16,18 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
node-version: [ 16 ]
node-version: [ 20 ]
steps:
- name: Checkout Git Source
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: Install dependencies
run: |
npm i npm@6 -g
npm i

- name: Continuous integration
Expand All @@ -36,6 +36,6 @@ jobs:
npm run test

- name: Code coverage
uses: codecov/codecov-action@v3.0.0
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
4 changes: 4 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
name: E2E Tests

on:
workflow_dispatch:

push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
test:
timeout-minutes: 10
Expand Down
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npm run lint
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

This Husky hook is missing the standard hook wrapper/shebang that Husky installs (and may not run on some platforms or shells). Consider generating the hook via husky init / husky add (or add the expected header and ensure the file is executable) so the pre-commit hook runs reliably.

Copilot uses AI. Check for mistakes.
5 changes: 5 additions & 0 deletions .mocharc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
reporter: 'spec',
require: './test/unit/mock-setup.js',
Comment on lines +1 to +3
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

This file doesn’t include 'use strict';, but AGENTS.md states it should be at the top of every file. Either add 'use strict'; here or adjust the guideline to exclude config files, otherwise the new guidance is internally inconsistent.

Copilot uses AI. Check for mistakes.
spec: ['test/unit/*.test.js'],
};
3 changes: 3 additions & 0 deletions .nycrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"reporter": ["lcov", "text"]
}
133 changes: 133 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# AGENTS.md - Development Guide

This document provides guidelines for agents working on the electron-windows codebase.

## Project Overview

electron-windows is an Electron module for managing multiple windows gracefully. It provides window creation, retrieval by name/ID, state persistence, and loading views.

## Commands

### Development
```bash
pnpm run dev # Launch Electron demo app
```

### Testing
```bash
pnpm test # Run unit tests with nyc coverage
pnpm run test:e2e # Run end-to-end tests with Playwright
```

To run a **single test file**:
```bash
npx mocha test/unit/electron-windows.test.js
```

To run a **single test**:
```bash
npx mocha test/unit/electron-windows.test.js --grep "should create window with default options"
```

### Linting
```bash
pnpm run lint # Run ESLint (no auto-fix)
pnpm run lint:fix # Run ESLint with auto-fix
```

## Code Style Guidelines

### General
- Language: JavaScript (ES2018), CommonJS modules
- Always use `'use strict';` at the top of every file
- Use 2-space indentation

### Imports/Exports
- Use CommonJS: `const X = require('path')` and `module.exports = X`
- Order: built-in modules first, then external packages, then local files
- Empty line between groups

```javascript
'use strict';

const fs = require('fs');
const path = require('path');
const _ = require('lodash');
const { BrowserWindow } = require('electron');
const windowStateKeeper = require('electron-window-state');

const MyClass = require('./my-class');
```

### Naming Conventions
- **Classes**: PascalCase (e.g., `WindowsManager`)
- **Methods/variables**: camelCase (e.g., `createWindow`, `windowOptions`)
- **Constants**: SCREAMING_SNAKE_CASE (e.g., `DEFAULT_WIDTH`)
- **Private methods**: prefix with underscore (e.g., `_setGlobalUserAgent`)
- **Files**: kebab-case (e.g., `electron-windows.js`)

### Types and JSDoc
- This project does not use TypeScript
- Use JSDoc comments for public APIs (though `valid-jsdoc` is disabled)
- Document params and return values

```javascript
/**
* Creates a new window.
* @param {Object} options - Window configuration
* @param {string} [options.name='anonymous'] - Window identifier
* @returns {BrowserWindow} The created window
*/
```

### Error Handling
- Use `assert` for tests
- Return early on error conditions
- Check for destroyed windows before operations

```javascript
if (window.isDestroyed()) {
return;
}
```

### Best Practices
- Always check if Electron objects are destroyed before using them:
```javascript
if (window && !window.isDestroyed()) { ... }
if (webContents && !webContents.isDestroyed()) { ... }
```
- Use lodash utilities (already a dependency): `_.pick`, `_.debounce`, etc.
- Avoid arrow functions for methods that need `this` binding

## Architecture

- **Main entry**: `index.js` exports from `lib/electron-windows.js`
- **Core class**: `WindowsManager` in `lib/electron-windows.js`
- **Tests**: `test/unit/` for unit tests, `test/e2e/` for E2E tests
- **Mock setup**: `test/unit/mock-setup.js` provides Electron mocks

## Testing Patterns

- Use Mocha's `describe`/`it` structure
- Use `beforeEach` to reset mocks
- Use Node's `assert` module for assertions

```javascript
const assert = require('assert');
const WindowsManager = require('../../lib/electron-windows');

describe('WindowsManager', () => {
beforeEach(() => {
require('./mock-setup').reset();
});

describe('create()', () => {
it('should create window with default options', () => {
const manager = new WindowsManager();
const window = manager.create({});
assert.strictEqual(window._name, 'anonymous');
});
});
});
```
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CLAUDE.md

See [AGENTS.md](AGENTS.md) for project guidelines and code style documentation.
135 changes: 90 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,70 +17,115 @@
[download-image]: https://img.shields.io/npm/dm/electron-windows.svg
[download-url]: https://npmjs.org/package/electron-windows

> Manage multiple windows of Electron gracefully and provides powerful features.
> Manage multiple windows of Electron gracefully and provide powerful features.

<!-- GITCONTRIBUTOR_START -->

## Contributors
<p align="center"><img src="screenshot.png" width="600px"/></p>

|[<img src="https://avatars.githubusercontent.com/u/1011681?v=4" width="100px;"/><br/><sub><b>xudafeng</b></sub>](https://github.com/xudafeng)<br/>|[<img src="https://avatars.githubusercontent.com/u/17586742?v=4" width="100px;"/><br/><sub><b>sriting</b></sub>](https://github.com/sriting)<br/>|[<img src="https://avatars.githubusercontent.com/u/52845048?v=4" width="100px;"/><br/><sub><b>snapre</b></sub>](https://github.com/snapre)<br/>|[<img src="https://avatars.githubusercontent.com/u/12660278?v=4" width="100px;"/><br/><sub><b>ColaDaddyz</b></sub>](https://github.com/ColaDaddyz)<br/>|[<img src="https://avatars.githubusercontent.com/u/30524126?v=4" width="100px;"/><br/><sub><b>z0gSh1u</b></sub>](https://github.com/z0gSh1u)<br/>|[<img src="https://avatars.githubusercontent.com/u/4081746?v=4" width="100px;"/><br/><sub><b>zlyi</b></sub>](https://github.com/zlyi)<br/>|
| :---: | :---: | :---: | :---: | :---: | :---: |
[<img src="https://avatars.githubusercontent.com/u/50158871?v=4" width="100px;"/><br/><sub><b>moshangqi</b></sub>](https://github.com/moshangqi)<br/>

This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Fri Apr 25 2025 11:40:35 GMT+0800`.

<!-- GITCONTRIBUTOR_END -->

## Installment
## Installation

```bash
$ npm i electron-windows --save
```

## Demo

![](./sceenshot.png)

## APIs

### init
## Quick Start

```javascript
const WindowManager = require('electron-windows');
const windowManager = new WindowManager();
```
const WindowManager = require('electron-windows')

### create
// Create a window manager instance
const windowManager = new WindowManager()

```javascript
const { app } = require('electron');
const winRef = windowManager.create({
name: 'window1',
loadingView: {
url: '',
},
// Create a window
const mainWindow = windowManager.create({
name: 'main',
browserWindow: {
width: 800,
height: 600,
titleBarStyle: 'hidden',
title: 'demo',
show: false,
webPreferences: {
nodeIntegration: app.isDev,
webSecurity: true,
webviewTag: true,
},
},
openDevTools: true,
storageKey: 'storage-filename', // optional. The name of file. Support storage of window state
storagePath: app.getPath('userData'), // optional. The path of file, only used when storageKey is not empty
});
})

// Get window by name
const theWindow = windowManager.get('main')

// Get window by id
const theWindowToo = windowManager.getById(mainWindow.id)

// Get all windows
const all = windowManager.getAll()
```

## TODO
## API Reference

### Types

- **Window** - A union type of `StatefulWindow` and common `BrowserWindow`. When `storageKey` option is provided, returns `StatefulWindow`; otherwise returns `BrowserWindow`.

### `new WindowManager()`

Creates a new WindowManager instance.

### `windowManager.create(options)`

Creates and manages a new window.

- **options** `Object` - Configuration for the window:
- **name** `string` - Window identifier, used by `get()`. Default: `'anonymous'`
- **browserWindow** `Object` - [Electron BrowserWindow options](https://www.electronjs.org/docs/latest/api/browser-window)
- **loadingView** `Object` - Loading view configuration:
- **url** `string` - URL to show while main content loads
- **openDevTools** `boolean` - Auto open DevTools when window is ready. Default: `false`
- **preventOriginClose** `boolean` - Prevent window from closing, need manually close. Default: `false`
- **preventOriginNavigate** `boolean` - Prevent webContents navigation. Default: `false`
- **storageKey** `string` - Save/restore window position and size using `electron-window-state`
- **storagePath** `string` - Custom storage path for window state file
- **globalUserAgent** `string` - Custom User-Agent for all `loadURL` calls in this window

Returns: `Window` (`BrowserWindow` or `StatefulWindow`)

### `windowManager.get(name)`

Get a managed window by name.

- **name** `string` - Window name

Returns: `Window | undefined`

### `windowManager.getById(id)`

Get a managed window by Electron window id.

- **id** `number` - Window id

Returns: `Window | undefined`

### `windowManager.getAll()`

Get all managed windows.

Returns: `Object` - Object with window IDs as keys

### `WindowManager.setGlobalUserAgent(ua)`

Static method. Set global user agent for all windows.

- **ua** `string` - User agent string

## Roadmap

- [ ] support storage of window configuration
- [ ] clone pointed window
- [ ] support window cloning

<!-- GITCONTRIBUTOR_START -->

## Contributors

|[<img src="https://avatars.githubusercontent.com/u/1011681?v=4" width="80px;"/><br/><sub><b>xudafeng</b></sub>](https://github.com/xudafeng)<br/>|[<img src="https://avatars.githubusercontent.com/u/17586742?v=4" width="80px;"/><br/><sub><b>sriting</b></sub>](https://github.com/sriting)<br/>|[<img src="https://avatars.githubusercontent.com/u/30524126?v=4" width="80px;"/><br/><sub><b>z0gSh1u</b></sub>](https://github.com/z0gSh1u)<br/>|[<img src="https://avatars.githubusercontent.com/u/52845048?v=4" width="80px;"/><br/><sub><b>snapre</b></sub>](https://github.com/snapre)<br/>|[<img src="https://avatars.githubusercontent.com/u/12660278?v=4" width="80px;"/><br/><sub><b>ColaDaddyz</b></sub>](https://github.com/ColaDaddyz)<br/>|[<img src="https://avatars.githubusercontent.com/in/1143301?v=4" width="80px;"/><br/><sub><b>Copilot</b></sub>](https://github.com/apps/copilot-swe-agent)<br/>|
| :---: | :---: | :---: | :---: | :---: | :---: |
[<img src="https://avatars.githubusercontent.com/u/11213298?v=4" width="80px;"/><br/><sub><b>WynterDing</b></sub>](https://github.com/WynterDing)<br/>|[<img src="https://avatars.githubusercontent.com/u/4081746?v=4" width="80px;"/><br/><sub><b>zlyi</b></sub>](https://github.com/zlyi)<br/>|[<img src="https://avatars.githubusercontent.com/u/50158871?v=4" width="80px;"/><br/><sub><b>moshangqi</b></sub>](https://github.com/moshangqi)<br/>

This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Tue Feb 24 2026 17:35:12 GMT+0800`.

<!-- GITCONTRIBUTOR_END -->

## License

Expand Down
4 changes: 4 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,7 @@ declare class WindowsManager {
}

export = WindowsManager

declare namespace WindowsManager {
export type { Window, StatefulWindow }
}
3 changes: 0 additions & 3 deletions lib/helper.js

This file was deleted.

Loading