Contents

Project Overview

Set up a MōBrowser project from scratch and explore its structure and configuration.

To create a cross-platform desktop application, you need to create a MōBrowser project. It contains all the files and dependencies required to build and run your desktop application.

Creating a Project 

Make sure your current working directory is the one where you intend to create a project. Run the following command in your command line:

npm create mobrowser-app@latest

This command will install and execute create-mobrowser-app, the official project scaffolding tool. It asks for:

  1. The project name.
  2. Whether to add a native module to access platform-specific APIs. The native module can be written in C++ or Rust.
  3. Whether to initialize a Git repository in the project directory.
✔ Project name: App
✔ Add a native module? No
✔ Initialize a Git repository? Yes

The tool then creates the project in a new directory, installs its dependencies, and runs code generation, so the project is ready to run with npm run dev.

Project template 

Every project is created from the same template: a React and TypeScript app with shadcn/ui components. It’s a small working desktop app that you can build on:

  • A “Hello, world!” main window with a hidden title bar.
  • An application menu with File, Edit, View, and Window menus (Help on Windows).
  • A Settings window with a theme picker, launch at login, automatic update checks, and an About page.
  • Links that open external URLs in the default browser.
  • The shadcn app kit: Base UI components and ready-made patterns such as pages, a router, a search field, faceted filters, a data table, a confirmation dialog, and a save bar.
  • End-to-end tests for the main window and the Settings window, and GitHub Actions workflows that run them and release the app.

The project is ready for development with an AI coding agent. AGENTS.md, mirrored by CLAUDE.md, explains to the agent how to run, inspect, and test the app. The .agents/skills/ directory contains skills with rules the agent follows:

SkillDescription
shadcnBuilds the renderer UI from the app kit components. Includes the component docs, so the agent doesn’t rely on its training data.
desktop-shellShapes the native side of the app in the main process: the window, the application menu, the tray, the Dock, and the app lifecycle. Makes the agent decide them from the purpose of the app instead of keeping the defaults.
mobrowser-testingMakes the agent keep the checks it makes with the automation commands as end-to-end tests.

Git repository 

When you run the tool outside a Git repository, it asks whether to initialize a Git repository in the new project directory, and runs git init by default. The question is skipped if Git isn’t installed. If git init fails, the tool warns you and keeps the project.

You can also create a repository first, for example on GitHub, clone it, and create the project inside the clone:

gh repo create my-app --private --clone --add-readme
cd my-app
npm create mobrowser-app@latest

When the current directory is the root of a Git repository that has only a README, the tool asks where to create the project:

? This directory is a Git repository root. Generate the project here? (Use arrow keys)
❯ Yes
  No, in a subdirectory

With Yes, the project is created at the repository root, next to the README, and the project name defaults to the name of the directory. The generated GitHub Actions workflows are then at the root of the repository, where GitHub runs them. If the repository has other files, the project is created in a subdirectory without asking.

Opening the project in a coding agent or IDE 

When the project is ready, the tool lists the coding agents and IDEs installed on your machine and offers to open the project in one of them:

? Open it in a coding agent/IDE: (Use arrow keys)
❯ ChatGPT Desktop (Codex)
  Claude Code
  Claude Desktop
  Codex
  Cursor
  VS Code
  No

Only the tools found on your machine are listed:

ToolWhat happens
Cursor, VS CodeOpens the project directory in the IDE.
Claude DesktopStarts a new Claude Code session in the project directory.
ChatGPT Desktop (Codex)Starts a new Codex thread in the project directory.
Claude Code, CodexPrints the commands that start the agent in the project directory. The agent is not listed when you run the tool inside it.

For example, for Claude Code:

To open the project in Claude Code, run:
  cd App
  claude

Choose No to see how to run the app instead. The tool doesn’t ask this question when no coding agent or IDE is found, or when it runs without an interactive terminal.

Creating a project without prompts 

To create a project without prompts, for example in a script, pass the options on the command line:

npm create mobrowser-app@latest -- --name App --native rust --git no
OptionDescription
--name <name>The project name. Required.
--native <no|cpp|rust>Adds a native C++ or Rust module. The default is no.
--setup <yes|no>Installs dependencies and runs code generation. The default is yes.
--git <yes|no>Initializes a Git repository. The default is yes, or no inside an existing repository.
--dir .Creates the project in the current directory, which must be empty or the root of a Git repository with only a README.
--help, -hShows the available options.

Without prompts, the tool doesn’t offer to open the project in a coding agent or IDE.

Project structure 

If you add a native C++ module, you will see the following files and folders in the generated project:

