From 4fc9141ec91fd44f7cd68dcaa0ac542da2e15947 Mon Sep 17 00:00:00 2001 From: Raquel Smith Date: Tue, 10 Sep 2024 14:00:52 -0700 Subject: [PATCH 1/6] make a url field function --- .../authorizedUrlListLogic.ts | 2 +- .../iframedToolbarBrowserLogic.ts | 16 ++++ .../components/IframedToolbarBrowser/utils.ts | 2 + .../LemonInputSelect/LemonInputSelect.tsx | 11 ++- .../DashboardTemplateConfigureStep.tsx | 74 ++++++++++++++++++- frontend/src/toolbar/bar/toolbarLogic.ts | 31 ++++++++ 6 files changed, 130 insertions(+), 6 deletions(-) diff --git a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts index 20d0811578258..8cc977e20f2ae 100644 --- a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts +++ b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts @@ -241,7 +241,7 @@ export const authorizedUrlListLogic = kea([ [] as string[], { setAuthorizedUrls: (_, { authorizedUrls }) => authorizedUrls, - addUrl: (state, { url }) => state.concat([url]), + addUrl: (state, { url }) => (!state.includes(url) ? state.concat([url]) : state), updateUrl: (state, { index, url }) => Object.assign([...state], { [index]: url }), removeUrl: (state, { index }) => { const newUrls = [...state] diff --git a/frontend/src/lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic.ts b/frontend/src/lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic.ts index a2d74018d6aa8..71d1f657b1f5f 100644 --- a/frontend/src/lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic.ts +++ b/frontend/src/lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic.ts @@ -57,6 +57,7 @@ export const iframedToolbarBrowserLogic = kea([ disableElementSelector: true, setNewActionName: (name: string | null) => ({ name }), toolbarMessageReceived: (type: PostHogAppToolbarEvent, payload: Record) => ({ type, payload }), + setCurrentPath: (path: string, navigateIframe: boolean) => ({ path, navigateIframe }), }), reducers(({ props }) => ({ @@ -99,6 +100,12 @@ export const iframedToolbarBrowserLogic = kea([ setBrowserUrl: (_, { url }) => url, }, ], + currentPath: [ + '' as string, + { + setCurrentPath: (_, { path }) => path, + }, + ], loading: [ false as boolean, { @@ -255,6 +262,9 @@ export const iframedToolbarBrowserLogic = kea([ actions.setNewActionName(null) actions.disableElementSelector() return + case PostHogAppToolbarEvent.PH_TOOLBAR_NAVIGATED: + // remove leading / from path + return actions.setCurrentPath(payload.path.replace(/^\/+/, ''), false) default: console.warn(`[PostHog Heatmaps] Received unknown child window message: ${type}`) } @@ -271,6 +281,12 @@ export const iframedToolbarBrowserLogic = kea([ } }, + setCurrentPath: ({ path, navigateIframe }) => { + if (navigateIframe) { + actions.sendToolbarMessage(PostHogAppToolbarEvent.PH_NAVIGATE, { url: values.browserUrl + '/' + path }) + } + }, + startTrackingLoading: () => { actions.setIframeBanner(null) diff --git a/frontend/src/lib/components/IframedToolbarBrowser/utils.ts b/frontend/src/lib/components/IframedToolbarBrowser/utils.ts index 87d9cae13cf9f..5dd60eafa1093 100644 --- a/frontend/src/lib/components/IframedToolbarBrowser/utils.ts +++ b/frontend/src/lib/components/IframedToolbarBrowser/utils.ts @@ -17,6 +17,8 @@ export enum PostHogAppToolbarEvent { PH_ELEMENT_SELECTOR = 'ph-element-selector', PH_NEW_ACTION_NAME = 'ph-new-action-name', PH_NEW_ACTION_CREATED = 'ph-new-action-created', + PH_NAVIGATE = 'ph-navigate', + PH_TOOLBAR_NAVIGATED = 'ph-toolbar-navigated', } export const DEFAULT_HEATMAP_FILTERS: HeatmapFilters = { diff --git a/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx b/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx index 6187ea318f6b7..4e457ed8f01dc 100644 --- a/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx +++ b/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx @@ -46,6 +46,9 @@ export type LemonInputSelectProps = Pick< onInputChange?: (newValue: string) => void 'data-attr'?: string popoverClassName?: string + size?: 'xsmall' | 'small' | 'medium' | 'large' + borderless?: boolean + transparentBackground?: boolean } export function LemonInputSelect({ @@ -65,6 +68,9 @@ export function LemonInputSelect({ autoFocus = false, popoverClassName, 'data-attr': dataAttr, + size = 'medium', + borderless, + transparentBackground, }: LemonInputSelectProps): JSX.Element { const [showPopover, setShowPopover] = useState(false) const [inputValue, _setInputValue] = useState('') @@ -444,6 +450,7 @@ export function LemonInputSelect({ onKeyDown={_onKeyDown} disabled={disabled} autoFocus={autoFocus} + transparentBackground={transparentBackground} className={clsx( 'flex-wrap h-auto min-w-24', // Putting button-like text styling on the single-select unfocused placeholder @@ -451,9 +458,11 @@ export function LemonInputSelect({ mode === 'single' && values.length > 0 && !showPopover && - 'cursor-pointer placeholder:*:text-default' + 'cursor-pointer placeholder:*:text-default', + borderless && 'border-none' )} data-attr={dataAttr} + size={size} /> ) diff --git a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx index 476cef6de6275..cc432139aad05 100644 --- a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx +++ b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx @@ -1,10 +1,10 @@ import { IconArrowRight } from '@posthog/icons' -import { LemonButton, LemonCard, LemonSkeleton, Link } from '@posthog/lemon-ui' +import { LemonButton, LemonCard, LemonInput, LemonInputSelect, LemonSkeleton, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { authorizedUrlListLogic, AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' import { IframedToolbarBrowser } from 'lib/components/IframedToolbarBrowser/IframedToolbarBrowser' import { iframedToolbarBrowserLogic } from 'lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic' -import { useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { dashboardTemplateVariablesLogic } from 'scenes/dashboard/dashboardTemplateVariablesLogic' import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' @@ -14,6 +14,69 @@ import { sdksLogic } from '../sdks/sdksLogic' import { DashboardTemplateVariables } from './DashboardTemplateVariables' import { onboardingTemplateConfigLogic } from './onboardingTemplateConfigLogic' +const UrlInput = ({ iframeRef }: { iframeRef: React.RefObject }): JSX.Element => { + const { setBrowserUrl, setCurrentPath } = useActions( + iframedToolbarBrowserLogic({ iframeRef, clearBrowserUrlOnUnmount: true }) + ) + const { browserUrl, currentPath } = useValues( + iframedToolbarBrowserLogic({ iframeRef, clearBrowserUrlOnUnmount: true }) + ) + const { snippetHosts } = useValues(sdksLogic) + const { addUrl } = useActions(authorizedUrlListLogic({ actionId: null, type: AuthorizedUrlListType.TOOLBAR_URLS })) + const [inputValue, setInputValue] = useState(currentPath) + + useEffect(() => { + setInputValue(currentPath) + }, [currentPath]) + + return ( +
+ setInputValue(v)} + onPressEnter={() => { + setCurrentPath(inputValue || '', true) + }} + prefix={ + +
+ ({ key: host, label: host }))} + allowCustomValues={false} + onChange={(v) => { + addUrl(v[0]) + setBrowserUrl(v[0]) + setCurrentPath('', false) + }} + size="xsmall" + transparentBackground + borderless + /> +
+ / +
+ } + /> + } + onClick={() => { + setCurrentPath(inputValue || '', true) + }} + /> + + Select pageview + +
+ ) +} + export const OnboardingDashboardTemplateConfigureStep = ({ stepKey = OnboardingStepKey.DASHBOARD_TEMPLATE, }: { @@ -50,8 +113,11 @@ export const OnboardingDashboardTemplateConfigureStep = ({
{browserUrl ? ( -
- +
+ +
+ +
) : ( <> diff --git a/frontend/src/toolbar/bar/toolbarLogic.ts b/frontend/src/toolbar/bar/toolbarLogic.ts index dd5a8dd6a2f2b..8546f2448cd39 100644 --- a/frontend/src/toolbar/bar/toolbarLogic.ts +++ b/frontend/src/toolbar/bar/toolbarLogic.ts @@ -72,6 +72,8 @@ export const toolbarLogic = kea([ setIsBlurred: (isBlurred: boolean) => ({ isBlurred }), setIsEmbeddedInApp: (isEmbedded: boolean) => ({ isEmbedded }), setFixedPosition: (position: ToolbarPositionType) => ({ position }), + setCurrentPathname: (pathname: string) => ({ pathname }), + maybeSendNavigationMessage: true, })), windowValues(() => ({ windowHeight: (window: Window) => window.innerHeight, @@ -164,6 +166,12 @@ export const toolbarLogic = kea([ setIsEmbeddedInApp: (_, { isEmbedded }) => isEmbedded, }, ], + currentPathname: [ + '', + { + setCurrentPathname: (_, { pathname }) => pathname, + }, + ], })), selectors({ position: [ @@ -403,6 +411,16 @@ export const toolbarLogic = kea([ // if embedded, we need to tell the parent window that a new action was created window.parent.postMessage({ type: PostHogAppToolbarEvent.PH_NEW_ACTION_CREATED, payload: action }, '*') }, + maybeSendNavigationMessage: () => { + const currentPath = window.location.pathname + if (currentPath !== values.currentPathname) { + actions.setCurrentPathname(currentPath) + window.parent.postMessage( + { type: PostHogAppToolbarEvent.PH_TOOLBAR_NAVIGATED, payload: { path: currentPath } }, + '*' + ) + } + }, })), afterMount(({ actions, values, cache }) => { cache.clickListener = (e: MouseEvent): void => { @@ -412,6 +430,16 @@ export const toolbarLogic = kea([ } } window.addEventListener('mousedown', cache.clickListener) + window.addEventListener('popstate', () => { + actions.maybeSendNavigationMessage() + }) + + // Use a setInterval to periodically check for URL changes + // We do this because we don't want to write over the history.pushState function in case other scripts rely on it + // And mutation observers don't seem to work :shrug: + setInterval(() => { + actions.maybeSendNavigationMessage() + }, 500) // the toolbar can be run within the posthog parent app // if it is then it listens to parent messages @@ -461,6 +489,9 @@ export const toolbarLogic = kea([ case PostHogAppToolbarEvent.PH_NEW_ACTION_NAME: actions.setAutomaticActionCreationEnabled(true, e.data.payload.name) return + case PostHogAppToolbarEvent.PH_NAVIGATE: + window.location.href = e.data.payload.url + return default: console.warn(`[PostHog Toolbar] Received unknown parent window message: ${type}`) } From 206551a65225f4d560f592fdf79e20261843b462 Mon Sep 17 00:00:00 2001 From: Raquel Smith Date: Tue, 10 Sep 2024 16:25:56 -0700 Subject: [PATCH 2/6] add pageview event variable --- .../iframedToolbarBrowserLogic.ts | 6 ++++ .../scenes/dashboard/Dashboards.stories.tsx | 14 ++++---- .../dashboardTemplateVariablesLogic.ts | 27 ++++++++++++++-- .../src/scenes/dashboard/newDashboardLogic.ts | 2 +- .../DashboardTemplateConfigureStep.tsx | 15 +++++++-- .../DashboardTemplateVariables.tsx | 32 ++++++++++++------- frontend/src/toolbar/bar/toolbarLogic.ts | 4 ++- frontend/src/types.ts | 14 ++++---- 8 files changed, 84 insertions(+), 30 deletions(-) diff --git a/frontend/src/lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic.ts b/frontend/src/lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic.ts index 71d1f657b1f5f..9aafee105cd76 100644 --- a/frontend/src/lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic.ts +++ b/frontend/src/lib/components/IframedToolbarBrowser/iframedToolbarBrowserLogic.ts @@ -140,6 +140,12 @@ export const iframedToolbarBrowserLogic = kea([ return iframeWidth ? calculateViewportRange(heatmapFilters, iframeWidth) : { min: 0, max: 1800 } }, ], + currentFullUrl: [ + (s) => [s.browserUrl, s.currentPath], + (browserUrl, currentPath) => { + return browserUrl + '/' + currentPath + }, + ], }), listeners(({ actions, cache, props, values }) => ({ diff --git a/frontend/src/scenes/dashboard/Dashboards.stories.tsx b/frontend/src/scenes/dashboard/Dashboards.stories.tsx index 1e25ad376ba95..34e6be5f20878 100644 --- a/frontend/src/scenes/dashboard/Dashboards.stories.tsx +++ b/frontend/src/scenes/dashboard/Dashboards.stories.tsx @@ -9,7 +9,7 @@ import { urls } from 'scenes/urls' import { mswDecorator } from '~/mocks/browser' import { useAvailableFeatures } from '~/mocks/features' -import { DashboardMode } from '~/types' +import { BaseMathType, DashboardMode, EntityTypes } from '~/types' import { dashboardTemplatesLogic } from './dashboards/templates/dashboardTemplatesLogic' @@ -76,8 +76,8 @@ export const NewSelectVariables = (): JSX.Element => { type: 'event', default: { id: '$pageview', - math: 'dau', - type: 'events', + math: BaseMathType.UniqueUsers, + type: EntityTypes.EVENTS, }, required: true, description: 'Add the current_url filter that matches your sign up page', @@ -88,8 +88,8 @@ export const NewSelectVariables = (): JSX.Element => { type: 'event', default: { id: '$pageview', - math: 'dau', - type: 'events', + math: BaseMathType.UniqueUsers, + type: EntityTypes.EVENTS, }, required: true, description: @@ -101,8 +101,8 @@ export const NewSelectVariables = (): JSX.Element => { type: 'event', default: { id: '$pageview', - math: 'dau', - type: 'events', + math: BaseMathType.UniqueUsers, + type: EntityTypes.EVENTS, }, required: false, description: 'Select the event which best represents when a user is activated', diff --git a/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts b/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts index e12513e025d38..340b6b2ed278e 100644 --- a/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts +++ b/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts @@ -22,8 +22,8 @@ export interface DashboardTemplateVariablesLogicProps { const FALLBACK_EVENT = { id: '$pageview', - math: 'dau', - type: 'events', + math: BaseMathType.UniqueUsers, + type: EntityTypes.EVENTS, } export const dashboardTemplateVariablesLogic = kea([ @@ -39,6 +39,7 @@ export const dashboardTemplateVariablesLogic = kea ({ variableName, action }), + setVariableForPageview: (variableName: string, url: string) => ({ variableName, url }), setActiveVariableIndex: (index: number) => ({ index }), incrementActiveVariableIndex: true, possiblyIncrementActiveVariableIndex: true, @@ -162,6 +163,28 @@ export const dashboardTemplateVariablesLogic = kea { + const step: TemplateVariableStep = { + id: '$pageview', + math: BaseMathType.UniqueUsers, + type: EntityTypes.EVENTS, + order: 0, + name: '$pageview', + properties: [ + { + key: '$current_url', + value: url, + operator: 'icontains', + type: 'event', + }, + ], + } + const filterGroup: FilterType = { + events: [step], + } + actions.setVariable(variableName, filterGroup) + actions.setIsCurrentlySelectingElement(false) + }, toolbarMessageReceived: ({ type, payload }) => { if (type === PostHogAppToolbarEvent.PH_NEW_ACTION_CREATED) { actions.setVariableFromAction(payload.action.name, payload.action as ActionType) diff --git a/frontend/src/scenes/dashboard/newDashboardLogic.ts b/frontend/src/scenes/dashboard/newDashboardLogic.ts index 7e03ff6170fe1..fb83b3ca15b70 100644 --- a/frontend/src/scenes/dashboard/newDashboardLogic.ts +++ b/frontend/src/scenes/dashboard/newDashboardLogic.ts @@ -41,7 +41,7 @@ export function applyTemplate(obj: DashboardTile | JsonType, variables: Dashboar const variableId = obj.substring(1, obj.length - 1) const variable = variables.find((variable) => variable.id === variableId) if (variable && variable.default) { - return variable.default + return variable.default as JsonType } return obj } diff --git a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx index cc432139aad05..ebda07635fcbb 100644 --- a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx +++ b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateConfigureStep.tsx @@ -18,12 +18,18 @@ const UrlInput = ({ iframeRef }: { iframeRef: React.RefObject const { setBrowserUrl, setCurrentPath } = useActions( iframedToolbarBrowserLogic({ iframeRef, clearBrowserUrlOnUnmount: true }) ) - const { browserUrl, currentPath } = useValues( + const { browserUrl, currentPath, currentFullUrl } = useValues( iframedToolbarBrowserLogic({ iframeRef, clearBrowserUrlOnUnmount: true }) ) const { snippetHosts } = useValues(sdksLogic) const { addUrl } = useActions(authorizedUrlListLogic({ actionId: null, type: AuthorizedUrlListType.TOOLBAR_URLS })) const [inputValue, setInputValue] = useState(currentPath) + const { activeDashboardTemplate } = useValues(newDashboardLogic) + const theDashboardTemplateVariablesLogic = dashboardTemplateVariablesLogic({ + variables: activeDashboardTemplate?.variables || [], + }) + const { setVariableForPageview } = useActions(theDashboardTemplateVariablesLogic) + const { activeVariable } = useValues(theDashboardTemplateVariablesLogic) useEffect(() => { setInputValue(currentPath) @@ -70,7 +76,12 @@ const UrlInput = ({ iframeRef }: { iframeRef: React.RefObject setCurrentPath(inputValue || '', true) }} /> - + setVariableForPageview(activeVariable.name, currentFullUrl)} + > Select pageview
diff --git a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateVariables.tsx b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateVariables.tsx index 9f87ff0aeb0f4..a6fe3e4cf6e9b 100644 --- a/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateVariables.tsx +++ b/frontend/src/scenes/onboarding/productAnalyticsSteps/DashboardTemplateVariables.tsx @@ -6,7 +6,7 @@ import { useEffect, useState } from 'react' import { dashboardTemplateVariablesLogic } from 'scenes/dashboard/dashboardTemplateVariablesLogic' import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' -import { DashboardTemplateVariableType } from '~/types' +import { DashboardTemplateVariableType, EntityTypes } from '~/types' function VariableSelector({ variableName, @@ -64,16 +64,26 @@ function VariableSelector({ Selected

-

- CSS selector:{' '} - {variable.default.selector || 'not set'} -

-

- Element href: {variable.default.href || 'not set'} -

-

- Page URL: {variable.default.url || 'any url'} -

+ {variable.default.type === EntityTypes.ACTIONS ? ( + <> +

+ CSS selector:{' '} + {variable.default.selector || 'not set'} +

+

+ Element href:{' '} + {variable.default.href || 'not set'} +

+

+ Page URL: {variable.default.url || 'any url'} +

+ + ) : variable.default.type === EntityTypes.EVENTS ? ( +

+ Pageview URL:{' '} + {variable.default.properties?.[0].value || 'any url'} +

+ ) : null}
diff --git a/frontend/src/toolbar/bar/toolbarLogic.ts b/frontend/src/toolbar/bar/toolbarLogic.ts index 8546f2448cd39..8e279f2a971d6 100644 --- a/frontend/src/toolbar/bar/toolbarLogic.ts +++ b/frontend/src/toolbar/bar/toolbarLogic.ts @@ -490,7 +490,9 @@ export const toolbarLogic = kea([ actions.setAutomaticActionCreationEnabled(true, e.data.payload.name) return case PostHogAppToolbarEvent.PH_NAVIGATE: - window.location.href = e.data.payload.url + if (window.location.href !== e.data.payload.url) { + window.location.href = e.data.payload.url + } return default: console.warn(`[PostHog Toolbar] Received unknown parent window message: ${type}`) diff --git a/frontend/src/types.ts b/frontend/src/types.ts index c981ee4428725..40a74c7fb3da4 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -1809,7 +1809,7 @@ export interface DashboardTemplateVariableType { name: string description: string type: 'event' - default: Record + default: TemplateVariableStep required: boolean touched?: boolean selector?: string @@ -2147,14 +2147,16 @@ export interface FilterType { } export interface TemplateVariableStep { - id: string - math: BaseMathType - name: string | null - order: number - type: EntityTypes + id?: string + math?: BaseMathType + name?: string | null + order?: number + type?: EntityTypes + event?: string selector?: string | null href?: string | null url?: string | null + properties?: Record[] } export interface PropertiesTimelineFilterType { From 3ec18000d592c25a2b40bc754a18f66db8b43042 Mon Sep 17 00:00:00 2001 From: Raquel Smith Date: Thu, 12 Sep 2024 08:23:33 -0700 Subject: [PATCH 3/6] fix xss warning --- .../IframedToolbarBrowser.tsx | 4 +- .../iframedToolbarBrowserLogic.ts | 22 ++- .../components/IframedToolbarBrowser/utils.ts | 1 - .../DashboardTemplateConfigureStep.tsx | 163 +++++++++--------- frontend/src/toolbar/bar/toolbarLogic.ts | 5 - 5 files changed, 99 insertions(+), 96 deletions(-) diff --git a/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx b/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx index aa32b83e36ee7..2013b1a5e32c0 100644 --- a/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx +++ b/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx @@ -40,7 +40,7 @@ export function IframedToolbarBrowser({ }): JSX.Element | null { const logic = iframedToolbarBrowserLogic({ iframeRef, userIntent: userIntent }) - const { browserUrl } = useValues(logic) + const { browserUrl, initialPath } = useValues(logic) const { onIframeLoad, setIframeWidth } = useActions(logic) const { width: iframeWidth } = useResizeObserver({ ref: iframeRef }) @@ -55,7 +55,7 @@ export function IframedToolbarBrowser({