Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
node_modules
dist
out
.DS_Store
.eslintcache
*.log*

# Development certificate
devcert.pfx

# Windows SDK packages and generated files
.winapp
Comment thread
lei9444 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
out
dist
pnpm-lock.yaml
LICENSE.md
tsconfig.json
tsconfig.*.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
singleQuote: true
semi: false
printWidth: 100
trailingComma: none
265 changes: 265 additions & 0 deletions WindowsAppSDK-ProjectTemplates/electron/electron-win-app/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
# Electron Windows App (Electron + React + Native Addon)

## What this is
- Electron app with React frontend, using a native C++ addon (node-gyp) to access Windows APIs via WinRT/C++.
- Sparse MSIX packaging provides Package Identity for Windows notifications and other identity-required features.
- Hot reload for renderer (React), with native addon rebuild only when C++ changes.

## Start here
- One-time setup: Run `npm install` (triggers `postinstall` which restores packages, builds addon, and sets up debug identity)
- Daily dev (hot reload): `npm run dev`
- Build addon after C++ changes: `npm run build-addon`
- Build everything: `npm run build-all` (addon + electron)
- Package MSIX: `npm run package-msix`

## Repo map
```
electron-win-app/
├── src/
│ ├── main/ # Electron main process (Node.js)
│ ├── preload/ # Preload scripts (bridge between main and renderer)
│ └── renderer/ # React frontend (Vite + React + TypeScript)
├── addon/ # C++ native addon using WinRT APIs
│ ├── binding.gyp # node-gyp build configuration
│ └── addon.cc # C++ source with WinRT calls
├── .winapp/ # SDK packages, headers, libs (generated by winapp)
├── Assets/ # MSIX package assets (icons, logos)
├── appxmanifest.xml # MSIX manifest for sparse package identity
└── winapp.yaml # winapp CLI configuration
```

## Fast paths

### Hot reload (daily development)
```bash
npm run dev
```
- Starts Vite dev server with HMR for React frontend
- Main process changes require restart (Ctrl+C, then `npm run dev` again)
- Native addon changes require `npm run build-addon` first

### Build all (addon + electron)
```bash
npm run build-all
```
- Builds C++ addon first, then Electron (typecheck + electron-vite build)

### Build native addon (after C++ changes)
```bash
npm run build-addon
```
- Compiles `addon.cc` to `.node` binary
- Required before `npm run dev` if C++ code changed

### Package MSIX (release)
```bash
npm run package-msix # x64
npm run package-msix:arm64 # ARM64
```
- Builds everything (addon + electron)
- Creates unpacked app via electron-builder
- Packages as signed MSIX using `winapp package`
- Output: `dist/electron-win-app.msix`

## Architecture

### IPC Communication
```
[Renderer Process] <--IPC--> [Main Process] <--N-API--> [Native Addon]
(React) (Node.js) (C++/WinRT)
```

1. **Renderer** calls `window.electron.ipcRenderer.invoke('method', args)`
2. **Preload** exposes safe IPC methods via `contextBridge`
3. **Main** handles IPC with `ipcMain.handle('method', handler)`
4. **Native addon** is loaded via `require()` and called synchronously

### Example: Windows Notification
```typescript
// Renderer (src/renderer/src/App.tsx)
await window.electron.ipcRenderer.invoke('show-notification', 'Title', 'Message')

// Main (src/main/index.ts)
ipcMain.handle('show-notification', (_event, title, message) => {
nativeAddon.showNotification(title, message)
})

// Native (addon/addon.cc)
void ShowNotification(const Napi::CallbackInfo& info) {
// WinRT ToastNotification API
}
```

## Adding native Windows capabilities

### 1. Add function in C++ addon
Edit `addon/addon.cc`:
```cpp
void YourNewFunction(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
// Get args: info[0].As<Napi::String>(), etc.
// Call WinRT APIs
// Return: return Napi::String::New(env, result);
}

// Register in Init()
exports.Set("yourNewFunction", Napi::Function::New(env, YourNewFunction));
```

### 2. Rebuild addon
```bash
npm run build-addon
```

### 3. Add IPC handler in main process
Edit `src/main/index.ts`:
```typescript
ipcMain.handle('your-new-method', (_event, arg1, arg2) => {
return nativeAddon.yourNewFunction(arg1, arg2)
})
```

### 4. Expose in preload (if needed)
Edit `src/preload/index.ts` to add to `contextBridge.exposeInMainWorld`

### 5. Call from renderer
```typescript
const result = await window.electron.ipcRenderer.invoke('your-new-method', arg1, arg2)
```

## Package Identity (Sparse MSIX)

### Why it matters
- Windows notifications require Package Identity
- Other Windows APIs (background tasks, share target, etc.) also need identity
- Sparse packaging gives identity without full MSIX bundling

### How it works
1. `winapp restore` downloads Windows SDK and WinAppSDK packages to `.winapp/`
2. `winapp node add-electron-debug-identity` registers a sparse MSIX manifest
3. The Electron process inherits this identity at runtime