.agents/
├── skills/
.github/
├── workflows/
.vscode/
assets/
resources/
src/
├── main/
├── native/
├── renderer/
test/
├── e2e/
.gitignore
AGENTS.md
CLAUDE.md
CMakeLists.txt
components.json
mobrowser.conf.json
package.json
playwright.config.ts
postcss.config.js
tsconfig.json
tsconfig.node.json
vite.config.ts

Here is the description of the files and folders in the project structure:

File/FolderDescription
.agents/skills/Skills for coding agents.
.github/workflows/GitHub Actions workflows that test and release the app.
.vscode/Visual Studio Code launch and task configuration.
assets/Application assets such as icons, images, and other resources.
resources/Application resources that shouldn’t be packed into app.bin.
src/main/Main process source code.
src/renderer/Renderer process source code.
src/native/Native C++ module source code.
test/e2e/End-to-end tests.
.gitignoreFiles and folders Git ignores, such as node_modules/ and build/.
AGENTS.mdInstructions for coding agents.
CLAUDE.mdInstructions for Claude Code. It refers to AGENTS.md.
CMakeLists.txtCMake build configuration of the native C++ module.
components.jsonshadcn/ui configuration file.
mobrowser.conf.jsonApplication configuration file.
package.jsonnpm package configuration file with the project dependencies and scripts.
playwright.config.tsPlaywright configuration file for the end-to-end tests.
postcss.config.jsPostCSS configuration file for Tailwind CSS.
tsconfig.jsonTypeScript configuration file.
tsconfig.node.jsonTypeScript configuration file for Node.js.
vite.config.tsVite configuration file.

A project without a native module has no src/native/ and CMakeLists.txt.

A project with a native Rust module has Cargo.toml, Cargo.lock, build.rs, and rust-toolchain.toml instead of CMakeLists.txt. See Native Rust Module.

Project configuration 

The mobrowser.conf.json file is the main project and application configuration file. It contains the application name, version, author, copyright, description, and other properties.

Here’s an example of the mobrowser.conf.json file:

{
  "app": {
    "name": "Stats",
    "version": {
      "major": "1",
      "minor": "0",
      "patch": "0"
    },
    "author": "TeamDev",
    "copyright": "Copyright © 2026 TeamDev",
    "description": "System resources monitor and process explorer.",
    "locales": ["en-US"],
    "trustedOrigins": [],
    "schemes": [],
    "updateServerUrl": "",
    "bundle": {
      "macOS": {
        "icon": "assets/app.icns",
        "executableName": "Stats",
        "bundleID": "com.teamdev.Stats",
        "codesignIdentity": "${MACOS_CODESIGN_IDENTITY}",
        "codesignKeychain": "${KEYCHAIN_PATH}",
        "codesignEntitlements": "assets/entitlements.plist",
        "teamID": "${MACOS_TEAM_ID}",
        "appleID": "${MACOS_APPLE_ID}",
        "password": "${MACOS_APPLE_PASSWORD}",
        "extraInfo": "",
        "installer": {
          "dmg": {
            "name": "",
            "volumeName": "",
            "volumeIcon": "assets/app.icns",
            "eula": "",
            "deltaStrategy": "BestSize",
            "window": {
              "textSize": 14,
              "skipPrettifying": false,
              "backgroundImage": "",
              "position": {
                "x": 500,
                "y": 400
              },
              "size": {
                "width": 600,
                "height": 400
              },
              "icon": {
                "size": 150,
                "position": {
                  "x": 160,
                  "y": 160
                }
              },
              "appDropLink": {
                "x": 430,
                "y": 160
              }
            }
          }
        }
      },
      "Windows": {
        "icon": "assets/app.ico",
        "executableName": "Stats",
        "signCommand": "",
        "installer": {
          "exe": {
            "name": "",
            "icon": "assets/app.ico",
            "installationGif": "",
            "packageId": "Stats",
            "installationFolderName": "Stats",
            "displayName": "Stats",
            "shortcuts": "Desktop,StartMenuRoot",
            "deltaStrategy": "BestSpeed"
          }
        }
      },
      "Linux": {
        "icon": "assets/app.png",
        "executableName": "Stats"
      }
    }
  }
}

Here’s the description of the properties in the mobrowser.conf.json file. Paths may be absolute or relative to the project root. You can reference environment variables in string values using the ${ENV_NAME} syntax, which is especially useful for signing credentials.

Application properties 

