Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
96b5b4f
Generate project
Danil-Didkovskiy Jul 27, 2026
e3d5fb1
Add `docs/` to README
Danil-Didkovskiy Jul 27, 2026
12c9903
Refactor application structure
Danil-Didkovskiy Jul 27, 2026
a2a2761
Add CI workflow for linting and type-checking TypeScript sources
Danil-Didkovskiy Jul 27, 2026
26b50e0
Implement DeviceStack for managing devices and their settings
Danil-Didkovskiy Jul 27, 2026
622fd08
Add SimulatorService for simulating device activity and update Device…
Danil-Didkovskiy Jul 27, 2026
4e2117d
Update README with and clean up simulator code
Danil-Didkovskiy Jul 27, 2026
84da2d6
Extract DeviceStackService and update README.md
Danil-Didkovskiy Jul 27, 2026
925c31b
Update dependencies
Danil-Didkovskiy Jul 27, 2026
459367c
Add device management and simulator services with protobuf definitions
Danil-Didkovskiy Jul 28, 2026
aaf6159
Implement device management UI
Danil-Didkovskiy Jul 28, 2026
6653702
Add device art components and enhance device status display
Danil-Didkovskiy Jul 28, 2026
d0051b7
Refactor device status components
Danil-Didkovskiy Jul 28, 2026
507d299
Implement device carousel with scroll controls
Danil-Didkovskiy Jul 28, 2026
113b375
Clean up DeviceArt and DeviceView
Danil-Didkovskiy Jul 28, 2026
21b3bf9
Enhance device management with improved home focus handling and conne…
Danil-Didkovskiy Jul 28, 2026
ecd5932
Enhance carousel controls with overflow handling and disable state
Danil-Didkovskiy Jul 28, 2026
74897ae
Refactor device components
Danil-Didkovskiy Jul 28, 2026
1e5031f
Add action handling and control editor components for mouse settings
Danil-Didkovskiy Jul 28, 2026
94236f7
Enhance control editor and device components
Danil-Didkovskiy Jul 28, 2026
4915875
Refactor action handling and control editor components
Danil-Didkovskiy Jul 28, 2026
cd9709e
Refactor control editor components and update action handling
Danil-Didkovskiy Jul 28, 2026
545ed6a
Refactor action handling
Danil-Didkovskiy Jul 29, 2026
73f7d00
Refactor control editor
Danil-Didkovskiy Jul 29, 2026
d06a9bb
Refactor control editor and enhance action handling
Danil-Didkovskiy Jul 29, 2026
cc25a8c
Add device discovery and pairing functionality with UI components
Danil-Didkovskiy Jul 29, 2026
4a68ded
Add onRemoved callback to DeviceView and RemoveDeviceDialog for bette…
Danil-Didkovskiy Jul 29, 2026
b01ea60
Add reset functionality for mouse and keyboard bindings in control ed…
Danil-Didkovskiy Jul 29, 2026
978c43e
Enhance keyboard art styling
Danil-Didkovskiy Jul 29, 2026
afe78a0
Add application settings management and UI components
Danil-Didkovskiy Jul 29, 2026
bd40776
Update .gitignore, enhance Home component state management, and impro…
Danil-Didkovskiy Jul 29, 2026
46d9228
Fix low battery alert handling
Danil-Didkovskiy Jul 30, 2026
503a778
Align device model and settings management with native and renderer I…
Danil-Didkovskiy Jul 30, 2026
4468fb7
Rename `entries_` -> `paired_`
Danil-Didkovskiy Jul 30, 2026
8fa93e4
Refactor BrowserWindow initialization
Danil-Didkovskiy Jul 30, 2026
ac5a39b
Improve readability in App.tsx
Danil-Didkovskiy Jul 30, 2026
4569689
Enhance device management and UI components with improved state handl…
Danil-Didkovskiy Jul 30, 2026
45d9a8d
Update README.md
Danil-Didkovskiy Jul 30, 2026
75c7e88
Add detailed JSDoc comments
Danil-Didkovskiy Jul 30, 2026
ff9aaf7
Split control editor
Danil-Didkovskiy Jul 30, 2026
1fcfeb2
Use model IDs and add model_id field to Device structure
Danil-Didkovskiy Jul 30, 2026
c8d44eb
Refactor screen state management to use a structured type
Danil-Didkovskiy Jul 30, 2026
f62bc5d
Add automatic update download setting and functionality
Danil-Didkovskiy Aug 3, 2026
c067d38
Update README
Danil-Didkovskiy Aug 3, 2026
5daa0bb
Update version to 1.0.0 and enhance README with additional screenshots
Danil-Didkovskiy Aug 3, 2026
7a3f0d1
Refactor release workflow and enhance battery check logic
Danil-Didkovskiy Aug 3, 2026
c09a1e8
Update README to enhance screenshot presentation and layout
Danil-Didkovskiy Aug 3, 2026
6c0d951
Update README
Danil-Didkovskiy Aug 3, 2026
cbd11a1
Update README
Danil-Didkovskiy Aug 3, 2026
4432b78
Rename project to lowercase and update version in package files
Danil-Didkovskiy Aug 3, 2026
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
37 changes: 37 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Lint, type-check, and build the application.
name: Check

