@@ -72,6 +72,15 @@ export type MessageListSettingsState = {
7272 type : 'standard' | 'virtualized' ;
7373} ;
7474
75+ export type PageLayoutSettingsState = {
76+ /**
77+ * Dev harness: renders the chat as a fixed-height widget between host-page content that
78+ * overflows the viewport, so the window itself scrolls. Reproduces integrations where
79+ * scrolling inside the chat must not move the surrounding page.
80+ */
81+ embedded : boolean ;
82+ } ;
83+
7584export type ComposerSettingsState = {
7685 /**
7786 * POC: allow sending a message while its attachments are still uploading.
@@ -102,6 +111,7 @@ export type AppSettingsState = {
102111 messageActions : MessageActionsSettingsState ;
103112 messageList : MessageListSettingsState ;
104113 notifications : NotificationsSettingsState ;
114+ pageLayout : PageLayoutSettingsState ;
105115 panelLayout : PanelLayoutSettingsState ;
106116 reactions : ReactionsSettingsState ;
107117 theme : ThemeSettingsState ;
@@ -165,6 +175,9 @@ const defaultAppSettingsState: AppSettingsState = {
165175 notifications : {
166176 verticalAlignment : 'bottom' ,
167177 } ,
178+ pageLayout : {
179+ embedded : false ,
180+ } ,
168181 panelLayout : {
169182 leftPanel : {
170183 collapsed : false ,
@@ -300,6 +313,45 @@ const getSendMessagesWithPendingUploadsFromUrl = (): boolean | undefined => {
300313 return raw !== '0' && raw !== 'false' ;
301314} ;
302315
316+ const embeddedLayoutUrlParam = 'embedded_layout' ;
317+
318+ /** Seeded from `?embedded_layout=1`; kept in the URL (not localStorage) so a reload keeps it. */
319+ const getEmbeddedLayoutFromUrl = ( ) : boolean | undefined => {
320+ if ( typeof window === 'undefined' ) return ;
321+
322+ const raw = new URLSearchParams ( window . location . search ) . get ( embeddedLayoutUrlParam ) ;
323+
324+ if ( raw === null ) return ;
325+
326+ return raw !== '0' && raw !== 'false' ;
327+ } ;
328+
329+ const persistEmbeddedLayoutInUrl = ( embedded : boolean ) => {
330+ if ( typeof window === 'undefined' ) return ;
331+
332+ const url = new URL ( window . location . href ) ;
333+ const hasParam = url . searchParams . get ( embeddedLayoutUrlParam ) === '1' ;
334+
335+ if ( hasParam === embedded ) return ;
336+
337+ if ( embedded ) url . searchParams . set ( embeddedLayoutUrlParam , '1' ) ;
338+ else url . searchParams . delete ( embeddedLayoutUrlParam ) ;
339+
340+ window . history . replaceState (
341+ window . history . state ,
342+ '' ,
343+ `${ url . pathname } ${ url . search } ${ url . hash } ` ,
344+ ) ;
345+ } ;
346+
347+ /** The root stylesheet switches #root from viewport height to page flow off this attribute. */
348+ const applyEmbeddedLayout = ( embedded : boolean ) => {
349+ if ( typeof document === 'undefined' ) return ;
350+
351+ if ( embedded ) document . documentElement . setAttribute ( 'data-embedded-layout' , '' ) ;
352+ else document . documentElement . removeAttribute ( 'data-embedded-layout' ) ;
353+ } ;
354+
303355const getThemeModeFromUrl = ( ) : ThemeSettingsState [ 'mode' ] | undefined => {
304356 if ( typeof window === 'undefined' ) return ;
305357
@@ -373,6 +425,9 @@ const initialAppSettingsState: AppSettingsState = {
373425 // A delay in the URL means the harness is wanted, so it arms the switch too.
374426 slowUploads : ( getSlowUploadMsFromUrl ( ) ?? 0 ) > 0 ,
375427 } ,
428+ pageLayout : {
429+ embedded : getEmbeddedLayoutFromUrl ( ) ?? defaultAppSettingsState . pageLayout . embedded ,
430+ } ,
376431 panelLayout : getStoredPanelLayoutSettings ( ) ?? defaultAppSettingsState . panelLayout ,
377432 theme : {
378433 ...defaultAppSettingsState . theme ,
@@ -403,6 +458,14 @@ appSettingsStore.subscribeWithSelector(
403458// Apply initial direction on load
404459applyDirection ( initialAppSettingsState . theme . direction ) ;
405460
461+ appSettingsStore . subscribeWithSelector (
462+ ( { pageLayout } ) => ( { embedded : pageLayout . embedded } ) ,
463+ ( { embedded } ) => {
464+ persistEmbeddedLayoutInUrl ( embedded ) ;
465+ applyEmbeddedLayout ( embedded ) ;
466+ } ,
467+ ) ;
468+
406469appSettingsStore . subscribeWithSelector (
407470 ( { panelLayout } ) => panelLayout ,
408471 ( panelLayout ) => {
0 commit comments