PropertyDescription
app.nameThe user-visible application name. It appears in places such as window titles, the About dialog, and the system menu. It is also used for executable and installer names unless you set a platform-specific executableName. The display name may contain spaces and non-ASCII characters.
app.versionThe application version, represented by the major, minor, and patch string properties. MōBrowser combines them into a version such as 1.0.0; this value is available at runtime as app.version and is used in packaged artifact names and update metadata.
app.version.majorThe major version component. Increment it for incompatible or otherwise major releases; for example, "2" in version 2.0.0.
app.version.minorThe minor version component. Increment it for backward-compatible feature releases; for example, "3" in version 2.3.0.
app.version.patchThe patch version component. Increment it for backward-compatible fixes; for example, "4" in version 2.3.4.
app.authorThe person, team, or organization that publishes the application, for example, "TeamDev". This value is included in the packaged application’s metadata and is displayed in Windows file properties.
app.copyrightThe application’s copyright notice, for example, "Copyright © 2026 TeamDev". This value is included in package metadata, is displayed in Windows file properties, and is available at runtime as app.copyright.
app.descriptionA short, user-facing summary of the application’s purpose. This value is included in package metadata, is displayed in Windows file properties, and is available at runtime as app.description.
app.localesAn array of locale identifiers to include in the application, for example, ["en-US", "de", "fr"]. Limiting this list reduces the bundle size. Use ["all"] to include every supported locale. If this property is omitted or no configured locale matches the system locale, the application uses en-US. See Localization for the supported identifiers.
app.trustedOriginsAn array of remote origins that receive privileges normally reserved for the bundled application origin: access to the renderer-to-main IPC bridge, automatic grants for framework permission requests, and permission to be used as top-level in-app navigation targets. Entries may be exact origins ("https://app.example.com", "http://localhost:8000"), hostname patterns ("*.example.com"), or scheme-qualified hostname patterns ("https://*.example.com"). URL paths and broad public-suffix patterns such as "*.com" are not allowed. Keep this list as narrow as possible because trusted content can invoke exposed main-process services and receive sensitive permissions.
app.schemesAn array of custom URL scheme names, without ://, for example, ["stats"] for stats://open. The packaged application is registered as a handler for these schemes so links can launch or activate it. Handle incoming URLs with the openUrl app handler; on Windows and Linux, also inspect the url command-line switch when a link launches an app that was not already running. See Schemes.
app.updateServerUrlThe HTTPS URL of the server or cloud bucket that hosts application updates. When set, a packaged macOS application adds the built-in Check for Updates… menu item, and a Windows window menu can expose the same flow through the checkForUpdates role. Native dialogs report progress on both platforms. Leave it empty to implement a custom flow with the auto-update API. See Built-in update flow.
app.bundlePlatform-specific packaging, branding, signing, and installer settings. Configure the macOS, Windows, and Linux objects independently.

app.bundle.macOS properties 

PropertyDescription
iconThe path to the application icon in .icns format.
executableNameThe file-system name used for the executable and the .app bundle. It may contain only ASCII letters, digits, spaces, hyphens, and underscores. Do not include a file extension. If omitted, app.name is used. See Executable name.
extrasAn array of files and directories to copy into the packaged application’s Contents directory. An entry may be a path, a directory path ending in / to copy its contents, or a path with a wildcard in its final file-name segment, such as "schemas/*.json". For a custom destination, use an object such as { "from": "native/lib.dylib", "to": "Frameworks/lib.dylib", "sign": true }; from is the source path, to is relative to Contents, and the optional sign Boolean requests code signing for copied binaries. See Third-party resources.
bundleIDThe unique application bundle identifier in reverse-DNS format, for example, "com.teamdev.Stats".
codesignIdentityThe name of the code-signing certificate installed in Keychain Access, such as a Developer ID Application identity. Leave it empty only when producing an unsigned development build.
codesignKeychainThe path to the keychain containing the code-signing identity. Set it when the identity is stored outside the default keychain, which is common on CI build agents.
codesignEntitlementsThe path to the .plist file containing the entitlements applied during code signing.
provisioningProfileThe path to an Apple-issued .provisionprofile file to embed in the application bundle. It is required when the entitlements include keychain-access-groups, for example, for the Touch ID WebAuthn authenticator.
teamIDThe Team ID of the Apple Developer account used to sign and notarize the application.
appleIDThe Apple Developer account email used for notarization.
passwordThe app-specific password for the Apple ID used for notarization. Store it in an environment variable instead of committing it to the configuration file.
extraInfoThe path to a valid .plist file whose keys are merged into the generated application Info.plist. Values in this file override generated values with the same keys. Use it for settings such as privacy usage descriptions or LSUIElement.
installer.dmgSettings for the macOS DMG installer.
installer.dmg.nameThe DMG file name. Leave it empty to generate a name from the application name, version, and architecture, such as Stats-1.0.0-arm64.dmg.
installer.dmg.volumeNameThe mounted volume name shown in the Finder sidebar. Leave it empty to derive it from app.name.
installer.dmg.volumeIconThe path to the mounted volume’s .icns icon. Leave it empty to use the standard macOS volume icon.
installer.dmg.eulaThe path to an End User License Agreement text file. When set, users must accept or decline the agreement when opening the DMG; leave it empty to omit this dialog.
installer.dmg.deltaStrategyThe delta-package generation strategy: "None" disables delta packages, "BestSpeed" optimizes their generation speed, and "BestSize" minimizes their size.
installer.dmg.window.textSizeThe text size used in the Finder window for the mounted DMG.
installer.dmg.window.skipPrettifyingA Boolean. Set it to true to skip Finder window customization, for example, on a CI agent without a logged-in macOS user.
installer.dmg.window.backgroundImageThe path to the DMG window background image. Leave it empty for no background image.
installer.dmg.window.positionThe initial x and y coordinates of the DMG window on the main screen.
installer.dmg.window.sizeThe width and height of the DMG window in points.
installer.dmg.window.icon.sizeThe application icon size in the DMG window.
installer.dmg.window.icon.positionThe x and y coordinates of the application icon in the DMG window.
installer.dmg.window.appDropLinkThe x and y coordinates of the Applications folder drop link in the DMG window.