on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches: [main]
workflow_dispatch:

jobs:
check:
name: Check
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm

- name: Install
run: npm ci

- name: Generate
run: npm run gen

- name: Lint
run: npm run lint

- name: Typecheck
run: npm run typecheck

- name: Build
run: npm run build -- --verbose
237 changes: 237 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# Build production artifacts on macOS and Windows, then draft a GitHub Release.
#
# Requires a committed package-lock.json (npm ci).
name: Release

on:
push:
tags:
- 'v*'
workflow_dispatch:

permissions:
contents: read

jobs:
build-macos:
environment: production
runs-on: macos-latest

env:
# Base64-encoded Developer ID Application certificate (.p12).
MAC_CERTIFICATE: ${{ secrets.MAC_CERTIFICATE }}
# Password used to export the .p12 certificate from Keychain Access.
MAC_CERTIFICATE_PWD: ${{ secrets.MAC_CERTIFICATE_PWD }}
# Password for the temporary CI keychain created during the build.
MAC_KEYCHAIN_PWD: ${{ secrets.MAC_KEYCHAIN_PWD }}
# Signing identity string, e.g. "Developer ID Application: Your Name (TEAMID)".
MAC_CODESIGN_IDENTITY: ${{ secrets.MAC_CODESIGN_IDENTITY }}
# Apple Developer Team ID (10-character string found in developer.apple.com).
MAC_TEAM_ID: ${{ secrets.MAC_TEAM_ID }}
# Apple ID email used for notarization.
MAC_APPLE_ID: ${{ secrets.MAC_APPLE_ID }}
# App-specific password generated at appleid.apple.com for notarization.
MAC_APPLE_PASSWORD: ${{ secrets.MAC_APPLE_PASSWORD }}
# Shared private key used to sign auto-update packages on every platform.
CI_MOBROWSER_AUTOUPDATE_SIGNING_KEY: ${{ secrets.CI_MOBROWSER_AUTOUPDATE_SIGNING_KEY }}

steps:
- uses: actions/checkout@v4

