diff --git a/modules/services/embeddedAppSessionManager.js b/modules/services/embeddedAppSessionManager.js index eec2b2376..bf38fd007 100644 --- a/modules/services/embeddedAppSessionManager.js +++ b/modules/services/embeddedAppSessionManager.js @@ -183,7 +183,19 @@ function createEmbeddedAppSessionManager({ mainWindow, launchStandalone, powerMo // native child view transparent lets the parent navigation material // continue behind the embedded sidebar without color approximation. view.setBackgroundColor?.('#00000000'); - const session = { action: appAction, view, bounds: { x: 0, y: 44, width: 1, height: 1 }, insets: null }; + const initialParentBounds = mainWindow.getContentBounds(); + const defaultBounds = { + x: 0, + y: 44, + width: Math.max(1, initialParentBounds.width), + height: Math.max(1, initialParentBounds.height - 44), + }; + const session = { + action: appAction, + view, + bounds: defaultBounds, + insets: getBoundsInsets(defaultBounds, initialParentBounds), + }; sessions.set(appAction, session); mainWindow.contentView.addChildView(view); view.setVisible(false); @@ -239,9 +251,31 @@ function createEmbeddedAppSessionManager({ mainWindow, launchStandalone, powerMo if (!session || session.view.webContents.isDestroyed()) { return { success: false, error: '内嵌应用会话不存在。' }; } - session.view.setBounds(normalizeBounds(session.bounds, mainWindow.getContentBounds())); + const parentBounds = mainWindow.getContentBounds(); + if (session.insets) { + session.bounds = boundsFromInsets(session.insets, parentBounds); + } else if ((session.bounds.width || 0) <= 1 || (session.bounds.height || 0) <= 1) { + session.bounds = { + x: 0, + y: 44, + width: parentBounds.width, + height: Math.max(1, parentBounds.height - 44), + }; + session.insets = getBoundsInsets(session.bounds, parentBounds); + } + if (mainWindow && !mainWindow.isDestroyed()) { + try { + mainWindow.contentView.removeChildView(session.view); + mainWindow.contentView.addChildView(session.view); + } catch { /* ignored */ } + } + session.view.setBounds(normalizeBounds(session.bounds, parentBounds)); session.view.setVisible(true); activeAction = appAction; + try { + session.view.webContents?.focus?.(); + session.view.webContents?.invalidate?.(); + } catch { /* ignored */ } return { success: true }; } @@ -252,6 +286,9 @@ function createEmbeddedAppSessionManager({ mainWindow, launchStandalone, powerMo if (!session || session.view.webContents.isDestroyed()) { return { success: false, error: '内嵌应用会话不存在。' }; } + if ((Number(bounds?.width) || 0) <= 1 || (Number(bounds?.height) || 0) <= 1) { + return { success: true, ignored: true }; + } const parentBounds = mainWindow.getContentBounds(); const zoomFactor = mainWindow.webContents?.getZoomFactor?.() || 1; session.bounds = normalizeBounds(scaleBounds(bounds, zoomFactor), parentBounds); diff --git a/modules/ui-system/next-shell/next-shell-controller.js b/modules/ui-system/next-shell/next-shell-controller.js index ceb305ae0..05b90a637 100644 --- a/modules/ui-system/next-shell/next-shell-controller.js +++ b/modules/ui-system/next-shell/next-shell-controller.js @@ -158,20 +158,34 @@ function syncEmbeddedBounds(view) { if (view?.kind !== 'embedded' || !view.container.isConnected) return; - embeddedAppController?.setBounds(view.action, getEmbeddedBounds(view.container))?.catch(error => { + const bounds = getEmbeddedBounds(view.container); + if (bounds.width <= 1 || bounds.height <= 1) return; + embeddedAppController?.setBounds(view.action, bounds)?.catch(error => { console.warn(`[NextUI] Failed to resize embedded app ${view.action}:`, error); }); } function syncEmbeddedActivation() { - const activeView = appTabHost.views.get(appTabHost.activeViewId); + const activeViewId = appTabHost.activeViewId; + const activeView = appTabHost.views.get(activeViewId); const action = mounted && !restoringTabs && !overlayCoordinator?.active && activeView?.kind === 'embedded' ? activeView.action : null; const activation = embeddedAppController?.activate(action); if (!activation) return; trackTabPromise(`activate:${action || 'home'}`, activation).then(result => { - if (result?.success && activeView?.kind === 'embedded') syncEmbeddedBounds(activeView); + if (!mounted || appTabHost.activeViewId !== activeViewId) return; + if (result?.success && activeView?.kind === 'embedded') { + syncEmbeddedBounds(activeView); + const scheduleFrame = typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function' + ? window.requestAnimationFrame + : (typeof requestAnimationFrame === 'function' ? requestAnimationFrame : fn => setTimeout(fn, 16)); + scheduleFrame(() => { + if (mounted && appTabHost.activeViewId === activeViewId) { + syncEmbeddedBounds(activeView); + } + }); + } }).catch(error => console.warn('[NextUI] Failed to activate embedded app:', error)); } @@ -378,6 +392,8 @@ } if (!result?.success) throw new Error(result?.error || '应用无法内嵌打开。'); container.dataset.state = 'ready'; + const statusNode = container.querySelector('.next-ui-embedded-app-status'); + if (statusNode) statusNode.remove(); if (appTabHost.activeViewId === viewId && !restoringTabs) { syncEmbeddedBounds(view); await embeddedAppController.activate(app.action); diff --git a/styles/ui-next.css b/styles/ui-next.css index cb42192fc..cd4944711 100644 --- a/styles/ui-next.css +++ b/styles/ui-next.css @@ -424,6 +424,10 @@ html .next-ui-embedded-app-status { font-size: 14px; } +html .next-ui-embedded-app-view[data-state="ready"] .next-ui-embedded-app-status { + display: none; +} + html .next-ui-embedded-app-status .vcp-ui-icon { animation: next-ui-embedded-spin var(--vcp-motion-duration-spinner) var(--vcp-motion-ease-linear) infinite; }