app.bundle.Windows properties 

PropertyDescription
iconThe path to the application icon in .ico format.
executableNameThe file-system name used for the application executable and other platform artifacts. It may contain only ASCII letters, digits, spaces, hyphens, and underscores. Do not include the .exe extension. If omitted, app.name is used. See Executable name.
extrasAn array of files and directories to copy into the packaged application. An entry may be a path, a directory path ending in / to copy its contents, or a path with a wildcard in its final file-name segment, such as "schemas/*.json". For a custom destination, use an object such as { "from": "native/lib.dll", "to": "lib/lib.dll", "sign": true }; from is the source path, to is relative to the packaged application, and the optional sign Boolean requests code signing for copied binaries. See Third-party resources.
signCommandThe command used to sign application binaries and the installer. Include @@BINARY_PATH@@ where the absolute path of each binary should be inserted, for example, "signtool sign /a @@BINARY_PATH@@". MōBrowser runs the command once for every binary that requires signing.
installer.exeSettings for the Windows executable installer.
installer.exe.nameThe installer file name. Leave it empty to generate a name from the application name and version, such as Stats-1.0.0.exe.
installer.exe.iconThe path to the installer’s .ico icon. Leave it empty to use the system default icon.
installer.exe.installationGifThe path to the GIF displayed while installation is in progress. Leave it empty to show the default system progress dialog.
installer.exe.packageIdThe NuGet package identifier under which the application is stored on the system. If omitted, MōBrowser uses app.name without spaces. It is also the default installation folder name.
installer.exe.installationFolderNameThe folder under %LOCALAPPDATA% in which the application is installed. It may contain spaces and other file-system-safe characters. If omitted, packageId is used.
installer.exe.displayNameThe application name shown by Windows and used for shortcuts. If omitted, app.name is used.
installer.exe.shortcutsA comma-separated list of shortcut locations. Supported values are None, StartMenu, Desktop, Startup, and StartMenuRoot; for example, "Desktop,StartMenuRoot". If omitted, Desktop,StartMenuRoot is used.
installer.exe.deltaStrategyThe delta-package generation strategy: "None" disables delta packages, "BestSpeed" optimizes their generation speed, and "BestSize" minimizes their size.
installer.msixSettings for the Microsoft Store (MSIX) package. See Publishing to Microsoft Store.

app.bundle.Linux properties 

PropertyDescription
iconThe path to the application icon in PNG format. A 128×128 image is sufficient for most Linux distributions.
executableNameThe file-system name used for the application executable and other platform artifacts. It may contain only ASCII letters, digits, spaces, hyphens, and underscores. Do not include a file extension. If omitted, app.name is used. See Executable name.
extrasAn array of files and directories to copy into the packaged application. An entry may be a path, a directory path ending in / to copy its contents, or a path with a wildcard in its final file-name segment, such as "schemas/*.json". For a custom destination, use an object such as { "from": "native/lib.so", "to": "lib/lib.so" }; from is the source path and to is relative to the packaged application. See Third-party resources.

vite.config.ts 

The framework uses Vite as the build tool for both the main and the renderer processes.

Each MōBrowser project includes a vite.config.ts file. It defines the build configuration for the main and the renderer processes.