### Debug identity setup
```bash
npm run setup-debug
# or manually:
winapp node add-electron-debug-identity
```

### Clear debug identity
```bash
npm run clean-debug
# or manually:
winapp node clear-electron-debug-identity
```
Use this when cleaning up the project or before uninstalling.

### Verify identity
```powershell
# Check if process has identity
Get-Process -Name "electron" | ForEach-Object {
# Uses kernel32 GetPackageFullName API
}
```

## Debugging and logging

### Terminal is the primary log destination
During development (`npm run dev`), **all logs go to the terminal** where the command is running:
- Main process `console.log/error` → terminal
- Renderer process errors → forwarded to terminal via IPC
- Native addon exceptions → caught and logged to terminal

**Always check the terminal first when debugging issues.**

### Logger utility (renderer)
Use the built-in logger to send renderer logs to terminal:
```typescript
import { logger } from './utils/logger'

logger.info('feature', 'User action', { extra: 'data' })
logger.warn('validation', 'Input warning')
logger.error('api', 'Request failed', { status: 500 })
```

Output in terminal: `[timestamp] [Renderer:feature] User action`

### Global error handling
All errors are automatically forwarded to terminal:
- `window.onerror` - renderer sync errors
- `window.onunhandledrejection` - renderer promise rejections
- React ErrorBoundary - component errors
- Native addon exceptions - caught in main process `try-catch` and logged via `console.error`

**AI Agents: When debugging runtime errors, use `get_terminal_output` or check the terminal running `npm run dev` for error messages. All errors from all layers appear there.**

### DevTools (secondary)
- Press F12 in the app window to open Chromium DevTools
- Useful for inspecting React components, network, DOM

### Native addon debugging
- `binding.gyp` already configured with debug symbols (`GenerateDebugInformation: true`)
- Run `npm run dev` to start the app
- Open Visual Studio → Debug → Attach to Process → select `electron.exe`
- Open `addon/addon.cc` in VS and set breakpoints
- Trigger the code path from the app UI

### Common issues
| Issue | Solution |
|-------|----------|
| Notification not showing | Verify Package Identity is active |
| Addon build fails | Check `.winapp/lib/x64` exists, run `winapp restore` |
| IPC timeout | Check main process logs for errors |

## Build configuration

### npm scripts summary
| Script | Description |
|--------|-------------|
| `dev` | Hot reload development |
| `build-addon` | Build C++ native addon only |
| `build` | Build Electron (typecheck + electron-vite) |
| `build-all` | Build addon + Electron |
| `build:unpack` | Build all + electron-builder (unpacked) |
| `build:unpack:arm64` | Build all + electron-builder ARM64 |
| `package-msix` | Full x64 MSIX package |
| `package-msix:arm64` | Full ARM64 MSIX package |

### binding.gyp (native addon)
- `include_dirs`: Headers from `.winapp/include` and node-addon-api
- `library_dirs`: Libs from `.winapp/lib/<arch>`
- `libraries`: `WindowsApp.lib`, `Microsoft.WindowsAppRuntime.Bootstrap.lib`

### electron-builder.yml
- Configures MSIX/exe packaging
- Sets app identity, icons, and installer options

### winapp.yaml
- Specifies Windows SDK and WinAppSDK versions
- Used by `winapp restore` to download packages

## AI Agent Task Loop

**For AI coding assistants: Follow these steps for feature implementations.**

1. **Understand the request** - Identify if it needs renderer, main, or native changes
2. **Implement the change**
- Renderer-only: Edit `src/renderer/`
- Main process: Edit `src/main/`
- Native capability: Edit `addon/addon.cc`
3. **Rebuild if needed**
- Native changes: `npm run build-addon`
- All changes: Restart `npm run dev`
4. **Test the change**
- Run `npm run dev` and verify in the app
- **Check terminal output for all logs and errors** (both main and renderer)
- Use `logger.info/warn/error` in renderer code for debugging
5. **Handle errors**
- Build errors: Check `binding.gyp` paths and `.winapp/` contents
- Runtime errors: Check terminal for `[Renderer:...]` prefixed logs
- Add `logger.error()` calls to trace issues

**Key insights**:
- Native addon changes require rebuild (`npm run build-addon`) before they take effect
- Renderer changes hot-reload automatically
- **Terminal shows ALL logs** - main process, renderer errors, and logger output
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions WindowsAppSDK-ProjectTemplates/electron/electron-win-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# electron-win-app

An Electron application with React and TypeScript

## Recommended IDE Setup

- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)

## Project Setup

### Install

```bash
$ npm install
```

### Development

```bash
$ npm run dev
```

### Build

```bash
# For windows
$ npm run build:win

# For macOS
$ npm run build:mac

# For Linux
$ npm run build:linux
Comment thread
lei9444 marked this conversation as resolved.
Outdated
```
Loading
Loading