diff --git a/packages/client/src/components/WaitForConnection.vue b/packages/client/src/components/WaitForConnection.vue index 27ce74eb..75ea2a19 100644 --- a/packages/client/src/components/WaitForConnection.vue +++ b/packages/client/src/components/WaitForConnection.vue @@ -1,5 +1,21 @@ diff --git a/packages/client/src/composables/state-tab.ts b/packages/client/src/composables/state-tab.ts index 4e88c2f4..44248f5b 100644 --- a/packages/client/src/composables/state-tab.ts +++ b/packages/client/src/composables/state-tab.ts @@ -1,6 +1,8 @@ import { useDevToolsBridgeRpc, useDevToolsState } from '@vue-devtools-next/core' import type { MaybeRef } from 'vue' import type { CustomTab } from '@vue-devtools-next/kit' +import { isInElectron } from '@vue-devtools-next/shared' + import type { ModuleBuiltinTab } from '~/types/tab' export interface TabSettings { @@ -31,7 +33,9 @@ export function useAllTabs() { if (currentTab) { if (currentTab[1].some(t => t.name === tab.name)) return - if (!vitePluginDetected && viteOnlyTabs.includes(tab.name)) + + // @TODO: electron app support vite only tabs + if ((!vitePluginDetected || isInElectron) && viteOnlyTabs.includes(tab.name)) return currentTab[1].push({ diff --git a/packages/client/src/constants/tab.ts b/packages/client/src/constants/tab.ts index 06c56732..ca7ab387 100644 --- a/packages/client/src/constants/tab.ts +++ b/packages/client/src/constants/tab.ts @@ -1,5 +1,5 @@ import type { DevtoolsBridgeAppRecord } from '@vue-devtools-next/core' -import { deepClone } from '@vue-devtools-next/shared' +import { deepClone, isInElectron } from '@vue-devtools-next/shared' import type { ModuleBuiltinTab } from '~/types' // @unocss-include @@ -93,7 +93,9 @@ export function getBuiltinTab(viteDetected: boolean, moduleDetectives?: Devtools if (item[0] === 'modules') item[1] = item[1].filter(t => moduleDetectives ? isDetected(moduleDetectives, t) : true) }) - return viteDetected + + // @TODO: electron app support vite only tabs + return (viteDetected && !isInElectron) ? tab : tab.map(([_, tabs]) => [_, tabs.filter(t => !viteOnlyTabs.includes(t.name))]) } diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 4f0579d9..5ed58524 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -2,6 +2,8 @@ import { VIEW_MODE_STORAGE_KEY, isBrowser, isInChromePanel, target } from '@vue- import { initDevTools as _initDevTools } from './main' +export { createConnectionApp } from './main' + export function initDevTools(shell) { if (!isBrowser) return diff --git a/packages/client/src/main.ts b/packages/client/src/main.ts index 853b691f..e809a4a5 100644 --- a/packages/client/src/main.ts +++ b/packages/client/src/main.ts @@ -2,7 +2,7 @@ import '@unocss/reset/tailwind.css' import 'floating-vue/dist/style.css' import type { BridgeInstanceType } from '@vue-devtools-next/core' -import { BROADCAST_CHANNEL_NAME, isInIframe } from '@vue-devtools-next/shared' +import { BROADCAST_CHANNEL_NAME, isInChromePanel, isInElectron, isInIframe } from '@vue-devtools-next/shared' import { Bridge, BridgeEvents, HandShakeServer, createDevToolsVuePlugin, registerBridgeRpc } from '@vue-devtools-next/core' import type { App as AppType } from 'vue' @@ -105,6 +105,14 @@ export async function initDevTools(shell, options: { viewMode?: 'overlay' | 'pan }) } +export function createConnectionApp(container: string = '#app', props?: Record) { + const app = createApp(WaitForConnection, { + ...props, + }) + app.mount(container) + return app +} + window.addEventListener('message', (event) => { if (event.data === '__VUE_DEVTOOLS_CREATE_CLIENT__') { initDevTools({ @@ -133,7 +141,7 @@ window.addEventListener('message', (event) => { }) // @TODO: refactor separate window channel -if (!isInIframe) { +if (!isInIframe && !isInChromePanel && !isInElectron) { function initSeparateWindowChannel() { const connectionInfo: { connected: boolean @@ -147,12 +155,6 @@ if (!isInIframe) { const channel = new BroadcastChannel(BROADCAST_CHANNEL_NAME) - function createConnectionApp() { - const app = createApp(WaitForConnection) - app.mount('#app') - return app - } - function connect() { connectionInfo.timer = setInterval(() => { channel.postMessage({ diff --git a/packages/electron/app.html b/packages/electron/app.html index 12e1c3fa..9cffc795 100644 --- a/packages/electron/app.html +++ b/packages/electron/app.html @@ -11,6 +11,14 @@ display: flex; height: 100%; } + .loading { + width: 100vw; + height: 100vh; + font-weight: bold; + display: flex; + align-items: center; + justify-content: center; + } diff --git a/packages/electron/package.json b/packages/electron/package.json index 9217d01b..16f1e2a1 100644 --- a/packages/electron/package.json +++ b/packages/electron/package.json @@ -34,6 +34,7 @@ "electron": "^28.0.0", "execa": "^8.0.1", "h3": "^1.9.0", + "ip": "^1.1.8", "socket.io": "^4.7.2", "socket.io-client": "^4.7.2" }, diff --git a/packages/electron/src/app.ts b/packages/electron/src/app.ts index 029940fd..1a8bdac0 100644 --- a/packages/electron/src/app.ts +++ b/packages/electron/src/app.ts @@ -15,7 +15,8 @@ function createWindow() { webSecurity: false, nodeIntegration: true, contextIsolation: false, - devTools: true, + // @TODO: enabled in dev mode + devTools: false, }, }) const appEntryPath = path.join(__dirname, '../app.html') diff --git a/packages/electron/src/devtools.ts b/packages/electron/src/devtools.ts index 7b27a3c7..4706843a 100644 --- a/packages/electron/src/devtools.ts +++ b/packages/electron/src/devtools.ts @@ -1,34 +1,54 @@ -import io from 'socket.io-client' -import { initDevTools } from '../client/devtools-panel' +import io from 'socket.io-client/dist/socket.io.js' +import ip from 'ip' +import { createConnectionApp, initDevTools } from '../client/devtools-panel' import { Bridge } from '../../core/src/bridge' const port = window.process.env.PORT || 8098 -const socket = io(`http://localhost:${port}`) - -let reload: Function | null = null - -socket.on('vue-devtools:init', () => { - // If new page is opened reload devtools - if (reload) - return reload() - - initDevTools({ - connect(cb) { - const bridge = new Bridge({ - tracker(fn) { - socket.on('vue-devtools:message', (data) => { - fn(data) - }) - }, - trigger(data) { - socket.emit('vue-devtools:message', data) - }, - }) - - cb(bridge) - }, - reload(fn) { - reload = fn - }, + +function init() { + const localhost = `http://localhost:${port}` + const socket = io(localhost) + let reload: Function | null = null + + const app = createConnectionApp('#app', { + local: localhost, + network: `http://${ip.address()}:${port}`, + }) + + socket.on('vue-devtools:init', () => { + app.unmount() + + // If new page is opened reload devtools + if (reload) + return reload() + + initDevTools({ + connect(cb) { + const bridge = new Bridge({ + tracker(fn) { + socket.on('vue-devtools:message', (data) => { + fn(data) + }) + }, + trigger(data) { + socket.emit('vue-devtools:message', data) + }, + }) + + cb(bridge) + }, + reload(fn) { + reload = fn + }, + }) }) -}) + + socket.on('vue-devtools:disconnect', () => { + app.unmount() + reload = null + socket.close() + init() + }) +} + +init() diff --git a/packages/electron/src/server.ts b/packages/electron/src/server.ts index ebcdeb95..1b560305 100644 --- a/packages/electron/src/server.ts +++ b/packages/electron/src/server.ts @@ -11,7 +11,8 @@ export function init() { '/', eventHandler(() => { const userAppContent = fs.readFileSync(path.join(__dirname, './user-app.js'), 'utf-8') - return userAppContent + const processSyntaxPolyfill = `if(!window.process){window.process={env:{}}};` + return processSyntaxPolyfill + userAppContent }), ) @@ -30,6 +31,10 @@ export function init() { socket.broadcast.emit('vue-devtools:init') }) + socket.on('vue-devtools:disconnect', () => { + socket.broadcast.emit('vue-devtools:disconnect') + }) + socket.on('disconnect', (reason) => { if (reason.indexOf('client')) socket.broadcast.emit('vue-devtools-disconnect-devtools') diff --git a/packages/electron/src/user-app.ts b/packages/electron/src/user-app.ts index c65f383d..5fd2f538 100644 --- a/packages/electron/src/user-app.ts +++ b/packages/electron/src/user-app.ts @@ -41,4 +41,6 @@ socket.on('vue-devtools:disconnect-user-app', () => { socket.disconnect() }) -// @TODO: disconnect logic +window.addEventListener('beforeunload', () => { + socket.emit('vue-devtools:disconnect') +}) diff --git a/packages/playground/index.html b/packages/playground/index.html index 4045e2b1..09ee9c37 100644 --- a/packages/playground/index.html +++ b/packages/playground/index.html @@ -5,7 +5,10 @@ Vue DevTools Playground - +
diff --git a/packages/playground/src/main.ts b/packages/playground/src/main.ts index 86dbd8a0..2e4c79f1 100644 --- a/packages/playground/src/main.ts +++ b/packages/playground/src/main.ts @@ -12,7 +12,7 @@ import Hey from './pages/Hey.vue' import './style.css' // connect to remote devtools -// devtools.connect('http://localhost', 8098) +// devtools.connect('http://localhost', 8080) const pinia = createPinia() const pinia2 = createPinia() diff --git a/packages/shared/src/env.ts b/packages/shared/src/env.ts index 52c9051a..7a01ea1c 100644 --- a/packages/shared/src/env.ts +++ b/packages/shared/src/env.ts @@ -11,3 +11,4 @@ export const target = (typeof globalThis !== 'undefined' export const isInChromePanel = typeof target.chrome !== 'undefined' && !!target.chrome.devtools export const isInIframe = isBrowser && target.self !== target.top +export const isInElectron = typeof navigator !== 'undefined' && navigator.userAgent.toLowerCase().includes('electron') diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 73bdb2f7..d9e07b72 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -336,6 +336,9 @@ importers: h3: specifier: ^1.9.0 version: 1.9.0 + ip: + specifier: ^1.1.8 + version: 1.1.8 socket.io: specifier: ^4.7.2 version: 4.7.2 @@ -7230,6 +7233,10 @@ packages: side-channel: 1.0.4 dev: true + /ip@1.1.8: + resolution: {integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==} + dev: false + /ip@2.0.0: resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} dev: true