From 491fc8c267945fe7b912b369278ff4774d72c275 Mon Sep 17 00:00:00 2001 From: Macbook Date: Mon, 15 Jun 2026 16:44:17 +0900 Subject: [PATCH] =?UTF-8?q?fix(desktop):=20Windows=20=EC=9C=84=EC=A0=AF=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=ED=8C=9D=EC=97=85=20=EC=A6=89=EC=8B=9C=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 창 크기 조절 완료를 기다리지 않고 picker를 먼저 열고, body portal· resize 타임아웃으로 WebView2에서 팝업이 뜨지 않던 문제를 해결합니다. Co-authored-by: Cursor --- desktop/src/widget-main.tsx | 58 +++++++------- .../floatingWidgets/FloatingWidgetWindow.tsx | 75 ++++++++++++------- frontend/src/styles/widgetDashboard.css | 10 +++ 3 files changed, 89 insertions(+), 54 deletions(-) diff --git a/desktop/src/widget-main.tsx b/desktop/src/widget-main.tsx index 147df9e..4663047 100644 --- a/desktop/src/widget-main.tsx +++ b/desktop/src/widget-main.tsx @@ -139,33 +139,39 @@ const WidgetApp: React.FC = () => { const handlePickerOpenChange = useCallback(async (open: boolean): Promise => { const win = getCurrentWindow(); - if (open) { - const size = await win.innerSize(); - sizeBeforePickerRef.current = { width: size.width, height: size.height }; - const maxW = Math.max(480, window.screen.availWidth - 48); - const maxH = Math.max(520, window.screen.availHeight - 48); - const w = Math.min(NATIVE_WIDGET_PICKER_WINDOW.width, maxW); - const h = Math.min(NATIVE_WIDGET_PICKER_WINDOW.height, maxH); - await win.setSize(new LogicalSize(w, h)); - await win.center(); - await new Promise(resolve => { - let settled = false; - const finish = () => { - if (settled) return; - settled = true; - unlisten?.(); - resolve(); - }; - let unlisten: (() => void) | undefined; - void win.onResized(finish).then(fn => { unlisten = fn; }); - window.setTimeout(finish, 150); - }); - return; + try { + if (open) { + const size = await win.innerSize(); + sizeBeforePickerRef.current = { width: size.width, height: size.height }; + const maxW = Math.max(480, window.screen.availWidth - 48); + const maxH = Math.max(520, window.screen.availHeight - 48); + const w = Math.min(NATIVE_WIDGET_PICKER_WINDOW.width, maxW); + const h = Math.min(NATIVE_WIDGET_PICKER_WINDOW.height, maxH); + await win.setSize(new LogicalSize(w, h)); + await win.center(); + await Promise.race([ + new Promise(resolve => { + let settled = false; + const finish = () => { + if (settled) return; + settled = true; + unlisten?.(); + resolve(); + }; + let unlisten: (() => void) | undefined; + void win.onResized(finish).then(fn => { unlisten = fn; }); + }), + new Promise(resolve => { window.setTimeout(resolve, 400); }), + ]); + return; + } + const prev = sizeBeforePickerRef.current; + if (!prev) return; + sizeBeforePickerRef.current = null; + await win.setSize(new LogicalSize(prev.width, prev.height)); + } catch (err) { + console.warn('[widget-main] picker window resize failed', err); } - const prev = sizeBeforePickerRef.current; - if (!prev) return; - sizeBeforePickerRef.current = null; - await win.setSize(new LogicalSize(prev.width, prev.height)); }, []); if (!instance) { diff --git a/frontend/src/components/floatingWidgets/FloatingWidgetWindow.tsx b/frontend/src/components/floatingWidgets/FloatingWidgetWindow.tsx index 8354c07..a1114ef 100644 --- a/frontend/src/components/floatingWidgets/FloatingWidgetWindow.tsx +++ b/frontend/src/components/floatingWidgets/FloatingWidgetWindow.tsx @@ -40,29 +40,36 @@ const FloatingWidgetWindow: React.FC = ({ onPickerOpenChange, }) => { const [pickSlotId, setPickSlotId] = useState(null); - const [pickerOpening, setPickerOpening] = useState(false); - const openPicker = useCallback(async (slotId: string) => { - if (nativeShell && onPickerOpenChange) { - setPickerOpening(true); - try { - await onPickerOpenChange(true); - await new Promise(resolve => { - requestAnimationFrame(() => requestAnimationFrame(() => resolve())); - }); - } finally { - setPickerOpening(false); - } - } + const openPicker = useCallback((slotId: string) => { setPickSlotId(slotId); + if (nativeShell) { + document.documentElement.classList.add('fw-native-picker-open'); + } + if (nativeShell && onPickerOpenChange) { + void onPickerOpenChange(true).catch(err => { + console.warn('[FloatingWidgetWindow] picker window resize failed', err); + }); + } }, [nativeShell, onPickerOpenChange]); const closePicker = useCallback(async () => { setPickSlotId(null); + if (nativeShell) { + document.documentElement.classList.remove('fw-native-picker-open'); + } if (nativeShell && onPickerOpenChange) { - await onPickerOpenChange(false); + try { + await onPickerOpenChange(false); + } catch (err) { + console.warn('[FloatingWidgetWindow] picker window restore failed', err); + } } }, [nativeShell, onPickerOpenChange]); + useEffect(() => () => { + document.documentElement.classList.remove('fw-native-picker-open'); + }, []); + const bodyWrapRef = useRef(null); const bodyRef = useRef(null); const [bodySize, setBodySize] = useState({ width: 0, height: 0 }); @@ -312,23 +319,35 @@ const FloatingWidgetWindow: React.FC = ({ )} - - {(pickerOpening || pickSlotId != null) && ( - { void closePicker(); }} - onSelect={applyWidgetType} - /> - )} - {pickerOpening && pickSlotId == null && nativeShell && ( -
위젯 목록 준비 중…
- )} ); - return nativeShell ? content : createPortal(content, document.body); + const pickerModal = pickSlotId != null ? ( + { void closePicker(); }} + onSelect={applyWidgetType} + /> + ) : null; + + if (nativeShell) { + return ( + <> + {content} + {pickerModal && createPortal(pickerModal, document.body)} + + ); + } + + return createPortal( + <> + {content} + {pickerModal} + , + document.body, + ); }; export default FloatingWidgetWindow; diff --git a/frontend/src/styles/widgetDashboard.css b/frontend/src/styles/widgetDashboard.css index 654d388..141d1b2 100644 --- a/frontend/src/styles/widgetDashboard.css +++ b/frontend/src/styles/widgetDashboard.css @@ -1329,6 +1329,16 @@ html.fw-native-widget .wd-picker-native-loading { font-size: 13px; } +html.fw-native-widget.fw-native-picker-open .fw-widget-native-root .fw-window { + visibility: hidden; + pointer-events: none; +} + +html.fw-native-widget.fw-native-picker-open .wd-picker-native-overlay { + padding: 0; + background: var(--bg, #1a1b26); +} + .bps-page--wd-no-left .bps-side-wrap--left, .bps-page--wd-no-left .bps-side-wrap--left + .bps-splitter { display: none !important;