- name: Verify macOS release secrets
run: |
set -euo pipefail
missing=()
[ -z "${MAC_CERTIFICATE:-}" ] && missing+=(MAC_CERTIFICATE)
[ -z "${MAC_CERTIFICATE_PWD:-}" ] && missing+=(MAC_CERTIFICATE_PWD)
[ -z "${MAC_KEYCHAIN_PWD:-}" ] && missing+=(MAC_KEYCHAIN_PWD)
[ -z "${MAC_CODESIGN_IDENTITY:-}" ] && missing+=(MAC_CODESIGN_IDENTITY)
[ -z "${MAC_TEAM_ID:-}" ] && missing+=(MAC_TEAM_ID)
[ -z "${MAC_APPLE_ID:-}" ] && missing+=(MAC_APPLE_ID)
[ -z "${MAC_APPLE_PASSWORD:-}" ] && missing+=(MAC_APPLE_PASSWORD)
[ -z "${CI_MOBROWSER_AUTOUPDATE_SIGNING_KEY:-}" ] && missing+=(CI_MOBROWSER_AUTOUPDATE_SIGNING_KEY)
if [ ${#missing[@]} -gt 0 ]; then
echo "Missing required macOS release secrets: ${missing[*]}"
exit 1
fi

- uses: actions/setup-node@v4
with:
node-version: 24

- name: Install dependencies
run: npm ci

- name: Set up macOS code signing
run: |
set -euo pipefail
KEYCHAIN_PATH=$RUNNER_TEMP/build.keychain
security create-keychain -p "$MAC_KEYCHAIN_PWD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$MAC_KEYCHAIN_PWD" "$KEYCHAIN_PATH"

curl -fsSL https://www.apple.com/certificateauthority/AppleRootCA-G3.cer -o AppleRootCA-G3.cer
curl -fsSL https://www.apple.com/certificateauthority/AppleWWDRCAG6.cer -o AppleWWDRCAG6.cer
curl -fsSL https://www.apple.com/certificateauthority/DeveloperIDG2CA.cer -o DeveloperIDG2CA.cer

security import ./AppleRootCA-G3.cer -k "$KEYCHAIN_PATH"
security import ./AppleWWDRCAG6.cer -k "$KEYCHAIN_PATH"
security import ./DeveloperIDG2CA.cer -k "$KEYCHAIN_PATH"

echo "$MAC_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/certificate.p12"
security import "$RUNNER_TEMP/certificate.p12" -k "$KEYCHAIN_PATH" -P "$MAC_CERTIFICATE_PWD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$MAC_KEYCHAIN_PWD" "$KEYCHAIN_PATH"
security list-keychain -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')

- name: Build release package
run: npm run build -- --verbose

- name: Build DMG and auto-update packages
run: npm run pack -- --verbose

- name: Upload macOS artifact
uses: actions/upload-artifact@v4
with:
name: artifacts-mac-arm64
path: |
build/dist/mac-arm64/pack/*.dmg
build/dist/mac-arm64/pack/*.nupkg
build/dist/mac-arm64/pack/releases.osx.json
if-no-files-found: error

build-windows:
# To securely authenticate GitHub Actions with Azure using federated credentials we need
# an environment-based approach that can be triggered by any event (including version tags).
environment: production
runs-on: windows-latest

permissions:
contents: read
id-token: write

env:
# Azure Artifact Signing account name (Artifact Signing resource in the Azure portal).
AZURE_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_SIGNING_ACCOUNT_NAME }}
# Certificate profile name within the Artifact Signing account.
AZURE_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_CERTIFICATE_PROFILE_NAME }}
# Azure Artifact Signing endpoint URL (e.g. "https://weu.codesigning.azure.net/").
AZURE_SIGNING_ENDPOINT: ${{ secrets.AZURE_SIGNING_ENDPOINT }}
# Service principal client ID (app registration) used by DefaultAzureCredential.
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
# Azure Active Directory tenant ID of the service principal.
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
# Azure subscription ID of the service principal.
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
# Shared private key used to sign auto-update packages on every platform.
CI_MOBROWSER_AUTOUPDATE_SIGNING_KEY: ${{ secrets.CI_MOBROWSER_AUTOUPDATE_SIGNING_KEY }}

steps:
- uses: actions/checkout@v4

- name: Verify Windows release secrets
shell: pwsh
run: |
$missing = @()
if ([string]::IsNullOrWhiteSpace($env:AZURE_SIGNING_ACCOUNT_NAME)) { $missing += "AZURE_SIGNING_ACCOUNT_NAME" }
if ([string]::IsNullOrWhiteSpace($env:AZURE_CERTIFICATE_PROFILE_NAME)) { $missing += "AZURE_CERTIFICATE_PROFILE_NAME" }
if ([string]::IsNullOrWhiteSpace($env:AZURE_SIGNING_ENDPOINT)) { $missing += "AZURE_SIGNING_ENDPOINT" }
if ([string]::IsNullOrWhiteSpace($env:AZURE_CLIENT_ID)) { $missing += "AZURE_CLIENT_ID" }
if ([string]::IsNullOrWhiteSpace($env:AZURE_TENANT_ID)) { $missing += "AZURE_TENANT_ID" }
if ([string]::IsNullOrWhiteSpace($env:AZURE_SUBSCRIPTION_ID)) { $missing += "AZURE_SUBSCRIPTION_ID" }
if ([string]::IsNullOrWhiteSpace($env:CI_MOBROWSER_AUTOUPDATE_SIGNING_KEY)) { $missing += "CI_MOBROWSER_AUTOUPDATE_SIGNING_KEY" }
if ($missing.Count -gt 0) {
Write-Error "Missing required Windows release secrets: $($missing -join ', ')"
exit 1
}

- uses: actions/setup-node@v4
with:
node-version: 24

- name: Install dependencies
run: npm ci

- name: Azure login (for code signing)
uses: azure/login@v3
with:
client-id: ${{ env.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.AZURE_SUBSCRIPTION_ID }}

- name: Build release package
run: npm run build -- --verbose

- name: Sign Windows binaries
uses: azure/artifact-signing-action@v2
with:
endpoint: ${{ env.AZURE_SIGNING_ENDPOINT }}
signing-account-name: ${{ env.AZURE_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ env.AZURE_CERTIFICATE_PROFILE_NAME }}
files-folder: ${{ github.workspace }}\build\dist\win-x64\bin
files-folder-filter: exe,dll
files-folder-recurse: true
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256

- name: Build installer and auto-update packages
id: pack
shell: pwsh
run: npm run pack -- --verbose

- name: Sign installer
uses: azure/artifact-signing-action@v2
with:
endpoint: ${{ env.AZURE_SIGNING_ENDPOINT }}
signing-account-name: ${{ env.AZURE_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ env.AZURE_CERTIFICATE_PROFILE_NAME }}
files-folder: ${{ github.workspace }}\build\dist\win-x64\pack
files-folder-filter: exe
file-digest: SHA256
timestamp-rfc3161: http://timestamp.acs.microsoft.com
timestamp-digest: SHA256

- name: Upload Windows artifact
uses: actions/upload-artifact@v4
with:
name: artifacts-win-x64
path: |
build/dist/win-x64/pack/*.exe
build/dist/win-x64/pack/*.nupkg
build/dist/win-x64/pack/releases.win.json
if-no-files-found: error

release:
name: Create GitHub Release draft
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
needs:
- build-macos
- build-windows

permissions:
contents: write

steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: dist/

- name: Create GitHub Release draft
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
with:
files: |
dist/artifacts-mac-arm64/**/*.dmg
dist/artifacts-mac-arm64/**/*.nupkg
dist/artifacts-mac-arm64/**/releases.osx.json
dist/artifacts-win-x64/**/*.exe
dist/artifacts-win-x64/**/*.nupkg
dist/artifacts-win-x64/**/releases.win.json
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
draft: true
generate_release_notes: true

- name: List release artifacts
run: ls -R dist
30 changes: 30 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Node dependencies.
node_modules/

# Build output.
build/
dist/
out/

# IDEs and editors.
.idea/
.vscode/

# Local environment and secrets.
.env
.env.*
!.env.example

# Agent session state.
.mobrowser/

# Generated code.
gen/
*.tsbuildinfo

# macOS metadata.
.DS_Store

# Windows metadata.
Thumbs.db
Desktop.ini
87 changes: 87 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<!-- BEGIN:mobrowser-agent-rules -->
# Do not rely on training data for MōBrowser

Your MōBrowser training data is outdated — APIs have been renamed and reorganized, so code written
from prior knowledge will not compile. **Before writing or modifying any MōBrowser code**, read the
docs in `node_modules/@mobrowser/api/docs/`:
- `guides/` — architecture, project structure, process model, IPC, native C++ module, features, examples.
- `api/` — API reference with code examples.

Don't guess API names, signatures, or import paths — look them up. If `docs/` is missing, ask the
user to run `npm run gen` (it downloads the docs when the project contains this `AGENTS.md`).
<!-- END:mobrowser-agent-rules -->

<!-- BEGIN:mobrowser-build -->
# Building & code generation

The `mobrowser` CLI is a **local** dependency, not on your `PATH`. Always run it via npm:
`npm run mobrowser -- <command>` (or `npx --no-install mobrowser <command>`). A bare `mobrowser …`
fails with "command not found".

- `npm run gen` — regenerate IPC/protobuf bindings after editing a `.proto` file (output in
`src/*/gen/`). No launch.
- `npm run dev:build` — build without launching: code generation, the **main** process (plus the C++
module for native projects — slow on first run), and the **renderer** bundle. Run it before
launching for automation, and re-run after main-process, native, or renderer changes. Build errors
fail here — fix them first.
- `npm run mobrowser -- agent launch` — launches the `dev:build` output (does **not** build; errors
if no build exists).
- `npm run dev` — human build-and-run (build + launch). Under `dev` (and `dev -- --automation`) the
renderer is served live by Vite (hot reload, no rebuild); under `agent launch` it comes from the
`dev:build` bundle. The renderer is **not type-checked in dev** — run `npm run build` to catch
renderer/type errors.
- Automation mode lives in `mobrowser.conf.json` (`"automation": { "mode": ... }`): `"interactive"`
(default — window shown, left running) or `"autonomous"` (window hidden, agent stops it when done).
<!-- END:mobrowser-build -->

<!-- BEGIN:mobrowser-agent -->
# Verifying changes by driving the app

`npm run mobrowser -- agent <command>` drives a running app so you can verify changes against the
real thing. Each command prints JSON on stdout and exits non-zero on failure; see `agent --help` for
the full list. (Commands are shortened to `agent …` below — always prefix with `npm run mobrowser --`.)

## Modes: interactive (default) and autonomous

Default to **interactive**; use **autonomous** only when asked ("headless", "run autonomously") or
when `mobrowser.conf.json` sets it. Launch the app **once** and reuse it for every command — never
relaunch per command.
- **Interactive:** window shown; **leave it running** when done (the user closes it) and re-attach on
later turns. Don't `agent stop` unless asked.
- **Autonomous:** window hidden; finish the job, then **always** stop it (even on failure) via
`agent stop` or by terminating the `npm run dev -- --automation` process.

Pick the launch command by task (mode sets visibility automatically):
- **`npm run dev -- --automation`** for **renderer/UI** work: renderer live via Vite (hot reload).
Runs in the foreground — start it in a background terminal, drive from another, stop by terminating it.
- **`npm run mobrowser -- agent launch`** otherwise: the prebuilt app (`dev:build` first), detached;
re-run `dev:build` and relaunch after main-process, native, or renderer changes.

`--show-window[=false]` overrides the mode's default visibility.

Typical loop:
1. `dev:build`, then start the app once (see modes above).
2. `agent snapshot` — accessibility tree with stable `[ref=eN]` markers; prefer it over screenshots.
3. Act by `ref`: `agent click --ref e12`, `fill --ref e5 --value "hi"`, `type --ref e5 --text "hi"`,
`press-key --key Enter`. Confirm with `agent snapshot` or `agent screenshot --out shot.png`.
4. Inspect: `agent eval "location.href"`, `agent console`, `agent network`; `agent list-windows` /
`select-window` for multi-window apps.
5. Finish per mode: interactive — leave running; autonomous — stop.

Native dialogs (alerts, confirms, prompts, file pickers) are app-modal and **intercepted** under
automation. Pre-arm an answer **before** the triggering action (consumed in order):
`agent answer-dialog --button OK | --text "Robot" | --path /tmp/file.txt | --cancel`; inspect with
`agent dialogs`.

Scope: the agent drives the **renderer** (plus native screenshots and dialogs above); it does not
drive or read the main process.

## Falling back to CDP

Prefer the `agent` commands above — use them first. Only when they cannot accomplish the task (e.g.
you need a CDP domain they don't expose, such as tracing, coverage, emulation, or precise input
timing) fall back to the app's Chrome DevTools Protocol (CDP) endpoint with any CDP-compatible tool.
Under `npm run dev`/`npm run run` it is exposed at `http://localhost:9222` by default (override with
`--remote-debugging-port=<port>`; for `agent launch`, enable it with `-- --remote-debugging-port=9222`).
Automate **this app** over that endpoint — do not launch a separate browser.
<!-- END:mobrowser-agent -->
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
Loading
Loading