16 |
17 | window.on('closed', () => {
18 | window = null
19 | })
20 |
21 | callback?.(window, event)
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/template/src/renderer/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/build/fileFetcher.js:
--------------------------------------------------------------------------------
1 | (()=>{function fetchResource(e){const reject=t=>{chrome.runtime.sendMessage({source:"react-devtools-fetch-resource-content-script",payload:{type:"fetch-file-with-cache-error",url:e,value:t}})};fetch(e,{cache:"force-cache",signal:AbortSignal.timeout(6e4)}).then((t=>{t.ok?t.text().then((t=>{return c=t,void chrome.runtime.sendMessage({source:"react-devtools-fetch-resource-content-script",payload:{type:"fetch-file-with-cache-complete",url:e,value:c}});var c})).catch((e=>reject(null))):reject(null)}),(e=>reject(null)))}chrome.runtime.onMessage.addListener((e=>{"devtools-page"===e?.source&&"fetch-file-with-cache"===e?.payload?.type&&fetchResource(e.payload.url)}))})();
--------------------------------------------------------------------------------
/template/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "[javascript]": {
3 | "editor.formatOnSave": true
4 | },
5 |
6 | "[json]": {
7 | "editor.formatOnSave": true
8 | },
9 |
10 | "[typescript]": {
11 | "editor.formatOnSave": true
12 | },
13 |
14 | "[typescriptreact]": {
15 | "editor.formatOnSave": true
16 | },
17 |
18 | "biome.enabled": true,
19 |
20 | "editor.codeActionsOnSave": {
21 | "source.fixAll.biome": "explicit"
22 | },
23 |
24 | "editor.defaultFormatter": "biomejs.biome",
25 |
26 | "editor.quickSuggestions": {
27 | "strings": "on"
28 | },
29 |
30 | "files.associations": {
31 | "*.css": "tailwindcss"
32 | },
33 |
34 | "tailwindCSS.experimental.configFile": "src/renderer/globals.css"
35 | }
36 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/popups/outdated.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 | This page is using an outdated version of React. ⌛
15 |
16 |
17 | We recommend updating React to ensure that you receive important bugfixes and performance improvements.
18 |
19 |
20 | You can find the upgrade instructions on the React blog .
21 |
22 |
23 |
24 | Open the developer tools, and "Components" and "Profiler" tabs will appear to the right.
25 |
26 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/popups/development.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 | This page is using the development build of React. 🚧
15 |
16 |
17 | Note that the development build is not suitable for production.
18 |
19 | Make sure to use the production build before deployment.
20 |
21 |
22 |
23 | Open the developer tools, and "Components" and "Profiler" tabs will appear to the right.
24 |
25 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/release/utils/exec.ts:
--------------------------------------------------------------------------------
1 | import {
2 | execSync,
3 | type ExecSyncOptionsWithStringEncoding,
4 | } from 'node:child_process'
5 |
6 | import { resolve } from 'node:path'
7 |
8 | interface Options {
9 | inherit?: boolean
10 | }
11 |
12 | function makeOptions(
13 | options?: Options
14 | ): Partial {
15 | return {
16 | stdio: options?.inherit ? 'inherit' : 'pipe',
17 | cwd: resolve(),
18 | encoding: 'utf8',
19 | }
20 | }
21 |
22 | export function exec(commands: string[], options?: Options) {
23 | const outputs = []
24 |
25 | for (const command of commands) {
26 | const output = execSync(command, makeOptions(options)) as string
27 | outputs.push(output)
28 | }
29 |
30 | return outputs
31 | }
32 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/build/hookSettingsInjector.js:
--------------------------------------------------------------------------------
1 | window.addEventListener("message",(async function messageListener(e){if(e.source===window&&"react-devtools-hook-installer"===e.data.source&&e.data.payload.handshake){const e=await chrome.storage.local.get();"boolean"!=typeof e.appendComponentStack&&(e.appendComponentStack=!0),"boolean"!=typeof e.breakOnConsoleErrors&&(e.breakOnConsoleErrors=!1),"boolean"!=typeof e.showInlineWarningsAndErrors&&(e.showInlineWarningsAndErrors=!0),"boolean"!=typeof e.hideConsoleLogsInStrictMode&&(e.hideConsoleLogsInStrictMode=!1),window.postMessage({source:"react-devtools-hook-settings-injector",payload:{settings:e}}),window.removeEventListener("message",messageListener)}})),window.postMessage({source:"react-devtools-hook-settings-injector",payload:{handshake:!0}});
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/popups/shared.js:
--------------------------------------------------------------------------------
1 | /* globals chrome */
2 |
3 | 'use strict';
4 |
5 | document.addEventListener('DOMContentLoaded', function () {
6 | // Make links work
7 | const links = document.getElementsByTagName('a');
8 | for (let i = 0; i < links.length; i++) {
9 | (function () {
10 | const ln = links[i];
11 | const location = ln.href;
12 | ln.onclick = function () {
13 | chrome.tabs.create({active: true, url: location});
14 | return false;
15 | };
16 | })();
17 | }
18 |
19 | // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=428044
20 | document.body.style.opacity = 0;
21 | document.body.style.transition = 'opacity ease-out .4s';
22 | requestAnimationFrame(function () {
23 | document.body.style.opacity = 1;
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/template/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "target": "esnext",
5 | "lib": ["esnext", "dom", "dom.iterable"],
6 | "jsx": "react-jsx",
7 | "importHelpers": true,
8 | "moduleResolution": "bundler",
9 | "module": "ESNext",
10 | "resolveJsonModule": true,
11 | "esModuleInterop": true,
12 | "sourceMap": true,
13 | "isolatedModules": true,
14 | "allowJs": true,
15 | "allowSyntheticDefaultImports": true,
16 | "skipLibCheck": true,
17 | "forceConsistentCasingInFileNames": true,
18 | "noEmit": true,
19 | "baseUrl": ".",
20 | "paths": {
21 | "*": ["src/*"],
22 | "~/*": ["./*"]
23 | }
24 | },
25 | "include": [
26 | "./src",
27 | "electron.vite.config.*",
28 | "electron-builder.ts",
29 | "index.d.ts"
30 | ],
31 | "exclude": ["node_modules"]
32 | }
33 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/popups/unminified.html:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 | This page is using an unminified build of React. 🚧
19 |
20 |
21 | The React build on this page appears to be unminified.
22 |
23 | This makes its size larger, and causes React to run slower.
24 |
25 |
26 | Make sure to set up minification before deployment.
27 |
28 |
29 |
30 | Open the developer tools, and "Components" and "Profiler" tabs will appear to the right.
31 |
32 |
--------------------------------------------------------------------------------
/template/src/main/index.ts:
--------------------------------------------------------------------------------
1 | import { app } from 'electron'
2 |
3 | import { makeAppWithSingleInstanceLock } from 'lib/electron-app/factories/app/instance'
4 | import { makeAppSetup } from 'lib/electron-app/factories/app/setup'
5 | import { loadReactDevtools } from 'lib/electron-app/utils'
6 | import { ENVIRONMENT } from 'shared/constants'
7 | import { MainWindow } from './windows/main'
8 | import { waitFor } from 'shared/utils'
9 |
10 | makeAppWithSingleInstanceLock(async () => {
11 | await app.whenReady()
12 | const window = await makeAppSetup(MainWindow)
13 |
14 | if (ENVIRONMENT.IS_DEV) {
15 | await loadReactDevtools()
16 | /* This trick is necessary to get the new
17 | React Developer Tools working at app initial load.
18 | Otherwise, it only works on manual reload.
19 | */
20 | window.webContents.once('devtools-opened', async () => {
21 | await waitFor(1000)
22 | window.webContents.reload()
23 | })
24 | }
25 | })
26 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/popups/deadcode.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 | This page includes an extra development build of React. 🚧
15 |
16 |
17 | The React build on this page includes both development and production versions because dead code elimination has not been applied correctly.
18 |
19 |
20 | This makes its size larger, and causes React to run slower.
21 |
22 |
23 | Make sure to set up dead code elimination before deployment.
24 |
25 |
26 |
27 | Open the developer tools, and "Components" and "Profiler" tabs will appear to the right.
28 |
29 |
--------------------------------------------------------------------------------
/template/src/shared/utils.ts:
--------------------------------------------------------------------------------
1 | import { author as _author, name } from '~/package.json'
2 |
3 | const author = _author.name ?? _author
4 | const authorInKebabCase = author.replace(/\s+/g, '-')
5 | const appId = `com.${authorInKebabCase}.${name}`.toLowerCase()
6 |
7 | /**
8 | * @param {string} id
9 | * @description Create the app id using the name and author from package.json transformed to kebab case if the id is not provided.
10 | * @default 'com.{author}.{app}' - the author and app comes from package.json
11 | * @example
12 | * makeAppId('com.example.app')
13 | * // => 'com.example.app'
14 | */
15 | export function makeAppId(id: string = appId): string {
16 | return id
17 | }
18 |
19 | /**
20 | *
21 | * @param {number} ms
22 | * @description Wait for a given number of milliseconds.
23 | * @example
24 | * await waitFor(1000) // Waits for 1 second
25 | */
26 | export function waitFor(ms: number) {
27 | return new Promise(resolve => setTimeout(resolve, ms))
28 | }
29 |
--------------------------------------------------------------------------------
/docs/STRUCTURE.md:
--------------------------------------------------------------------------------
1 | Structure Overview
2 |
3 |
4 |
5 | ## src/lib
6 |
7 | A folder containing lib configurations/instances.
8 |
9 | ## src/main
10 |
11 | A folder containing the main process files and folders.
12 |
13 | ## src/renderer
14 |
15 | A folder containing the renderer process files and folders. ReactJS lives here!
16 |
17 | ## src/preload
18 | A folder containing the preload script that expose the API connection between main and renderer world by IPC in the context bridge.
19 |
20 | ## src/resources
21 |
22 | A folder containing public assets and assets for the build process like icons.
23 |
24 | > **Note**: all the content inside the **public** folder will be copied to the builded version as its.
25 |
26 | ## src/shared
27 |
28 | A folder containing data shared between one or more processes, such as constants, utilities, types, etc.
29 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/release/utils/validations.ts:
--------------------------------------------------------------------------------
1 | import semver from 'semver'
2 |
3 | import { COLORS } from '../constants/colors'
4 |
5 | export function checkValidations({
6 | version,
7 | newVersion,
8 | }: {
9 | version: string
10 | newVersion: string
11 | }) {
12 | if (!newVersion) {
13 | console.log(`${COLORS.RED}No version entered${COLORS.RESET}`)
14 |
15 | return true
16 | }
17 |
18 | if (!semver.valid(newVersion)) {
19 | console.log(
20 | `${COLORS.RED}Version must have a semver format (${COLORS.SOFT_GRAY}x.x.x${COLORS.RESET} example: ${COLORS.GREEN}1.0.1${COLORS.RESET}${COLORS.RED})${COLORS.RESET}`
21 | )
22 |
23 | return true
24 | }
25 |
26 | if (semver.ltr(newVersion, version)) {
27 | console.log(
28 | `${COLORS.RED}New version is lower than current version${COLORS.RESET}`
29 | )
30 |
31 | return true
32 | }
33 |
34 | if (semver.eq(newVersion, version)) {
35 | console.log(
36 | `${COLORS.RED}New version is equal to current version${COLORS.RESET}`
37 | )
38 |
39 | return true
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/template/src/main/windows/main.ts:
--------------------------------------------------------------------------------
1 | import { BrowserWindow } from 'electron'
2 | import { join } from 'node:path'
3 |
4 | import { createWindow } from 'lib/electron-app/factories/windows/create'
5 | import { ENVIRONMENT } from 'shared/constants'
6 | import { displayName } from '~/package.json'
7 |
8 | export async function MainWindow() {
9 | const window = createWindow({
10 | id: 'main',
11 | title: displayName,
12 | width: 700,
13 | height: 473,
14 | show: false,
15 | center: true,
16 | movable: true,
17 | resizable: false,
18 | alwaysOnTop: true,
19 | autoHideMenuBar: true,
20 |
21 | webPreferences: {
22 | preload: join(__dirname, '../preload/index.js'),
23 | },
24 | })
25 |
26 | window.webContents.on('did-finish-load', () => {
27 | if (ENVIRONMENT.IS_DEV) {
28 | window.webContents.openDevTools({ mode: 'detach' })
29 | }
30 |
31 | window.show()
32 | })
33 |
34 | window.on('close', () => {
35 | for (const window of BrowserWindow.getAllWindows()) {
36 | window.destroy()
37 | }
38 | })
39 |
40 | return window
41 | }
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 - * Dalton Menezes
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/template/src/renderer/screens/main.tsx:
--------------------------------------------------------------------------------
1 | import { Terminal } from 'lucide-react'
2 | import { useEffect } from 'react'
3 |
4 | import {
5 | Alert,
6 | AlertTitle,
7 | AlertDescription,
8 | } from 'renderer/components/ui/alert'
9 |
10 | // The "App" comes from the context bridge in preload/index.ts
11 | const { App } = window
12 |
13 | export function MainScreen() {
14 | useEffect(() => {
15 | // check the console on dev tools
16 | App.sayHelloFromBridge()
17 | }, [])
18 |
19 | const userName = App.username || 'there'
20 |
21 | return (
22 |
23 |
24 |
25 | Hi, {userName}!
26 |
27 |
28 |
29 |
30 |
31 |
32 | It's time to build something awesome!
33 |
34 |
35 |
36 |
37 | )
38 | }
39 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/factories/app/setup.ts:
--------------------------------------------------------------------------------
1 | import { app, BrowserWindow } from 'electron'
2 |
3 | import { PLATFORM, ENVIRONMENT } from 'shared/constants'
4 | import { ignoreConsoleWarnings } from '../../utils'
5 | import { makeAppId } from 'shared/utils'
6 |
7 | ignoreConsoleWarnings(['Manifest version 2 is deprecated'])
8 |
9 | export async function makeAppSetup(createWindow: () => Promise) {
10 | let window = await createWindow()
11 |
12 | app.on('activate', async () => {
13 | const windows = BrowserWindow.getAllWindows()
14 |
15 | if (!windows.length) {
16 | window = await createWindow()
17 | } else {
18 | for (window of windows.reverse()) {
19 | window.restore()
20 | }
21 | }
22 | })
23 |
24 | app.on('web-contents-created', (_, contents) =>
25 | contents.on(
26 | 'will-navigate',
27 | (event, _) => !ENVIRONMENT.IS_DEV && event.preventDefault()
28 | )
29 | )
30 |
31 | app.on('window-all-closed', () => !PLATFORM.IS_MAC && app.quit())
32 |
33 | return window
34 | }
35 |
36 | PLATFORM.IS_LINUX && app.disableHardwareAcceleration()
37 |
38 | PLATFORM.IS_WINDOWS &&
39 | app.setAppUserModelId(ENVIRONMENT.IS_DEV ? process.execPath : makeAppId())
40 |
41 | app.commandLine.appendSwitch('force-color-profile', 'srgb')
42 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/release/modules/prebuild.ts:
--------------------------------------------------------------------------------
1 | /** biome-ignore-all lint/correctness/noUnusedVariables: <> */
2 | import { resolve, basename } from 'node:path'
3 | import { writeFile } from 'node:fs/promises'
4 |
5 | import trustedDependencies from '../../../../../trusted-dependencies-scripts.json'
6 | import packageJSON from '../../../../../package.json'
7 | import { getDevFolder } from '../utils/path'
8 |
9 | async function createPackageJSONDistVersion() {
10 | const { main, scripts, resources, devDependencies, ...rest } = packageJSON
11 |
12 | const packageJSONDistVersion = {
13 | main: `./main/${basename(main || 'index.mjs')}`,
14 | ...rest,
15 | }
16 |
17 | try {
18 | await Promise.all([
19 | writeFile(
20 | resolve(getDevFolder(main), 'package.json'),
21 | JSON.stringify(packageJSONDistVersion, null, 2)
22 | ),
23 |
24 | writeFile(
25 | resolve(getDevFolder(main), packageJSON.pnpm.onlyBuiltDependenciesFile),
26 | JSON.stringify(trustedDependencies, null, 2)
27 | ),
28 | ])
29 | } catch ({ message }: any) {
30 | console.log(`
31 | 🛑 Something went wrong!\n
32 | 🧐 There was a problem creating the package.json dist version...\n
33 | 👀 Error: ${message}
34 | `)
35 | }
36 | }
37 |
38 | createPackageJSONDistVersion()
39 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/build/proxy.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";function injectProxy({target:e}){if(!window.__REACT_DEVTOOLS_PROXY_INJECTED__){window.__REACT_DEVTOOLS_PROXY_INJECTED__=!0,connectPort(),sayHelloToBackendManager();const e=setInterval((()=>{n?clearInterval(e):sayHelloToBackendManager()}),500)}}window.addEventListener("pagereveal",injectProxy),window.addEventListener("pageshow",injectProxy),window.addEventListener("pagehide",(function({target:e}){e===window.document&&delete window.__REACT_DEVTOOLS_PROXY_INJECTED__}));let e=null,n=!1;function sayHelloToBackendManager(){window.postMessage({source:"react-devtools-content-script",hello:!0},"*")}function handleMessageFromDevtools(e){window.postMessage({source:"react-devtools-content-script",payload:e},"*")}function handleMessageFromPage(o){if(o.source===window&&o.data)switch(o.data.source){case"react-devtools-bridge":n=!0,e.postMessage(o.data.payload);break;case"react-devtools-backend-manager":{const{source:e,payload:n}=o.data;chrome.runtime.sendMessage({source:e,payload:n});break}}}function handleDisconnect(){window.removeEventListener("message",handleMessageFromPage),e=null,connectPort()}function connectPort(){e=chrome.runtime.connect({name:"proxy"}),window.addEventListener("message",handleMessageFromPage),e.onMessage.addListener(handleMessageFromDevtools),e.onDisconnect.addListener(handleDisconnect)}})();
--------------------------------------------------------------------------------
/template/electron-builder.ts:
--------------------------------------------------------------------------------
1 | /** biome-ignore-all lint/suspicious/noTemplateCurlyInString: <> */
2 | import type { Configuration } from 'electron-builder'
3 |
4 | import {
5 | main,
6 | name,
7 | version,
8 | resources,
9 | description,
10 | displayName,
11 | author as _author,
12 | } from './package.json'
13 |
14 | import { getDevFolder } from './src/lib/electron-app/release/utils/path'
15 |
16 | const author = _author?.name ?? _author
17 | const currentYear = new Date().getFullYear()
18 | const authorInKebabCase = author.replace(/\s+/g, '-')
19 | const appId = `com.${authorInKebabCase}.${name}`.toLowerCase()
20 |
21 | const artifactName = [`${name}-v${version}`, '-${os}.${ext}'].join('')
22 |
23 | export default {
24 | appId,
25 | productName: displayName,
26 | copyright: `Copyright © ${currentYear} — ${author}`,
27 |
28 | directories: {
29 | app: getDevFolder(main),
30 | output: `dist/v${version}`,
31 | },
32 |
33 | mac: {
34 | artifactName,
35 | icon: `${resources}/build/icons/icon.icns`,
36 | category: 'public.app-category.utilities',
37 | target: ['zip', 'dmg', 'dir'],
38 | },
39 |
40 | linux: {
41 | artifactName,
42 | category: 'Utilities',
43 | synopsis: description,
44 | target: ['AppImage', 'deb', 'pacman', 'freebsd', 'rpm'],
45 | },
46 |
47 | win: {
48 | artifactName,
49 | icon: `${resources}/build/icons/icon.ico`,
50 | target: ['zip', 'portable'],
51 | },
52 | } satisfies Configuration
53 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/icons/outdated.svg:
--------------------------------------------------------------------------------
1 | outdated
--------------------------------------------------------------------------------
/template/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags: ["*"]
4 |
5 | jobs:
6 | release:
7 | if: startsWith(github.ref, 'refs/tags/v')
8 | runs-on: ${{ matrix.os }}
9 |
10 | strategy:
11 | matrix:
12 | os: [macos-latest, ubuntu-latest, windows-latest]
13 |
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v4
17 | - run: npm i -g --force corepack && corepack enable
18 |
19 | - name: Install Node.js, NPM and PNPM
20 | uses: actions/setup-node@v4
21 | with:
22 | node-version: 20
23 | cache: "pnpm"
24 | - run: pnpm install
25 |
26 | - name: apt-update
27 | if: startsWith(matrix.os, 'ubuntu-latest')
28 | run: sudo apt-get update
29 |
30 | - name: autoremove
31 | if: startsWith(matrix.os, 'ubuntu-latest')
32 | run: sudo apt autoremove
33 |
34 | - name: Install libarchive rpm on Linux
35 | if: startsWith(matrix.os, 'ubuntu-latest')
36 | run: sudo apt-get install libarchive-tools rpm
37 |
38 | - name: Release Electron app
39 | uses: daltonmenezes/action-electron-builder@v1.0.1
40 | with:
41 | package_manager: "pnpm"
42 | # GitHub token, automatically provided to the action
43 | # (No need to define this secret in the repo settings)
44 | github_token: ${{ secrets.github_token }}
45 |
46 | # If the commit is tagged with a version (e.g. "v1.0.0"),
47 | # release the app after building
48 | release: true
49 |
--------------------------------------------------------------------------------
/docs/SOURCE_CODE_PROTECTION.md:
--------------------------------------------------------------------------------
1 | Source Code Protection
2 |
3 | > This process is done via [v8 bytecode compilation](https://nodejs.org/api/vm.html#vm_script_createcacheddata), to get more knowledge about it, please, [check the Electron Vite docs](https://evite.netlify.app/guide/source-code-protection.html).
4 |
5 | Use the `bytecodePlugin` from `electron-vite` to enable it in the **electron.vite.config.ts**:
6 |
7 | ```ts
8 | import { defineConfig, bytecodePlugin } from 'electron-vite'
9 |
10 | export default defineConfig({
11 | main: {
12 | plugins: [tsconfigPaths, bytecodePlugin({ transformArrowFunctions: false })]
13 | },
14 |
15 | preload: {
16 | // Note: you will get the following warning using bytecodePlugin in the preload script in production build: "The vm module of Node.js is deprecated in the renderer process and will be removed", is up to you to keep bytecodePlugin here. Also, keep following the Electron Vite docs for more updates about this plugin!
17 | plugins: [tsconfigPaths, bytecodePlugin({ transformArrowFunctions: false })]
18 | },
19 |
20 | renderer: {
21 | // ...
22 | }
23 | })
24 | ```
25 | Also, `sandbox` should be `false` in `webPreferences` for the windows you are using a preload script like:
26 | ```ts
27 | const window = createWindow({
28 | id: 'main',
29 |
30 | webPreferences: {
31 | preload: join(__dirname, '../preload/index.js'),
32 | sandbox: false,
33 | },
34 | })
35 | ```
36 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/icons/disabled.svg:
--------------------------------------------------------------------------------
1 | disabled
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/icons/restricted.svg:
--------------------------------------------------------------------------------
1 | disabled
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/icons/production.svg:
--------------------------------------------------------------------------------
1 | production
--------------------------------------------------------------------------------
/docs/FAQ.md:
--------------------------------------------------------------------------------
1 | FAQ - Frequently Asked Questions
2 |
3 | ## How can I fix the `electron-builder install-app-deps Windows Script Host` error?
4 | If you are trying to use this boilerplate with npm instead pnpm on Windows, you will get that error related to `electron-builder install-app-deps`, so:
5 | - Take a look at [this comment](https://github.com/ficonsulting/RInno/issues/44#issuecomment-992299431) to fix it!
6 |
7 | ## What should I do if the release action fails and the re-run failed jobs keep failing?
8 | First, make sure you have the necessary permissions to run the action, see [Releasing](./RELEASING.md) section.
9 |
10 | Then, go to the releases page in your repository and delete the draft release, then execute the action again but in the `re-run all jobs` mode.
11 |
12 | ## What are Autofill errors in the terminal?
13 | If you see the following errors in the terminal, you can ignore them:
14 | ```bash
15 | [97794:0301/202707.879855:ERROR:CONSOLE(1)] "Request Autofill.enable failed. {"code":-32601,"message":"'Autofill.enable' wasn't found"}", source: devtools://devtools/bundled/core/protocol_client/protocol_client.js (1)
16 | [97794:0301/202707.879884:ERROR:CONSOLE(1)] "Request Autofill.setAddresses failed. {"code":-32601,"message":"'Autofill.setAddresses' wasn't found"}", source: devtools://devtools/bundled/core/protocol_client/protocol_client.js (1)
17 | ```
18 | It only happens when devtools panel is open and it's not an issue with your app.
19 |
20 | For more information, take a look at [this issue](https://github.com/electron/electron/issues/41614).
21 |
22 |
--------------------------------------------------------------------------------
/docs/images/bullet.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/RELEASING.md:
--------------------------------------------------------------------------------
1 | Releasing
2 |
3 | > [!NOTE]
4 | > to be able to perform `auto-updates` you will need a `code signed app`, for this purpose you will need to configure it by yourself, so check the [electron-builder](https://www.electron.build/code-signing) and [action-electron-builder](https://github.com/daltonmenezes/action-electron-builder#code-signing) docs please to get know how to do this.
5 |
6 | > [!WARNING]
7 | > to be able to perform releases from GitHub Actions, your repo **must have the necessary permissions**, so:
8 | > - Go to `/settings/actions` in your repo and:
9 | > - in the `Actions permissions` section, select `Allow all actions and reusable workflows` option
10 | > - in the `Workflow permissions` section, select `Read and write permissions` option
11 | > - Save the changes
12 |
13 | To release your app on a GitHub release with `Windows`, `Mac` and `Linux` binaries, you can perform the following commands:
14 |
15 | ```bash
16 | git pull
17 | pnpm make:release
18 | ```
19 |
20 | Then, enter the new version of your app, so it will produce the following binaries in a `draft release` from the action:
21 | - `Windows` → `zip (portable)`, `.exe`
22 | - `Mac` → `.zip (app)`, `.dmg`
23 | - `Linux` → `AppImage`, `freebsd`, `pacman`, `rpm`, `deb`
24 |
25 | In this process, the action will be triggered and the previous command will open the `releases` and `actions` page in your browser. When the action is finished, you can click in the `releases` page and refresh it to see the draft release with the binaries, so you can edit it and release it for your users.
26 |
27 | https://user-images.githubusercontent.com/1149845/156939675-5ea0c510-ddd3-4de7-b293-87d3697bd1a8.mp4
28 |
29 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "update_url": "https://clients2.google.com/service/update2/crx",
3 |
4 | "manifest_version": 3,
5 | "name": "React Developer Tools",
6 | "description": "Adds React debugging tools to the Chrome Developer Tools.\n\nCreated from revision 3cde211b0c on 10/20/2025.",
7 | "version": "7.0.1",
8 | "version_name": "7.0.1 (10/20/2025)",
9 | "minimum_chrome_version": "114",
10 | "icons": {
11 | "16": "icons/16-production.png",
12 | "32": "icons/32-production.png",
13 | "48": "icons/48-production.png",
14 | "128": "icons/128-production.png"
15 | },
16 | "action": {
17 | "default_icon": {
18 | "16": "icons/16-disabled.png",
19 | "32": "icons/32-disabled.png",
20 | "48": "icons/48-disabled.png",
21 | "128": "icons/128-disabled.png"
22 | },
23 | "default_popup": "popups/disabled.html"
24 | },
25 | "devtools_page": "main.html",
26 | "content_security_policy": {
27 | "extension_pages": "script-src 'self'; object-src 'self'"
28 | },
29 | "web_accessible_resources": [
30 | {
31 | "resources": [
32 | "main.html",
33 | "panel.html",
34 | "build/*.js"
35 | ],
36 | "matches": [
37 | ""
38 | ],
39 | "extension_ids": []
40 | }
41 | ],
42 | "background": {
43 | "service_worker": "build/background.js"
44 | },
45 | "permissions": [
46 | "scripting",
47 | "storage",
48 | "tabs"
49 | ],
50 | "optional_permissions": [
51 | "clipboardWrite"
52 | ],
53 | "host_permissions": [
54 | ""
55 | ],
56 | "content_scripts": [
57 | {
58 | "matches": [
59 | ""
60 | ],
61 | "js": [
62 | "build/prepareInjection.js"
63 | ],
64 | "run_at": "document_start"
65 | }
66 | ]
67 | }
--------------------------------------------------------------------------------
/docs/UNSIGNED_APPS.md:
--------------------------------------------------------------------------------
1 | Running released unsigend apps
2 |
3 | ## Introduction
4 |
5 | This document explains how to run your distributed unsigned apps, which is a common scenario when you are developing and can't afford to sign your apps yet.
6 |
7 | ## macOS
8 | By default, macOS blocks the execution of unsigned apps. To bypass this:
9 |
10 | 1. **Download the app** and move it to the `Applications` folder (optional).
11 | 2. **When trying to open it, you will see a warning that the app cannot be opened.**
12 | 3. **Go to** `System Preferences` > `Security & Privacy` > `General`.
13 | 4. **In the "Allow apps downloaded from" section**, click `Open Anyway` for the blocked app.
14 | 5. If needed, run this command in the Terminal:
15 | ```sh
16 | xattr -c '/Applications/Your App.app'
17 | ```
18 | or
19 | ```sh
20 | xattr -d com.apple.quarantine '/Applications/Your App.app'
21 | ```
22 | or
23 | ```sh
24 | codesign --force --deep --sign - '/Applications/Your App.app'
25 | ```
26 | 6. The app should now open normally.
27 |
28 | ## Linux
29 | On Linux, execution permission may be disabled by default. To run the app:
30 |
31 | 1. **Download the app** and move it to the desired folder.
32 | 2. **Open the Terminal** and navigate to the app's folder.
33 | 3. **Run the following command**:
34 | ```sh
35 | chmod +x YourApp
36 | ```
37 | 4. **Now you can run the app** by double-clicking it or running it from the Terminal.
38 |
39 | ## Windows
40 | On Windows, SmartScreen may block the app. To run it:
41 |
42 | 1. **When trying to open the app, a Windows Defender SmartScreen warning will appear.**
43 | 2. Click `More info`.
44 | 3. Click `Run anyway`.
45 |
46 | Now you can run your Electron app without issues!
47 |
48 |
--------------------------------------------------------------------------------
/template/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
3 | "assist": {
4 | "actions": {
5 | "source": {
6 | "organizeImports": "off",
7 | "useSortedAttributes": "on",
8 | "useSortedKeys": "off",
9 | "useSortedProperties": "on"
10 | }
11 | },
12 | "enabled": true
13 | },
14 | "formatter": {
15 | "enabled": true,
16 | "indentStyle": "space",
17 | "indentWidth": 2,
18 | "lineEnding": "lf",
19 | "lineWidth": 80
20 | },
21 | "javascript": {
22 | "formatter": {
23 | "arrowParentheses": "asNeeded",
24 | "bracketSpacing": true,
25 | "indentStyle": "space",
26 | "jsxQuoteStyle": "double",
27 | "quoteStyle": "single",
28 | "semicolons": "asNeeded",
29 | "trailingCommas": "es5"
30 | }
31 | },
32 | "linter": {
33 | "enabled": true,
34 | "rules": {
35 | "a11y": {
36 | "noSvgWithoutTitle": "off",
37 | "useButtonType": "off"
38 | },
39 | "complexity": {
40 | "noBannedTypes": "warn"
41 | },
42 | "correctness": {
43 | "noUnusedImports": "warn",
44 | "useExhaustiveDependencies": "off"
45 | },
46 | "recommended": true,
47 | "style": {
48 | "noUselessElse": "warn"
49 | },
50 | "suspicious": {
51 | "noArrayIndexKey": "info",
52 | "noDuplicateObjectKeys": "warn",
53 | "noDuplicateProperties": "warn",
54 | "noEmptyInterface": "off",
55 | "noExplicitAny": "off",
56 | "noUnknownAtRules": "off"
57 | }
58 | }
59 | },
60 | "css": {
61 | "parser": {
62 | "tailwindDirectives": true
63 | }
64 | },
65 | "files": {
66 | "includes": [
67 | ".",
68 | "src/**/*",
69 | "!src/lib/electron-app/extensions/**/*",
70 | "!node_modules/**/*",
71 | "!dist/**/*"
72 | ]
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/template/src/renderer/components/ui/alert.tsx:
--------------------------------------------------------------------------------
1 | import type * as React from 'react'
2 | import { cva, type VariantProps } from 'class-variance-authority'
3 |
4 | import { cn } from 'renderer/lib/utils'
5 |
6 | const alertVariants = cva(
7 | 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
8 | {
9 | variants: {
10 | variant: {
11 | default: 'bg-background text-foreground',
12 | destructive:
13 | 'text-destructive-foreground [&>svg]:text-current *:data-[slot=alert-description]:text-destructive-foreground/80',
14 | },
15 | },
16 | defaultVariants: {
17 | variant: 'default',
18 | },
19 | }
20 | )
21 |
22 | function Alert({
23 | className,
24 | variant,
25 | ...props
26 | }: React.ComponentProps<'div'> & VariantProps) {
27 | return (
28 |
34 | )
35 | }
36 |
37 | function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
38 | return (
39 |
47 | )
48 | }
49 |
50 | function AlertDescription({
51 | className,
52 | ...props
53 | }: React.ComponentProps<'div'>) {
54 | return (
55 |
63 | )
64 | }
65 |
66 | export { Alert, AlertTitle, AlertDescription }
67 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
57 |
58 |
59 |
60 |
61 |
Looks like this page doesn't have React, or it hasn't been loaded yet.
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/release/modules/release.ts:
--------------------------------------------------------------------------------
1 | import { writeFile } from 'node:fs/promises'
2 | import { resolve } from 'node:path'
3 | import open from 'open'
4 |
5 | import { extractOwnerAndRepoFromGitRemoteURL } from '../utils/extractors'
6 | import { checkValidations } from '../utils/validations'
7 | import packageJSON from '../../../../../package.json'
8 | import { question } from '../utils/question'
9 | import { COLORS } from '../constants/colors'
10 | import { exec } from '../utils/exec'
11 |
12 | async function makeRelease() {
13 | console.clear()
14 |
15 | const { version } = packageJSON
16 |
17 | const newVersion = await question(
18 | `Enter a new version: ${COLORS.SOFT_GRAY}(current is ${version})${COLORS.RESET} `
19 | )
20 |
21 | if (checkValidations({ version, newVersion })) {
22 | return
23 | }
24 |
25 | packageJSON.version = newVersion
26 |
27 | try {
28 | console.log(
29 | `${COLORS.CYAN}> Updating package.json version...${COLORS.RESET}`
30 | )
31 |
32 | await writeFile(
33 | resolve('package.json'),
34 | JSON.stringify(packageJSON, null, 2)
35 | )
36 |
37 | console.log(`\n${COLORS.GREEN}Done!${COLORS.RESET}\n`)
38 | console.log(`${COLORS.CYAN}> Trying to release it...${COLORS.RESET}`)
39 |
40 | exec(
41 | [
42 | `git commit -am v${newVersion}`,
43 | `git tag v${newVersion}`,
44 | 'git push',
45 | 'git push --tags',
46 | ],
47 | {
48 | inherit: true,
49 | }
50 | )
51 |
52 | const [repository] = exec(['git remote get-url --push origin'])
53 | const ownerAndRepo = extractOwnerAndRepoFromGitRemoteURL(repository)
54 |
55 | console.log(
56 | `${COLORS.CYAN}> Opening the repository releases page...${COLORS.RESET}`
57 | )
58 |
59 | await open(`https://github.com/${ownerAndRepo}/releases`)
60 |
61 | console.log(
62 | `${COLORS.CYAN}> Opening the repository actions page...${COLORS.RESET}`
63 | )
64 |
65 | await open(`https://github.com/${ownerAndRepo}/actions`)
66 |
67 | console.log(`\n${COLORS.GREEN}Done!${COLORS.RESET}\n`)
68 | } catch ({ message }: any) {
69 | console.log(`
70 | 🛑 Something went wrong!\n
71 | 👀 Error: ${message}
72 | `)
73 | }
74 | }
75 |
76 | makeRelease()
77 |
--------------------------------------------------------------------------------
/template/electron.vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
2 | import { codeInspectorPlugin } from 'code-inspector-plugin'
3 | import { resolve, normalize, dirname } from 'node:path'
4 | import tailwindcss from '@tailwindcss/vite'
5 |
6 | import injectProcessEnvPlugin from 'rollup-plugin-inject-process-env'
7 | import tsconfigPathsPlugin from 'vite-tsconfig-paths'
8 | import reactPlugin from '@vitejs/plugin-react'
9 |
10 | import { settings } from './src/lib/electron-router-dom'
11 | import { main, resources } from './package.json'
12 |
13 | const [nodeModules, devFolder] = normalize(dirname(main)).split(/\/|\\/g)
14 | const devPath = [nodeModules, devFolder].join('/')
15 |
16 | const tsconfigPaths = tsconfigPathsPlugin({
17 | projects: [resolve('tsconfig.json')],
18 | })
19 |
20 | export default defineConfig({
21 | main: {
22 | mode: 'es2022',
23 | plugins: [tsconfigPaths, externalizeDepsPlugin()],
24 |
25 | build: {
26 | rollupOptions: {
27 | input: {
28 | index: resolve('src/main/index.ts'),
29 | },
30 |
31 | output: {
32 | dir: resolve(devPath, 'main'),
33 | format: 'es',
34 | },
35 | },
36 | },
37 | },
38 |
39 | preload: {
40 | mode: 'es2022',
41 | plugins: [tsconfigPaths, externalizeDepsPlugin()],
42 |
43 | build: {
44 | rollupOptions: {
45 | output: {
46 | dir: resolve(devPath, 'preload'),
47 | },
48 | },
49 | },
50 | },
51 |
52 | renderer: {
53 | define: {
54 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
55 | 'process.platform': JSON.stringify(process.platform),
56 | },
57 |
58 | server: {
59 | port: settings.port,
60 | },
61 |
62 | plugins: [
63 | tsconfigPaths,
64 | tailwindcss(),
65 | codeInspectorPlugin({
66 | bundler: 'vite',
67 | hotKeys: ['altKey'],
68 | hideConsole: true,
69 | }),
70 | reactPlugin(),
71 | ],
72 |
73 | publicDir: resolve(resources, 'public'),
74 |
75 | build: {
76 | outDir: resolve(devPath, 'renderer'),
77 |
78 | rollupOptions: {
79 | plugins: [
80 | injectProcessEnvPlugin({
81 | NODE_ENV: 'production',
82 | platform: process.platform,
83 | }),
84 | ],
85 |
86 | input: {
87 | index: resolve('src/renderer/index.html'),
88 | },
89 |
90 | output: {
91 | dir: resolve(devPath, 'renderer'),
92 | },
93 | },
94 | },
95 | },
96 | })
97 |
--------------------------------------------------------------------------------
/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "displayName": "My Electron App",
3 | "name": "my-electron-app",
4 | "description": "Your awesome app description",
5 | "version": "0.0.0",
6 | "main": "./node_modules/.dev/main/index.mjs",
7 | "resources": "src/resources",
8 | "author": {
9 | "name": "Dalton Menezes",
10 | "email": "daltonmenezes@outlook.com"
11 | },
12 | "license": "MIT",
13 | "packageManager": "pnpm@10.0.0",
14 | "pnpm": {
15 | "onlyBuiltDependenciesFile": "./trusted-dependencies-scripts.json"
16 | },
17 | "scripts": {
18 | "start": "electron-vite preview",
19 | "predev": "run-s clean:dev",
20 | "dev": "cross-env NODE_ENV=development electron-vite dev --watch",
21 | "compile:app": "electron-vite build",
22 | "compile:packageJSON": "tsx ./src/lib/electron-app/release/modules/prebuild.ts",
23 | "prebuild": "run-s clean:dev compile:app compile:packageJSON",
24 | "build": "pnpm electron-builder",
25 | "postinstall": "run-s prebuild install:deps",
26 | "install:deps": "electron-builder install-app-deps",
27 | "make:release": "tsx ./src/lib/electron-app/release/modules/release.ts",
28 | "release": "electron-builder --publish always",
29 | "clean:dev": "rimraf ./node_modules/.dev",
30 | "lint": "biome check --no-errors-on-unmatched",
31 | "lint:fix": "biome check --write --no-errors-on-unmatched --assist-enabled=true",
32 | "typecheck": "tsc --noEmit"
33 | },
34 | "dependencies": {
35 | "class-variance-authority": "^0.7.1",
36 | "clsx": "^2.1.1",
37 | "electron-router-dom": "^2.1.0",
38 | "lucide-react": "^0.556.0",
39 | "react": "^19.2.1",
40 | "react-dom": "^19.2.1",
41 | "react-router-dom": "^7.10.1",
42 | "tailwind-merge": "^3.4.0",
43 | "tailwindcss-animate": "^1.0.7"
44 | },
45 | "devDependencies": {
46 | "@biomejs/biome": "^2.3.8",
47 | "@tailwindcss/vite": "^4.1.17",
48 | "@types/node": "^24.10.1",
49 | "@types/react": "^19.2.7",
50 | "@types/react-dom": "^19.2.3",
51 | "@types/semver": "^7.7.1",
52 | "@vitejs/plugin-react": "^5.1.1",
53 | "code-inspector-plugin": "^1.3.0",
54 | "cross-env": "^10.1.0",
55 | "electron": "^39.2.6",
56 | "electron-builder": "^26.0.12",
57 | "electron-vite": "^4.0.1",
58 | "npm-run-all": "^4.1.5",
59 | "open": "^11.0.0",
60 | "rimraf": "^6.1.2",
61 | "rollup-plugin-inject-process-env": "^1.3.1",
62 | "semver": "^7.7.3",
63 | "tailwindcss": "^4.1.17",
64 | "tsx": "^4.21.0",
65 | "typescript": "^5.9.3",
66 | "vite": "^7.2.6",
67 | "vite-tsconfig-paths": "^5.1.4"
68 | }
69 | }
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/icons/deadcode.svg:
--------------------------------------------------------------------------------
1 | development 780 780
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/icons/development.svg:
--------------------------------------------------------------------------------
1 | development 780 780
--------------------------------------------------------------------------------
/template/src/renderer/globals.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 |
3 | @plugin "tailwindcss-animate";
4 |
5 | @custom-variant dark (&:is(.dark *));
6 |
7 | :root {
8 | --background: oklch(1 0 0);
9 | --foreground: oklch(0.145 0 0);
10 | --card: oklch(1 0 0);
11 | --card-foreground: oklch(0.145 0 0);
12 | --popover: oklch(1 0 0);
13 | --popover-foreground: oklch(0.145 0 0);
14 | --primary: oklch(0.205 0 0);
15 | --primary-foreground: oklch(0.985 0 0);
16 | --secondary: oklch(0.97 0 0);
17 | --secondary-foreground: oklch(0.205 0 0);
18 | --muted: oklch(0.97 0 0);
19 | --muted-foreground: oklch(0.556 0 0);
20 | --accent: oklch(0.97 0 0);
21 | --accent-foreground: oklch(0.205 0 0);
22 | --destructive: oklch(0.577 0.245 27.325);
23 | --destructive-foreground: oklch(0.577 0.245 27.325);
24 | --border: oklch(0.922 0 0);
25 | --input: oklch(0.922 0 0);
26 | --ring: oklch(0.708 0 0);
27 | --chart-1: oklch(0.646 0.222 41.116);
28 | --chart-2: oklch(0.6 0.118 184.704);
29 | --chart-3: oklch(0.398 0.07 227.392);
30 | --chart-4: oklch(0.828 0.189 84.429);
31 | --chart-5: oklch(0.769 0.188 70.08);
32 | --radius: 0.625rem;
33 | --sidebar: oklch(0.985 0 0);
34 | --sidebar-foreground: oklch(0.145 0 0);
35 | --sidebar-primary: oklch(0.205 0 0);
36 | --sidebar-primary-foreground: oklch(0.985 0 0);
37 | --sidebar-accent: oklch(0.97 0 0);
38 | --sidebar-accent-foreground: oklch(0.205 0 0);
39 | --sidebar-border: oklch(0.922 0 0);
40 | --sidebar-ring: oklch(0.708 0 0);
41 | }
42 |
43 | .dark {
44 | --background: oklch(0.145 0 0);
45 | --foreground: oklch(0.985 0 0);
46 | --card: oklch(0.145 0 0);
47 | --card-foreground: oklch(0.985 0 0);
48 | --popover: oklch(0.145 0 0);
49 | --popover-foreground: oklch(0.985 0 0);
50 | --primary: oklch(0.985 0 0);
51 | --primary-foreground: oklch(0.205 0 0);
52 | --secondary: oklch(0.269 0 0);
53 | --secondary-foreground: oklch(0.985 0 0);
54 | --muted: oklch(0.269 0 0);
55 | --muted-foreground: oklch(0.708 0 0);
56 | --accent: oklch(0.269 0 0);
57 | --accent-foreground: oklch(0.985 0 0);
58 | --destructive: oklch(0.396 0.141 25.723);
59 | --destructive-foreground: oklch(0.637 0.237 25.331);
60 | --border: oklch(0.269 0 0);
61 | --input: oklch(0.269 0 0);
62 | --ring: oklch(0.439 0 0);
63 | --chart-1: oklch(0.488 0.243 264.376);
64 | --chart-2: oklch(0.696 0.17 162.48);
65 | --chart-3: oklch(0.769 0.188 70.08);
66 | --chart-4: oklch(0.627 0.265 303.9);
67 | --chart-5: oklch(0.645 0.246 16.439);
68 | --sidebar: oklch(0.205 0 0);
69 | --sidebar-foreground: oklch(0.985 0 0);
70 | --sidebar-primary: oklch(0.488 0.243 264.376);
71 | --sidebar-primary-foreground: oklch(0.985 0 0);
72 | --sidebar-accent: oklch(0.269 0 0);
73 | --sidebar-accent-foreground: oklch(0.985 0 0);
74 | --sidebar-border: oklch(0.269 0 0);
75 | --sidebar-ring: oklch(0.439 0 0);
76 | }
77 |
78 | @theme inline {
79 | --color-background: var(--background);
80 | --color-foreground: var(--foreground);
81 | --color-card: var(--card);
82 | --color-card-foreground: var(--card-foreground);
83 | --color-popover: var(--popover);
84 | --color-popover-foreground: var(--popover-foreground);
85 | --color-primary: var(--primary);
86 | --color-primary-foreground: var(--primary-foreground);
87 | --color-secondary: var(--secondary);
88 | --color-secondary-foreground: var(--secondary-foreground);
89 | --color-muted: var(--muted);
90 | --color-muted-foreground: var(--muted-foreground);
91 | --color-accent: var(--accent);
92 | --color-accent-foreground: var(--accent-foreground);
93 | --color-destructive: var(--destructive);
94 | --color-destructive-foreground: var(--destructive-foreground);
95 | --color-border: var(--border);
96 | --color-input: var(--input);
97 | --color-ring: var(--ring);
98 | --color-chart-1: var(--chart-1);
99 | --color-chart-2: var(--chart-2);
100 | --color-chart-3: var(--chart-3);
101 | --color-chart-4: var(--chart-4);
102 | --color-chart-5: var(--chart-5);
103 | --radius-sm: calc(var(--radius) - 4px);
104 | --radius-md: calc(var(--radius) - 2px);
105 | --radius-lg: var(--radius);
106 | --radius-xl: calc(var(--radius) + 4px);
107 | --color-sidebar: var(--sidebar);
108 | --color-sidebar-foreground: var(--sidebar-foreground);
109 | --color-sidebar-primary: var(--sidebar-primary);
110 | --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
111 | --color-sidebar-accent: var(--sidebar-accent);
112 | --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
113 | --color-sidebar-border: var(--sidebar-border);
114 | --color-sidebar-ring: var(--sidebar-ring);
115 | }
116 |
117 | @layer base {
118 | * {
119 | @apply border-border outline-ring/50;
120 | @apply antialiased;
121 | }
122 |
123 | body {
124 | @apply bg-background text-foreground;
125 | }
126 |
127 | html,
128 | body {
129 | user-select: none;
130 | scroll-behavior: smooth;
131 | -webkit-app-region: no-drag;
132 | -webkit-font-smoothing: antialiased;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/build/background.js:
--------------------------------------------------------------------------------
1 | (()=>{var e={5603:()=>{const e=[{id:"@react-devtools/proxy",js:["build/proxy.js"],matches:[""],persistAcrossSessions:!0,runAt:"document_start",world:chrome.scripting.ExecutionWorld.ISOLATED},{id:"@react-devtools/file-fetcher",js:["build/fileFetcher.js"],matches:[""],persistAcrossSessions:!0,runAt:"document_end",world:chrome.scripting.ExecutionWorld.ISOLATED},{id:"@react-devtools/hook",js:["build/installHook.js"],matches:[""],persistAcrossSessions:!0,runAt:"document_start",world:chrome.scripting.ExecutionWorld.MAIN},{id:"@react-devtools/hook-settings-injector",js:["build/hookSettingsInjector.js"],matches:[""],persistAcrossSessions:!0,runAt:"document_start"}];!async function(){try{await chrome.scripting.unregisterContentScripts(),await chrome.scripting.registerContentScripts(e)}catch(e){console.error(e)}}()}},t={};function __webpack_require__(n){var o=t[n];if(void 0!==o)return o.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,__webpack_require__),r.exports}(()=>{"use strict";__webpack_require__(5603);const background_setExtensionIconAndPopup=function(e,t){chrome.action.setIcon({tabId:t,path:{16:chrome.runtime.getURL(`icons/16-${e}.png`),32:chrome.runtime.getURL(`icons/32-${e}.png`),48:chrome.runtime.getURL(`icons/48-${e}.png`),128:chrome.runtime.getURL(`icons/128-${e}.png`)}}),chrome.action.setPopup({tabId:t,popup:chrome.runtime.getURL(`popups/${e}.html`)})};function isRestrictedBrowserPage(e){if(!e)return!0;const t=new URL(e).protocol;return"chrome:"===t||"about:"===t}function checkAndHandleRestrictedPageIfSo(e){e&&isRestrictedBrowserPage(e.url)&&background_setExtensionIconAndPopup("restricted",e.id)}chrome.tabs.query({},(e=>e.forEach(checkAndHandleRestrictedPageIfSo))),chrome.tabs.onCreated.addListener((e=>checkAndHandleRestrictedPageIfSo(e))),chrome.tabs.onUpdated.addListener(((e,t,n)=>{t.url&&isRestrictedBrowserPage(t.url)&&background_setExtensionIconAndPopup("restricted",e)}));function executeScriptInMainWorld({target:e,files:t,injectImmediately:n}){return chrome.scripting.executeScript({target:e,files:t,injectImmediately:n,world:chrome.scripting.ExecutionWorld.MAIN})}const e=["compact"];const t={};function registerTab(e){t[e]||(t[e]={extension:null,proxy:null,disconnectPipe:null})}function connectExtensionAndProxyPorts(e,n,o){if(!e)throw new Error("Attempted to connect ports, when extension port is not present");if(!n)throw new Error("Attempted to connect ports, when proxy port is not present");if(t[o].disconnectPipe)throw new Error(`Attempted to connect already connected ports for tab with id ${o}`);function extensionPortMessageListener(e){try{n.postMessage(e)}catch(e){0,disconnectListener()}}function proxyPortMessageListener(t){try{e.postMessage(t)}catch(e){0,disconnectListener()}}function disconnectListener(){e.onMessage.removeListener(extensionPortMessageListener),n.onMessage.removeListener(proxyPortMessageListener),delete t[o].disconnectPipe}t[o].disconnectPipe=disconnectListener,e.onMessage.addListener(extensionPortMessageListener),n.onMessage.addListener(proxyPortMessageListener),e.onDisconnect.addListener(disconnectListener),n.onDisconnect.addListener(disconnectListener)}chrome.runtime.onConnect.addListener((e=>{if("proxy"===e.name){if(null==e.sender?.tab?.id)return;const n=e.sender.tab.id;return t[n]?.proxy&&(t[n].disconnectPipe?.(),t[n].proxy.disconnect()),registerTab(n),function(e,n){t[n].proxy=e,e.onDisconnect.addListener((()=>{t[n].disconnectPipe?.(),delete t[n].proxy}))}(e,n),void(t[n].extension&&connectExtensionAndProxyPorts(t[n].extension,t[n].proxy,n))}if(+(n=e.name)+""===n){const n=+e.name;return registerTab(n),function(e,n){t[n].extension=e,e.onDisconnect.addListener((()=>{t[n].disconnectPipe?.(),delete t[n].extension}))}(e,n),void(t[n].proxy&&connectExtensionAndProxyPorts(t[n].extension,t[n].proxy,n))}var n;console.warn(`Unknown port ${e.name} connected`)})),chrome.runtime.onMessage.addListener(((t,n)=>{switch(t?.source){case"devtools-page":!function(e){const{payload:t}=e;switch(t?.type){case"fetch-file-with-cache":{const{payload:{tabId:t,url:n}}=e;t&&n?chrome.tabs.sendMessage(t,{source:"devtools-page",payload:{type:"fetch-file-with-cache",url:n}}):chrome.runtime.sendMessage({source:"react-devtools-background",payload:{type:"fetch-file-with-cache-error",url:n,value:null}});break}case"inject-backend-manager":{const{payload:{tabId:t}}=e;if(!t)throw new Error("Couldn't inject backend manager: tabId not specified");executeScriptInMainWorld({injectImmediately:!0,target:{tabId:t},files:["/build/backendManager.js"]}).then((()=>{}),(e=>{console.error("Failed to inject backend manager:",e)}));break}}}(t);break;case"react-devtools-fetch-resource-content-script":!function(e){const{payload:t}=e;switch(t?.type){case"fetch-file-with-cache-complete":case"fetch-file-with-cache-error":chrome.runtime.sendMessage({source:"react-devtools-background",payload:t})}}(t);break;case"react-devtools-backend-manager":!function(t,n){const{payload:o}=t;"require-backends"===o?.type&&o.versions.forEach((t=>{e.includes(t)&&executeScriptInMainWorld({injectImmediately:!0,target:{tabId:n.tab.id},files:[`/build/react_devtools_backend_${t}.js`]})}))}(t,n);break;case"react-devtools-hook":!function(e,t){const{payload:n}=e;"react-renderer-attached"===n?.type&&background_setExtensionIconAndPopup(n.reactBuildType,t.tab.id)}(t,n)}})),chrome.tabs.onActivated.addListener((({tabId:e})=>{for(const n in t)if(null!=t[n].proxy&&null!=t[n].extension){const o=e===+n?"resumeElementPolling":"pauseElementPolling";t[n].extension.postMessage({event:o})}}))})()})();
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Electron App
2 |
3 | 💅 An Electron app boilerplate with React v19, TypeScript v5, Tailwind v4, shadcn/ui, Electron Vite, Biome, GitHub Action releases and more.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | # Features
30 | - **Stands out**
31 | - 🔥 Fast and Ready-to-go with a well-thought-out structure
32 | - 🚀 Auto reload for main and **Fast Refresh** for renderer process
33 | - 🎉 Window/Screen routing included
34 | - 😎 Preload (context bridge) already configured
35 | - 🔮 GitHub Action releases with `Windows`, `Mac` and `Linux` binaries
36 | - 🔒 Source Code Protection support
37 | - 🍪 Absolute paths support
38 | - **Technologies**:
39 | - 🔋 Electron
40 | - 🔥 ReactJS v19
41 | - 🌎 React Router DOM v7 and Electron Router DOM v2
42 | - 🧐 React Developer Tools
43 | - 🔍 Code inspector (holding `Alt` or `Option` key on DOM element and clicking on it)
44 | - 💙 TypeScript v5
45 | - 📦 Electron Vite
46 | - ✨ TailwindCSS v4
47 | - 🎨 shadcn/ui
48 | - 🍦 lucide-icons
49 | - 💫 Biome / EditorConfig
50 | - 📦 Electron Builder
51 | - 🔮 action-electron-builder
52 |
53 |
54 |
55 | > :warning: If **Windows 7** and **8** support is important for your project, you should know that Electron in a version greater than 22x no longer supports it. You can read more about it [here](https://www.electronjs.org/docs/latest/breaking-changes#removed-windows-7--8--81-support). Therefore, you must downgrade Electron to 22x version if it's important for you!
56 |
57 | # Requirements
58 | - [Node.js 22](https://nodejs.org/en/download/)
59 | - [pnpm 10](https://pnpm.io/installation)
60 |
61 | # Installation
62 | ```bash
63 | npx degit daltonmenezes/electron-app/template project_name
64 | ```
65 | ```bash
66 | cd project_name
67 | pnpm install
68 | pnpm dev
69 | ```
70 |
71 | Now, look at the **package.json** file in the root directory, you should update some of that settings with your project branding.
72 |
73 | # Adding new dependencies
74 | For security reasons, **pnpm** has the [onlyBuiltDependenciesFile](https://pnpm.io/package_json#pnpmonlybuiltdependenciesfile) property where only
75 | dependencies listed in the [trusted-dependencies-scripts.json](./template/trusted-dependencies-scripts.json) file can perform the postscripts execution. So, if you want to add a new dependency that needs to run a postscript, you should add it to the [trusted-dependencies-scripts.json](./template/trusted-dependencies-scripts.json) file list.
76 |
77 | # Distribution
78 |
79 | > [!NOTE]
80 | > this section refers to local distribution, to release your app from GitHub Actions, see [Releasing](./docs/RELEASING.md) section.
81 |
82 | ### For all platforms
83 |
84 | > **Note**: Check [Electron Builder docs](https://www.electron.build/cli) for more knowledge
85 |
86 | ```
87 | pnpm build
88 | ```
89 |
90 | ### For a specific one
91 |
92 | ```bash
93 | pnpm build --mac
94 | # OR
95 | pnpm build --win
96 | # OR
97 | pnpm build --linux
98 | ```
99 |
100 | The builded apps will be available in the `dist` folder.
101 |
102 | # Documents
103 |
147 |
148 | # Contributing
149 | > **Note**: contributions are always welcome, but always **ask first**, — please — before work on a PR.
150 |
151 | That said, there's a bunch of ways you can contribute to this project, like by:
152 |
153 | - :beetle: Reporting a bug
154 | - :page_facing_up: Improving this documentation
155 | - :rotating_light: Sharing this project and recommending it to your friends
156 | - :dollar: Supporting this project on GitHub Sponsors or Patreon
157 | - :star2: Giving a star on this repository
158 |
159 | # License
160 |
161 | [MIT © Dalton Menezes](https://github.com/daltonmenezes/electron-app/blob/main/LICENSE)
162 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/build/backendManager.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";var e={3018:(e,t,r)=>{const n=r(5986),i=Symbol("max"),s=Symbol("length"),o=Symbol("lengthCalculator"),l=Symbol("allowStale"),a=Symbol("maxAge"),h=Symbol("dispose"),u=Symbol("noDisposeOnSet"),c=Symbol("lruList"),d=Symbol("cache"),f=Symbol("updateAgeOnGet"),naiveLength=()=>1;const get=(e,t,r)=>{const n=e[d].get(t);if(n){const t=n.value;if(isStale(e,t)){if(del(e,n),!e[l])return}else r&&(e[f]&&(n.value.now=Date.now()),e[c].unshiftNode(n));return t.value}},isStale=(e,t)=>{if(!t||!t.maxAge&&!e[a])return!1;const r=Date.now()-t.now;return t.maxAge?r>t.maxAge:e[a]&&r>e[a]},trim=e=>{if(e[s]>e[i])for(let t=e[c].tail;e[s]>e[i]&&null!==t;){const r=t.prev;del(e,t),t=r}},del=(e,t)=>{if(t){const r=t.value;e[h]&&e[h](r.key,r.value),e[s]-=r.length,e[d].delete(r.key),e[c].removeNode(t)}};class p{constructor(e,t,r,n,i){this.key=e,this.value=t,this.length=r,this.now=n,this.maxAge=i||0}}const forEachStep=(e,t,r,n)=>{let i=r.value;isStale(e,i)&&(del(e,r),e[l]||(i=void 0)),i&&t.call(n,i.value,i.key,e)};e.exports=class{constructor(e){if("number"==typeof e&&(e={max:e}),e||(e={}),e.max&&("number"!=typeof e.max||e.max<0))throw new TypeError("max must be a non-negative number");this[i]=e.max||1/0;const t=e.length||naiveLength;if(this[o]="function"!=typeof t?naiveLength:t,this[l]=e.stale||!1,e.maxAge&&"number"!=typeof e.maxAge)throw new TypeError("maxAge must be a number");this[a]=e.maxAge||0,this[h]=e.dispose,this[u]=e.noDisposeOnSet||!1,this[f]=e.updateAgeOnGet||!1,this.reset()}set max(e){if("number"!=typeof e||e<0)throw new TypeError("max must be a non-negative number");this[i]=e||1/0,trim(this)}get max(){return this[i]}set allowStale(e){this[l]=!!e}get allowStale(){return this[l]}set maxAge(e){if("number"!=typeof e)throw new TypeError("maxAge must be a non-negative number");this[a]=e,trim(this)}get maxAge(){return this[a]}set lengthCalculator(e){"function"!=typeof e&&(e=naiveLength),e!==this[o]&&(this[o]=e,this[s]=0,this[c].forEach((e=>{e.length=this[o](e.value,e.key),this[s]+=e.length}))),trim(this)}get lengthCalculator(){return this[o]}get length(){return this[s]}get itemCount(){return this[c].length}rforEach(e,t){t=t||this;for(let r=this[c].tail;null!==r;){const n=r.prev;forEachStep(this,e,r,t),r=n}}forEach(e,t){t=t||this;for(let r=this[c].head;null!==r;){const n=r.next;forEachStep(this,e,r,t),r=n}}keys(){return this[c].toArray().map((e=>e.key))}values(){return this[c].toArray().map((e=>e.value))}reset(){this[h]&&this[c]&&this[c].length&&this[c].forEach((e=>this[h](e.key,e.value))),this[d]=new Map,this[c]=new n,this[s]=0}dump(){return this[c].map((e=>!isStale(this,e)&&{k:e.key,v:e.value,e:e.now+(e.maxAge||0)})).toArray().filter((e=>e))}dumpLru(){return this[c]}set(e,t,r){if((r=r||this[a])&&"number"!=typeof r)throw new TypeError("maxAge must be a number");const n=r?Date.now():0,l=this[o](t,e);if(this[d].has(e)){if(l>this[i])return del(this,this[d].get(e)),!1;const o=this[d].get(e).value;return this[h]&&(this[u]||this[h](e,o.value)),o.now=n,o.maxAge=r,o.value=t,this[s]+=l-o.length,o.length=l,this.get(e),trim(this),!0}const f=new p(e,t,l,n,r);return f.length>this[i]?(this[h]&&this[h](e,t),!1):(this[s]+=f.length,this[c].unshift(f),this[d].set(e,this[c].head),trim(this),!0)}has(e){if(!this[d].has(e))return!1;const t=this[d].get(e).value;return!isStale(this,t)}get(e){return get(this,e,!0)}peek(e){return get(this,e,!1)}pop(){const e=this[c].tail;return e?(del(this,e),e.value):null}del(e){del(this,this[d].get(e))}load(e){this.reset();const t=Date.now();for(let r=e.length-1;r>=0;r--){const n=e[r],i=n.e||0;if(0===i)this.set(n.k,n.v);else{const e=i-t;e>0&&this.set(n.k,n.v,e)}}}prune(){this[d].forEach(((e,t)=>get(this,t,!1)))}}},7533:e=>{e.exports=function(e){e.prototype[Symbol.iterator]=function*(){for(let e=this.head;e;e=e.next)yield e.value}}},5986:(e,t,r)=>{function Yallist(e){var t=this;if(t instanceof Yallist||(t=new Yallist),t.tail=null,t.head=null,t.length=0,e&&"function"==typeof e.forEach)e.forEach((function(e){t.push(e)}));else if(arguments.length>0)for(var r=0,n=arguments.length;r1)r=t;else{if(!this.head)throw new TypeError("Reduce of empty list with no initial value");n=this.head.next,r=this.head.value}for(var i=0;null!==n;i++)r=e(r,n.value,i),n=n.next;return r},Yallist.prototype.reduceReverse=function(e,t){var r,n=this.tail;if(arguments.length>1)r=t;else{if(!this.tail)throw new TypeError("Reduce of empty list with no initial value");n=this.tail.prev,r=this.tail.value}for(var i=this.length-1;null!==n;i--)r=e(r,n.value,i),n=n.prev;return r},Yallist.prototype.toArray=function(){for(var e=new Array(this.length),t=0,r=this.head;null!==r;t++)e[t]=r.value,r=r.next;return e},Yallist.prototype.toArrayReverse=function(){for(var e=new Array(this.length),t=0,r=this.tail;null!==r;t++)e[t]=r.value,r=r.prev;return e},Yallist.prototype.slice=function(e,t){(t=t||this.length)<0&&(t+=this.length),(e=e||0)<0&&(e+=this.length);var r=new Yallist;if(tthis.length&&(t=this.length);for(var n=0,i=this.head;null!==i&&nthis.length&&(t=this.length);for(var n=this.length,i=this.tail;null!==i&&n>t;n--)i=i.prev;for(;null!==i&&n>e;n--,i=i.prev)r.push(i.value);return r},Yallist.prototype.splice=function(e,t){e>this.length&&(e=this.length-1),e<0&&(e=this.length+e);for(var r=0,n=this.head;null!==n&&r{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},__webpack_require__.d=(e,t)=>{for(var r in t)__webpack_require__.o(t,r)&&!__webpack_require__.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{const esm_compareVersions=(e,t)=>{const r=validateAndParse(e),n=validateAndParse(t),i=r.pop(),s=n.pop(),o=compareSegments(r,n);return 0!==o?o:i&&s?compareSegments(i.split("."),s.split(".")):i||s?i?-1:1:0},e=/^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i,validateAndParse=t=>{if("string"!=typeof t)throw new TypeError("Invalid argument expected string");const r=t.match(e);if(!r)throw new Error(`Invalid argument not valid semver ('${t}' received)`);return r.shift(),r},isWildcard=e=>"*"===e||"x"===e||"X"===e,tryParse=e=>{const t=parseInt(e,10);return isNaN(t)?e:t},compareStrings=(e,t)=>{if(isWildcard(e)||isWildcard(t))return 0;const[r,n]=((e,t)=>typeof e!=typeof t?[String(e),String(t)]:[e,t])(tryParse(e),tryParse(t));return r>n?1:r{for(let r=0;r":[1],">=":[0,1],"=":[0],"<=":[-1,0],"<":[-1]};Object.keys(t);var r=__webpack_require__(3018),n=__webpack_require__.n(r);Symbol.for("react.element"),Symbol.for("react.transitional.element"),Symbol.for("react.portal"),Symbol.for("react.fragment"),Symbol.for("react.strict_mode"),Symbol.for("react.profiler"),Symbol.for("react.consumer"),Symbol.for("react.context"),Symbol.for("react.forward_ref"),Symbol.for("react.suspense"),Symbol.for("react.suspense_list"),Symbol.for("react.memo"),Symbol.for("react.lazy"),Symbol.for("react.scope"),Symbol.for("react.activity"),Symbol.for("react.legacy_hidden"),Symbol.for("react.tracing_marker"),Symbol.for("react.memo_cache_sentinel"),Symbol.for("react.postpone"),Symbol.for("react.view_transition"),Symbol.iterator;Symbol.asyncIterator;const i="React::DevTools::recordChangeDescriptions",s="React::DevTools::recordTimeline",o="React::DevTools::reloadAndProfile";function sessionStorageRemoveItem(e){try{sessionStorage.removeItem(e)}catch(e){}}function sessionStorageSetItem(e,t){try{return sessionStorage.setItem(e,t)}catch(e){}}Array.isArray,Object.prototype.hasOwnProperty,new WeakMap,new(n())({max:1e3}),Symbol.for("react.provider");function getIsReloadAndProfileSupported(){let e=!1;try{localStorage.getItem("test"),e=!0}catch(e){}return e&&!!(window.document&&window.document.featurePolicy&&window.document.featurePolicy.allowsFeature("sync-xhr"))}function getIfReloadedAndProfiling(){return"true"===function(e){try{return sessionStorage.getItem(e)}catch(e){return null}}(o)}function onReloadAndProfile(e,t){sessionStorageSetItem(o,"true"),sessionStorageSetItem(i,e?"true":"false"),sessionStorageSetItem(s,t?"true":"false")}Symbol("inspectable"),Symbol("inspected"),Symbol("name"),Symbol("preview_long"),Symbol("preview_short"),Symbol("readonly"),Symbol("size"),Symbol("type"),Symbol("unserializable");Array.isArray;const l="999.9.9";function hasAssignedBackend(e){return null!=e&&""!==e&&function(e="",t=""){return esm_compareVersions(e,t)>-1}(e,l)}const a="compact";let h=!1;const u=new Set;function registerRenderer(e,t){let r=e.reconcilerVersion||e.version;hasAssignedBackend(r)||(r=a),t.backends.has(r)||u.add(r)}function activateBackend(e,t){const r=t.backends.get(e);if(!r)throw new Error(`Could not find backend for version "${e}"`);const{Agent:n,Bridge:l,initBackend:a,setupNativeStyleEditor:h}=r,c=new l({listen(e){const listener=t=>{t.source===window&&t.data&&"react-devtools-content-script"===t.data.source&&t.data.payload&&e(t.data.payload)};return window.addEventListener("message",listener),()=>{window.removeEventListener("message",listener)}},send(e,t,r){window.postMessage({source:"react-devtools-bridge",payload:{event:e,payload:t}},"*",r)}}),d=new n(c,getIfReloadedAndProfiling(),onReloadAndProfile);sessionStorageRemoveItem(o),sessionStorageRemoveItem(i),sessionStorageRemoveItem(s),d.addListener("shutdown",(()=>{t.emit("shutdown"),delete window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__})),a(t,d,window,getIsReloadAndProfileSupported()),"function"==typeof h&&t.resolveRNStyle&&h(c,d,t.resolveRNStyle,t.nativeStyleEditorValidAttributes),c.send("extensionBackendInitialized"),u.delete(e)}function updateRequiredBackends(){0!==u.size&&window.postMessage({source:"react-devtools-backend-manager",payload:{type:"require-backends",versions:Array.from(u)}},"*")}window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__||(window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__=!0,window.addEventListener("message",(function welcome(e){e.source===window&&"react-devtools-content-script"===e.data.source&&(h?console.warn('React DevTools detected duplicate welcome "message" events from the content script.'):(h=!0,window.removeEventListener("message",welcome),function(e){if(null==e)return;e.renderers.forEach((t=>{registerRenderer(t,e)})),e.backends.forEach(((t,r)=>{u.delete(r),activateBackend(r,e)})),updateRequiredBackends();const t=e.sub("renderer",(({renderer:t})=>{registerRenderer(t,e),updateRequiredBackends()})),r=e.sub("devtools-backend-installed",(t=>{activateBackend(t,e),updateRequiredBackends()})),n=e.sub("shutdown",(()=>{t(),r(),n()}))}(window.__REACT_DEVTOOLS_GLOBAL_HOOK__)))})))})()})();
--------------------------------------------------------------------------------
/template/src/resources/public/illustration.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/template/src/lib/electron-app/extensions/react-developer-tools/build/react_devtools_backend_compact.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";var e={3018:(e,t,n)=>{const r=n(5986),i=Symbol("max"),o=Symbol("length"),s=Symbol("lengthCalculator"),l=Symbol("allowStale"),a=Symbol("maxAge"),d=Symbol("dispose"),h=Symbol("noDisposeOnSet"),c=Symbol("lruList"),u=Symbol("cache"),p=Symbol("updateAgeOnGet"),naiveLength=()=>1;const get=(e,t,n)=>{const r=e[u].get(t);if(r){const t=r.value;if(isStale(e,t)){if(del(e,r),!e[l])return}else n&&(e[p]&&(r.value.now=Date.now()),e[c].unshiftNode(r));return t.value}},isStale=(e,t)=>{if(!t||!t.maxAge&&!e[a])return!1;const n=Date.now()-t.now;return t.maxAge?n>t.maxAge:e[a]&&n>e[a]},trim=e=>{if(e[o]>e[i])for(let t=e[c].tail;e[o]>e[i]&&null!==t;){const n=t.prev;del(e,t),t=n}},del=(e,t)=>{if(t){const n=t.value;e[d]&&e[d](n.key,n.value),e[o]-=n.length,e[u].delete(n.key),e[c].removeNode(t)}};class f{constructor(e,t,n,r,i){this.key=e,this.value=t,this.length=n,this.now=r,this.maxAge=i||0}}const forEachStep=(e,t,n,r)=>{let i=n.value;isStale(e,i)&&(del(e,n),e[l]||(i=void 0)),i&&t.call(r,i.value,i.key,e)};e.exports=class{constructor(e){if("number"==typeof e&&(e={max:e}),e||(e={}),e.max&&("number"!=typeof e.max||e.max<0))throw new TypeError("max must be a non-negative number");this[i]=e.max||1/0;const t=e.length||naiveLength;if(this[s]="function"!=typeof t?naiveLength:t,this[l]=e.stale||!1,e.maxAge&&"number"!=typeof e.maxAge)throw new TypeError("maxAge must be a number");this[a]=e.maxAge||0,this[d]=e.dispose,this[h]=e.noDisposeOnSet||!1,this[p]=e.updateAgeOnGet||!1,this.reset()}set max(e){if("number"!=typeof e||e<0)throw new TypeError("max must be a non-negative number");this[i]=e||1/0,trim(this)}get max(){return this[i]}set allowStale(e){this[l]=!!e}get allowStale(){return this[l]}set maxAge(e){if("number"!=typeof e)throw new TypeError("maxAge must be a non-negative number");this[a]=e,trim(this)}get maxAge(){return this[a]}set lengthCalculator(e){"function"!=typeof e&&(e=naiveLength),e!==this[s]&&(this[s]=e,this[o]=0,this[c].forEach((e=>{e.length=this[s](e.value,e.key),this[o]+=e.length}))),trim(this)}get lengthCalculator(){return this[s]}get length(){return this[o]}get itemCount(){return this[c].length}rforEach(e,t){t=t||this;for(let n=this[c].tail;null!==n;){const r=n.prev;forEachStep(this,e,n,t),n=r}}forEach(e,t){t=t||this;for(let n=this[c].head;null!==n;){const r=n.next;forEachStep(this,e,n,t),n=r}}keys(){return this[c].toArray().map((e=>e.key))}values(){return this[c].toArray().map((e=>e.value))}reset(){this[d]&&this[c]&&this[c].length&&this[c].forEach((e=>this[d](e.key,e.value))),this[u]=new Map,this[c]=new r,this[o]=0}dump(){return this[c].map((e=>!isStale(this,e)&&{k:e.key,v:e.value,e:e.now+(e.maxAge||0)})).toArray().filter((e=>e))}dumpLru(){return this[c]}set(e,t,n){if((n=n||this[a])&&"number"!=typeof n)throw new TypeError("maxAge must be a number");const r=n?Date.now():0,l=this[s](t,e);if(this[u].has(e)){if(l>this[i])return del(this,this[u].get(e)),!1;const s=this[u].get(e).value;return this[d]&&(this[h]||this[d](e,s.value)),s.now=r,s.maxAge=n,s.value=t,this[o]+=l-s.length,s.length=l,this.get(e),trim(this),!0}const p=new f(e,t,l,r,n);return p.length>this[i]?(this[d]&&this[d](e,t),!1):(this[o]+=p.length,this[c].unshift(p),this[u].set(e,this[c].head),trim(this),!0)}has(e){if(!this[u].has(e))return!1;const t=this[u].get(e).value;return!isStale(this,t)}get(e){return get(this,e,!0)}peek(e){return get(this,e,!1)}pop(){const e=this[c].tail;return e?(del(this,e),e.value):null}del(e){del(this,this[u].get(e))}load(e){this.reset();const t=Date.now();for(let n=e.length-1;n>=0;n--){const r=e[n],i=r.e||0;if(0===i)this.set(r.k,r.v);else{const e=i-t;e>0&&this.set(r.k,r.v,e)}}}prune(){this[u].forEach(((e,t)=>get(this,t,!1)))}}},7533:e=>{e.exports=function(e){e.prototype[Symbol.iterator]=function*(){for(let e=this.head;e;e=e.next)yield e.value}}},5986:(e,t,n)=>{function Yallist(e){var t=this;if(t instanceof Yallist||(t=new Yallist),t.tail=null,t.head=null,t.length=0,e&&"function"==typeof e.forEach)e.forEach((function(e){t.push(e)}));else if(arguments.length>0)for(var n=0,r=arguments.length;n1)n=t;else{if(!this.head)throw new TypeError("Reduce of empty list with no initial value");r=this.head.next,n=this.head.value}for(var i=0;null!==r;i++)n=e(n,r.value,i),r=r.next;return n},Yallist.prototype.reduceReverse=function(e,t){var n,r=this.tail;if(arguments.length>1)n=t;else{if(!this.tail)throw new TypeError("Reduce of empty list with no initial value");r=this.tail.prev,n=this.tail.value}for(var i=this.length-1;null!==r;i--)n=e(n,r.value,i),r=r.prev;return n},Yallist.prototype.toArray=function(){for(var e=new Array(this.length),t=0,n=this.head;null!==n;t++)e[t]=n.value,n=n.next;return e},Yallist.prototype.toArrayReverse=function(){for(var e=new Array(this.length),t=0,n=this.tail;null!==n;t++)e[t]=n.value,n=n.prev;return e},Yallist.prototype.slice=function(e,t){(t=t||this.length)<0&&(t+=this.length),(e=e||0)<0&&(e+=this.length);var n=new Yallist;if(tthis.length&&(t=this.length);for(var r=0,i=this.head;null!==i&&rthis.length&&(t=this.length);for(var r=this.length,i=this.tail;null!==i&&r>t;r--)i=i.prev;for(;null!==i&&r>e;r--,i=i.prev)n.push(i.value);return n},Yallist.prototype.splice=function(e,t){e>this.length&&(e=this.length-1),e<0&&(e=this.length+e);for(var n=0,r=this.head;null!==r&&n{var t=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(t,{a:t}),t},__webpack_require__.d=(e,t)=>{for(var n in t)__webpack_require__.o(t,n)&&!__webpack_require__.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},__webpack_require__.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{function _defineProperty(e,t,n){var r;return(t="symbol"==typeof(r=function(e,t){if("object"!=typeof e||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t||"default");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(t,"string"))?r:r+"")in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}class e{constructor(){_defineProperty(this,"listenersMap",new Map)}addListener(e,t){const n=this.listenersMap.get(e);if(void 0===n)this.listenersMap.set(e,[t]);else{n.indexOf(t)<0&&n.push(t)}}emit(e,...t){const n=this.listenersMap.get(e);if(void 0!==n)if(1===n.length){n[0].apply(null,t)}else{let e=!1,r=null;const i=Array.from(n);for(let n=0;n=0&&n.splice(e,1)}}}const t="React::DevTools::lastSelection",esm_compareVersions=(e,t)=>{const n=validateAndParse(e),r=validateAndParse(t),i=n.pop(),o=r.pop(),s=compareSegments(n,r);return 0!==s?s:i&&o?compareSegments(i.split("."),o.split(".")):i||o?i?-1:1:0},n=/^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i,validateAndParse=e=>{if("string"!=typeof e)throw new TypeError("Invalid argument expected string");const t=e.match(n);if(!t)throw new Error(`Invalid argument not valid semver ('${e}' received)`);return t.shift(),t},isWildcard=e=>"*"===e||"x"===e||"X"===e,tryParse=e=>{const t=parseInt(e,10);return isNaN(t)?e:t},compareStrings=(e,t)=>{if(isWildcard(e)||isWildcard(t))return 0;const[n,r]=((e,t)=>typeof e!=typeof t?[String(e),String(t)]:[e,t])(tryParse(e),tryParse(t));return n>r?1:n{for(let n=0;n":[1],">=":[0,1],"=":[0],"<=":[-1,0],"<":[-1]};Object.keys(r);var i=__webpack_require__(3018),o=__webpack_require__.n(i);Symbol.for("react.element"),Symbol.for("react.transitional.element"),Symbol.for("react.portal"),Symbol.for("react.fragment"),Symbol.for("react.strict_mode"),Symbol.for("react.profiler"),Symbol.for("react.consumer"),Symbol.for("react.context"),Symbol.for("react.forward_ref"),Symbol.for("react.suspense"),Symbol.for("react.suspense_list"),Symbol.for("react.memo"),Symbol.for("react.lazy"),Symbol.for("react.scope"),Symbol.for("react.activity"),Symbol.for("react.legacy_hidden"),Symbol.for("react.tracing_marker"),Symbol.for("react.memo_cache_sentinel"),Symbol.for("react.postpone"),Symbol.for("react.view_transition"),Symbol.iterator;Symbol.asyncIterator;const s=Array.isArray;Object.prototype.hasOwnProperty,new WeakMap,new(o())({max:1e3}),Symbol.for("react.provider");Symbol("inspectable"),Symbol("inspected"),Symbol("name"),Symbol("preview_long"),Symbol("preview_short"),Symbol("readonly"),Symbol("size"),Symbol("type"),Symbol("unserializable");Array.isArray;function gte(e="",t=""){return esm_compareVersions(e,t)>-1}const isReactNativeEnvironment=()=>null==window.document;function getOwnerWindow(e){return e.ownerDocument?e.ownerDocument.defaultView:null}function getOwnerIframe(e){const t=getOwnerWindow(e);return t?t.frameElement:null}function getBoundingClientRectWithBorderOffset(e){const t=getElementDimensions(e);return mergeRectOffsets([e.getBoundingClientRect(),{top:t.borderTop,left:t.borderLeft,bottom:t.borderBottom,right:t.borderRight,width:0,height:0}])}function mergeRectOffsets(e){return e.reduce(((e,t)=>null==e?t:{top:e.top+t.top,left:e.left+t.left,width:e.width,height:e.height,bottom:e.bottom+t.bottom,right:e.right+t.right}))}function getNestedBoundingClientRect(e,t){const n=getOwnerIframe(e);if(n&&n!==t){const r=[e.getBoundingClientRect()];let i=n,o=!1;for(;i;){const e=getBoundingClientRectWithBorderOffset(i);if(r.push(e),i=getOwnerIframe(i),o)break;i&&getOwnerWindow(i)===t&&(o=!0)}return mergeRectOffsets(r)}return e.getBoundingClientRect()}function getElementDimensions(e){const t=window.getComputedStyle(e);return{borderLeft:parseInt(t.borderLeftWidth,10),borderRight:parseInt(t.borderRightWidth,10),borderTop:parseInt(t.borderTopWidth,10),borderBottom:parseInt(t.borderBottomWidth,10),marginLeft:parseInt(t.marginLeft,10),marginRight:parseInt(t.marginRight,10),marginTop:parseInt(t.marginTop,10),marginBottom:parseInt(t.marginBottom,10),paddingLeft:parseInt(t.paddingLeft,10),paddingRight:parseInt(t.paddingRight,10),paddingTop:parseInt(t.paddingTop,10),paddingBottom:parseInt(t.paddingBottom,10)}}const l=Object.assign;class a{constructor(e,t){this.node=e.createElement("div"),this.border=e.createElement("div"),this.padding=e.createElement("div"),this.content=e.createElement("div"),this.border.style.borderColor=c.border,this.padding.style.borderColor=c.padding,this.content.style.backgroundColor=c.background,l(this.node.style,{borderColor:c.margin,pointerEvents:"none",position:"fixed"}),this.node.style.zIndex="10000000",this.node.appendChild(this.border),this.border.appendChild(this.padding),this.padding.appendChild(this.content),t.appendChild(this.node)}remove(){this.node.parentNode&&this.node.parentNode.removeChild(this.node)}update(e,t){boxWrap(t,"margin",this.node),boxWrap(t,"border",this.border),boxWrap(t,"padding",this.padding),l(this.content.style,{height:e.height-t.borderTop-t.borderBottom-t.paddingTop-t.paddingBottom+"px",width:e.width-t.borderLeft-t.borderRight-t.paddingLeft-t.paddingRight+"px"}),l(this.node.style,{top:e.top-t.marginTop+"px",left:e.left-t.marginLeft+"px"})}}class d{constructor(e,t){this.tip=e.createElement("div"),l(this.tip.style,{display:"flex",flexFlow:"row nowrap",backgroundColor:"#333740",borderRadius:"2px",fontFamily:'"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',fontWeight:"bold",padding:"3px 5px",pointerEvents:"none",position:"fixed",fontSize:"12px",whiteSpace:"nowrap"}),this.nameSpan=e.createElement("span"),this.tip.appendChild(this.nameSpan),l(this.nameSpan.style,{color:"#ee78e6",borderRight:"1px solid #aaaaaa",paddingRight:"0.5rem",marginRight:"0.5rem"}),this.dimSpan=e.createElement("span"),this.tip.appendChild(this.dimSpan),l(this.dimSpan.style,{color:"#d7d7d7"}),this.tip.style.zIndex="10000000",t.appendChild(this.tip)}remove(){this.tip.parentNode&&this.tip.parentNode.removeChild(this.tip)}updateText(e,t,n){this.nameSpan.textContent=e,this.dimSpan.textContent=Math.round(t)+"px × "+Math.round(n)+"px"}updatePosition(e,t){const n=this.tip.getBoundingClientRect(),r=function(e,t,n){const r=Math.max(n.height,20),i=Math.max(n.width,60),o=5;let s;s=e.top+e.height+r<=t.top+t.height?e.top+e.heightt.left+t.width&&(l=t.left+t.width-i-o);return s+="px",l+="px",{style:{top:s,left:l}}}(e,t,{width:n.width,height:n.height});l(this.tip.style,r.style)}}class h{constructor(e){const t=window.__REACT_DEVTOOLS_TARGET_WINDOW__||window;this.window=t;const n=window.__REACT_DEVTOOLS_TARGET_WINDOW__||window;this.tipBoundsWindow=n;const r=t.document;this.container=r.createElement("div"),this.container.style.zIndex="10000000",this.tip=new d(r,this.container),this.rects=[],this.agent=e,r.body.appendChild(this.container)}remove(){this.tip.remove(),this.rects.forEach((e=>{e.remove()})),this.rects.length=0,this.container.parentNode&&this.container.parentNode.removeChild(this.container)}inspect(e,t){const n=e.filter((e=>e.nodeType===Node.ELEMENT_NODE));for(;this.rects.length>n.length;){this.rects.pop().remove()}if(0===n.length)return;for(;this.rects.length{const n=getNestedBoundingClientRect(e,this.window),i=getElementDimensions(e);r.top=Math.min(r.top,n.top-i.marginTop),r.right=Math.max(r.right,n.left+n.width+i.marginRight),r.bottom=Math.max(r.bottom,n.top+n.height+i.marginBottom),r.left=Math.min(r.left,n.left-i.marginLeft);this.rects[t].update(n,i)})),!t){t=n[0].nodeName.toLowerCase();const e=n[0],r=this.agent.getComponentNameForHostInstance(e);r&&(t+=" (in "+r+")")}this.tip.updateText(t,r.right-r.left,r.bottom-r.top);const i=getNestedBoundingClientRect(this.tipBoundsWindow.document.documentElement,this.window);this.tip.updatePosition({top:r.top,left:r.left,height:r.bottom-r.top,width:r.right-r.left},{top:i.top+this.tipBoundsWindow.scrollY,left:i.left+this.tipBoundsWindow.scrollX,height:this.tipBoundsWindow.innerHeight,width:this.tipBoundsWindow.innerWidth})}}function boxWrap(e,t,n){l(n.style,{borderTopWidth:e[t+"Top"]+"px",borderLeftWidth:e[t+"Left"]+"px",borderRightWidth:e[t+"Right"]+"px",borderBottomWidth:e[t+"Bottom"]+"px",borderStyle:"solid"})}const c={background:"rgba(120, 170, 210, 0.7)",padding:"rgba(77, 200, 0, 0.3)",margin:"rgba(255, 155, 0, 0.3)",border:"rgba(255, 200, 50, 0.3)"},u=2e3;let p=null,f=null;function hideOverlay(e){return isReactNativeEnvironment()?function(e){e.emit("hideNativeHighlight")}(e):(p=null,void(null!==f&&(f.remove(),f=null)))}function showOverlay(e,t,n,r){return isReactNativeEnvironment()?function(e,t){t.emit("showNativeHighlight",e)}(e,n):function(e,t,n,r){null!==p&&clearTimeout(p),null===f&&(f=new h(n)),f.inspect(e,t),r&&(p=setTimeout((()=>hideOverlay(n)),u))}(e,t,n,r)}let g=new Set,m=!1;const v=["#37afa9","#63b19e","#80b393","#97b488","#abb67d","#beb771","#cfb965","#dfba57","#efbb49","#febc38"];let y=null;function drawWeb(e){null===y&&function(){y=window.document.createElement("canvas"),y.setAttribute("popover","manual"),y.style.cssText="\n xx-background-color: red;\n xx-opacity: 0.5;\n bottom: 0;\n left: 0;\n pointer-events: none;\n position: fixed;\n right: 0;\n top: 0;\n background-color: transparent;\n outline: none;\n box-shadow: none;\n border: none;\n ";const e=window.document.documentElement;e.insertBefore(y,e.firstChild)}();const t=window.devicePixelRatio||1,n=y;n.width=window.innerWidth*t,n.height=window.innerHeight*t,n.style.width=`${window.innerWidth}px`,n.style.height=`${window.innerHeight}px`;const r=n.getContext("2d");r.scale(t,t),r.clearRect(0,0,n.width/t,n.height/t);if(groupAndSortNodes(e).forEach((e=>{!function(e,t){t.forEach((({color:t,rect:n})=>{e.beginPath(),e.strokeStyle=t,e.rect(n.left,n.top,n.width-1,n.height-1),e.stroke()}))}(r,e),function(e,t){const n=t.map((({displayName:e,count:t})=>e?`${e}${t>1?` x${t}`:""}`:"")).filter(Boolean).join(", ");n&&function(e,t,n,r){const{left:i,top:o}=t;e.font="10px monospace",e.textBaseline="middle",e.textAlign="center";const s=2,l=14,a=e.measureText(n),d=a.width+2*s,h=l,c=i,u=o-h;e.fillStyle=r,e.fillRect(c,u,d,h),e.fillStyle="#000000",e.fillText(n,c+d/2,u+h/2)}(e,t[0].rect,n,t[0].color)}(r,e)})),null!==y){if(0===e.size&&y.matches(":popover-open"))return void y.hidePopover();y.matches(":popover-open")&&y.hidePopover(),y.showPopover()}}function groupAndSortNodes(e){const t=new Map;return iterateNodes(e,(({rect:e,color:n,displayName:r,count:i})=>{if(!e)return;const o=`${e.left},${e.top}`;t.has(o)||t.set(o,[]),t.get(o)?.push({rect:e,color:n,displayName:r,count:i})})),Array.from(t.values()).sort(((e,t)=>Math.max(...e.map((e=>e.count)))-Math.max(...t.map((e=>e.count)))))}function draw(e,t){return isReactNativeEnvironment()?function(e,t){const n=[];iterateNodes(e,(({color:e,node:t})=>{n.push({node:t,color:e})})),t.emit("drawTraceUpdates",n);const r=groupAndSortNodes(e);t.emit("drawGroupedTraceUpdatesWithNames",r)}(e,t):drawWeb(e)}function iterateNodes(e,t){e.forEach(((e,n)=>{const r=Math.min(v.length-1,e.count-1),i=v[r];t({color:i,node:n,count:e.count,displayName:e.displayName,expirationTime:e.expirationTime,lastMeasuredAt:e.lastMeasuredAt,rect:e.rect})}))}function destroy(e){return isReactNativeEnvironment()?function(e){e.emit("disableTraceUpdates")}(e):void(null!==y&&(y.matches(":popover-open")&&y.hidePopover(),null!=y.parentNode&&y.parentNode.removeChild(y),y=null))}const w=250,_=3e3,b=250,I=new Map([["Forget","✨"],["Memo","🧠"]]),S="object"==typeof performance&&"function"==typeof performance.now?()=>performance.now():()=>Date.now(),E=new Map;let P=null,x=null,D=!1,N=null;function traceUpdates(e){D&&(e.forEach((e=>{const t=E.get(e),n=S();let r=null!=t?t.lastMeasuredAt:0,i=null!=t?t.rect:null;(null===i||r+bI.get(e)||"")).join("");o=n?`${n}${e}`:e}E.set(e,{count:null!=t?t.count+1:1,expirationTime:null!=t?Math.min(n+_,t.expirationTime+w):n+w,lastMeasuredAt:r,rect:i,displayName:o})})),null!==N&&(clearTimeout(N),N=null),null===x&&(x=requestAnimationFrame(prepareToDraw)))}function prepareToDraw(){x=null,N=null;const e=S();let t=Number.MAX_VALUE;E.forEach(((n,r)=>{n.expirationTime{try{if(this._messageQueue.length){for(let e=0;e{switch(r){case"context":this.send("overrideContext",{id:e,path:t,rendererID:n,wasForwarded:!0,value:i});break;case"hooks":this.send("overrideHookState",{id:e,path:t,rendererID:n,wasForwarded:!0,value:i});break;case"props":this.send("overrideProps",{id:e,path:t,rendererID:n,wasForwarded:!0,value:i});break;case"state":this.send("overrideState",{id:e,path:t,rendererID:n,wasForwarded:!0,value:i})}})),this._wall=e,this._wallUnlisten=e.listen((e=>{e&&e.event&&this.emit(e.event,e.payload)}))||null,this.addListener("overrideValueAtPath",this.overrideValueAtPath)}get wall(){return this._wall}send(e,...t){this._isShutdown?console.warn(`Cannot send message "${e}" through a Bridge that has been shutdown.`):(this._messageQueue.push(e,t),this._scheduledFlush||(this._scheduledFlush=!0,"function"==typeof devtoolsJestTestScheduler?devtoolsJestTestScheduler(this._flush):queueMicrotask(this._flush)))}shutdown(){if(this._isShutdown)return void console.warn("Bridge was already shutdown.");this.emit("shutdown"),this.send("shutdown"),this._isShutdown=!0,this.addListener=function(){},this.emit=function(){},this.removeAllListeners();const e=this._wallUnlisten;e&&e();do{this._flush()}while(this._messageQueue.length)}};function agent_defineProperty(e,t,n){var r;return(t="symbol"==typeof(r=function(e,t){if("object"!=typeof e||!e)return e;var n=e[Symbol.toPrimitive];if(void 0!==n){var r=n.call(e,t||"default");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(t,"string"))?r:r+"")in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function mergeRoots(e,t,n){const r=e.suspendedByRange,i=t.suspendedByRange;t.isErrored&&(e.isErrored=!0);for(let n=0;nr[1]&&(r[1]=i[1])))}class k extends e{constructor(e,n=!1,r){super(),agent_defineProperty(this,"_isProfiling",!1),agent_defineProperty(this,"_rendererInterfaces",{}),agent_defineProperty(this,"_persistedSelection",null),agent_defineProperty(this,"_persistedSelectionMatch",null),agent_defineProperty(this,"_traceUpdatesEnabled",!1),agent_defineProperty(this,"clearErrorsAndWarnings",(({rendererID:e})=>{const t=this._rendererInterfaces[e];null==t?console.warn(`Invalid renderer id "${e}"`):t.clearErrorsAndWarnings()})),agent_defineProperty(this,"clearErrorsForElementID",(({id:e,rendererID:t})=>{const n=this._rendererInterfaces[t];null==n?console.warn(`Invalid renderer id "${t}"`):n.clearErrorsForElementID(e)})),agent_defineProperty(this,"clearWarningsForElementID",(({id:e,rendererID:t})=>{const n=this._rendererInterfaces[t];null==n?console.warn(`Invalid renderer id "${t}"`):n.clearWarningsForElementID(e)})),agent_defineProperty(this,"copyElementPath",(({id:e,path:t,rendererID:n})=>{const r=this._rendererInterfaces[n];if(null==r)console.warn(`Invalid renderer id "${n}" for element "${e}"`);else{const n=r.getSerializedElementValueByPath(e,t);null!=n?this._bridge.send("saveToClipboard",n):console.warn(`Unable to obtain serialized value for element "${e}"`)}})),agent_defineProperty(this,"deletePath",(({hookID:e,id:t,path:n,rendererID:r,type:i})=>{const o=this._rendererInterfaces[r];null==o?console.warn(`Invalid renderer id "${r}" for element "${t}"`):o.deletePath(i,t,e,n)})),agent_defineProperty(this,"getBackendVersion",(()=>{const e="7.0.1-3cde211b0c";e&&this._bridge.send("backendVersion",e)})),agent_defineProperty(this,"getBridgeProtocol",(()=>{this._bridge.send("bridgeProtocol",A)})),agent_defineProperty(this,"getProfilingData",(({rendererID:e})=>{const t=this._rendererInterfaces[e];null==t&&console.warn(`Invalid renderer id "${e}"`),this._bridge.send("profilingData",t.getProfilingData())})),agent_defineProperty(this,"getProfilingStatus",(()=>{this._bridge.send("profilingStatus",this._isProfiling)})),agent_defineProperty(this,"getOwnersList",(({id:e,rendererID:t})=>{const n=this._rendererInterfaces[t];if(null==n)console.warn(`Invalid renderer id "${t}" for element "${e}"`);else{const t=n.getOwnersList(e);this._bridge.send("ownersList",{id:e,owners:t})}})),agent_defineProperty(this,"inspectElement",(({forceFullData:e,id:t,path:n,rendererID:r,requestID:i})=>{const o=this._rendererInterfaces[r];null==o?console.warn(`Invalid renderer id "${r}" for element "${t}"`):(this._bridge.send("inspectedElement",o.inspectElement(i,t,n,e)),null!==this._persistedSelectionMatch&&this._persistedSelectionMatch.id===t||(this._persistedSelection=null,this._persistedSelectionMatch=null,o.setTrackedPath(null),this._lastSelectedElementID=t,this._lastSelectedRendererID=r,this._persistSelectionTimerScheduled||(this._persistSelectionTimerScheduled=!0,setTimeout(this._persistSelection,1e3))))})),agent_defineProperty(this,"inspectScreen",(({requestID:e,id:t,forceFullData:n,path:r})=>{let i=null,o=!1,s=0,l=null,a=null;if(null!==r&&r.length>1){if("suspendedBy"!==r[0])throw new Error("Only hydrating suspendedBy paths is supported. This is a bug.");if("number"!=typeof r[1])throw new Error(`Expected suspendedBy index to be a number. Received '${r[1]}' instead. This is a bug.`);l=r[1],a=r.slice(2)}for(const r in this._rendererInterfaces){const c=this._rendererInterfaces[r];let u=null;if(null!==l&&null!==a){const e=l-s;void 0!==c.getElementAttributeByPath(t,["suspendedBy",e])&&(u=["suspendedBy",e].concat(a))}const p=c.inspectElement(e,t,u,n);switch(p.type){case"hydrated-path":if(p.path[1]+=s,null!==p.value)for(let e=0;e{const n=this._rendererInterfaces[t];null==n?console.warn(`Invalid renderer id "${t}" for element "${e}"`):n.logElementToConsole(e)})),agent_defineProperty(this,"overrideError",(({id:e,rendererID:t,forceError:n})=>{const r=this._rendererInterfaces[t];null==r?console.warn(`Invalid renderer id "${t}" for element "${e}"`):r.overrideError(e,n)})),agent_defineProperty(this,"overrideSuspense",(({id:e,rendererID:t,forceFallback:n})=>{const r=this._rendererInterfaces[t];null==r?console.warn(`Invalid renderer id "${t}" for element "${e}"`):r.overrideSuspense(e,n)})),agent_defineProperty(this,"overrideSuspenseMilestone",(({suspendedSet:e})=>{for(const t in this._rendererInterfaces){const n=this._rendererInterfaces[t];n.supportsTogglingSuspense&&n.overrideSuspenseMilestone(e)}})),agent_defineProperty(this,"overrideValueAtPath",(({hookID:e,id:t,path:n,rendererID:r,type:i,value:o})=>{const s=this._rendererInterfaces[r];null==s?console.warn(`Invalid renderer id "${r}" for element "${t}"`):s.overrideValueAtPath(i,t,e,n,o)})),agent_defineProperty(this,"overrideContext",(({id:e,path:t,rendererID:n,wasForwarded:r,value:i})=>{r||this.overrideValueAtPath({id:e,path:t,rendererID:n,type:"context",value:i})})),agent_defineProperty(this,"overrideHookState",(({id:e,hookID:t,path:n,rendererID:r,wasForwarded:i,value:o})=>{i||this.overrideValueAtPath({id:e,path:n,rendererID:r,type:"hooks",value:o})})),agent_defineProperty(this,"overrideProps",(({id:e,path:t,rendererID:n,wasForwarded:r,value:i})=>{r||this.overrideValueAtPath({id:e,path:t,rendererID:n,type:"props",value:i})})),agent_defineProperty(this,"overrideState",(({id:e,path:t,rendererID:n,wasForwarded:r,value:i})=>{r||this.overrideValueAtPath({id:e,path:t,rendererID:n,type:"state",value:i})})),agent_defineProperty(this,"onReloadAndProfileSupportedByHost",(()=>{this._bridge.send("isReloadAndProfileSupportedByBackend",!0)})),agent_defineProperty(this,"reloadAndProfile",(({recordChangeDescriptions:e,recordTimeline:t})=>{"function"==typeof this._onReloadAndProfile&&this._onReloadAndProfile(e,t),this._bridge.send("reloadAppForProfiling")})),agent_defineProperty(this,"renamePath",(({hookID:e,id:t,newPath:n,oldPath:r,rendererID:i,type:o})=>{const s=this._rendererInterfaces[i];null==s?console.warn(`Invalid renderer id "${i}" for element "${t}"`):s.renamePath(o,t,e,r,n)})),agent_defineProperty(this,"setTraceUpdatesEnabled",(e=>{this._traceUpdatesEnabled=e,D=e,D||(E.clear(),null!==x&&(cancelAnimationFrame(x),x=null),null!==N&&(clearTimeout(N),N=null),destroy(P));for(const t in this._rendererInterfaces){this._rendererInterfaces[t].setTraceUpdatesEnabled(e)}})),agent_defineProperty(this,"syncSelectionFromBuiltinElementsPanel",(()=>{const e=window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0;null!=e&&this.selectNode(e)})),agent_defineProperty(this,"shutdown",(()=>{this.emit("shutdown"),this._bridge.removeAllListeners(),this.removeAllListeners()})),agent_defineProperty(this,"startProfiling",(({recordChangeDescriptions:e,recordTimeline:t})=>{this._isProfiling=!0;for(const n in this._rendererInterfaces){this._rendererInterfaces[n].startProfiling(e,t)}this._bridge.send("profilingStatus",this._isProfiling)})),agent_defineProperty(this,"stopProfiling",(()=>{this._isProfiling=!1;for(const e in this._rendererInterfaces){this._rendererInterfaces[e].stopProfiling()}this._bridge.send("profilingStatus",this._isProfiling)})),agent_defineProperty(this,"stopInspectingNative",(e=>{this._bridge.send("stopInspectingHost",e)})),agent_defineProperty(this,"storeAsGlobal",(({count:e,id:t,path:n,rendererID:r})=>{const i=this._rendererInterfaces[r];null==i?console.warn(`Invalid renderer id "${r}" for element "${t}"`):i.storeAsGlobal(t,n,e)})),agent_defineProperty(this,"updateHookSettings",(e=>{this.emit("updateHookSettings",e)})),agent_defineProperty(this,"getHookSettings",(()=>{this.emit("getHookSettings")})),agent_defineProperty(this,"onHookSettings",(e=>{this._bridge.send("hookSettings",e)})),agent_defineProperty(this,"updateComponentFilters",(e=>{for(const t in this._rendererInterfaces){const n=+t,r=this._rendererInterfaces[n];if(this._lastSelectedRendererID===n){const e=r.getPathForElement(this._lastSelectedElementID);null!==e&&(r.setTrackedPath(e),this._persistedSelection={rendererID:n,path:e})}r.updateComponentFilters(e)}})),agent_defineProperty(this,"getEnvironmentNames",(()=>{let e=null;for(const t in this._rendererInterfaces){const n=this._rendererInterfaces[+t].getEnvironmentNames();if(null===e)e=n;else for(let t=0;t{this.emit("traceUpdates",e)})),agent_defineProperty(this,"onFastRefreshScheduled",(()=>{this._bridge.send("fastRefreshScheduled")})),agent_defineProperty(this,"onHookOperations",(e=>{if(this._bridge.send("operations",e),null!==this._persistedSelection){const t=e[0];if(this._persistedSelection.rendererID===t){const e=this._rendererInterfaces[t];if(null==e)console.warn(`Invalid renderer id "${t}"`);else{const t=this._persistedSelectionMatch,n=e.getBestMatchForTrackedPath();this._persistedSelectionMatch=n;const r=null!==t?t.id:null,i=null!==n?n.id:null;r!==i&&null!==i&&this._bridge.send("selectElement",i),null!==n&&n.isFullMatch&&(this._persistedSelection=null,this._persistedSelectionMatch=null,e.setTrackedPath(null))}}}})),agent_defineProperty(this,"getIfHasUnsupportedRendererVersion",(()=>{this.emit("getIfHasUnsupportedRendererVersion")})),agent_defineProperty(this,"_persistSelectionTimerScheduled",!1),agent_defineProperty(this,"_lastSelectedRendererID",-1),agent_defineProperty(this,"_lastSelectedElementID",-1),agent_defineProperty(this,"_persistSelection",(()=>{this._persistSelectionTimerScheduled=!1;const e=this._lastSelectedRendererID,n=this._lastSelectedElementID,r=this._rendererInterfaces[e],i=null!=r?r.getPathForElement(n):null;null!==i?function(e,t){try{return sessionStorage.setItem(e,t)}catch(e){}}(t,JSON.stringify({rendererID:e,path:i})):function(e){try{sessionStorage.removeItem(e)}catch(e){}}(t)})),this._isProfiling=n,this._onReloadAndProfile=r;const i=function(e){try{return sessionStorage.getItem(e)}catch(e){return null}}(t);null!=i&&(this._persistedSelection=JSON.parse(i)),this._bridge=e,e.addListener("clearErrorsAndWarnings",this.clearErrorsAndWarnings),e.addListener("clearErrorsForElementID",this.clearErrorsForElementID),e.addListener("clearWarningsForElementID",this.clearWarningsForElementID),e.addListener("copyElementPath",this.copyElementPath),e.addListener("deletePath",this.deletePath),e.addListener("getBackendVersion",this.getBackendVersion),e.addListener("getBridgeProtocol",this.getBridgeProtocol),e.addListener("getProfilingData",this.getProfilingData),e.addListener("getProfilingStatus",this.getProfilingStatus),e.addListener("getOwnersList",this.getOwnersList),e.addListener("inspectElement",this.inspectElement),e.addListener("inspectScreen",this.inspectScreen),e.addListener("logElementToConsole",this.logElementToConsole),e.addListener("overrideError",this.overrideError),e.addListener("overrideSuspense",this.overrideSuspense),e.addListener("overrideSuspenseMilestone",this.overrideSuspenseMilestone),e.addListener("overrideValueAtPath",this.overrideValueAtPath),e.addListener("reloadAndProfile",this.reloadAndProfile),e.addListener("renamePath",this.renamePath),e.addListener("setTraceUpdatesEnabled",this.setTraceUpdatesEnabled),e.addListener("startProfiling",this.startProfiling),e.addListener("stopProfiling",this.stopProfiling),e.addListener("storeAsGlobal",this.storeAsGlobal),e.addListener("syncSelectionFromBuiltinElementsPanel",this.syncSelectionFromBuiltinElementsPanel),e.addListener("shutdown",this.shutdown),e.addListener("updateHookSettings",this.updateHookSettings),e.addListener("getHookSettings",this.getHookSettings),e.addListener("updateComponentFilters",this.updateComponentFilters),e.addListener("getEnvironmentNames",this.getEnvironmentNames),e.addListener("getIfHasUnsupportedRendererVersion",this.getIfHasUnsupportedRendererVersion),e.addListener("overrideContext",this.overrideContext),e.addListener("overrideHookState",this.overrideHookState),e.addListener("overrideProps",this.overrideProps),e.addListener("overrideState",this.overrideState),function(e,t){function registerListenersOnWindow(e){e&&"function"==typeof e.addEventListener?(e.addEventListener("click",onClick,!0),e.addEventListener("mousedown",onMouseEvent,!0),e.addEventListener("mouseover",onMouseEvent,!0),e.addEventListener("mouseup",onMouseEvent,!0),e.addEventListener("pointerdown",onPointerDown,!0),e.addEventListener("pointermove",onPointerMove,!0),e.addEventListener("pointerup",onPointerUp,!0)):t.emit("startInspectingNative")}function stopInspectingHost(){hideOverlay(t),removeListenersOnWindow(window),g.forEach((function(e){try{removeListenersOnWindow(e.contentWindow)}catch(e){}})),g=new Set}function removeListenersOnWindow(e){e&&"function"==typeof e.removeEventListener?(e.removeEventListener("click",onClick,!0),e.removeEventListener("mousedown",onMouseEvent,!0),e.removeEventListener("mouseover",onMouseEvent,!0),e.removeEventListener("mouseup",onMouseEvent,!0),e.removeEventListener("pointerdown",onPointerDown,!0),e.removeEventListener("pointermove",onPointerMove,!0),e.removeEventListener("pointerup",onPointerUp,!0)):t.emit("stopInspectingNative")}function highlightHostInstance({displayName:r,hideAfterTimeout:i,id:o,openBuiltinElementsPanel:s,rendererID:l,scrollIntoView:a}){const d=t.rendererInterfaces[l];if(null==d)return console.warn(`Invalid renderer id "${l}" for element "${o}"`),void hideOverlay(t);if(!d.hasElementWithId(o))return void hideOverlay(t);const h=d.findHostInstancesForElementID(o);if(null!=h)for(let o=0;o0&&(d.length>2||d[0].width>0||d[0].height>0))return a&&"function"==typeof l.scrollIntoView&&(n&&(clearTimeout(n),n=null),l.scrollIntoView({block:"nearest",inline:"nearest"})),showOverlay(h,r,t,i),void(s&&(window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0=l,e.send("syncSelectionToBuiltinElementsPanel")))}hideOverlay(t)}function attemptScrollToHostInstance(e,t){const n=e.findHostInstancesForElementID(t);if(null!=n)for(let e=0;e0&&(r.length>2||r[0].width>0||r[0].height>0)&&"function"==typeof t.scrollIntoView)return t.scrollIntoView({block:"nearest",inline:"nearest",behavior:"smooth"}),!0}return!1}e.addListener("clearHostInstanceHighlight",(function(){hideOverlay(t)})),e.addListener("highlightHostInstance",highlightHostInstance),e.addListener("highlightHostInstances",(function({displayName:e,hideAfterTimeout:n,elements:r,scrollIntoView:i}){const o=[];for(let e=0;e0){const e=o[0];i&&"function"==typeof e.scrollIntoView&&e.scrollIntoView({block:"nearest",inline:"nearest"})}showOverlay(o,e,t,n)})),e.addListener("scrollToHostInstance",(function({id:e,rendererID:r}){hideOverlay(t),n&&(clearTimeout(n),n=null);const i=t.rendererInterfaces[r];if(null==i)return void console.warn(`Invalid renderer id "${r}" for element "${e}"`);if(!i.hasElementWithId(e))return;if(attemptScrollToHostInstance(i,e))return;const o=i.findLastKnownRectsForID(e);if(null!==o&&o.length>0){let t=1/0,r=1/0;for(let e=0;ewindow.scrollX+s.clientWidth||r>window.scrollY+s.clientHeight)&&window.scrollTo({top:r,left:t,behavior:"smooth"}),n=setTimeout((()=>{attemptScrollToHostInstance(i,e)}),100)}})),e.addListener("shutdown",stopInspectingHost),e.addListener("startInspectingHost",(function(e){m=e,registerListenersOnWindow(window)})),e.addListener("stopInspectingHost",stopInspectingHost);let n=null;function onClick(t){t.preventDefault(),t.stopPropagation(),stopInspectingHost(),e.send("stopInspectingHost",!0)}function onMouseEvent(e){e.preventDefault(),e.stopPropagation()}function onPointerDown(e){e.preventDefault(),e.stopPropagation(),selectElementForNode(getEventTarget(e))}let r=null;function onPointerMove(e){e.preventDefault(),e.stopPropagation();const n=getEventTarget(e);if(r!==n){if(r=n,"IFRAME"===n.tagName){const e=n;try{g.has(e)||(registerListenersOnWindow(e.contentWindow),g.add(e))}catch(e){}}if(m){const e=t.getIDForHostInstance(n,m);if(null!==e){const n=t.rendererInterfaces[e.rendererID];if(null==n)return void console.warn(`Invalid renderer id "${e.rendererID}" for element "${e.id}"`);highlightHostInstance({displayName:n.getDisplayNameForElementID(e.id),hideAfterTimeout:!1,id:e.id,openBuiltinElementsPanel:!1,rendererID:e.rendererID,scrollIntoView:!1})}}else showOverlay([n],null,t,!1)}}function onPointerUp(e){e.preventDefault(),e.stopPropagation()}const selectElementForNode=n=>{const r=t.getIDForHostInstance(n,m);null!==r&&e.send("selectElement",r.id)};function getEventTarget(e){return e.composed?e.composedPath()[0]:e.target}}(e,this),P=this,P.addListener("traceUpdates",traceUpdates),e.send("backendInitialized"),this._isProfiling&&e.send("profilingStatus",!0)}get rendererInterfaces(){return this._rendererInterfaces}getInstanceAndStyle({id:e,rendererID:t}){const n=this._rendererInterfaces[t];return null==n?(console.warn(`Invalid renderer id "${t}"`),null):n.getInstanceAndStyle(e)}getIDForHostInstance(e,t){if(isReactNativeEnvironment()||"number"!=typeof e.nodeType){for(const n in this._rendererInterfaces){const r=this._rendererInterfaces[n];try{const i=t?r.getSuspenseNodeIDForHostInstance(e):r.getElementIDForHostInstance(e);if(null!==i)return{id:i,rendererID:+n}}catch(e){}}return null}{let n=null,r=null,i=0;for(const t in this._rendererInterfaces){const o=this._rendererInterfaces[t],s=o.getNearestMountedDOMNode(e);if(null!==s){if(s===e){n=s,r=o,i=+t;break}(null===n||n.contains(s))&&(n=s,r=o,i=+t)}}if(null!=r&&null!=n)try{const e=t?r.getSuspenseNodeIDForHostInstance(n):r.getElementIDForHostInstance(n);if(null!==e)return{id:e,rendererID:i}}catch(e){}return null}}getComponentNameForHostInstance(e){const t=this.getIDForHostInstance(e);if(null!==t){return this._rendererInterfaces[t.rendererID].getDisplayNameForElementID(t.id)}return null}selectNode(e){const t=this.getIDForHostInstance(e);null!==t&&this._bridge.send("selectElement",t.id)}registerRendererInterface(e,t){this._rendererInterfaces[e]=t,t.setTraceUpdatesEnabled(this._traceUpdatesEnabled);const n=t.renderer;if(null!==n){1===n.bundleType&>e(n.version,"19.3.0-canary")&&this._bridge.send("enableSuspenseTab")}const r=this._persistedSelection;null!==r&&r.rendererID===e&&t.setTrackedPath(r.path)}onUnsupportedRenderer(){this._bridge.send("unsupportedRendererVersion")}}function initBackend(e,t,n,r){if(null==e)return()=>{};function registerRendererInterface(e,n){t.registerRendererInterface(e,n),n.flushInitialOperations()}const i=[e.sub("renderer-attached",(({id:e,rendererInterface:t})=>{registerRendererInterface(e,t)})),e.sub("unsupported-renderer-version",(()=>{t.onUnsupportedRenderer()})),e.sub("fastRefreshScheduled",t.onFastRefreshScheduled),e.sub("operations",t.onHookOperations),e.sub("traceUpdates",t.onTraceUpdates),e.sub("settingsInitialized",t.onHookSettings)];t.addListener("getIfHasUnsupportedRendererVersion",(()=>{e.hasUnsupportedRendererAttached&&t.onUnsupportedRenderer()})),e.rendererInterfaces.forEach(((e,t)=>{registerRendererInterface(t,e)})),e.emit("react-devtools",t),e.reactDevtoolsAgent=t;return t.addListener("shutdown",(()=>{i.forEach((e=>e())),e.rendererInterfaces.forEach((e=>{e.cleanup()})),e.reactDevtoolsAgent=null})),t.addListener("updateHookSettings",(t=>{e.settings=t})),t.addListener("getHookSettings",(()=>{null!=e.settings&&t.onHookSettings(e.settings)})),r&&t.onReloadAndProfileSupportedByHost(),()=>{i.forEach((e=>e()))}}function resolveBoxStyle(e,t){let n=!1;const r={bottom:0,left:0,right:0,top:0},i=t[e];if(null!=i){for(const e of Object.keys(r))r[e]=i;n=!0}const o=t[e+"Horizontal"];if(null!=o)r.left=o,r.right=o,n=!0;else{const i=t[e+"Left"];null!=i&&(r.left=i,n=!0);const o=t[e+"Right"];null!=o&&(r.right=o,n=!0);const s=t[e+"End"];null!=s&&(r.right=s,n=!0);const l=t[e+"Start"];null!=l&&(r.left=l,n=!0)}const s=t[e+"Vertical"];if(null!=s)r.bottom=s,r.top=s,n=!0;else{const i=t[e+"Bottom"];null!=i&&(r.bottom=i,n=!0);const o=t[e+"Top"];null!=o&&(r.top=o,n=!0)}return n?r:null}function setupNativeStyleEditor(e,t,n,r){e.addListener("NativeStyleEditor_measure",(({id:r,rendererID:i})=>{measureStyle(t,e,n,r,i)})),e.addListener("NativeStyleEditor_renameAttribute",(({id:r,rendererID:i,oldName:o,newName:l,value:a})=>{!function(e,t,n,r,i,o){const l=e.getInstanceAndStyle({id:t,rendererID:n});if(!l||!l.style)return;const{instance:a,style:d}=l,h=i?{[r]:void 0,[i]:o}:{[r]:void 0};let c;if(null!==a&&"function"==typeof a.setNativeProps){const e=O.get(t);e?Object.assign(e,h):O.set(t,h),a.setNativeProps({style:h})}else if(s(d)){const l=d.length-1;"object"!=typeof d[l]||s(d[l])?e.overrideValueAtPath({type:"props",id:t,rendererID:n,path:["style"],value:d.concat([h])}):(c=shallowClone(d[l]),delete c[r],i?c[i]=o:c[r]=void 0,e.overrideValueAtPath({type:"props",id:t,rendererID:n,path:["style",l],value:c}))}else"object"==typeof d?(c=shallowClone(d),delete c[r],i?c[i]=o:c[r]=void 0,e.overrideValueAtPath({type:"props",id:t,rendererID:n,path:["style"],value:c})):e.overrideValueAtPath({type:"props",id:t,rendererID:n,path:["style"],value:[d,h]});e.emit("hideNativeHighlight")}(t,r,i,o,l,a),setTimeout((()=>measureStyle(t,e,n,r,i)))})),e.addListener("NativeStyleEditor_setValue",(({id:r,rendererID:i,name:o,value:l})=>{!function(e,t,n,r,i){const o=e.getInstanceAndStyle({id:t,rendererID:n});if(!o||!o.style)return;const{instance:l,style:a}=o,d={[r]:i};if(null!==l&&"function"==typeof l.setNativeProps){const e=O.get(t);e?Object.assign(e,d):O.set(t,d),l.setNativeProps({style:d})}else if(s(a)){const o=a.length-1;"object"!=typeof a[o]||s(a[o])?e.overrideValueAtPath({type:"props",id:t,rendererID:n,path:["style"],value:a.concat([d])}):e.overrideValueAtPath({type:"props",id:t,rendererID:n,path:["style",o,r],value:i})}else e.overrideValueAtPath({type:"props",id:t,rendererID:n,path:["style"],value:[a,d]});e.emit("hideNativeHighlight")}(t,r,i,o,l),setTimeout((()=>measureStyle(t,e,n,r,i)))})),e.send("isNativeStyleEditorSupported",{isSupported:!0,validAttributes:r})}const R={top:0,left:0,right:0,bottom:0},O=new Map;function measureStyle(e,t,n,r,i){const o=e.getInstanceAndStyle({id:r,rendererID:i});if(!o||!o.style)return void t.send("NativeStyleEditor_styleAndLayout",{id:r,layout:null,style:null});const{instance:s,style:l}=o;let a=n(l);const d=O.get(r);null!=d&&(a=Object.assign({},a,d)),s&&"function"==typeof s.measure?s.measure(((e,n,i,o,s,l)=>{if("number"!=typeof e)return void t.send("NativeStyleEditor_styleAndLayout",{id:r,layout:null,style:a||null});const d=null!=a&&resolveBoxStyle("margin",a)||R,h=null!=a&&resolveBoxStyle("padding",a)||R;t.send("NativeStyleEditor_styleAndLayout",{id:r,layout:{x:e,y:n,width:i,height:o,left:s,top:l,margin:d,padding:h},style:a||null})})):t.send("NativeStyleEditor_styleAndLayout",{id:r,layout:null,style:a||null})}function shallowClone(e){const t={};for(const n in e)t[n]=e[n];return t}const B="compact";!function(e){if(null==e)return;e.backends.set(B,{Agent:k,Bridge:L,initBackend,setupNativeStyleEditor}),e.emit("devtools-backend-installed",B)}(window.__REACT_DEVTOOLS_GLOBAL_HOOK__)})()})();
2 | //# sourceMappingURL=react_devtools_backend_compact.js.map
--------------------------------------------------------------------------------