diff --git a/packages/ui/src/components/chat/ChatContainer.tsx b/packages/ui/src/components/chat/ChatContainer.tsx index 35a28c07..6ab7e364 100644 --- a/packages/ui/src/components/chat/ChatContainer.tsx +++ b/packages/ui/src/components/chat/ChatContainer.tsx @@ -586,7 +586,40 @@ export const ChatContainer: React.FC = ({ autoOpenDraft = tr {t('chat.container.returnToParent.label')} ) : null; - const promptReadOnly = readOnly || (Boolean(parentSession) && !allowPromptingSubagentSessions); + const promptReadOnly = parentSession ? !allowPromptingSubagentSessions : readOnly; + + React.useEffect(() => { + if (typeof window === 'undefined' || window.parent === window) { + return; + } + + const applySetting = (value: boolean) => { + useUIStore.getState().setAllowPromptingSubagentSessions(value); + }; + const scopedWindow = window as typeof window & { + __openchamberApplyChatSettingsSync?: (payload: { allowPromptingSubagentSessions: boolean }) => void; + }; + const applySync = (payload: { allowPromptingSubagentSessions: boolean }) => { + applySetting(payload.allowPromptingSubagentSessions); + }; + const handleMessage = (event: MessageEvent) => { + if (event.source !== window.parent || event.origin !== window.location.origin) return; + const data = event.data as { type?: unknown; payload?: { allowPromptingSubagentSessions?: unknown } }; + if (data?.type !== 'openchamber:chat-settings-sync' + || typeof data.payload?.allowPromptingSubagentSessions !== 'boolean') return; + applySetting(data.payload.allowPromptingSubagentSessions); + }; + + scopedWindow.__openchamberApplyChatSettingsSync = applySync; + window.addEventListener('message', handleMessage); + window.parent.postMessage({ type: 'openchamber:chat-settings-request' }, window.location.origin); + return () => { + window.removeEventListener('message', handleMessage); + if (scopedWindow.__openchamberApplyChatSettingsSync === applySync) { + delete scopedWindow.__openchamberApplyChatSettingsSync; + } + }; + }, []); React.useEffect(() => { if (autoOpenDraft && !currentSessionId && !draftOpen) { diff --git a/packages/ui/src/components/chat/ChatMessage.tsx b/packages/ui/src/components/chat/ChatMessage.tsx index 58485d5e..320031eb 100644 --- a/packages/ui/src/components/chat/ChatMessage.tsx +++ b/packages/ui/src/components/chat/ChatMessage.tsx @@ -1017,6 +1017,7 @@ const ChatMessage: React.FC = ({ isUser={isUser} isMessageCompleted={isMessageCompleted} messageFinish={messageFinish} + messageCreatedAt={messageCreatedAt ?? undefined} isMobile={isMobile} alwaysShowActions={alwaysShowMessageActions} hasTouchInput={hasTouchInput} @@ -1050,6 +1051,7 @@ const ChatMessage: React.FC = ({ isUser={isUser} isMessageCompleted={isMessageCompleted} messageFinish={messageFinish} + messageCreatedAt={messageCreatedAt ?? undefined} isMobile={isMobile} alwaysShowActions={alwaysShowMessageActions} hasTouchInput={hasTouchInput} diff --git a/packages/ui/src/components/layout/ContextPanel.tsx b/packages/ui/src/components/layout/ContextPanel.tsx index de5e3026..23086158 100644 --- a/packages/ui/src/components/layout/ContextPanel.tsx +++ b/packages/ui/src/components/layout/ContextPanel.tsx @@ -2070,6 +2070,7 @@ export const ContextPanel: React.FC = () => { const reorderContextPanelTabs = useUIStore((state) => state.reorderContextPanelTabs); const setSelectedFilePath = useFilesViewTabsStore((state) => state.setSelectedPath); const openContextPreview = useUIStore((state) => state.openContextPreview); + const allowPromptingSubagentSessions = useUIStore((state) => state.allowPromptingSubagentSessions); const { themeMode, setThemeMode, lightThemeId, darkThemeId, currentTheme } = useThemeSystem(); const tabs = React.useMemo(() => panelState?.tabs ?? [], [panelState?.tabs]); @@ -2359,6 +2360,30 @@ export const ContextPanel: React.FC = () => { } }, [currentTheme, darkThemeId, lightThemeId, themeMode]); + const postChatSettingsSyncToEmbeddedChat = React.useCallback(() => { + if (typeof window === 'undefined') return; + + const payload = { allowPromptingSubagentSessions }; + for (const frame of chatFrameRefs.current.values()) { + const frameWindow = frame.contentWindow; + if (!frameWindow) continue; + + const directSync = (frameWindow as unknown as { + __openchamberApplyChatSettingsSync?: (settings: typeof payload) => void; + }).__openchamberApplyChatSettingsSync; + if (typeof directSync === 'function') { + try { + directSync(payload); + continue; + } catch { + // fallback to postMessage below + } + } + + frameWindow.postMessage({ type: 'openchamber:chat-settings-sync', payload }, window.location.origin); + } + }, [allowPromptingSubagentSessions]); + const postEmbeddedVisibilityToChats = React.useCallback(() => { if (typeof window === 'undefined') { return; @@ -2411,6 +2436,10 @@ export const ContextPanel: React.FC = () => { } const data = event.data as { type?: unknown }; + if (data?.type === 'openchamber:chat-settings-request') { + postChatSettingsSyncToEmbeddedChat(); + return; + } if (data?.type !== 'openchamber:cycle-theme-request') { return; } @@ -2423,7 +2452,7 @@ export const ContextPanel: React.FC = () => { window.addEventListener('message', handleMessage); return () => window.removeEventListener('message', handleMessage); - }, [setThemeMode, themeMode]); + }, [postChatSettingsSyncToEmbeddedChat, setThemeMode, themeMode]); React.useLayoutEffect(() => { const hasAnyChatTab = tabs.some((tab) => tab.mode === 'chat'); @@ -2432,8 +2461,9 @@ export const ContextPanel: React.FC = () => { } postThemeSyncToEmbeddedChat(); + postChatSettingsSyncToEmbeddedChat(); postEmbeddedVisibilityToChats(); - }, [darkThemeId, lightThemeId, postEmbeddedVisibilityToChats, postThemeSyncToEmbeddedChat, tabs, themeMode]); + }, [darkThemeId, lightThemeId, postChatSettingsSyncToEmbeddedChat, postEmbeddedVisibilityToChats, postThemeSyncToEmbeddedChat, tabs, themeMode]); const tabItems = React.useMemo(() => tabs.map((tab) => { const rawLabel = getTabLabel(tab, sessionTitleById, t); @@ -2628,6 +2658,7 @@ export const ContextPanel: React.FC = () => { )} onLoad={() => { postThemeSyncToEmbeddedChat(); + postChatSettingsSyncToEmbeddedChat(); postEmbeddedVisibilityToChats(); }} />