Skip to content
6 changes: 6 additions & 0 deletions app/client/src/actions/windowActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";

export const updateWindowDimensions = (height: number, width: number) => ({
type: ReduxActionTypes.UPDATE_WINDOW_DIMENSIONS,
payload: { height, width },
});
5 changes: 5 additions & 0 deletions app/client/src/ce/constants/ReduxActionConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1213,6 +1213,10 @@ const AppThemeActionsTypes = {
RESET_APP_THEME_SUCCESS: "RESET_APP_THEME_SUCCESS",
};

const WindowActionsTypes = {
UPDATE_WINDOW_DIMENSIONS: "UPDATE_WINDOW_DIMENSIONS",
};

const AppThemeActionErrorTypes = {
FETCH_APP_THEMES_ERROR: "FETCH_APP_THEMES_ERROR",
SET_DEFAULT_SELECTED_THEME_ERROR: "SET_DEFAULT_SELECTED_THEME_ERROR",
Expand Down Expand Up @@ -1304,6 +1308,7 @@ export const ReduxActionTypes = {
...AIActionTypes,
...ApplicationActionTypes,
...AppThemeActionsTypes,
...WindowActionsTypes,
...AppViewActionTypes,
...AppSettingsActionTypes,
...BatchUpdateActionTypes,
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/entities/FeatureFlag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const FEATURE_FLAG = {
"release_jsobjects_onpageunloadactions_enabled",
configure_block_event_tracking_for_anonymous_users:
"configure_block_event_tracking_for_anonymous_users",
release_window_dimensions_enabled: "release_window_dimensions_enabled",
} as const;

export type FeatureFlag = keyof typeof FEATURE_FLAG;
Expand Down Expand Up @@ -116,6 +117,7 @@ export const DEFAULT_FEATURE_FLAG_VALUE: FeatureFlags = {
license_ai_agent_instance_enabled: false,
release_jsobjects_onpageunloadactions_enabled: false,
configure_block_event_tracking_for_anonymous_users: false,
release_window_dimensions_enabled: true,
};

export const AB_TESTING_EVENT_KEYS = {
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/reducers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import type { CrudInfoModalReduxState } from "reducers/uiReducers/crudInfoModalR
import type { FormEvaluationState } from "reducers/evaluationReducers/formEvaluationReducer";
import type { widgetReflow } from "reducers/uiReducers/reflowReducer";
import type { AppThemingState } from "reducers/uiReducers/appThemingReducer";
import type { WindowDimensionsState } from "reducers/uiReducers/windowReducer";
import type { MainCanvasReduxState } from "ee/reducers/uiReducers/mainCanvasReducer";
import type { SettingsReduxState } from "ee/reducers/settingsReducer";
import SettingsReducer from "ee/reducers/settingsReducer";
Expand Down Expand Up @@ -147,6 +148,7 @@ export interface AppState {
activeField: ActiveField;
ide: IDEState;
pluginActionEditor: PluginActionEditorState;
windowDimensions: WindowDimensionsState;
};
entities: {
canvasWidgetsStructure: CanvasWidgetStructure;
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/reducers/uiReducers/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import crudInfoModalReducer from "reducers/uiReducers/crudInfoModalReducer";
import { widgetReflowReducer } from "reducers/uiReducers/reflowReducer";
import jsObjectNameReducer from "reducers/uiReducers/jsObjectNameReducer";
import appThemingReducer from "reducers/uiReducers/appThemingReducer";
import windowReducer from "reducers/uiReducers/windowReducer";
import mainCanvasReducer from "ee/reducers/uiReducers/mainCanvasReducer";
import focusHistoryReducer from "reducers/uiReducers/focusHistoryReducer";
import { editorContextReducer } from "ee/reducers/uiReducers/editorContextReducer";
Expand Down Expand Up @@ -85,6 +86,7 @@ export const uiReducerObject = {
crudInfoModal: crudInfoModalReducer,
widgetReflow: widgetReflowReducer,
appTheming: appThemingReducer,
windowDimensions: windowReducer,
mainCanvas: mainCanvasReducer,
appSettingsPane: appSettingsPaneReducer,
focusHistory: focusHistoryReducer,
Expand Down
2 changes: 2 additions & 0 deletions app/client/src/ce/sagas/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { watchActionSagas } from "sagas/ActionSagas";
import apiPaneSagas from "sagas/ApiPaneSagas";
import applicationSagas from "ee/sagas/ApplicationSagas";
import appThemingSaga from "sagas/AppThemingSaga";
import windowSaga from "sagas/WindowSaga";
import AutoHeightSagas from "sagas/autoHeightSagas";
import autoLayoutUpdateSagas from "sagas/AutoLayoutUpdateSagas";
import batchSagas from "sagas/BatchSagas";
Expand Down Expand Up @@ -95,6 +96,7 @@ export const sagas = [
gitSyncSagas,
SuperUserSagas,
appThemingSaga,
windowSaga,
NavigationSagas,
editorContextSagas,
AutoHeightSagas,
Expand Down
7 changes: 7 additions & 0 deletions app/client/src/pages/AppIDE/AppIDE.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import type { Page } from "entities/Page";
import { IDE_HEADER_HEIGHT } from "@appsmith/ads";
import { GitApplicationContextProvider } from "git-artifact-helpers/application/components";
import { AppIDEModals } from "ee/pages/AppIDE/components/AppIDEModals";
import { updateWindowDimensions } from "actions/windowActions";

interface EditorProps {
currentApplicationId?: string;
Expand All @@ -60,6 +61,7 @@ interface EditorProps {
isMultiPane: boolean;
widgetConfigBuildSuccess: () => void;
pages: Page[];
updateWindowDimensions: (height: number, width: number) => void;
}

type Props = EditorProps & RouteComponentProps<BuilderRouteParams>;
Expand All @@ -75,6 +77,9 @@ class Editor extends Component<Props> {
editorInitializer().then(() => {
this.props.widgetConfigBuildSuccess();
});

// Set initial window dimensions
this.props.updateWindowDimensions(window.innerHeight, window.innerWidth);
}

shouldComponentUpdate(nextProps: Props) {
Expand Down Expand Up @@ -218,6 +223,8 @@ const mapDispatchToProps = (dispatch: any) => {
setupPage: (pageId: string) => dispatch(setupPageAction({ id: pageId })),
updateCurrentPage: (pageId: string) => dispatch(updateCurrentPage(pageId)),
widgetConfigBuildSuccess: () => dispatch(widgetInitialisationSuccess()),
updateWindowDimensions: (height: number, width: number) =>
dispatch(updateWindowDimensions(height, width)),
};
};

Expand Down
9 changes: 8 additions & 1 deletion app/client/src/pages/AppViewer/AppPage/AppPage.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useEffect, useMemo, useRef } from "react";
import AnalyticsUtil from "ee/utils/AnalyticsUtil";
import type { CanvasWidgetStructure } from "WidgetProvider/types";
import { useSelector } from "react-redux";
import { useSelector, useDispatch } from "react-redux";
import { getAppMode } from "ee/selectors/applicationSelectors";
import { APP_MODE } from "entities/App";
import { renderAppsmithCanvas } from "layoutSystems/CanvasFactory";
import type { WidgetProps } from "widgets/BaseWidget";
import { useAppViewerSidebarProperties } from "utils/hooks/useAppViewerSidebarProperties";
import { getIsAnvilLayout } from "layoutSystems/anvil/integrations/selectors";
import { updateWindowDimensions } from "actions/windowActions";

import { PageView, PageViewWrapper } from "./AppPage.styled";
import { useCanvasWidthAutoResize } from "../../hooks/useCanvasWidthAutoResize";
Expand All @@ -24,6 +25,7 @@ export function AppPage(props: AppPageProps) {
const { appName, basePageId, canvasWidth, pageName, widgetsStructure } =
props;

const dispatch = useDispatch();
const appMode = useSelector(getAppMode);
const isPublished = appMode === APP_MODE.PUBLISHED;
const isAnvilLayout = useSelector(getIsAnvilLayout);
Expand All @@ -46,6 +48,11 @@ export function AppPage(props: AppPageProps) {
});
}, [appName, basePageId, pageName]);

// Set initial window dimensions
useEffect(() => {
dispatch(updateWindowDimensions(window.innerHeight, window.innerWidth));
}, [dispatch]);

return (
<PageViewWrapper
hasPinnedSidebar={hasSidebarPinned}
Expand Down
25 changes: 25 additions & 0 deletions app/client/src/reducers/uiReducers/windowReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { createImmerReducer } from "utils/ReducerUtils";
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import type { ReduxAction } from "actions/ReduxActionTypes";

export interface WindowDimensionsState {
height: number;
width: number;
}

const initialState: WindowDimensionsState = {
height: typeof window !== "undefined" ? window.innerHeight : 0,
width: typeof window !== "undefined" ? window.innerWidth : 0,
};

const windowReducer = createImmerReducer(initialState, {
[ReduxActionTypes.UPDATE_WINDOW_DIMENSIONS]: (
state: WindowDimensionsState,
action: ReduxAction<{ height: number; width: number }>,
) => {
state.height = action.payload.height;
state.width = action.payload.width;
},
});

export default windowReducer;
18 changes: 18 additions & 0 deletions app/client/src/sagas/WindowSaga.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ReduxActionTypes } from "ee/constants/ReduxActionConstants";
import { takeLatest, select, call } from "redux-saga/effects";
import { evaluateTreeSaga } from "./EvaluationsSaga";
import { getUnevaluatedDataTree } from "selectors/dataTreeSelectors";

export function* handleWindowDimensionsUpdate() {
const unEvalAndConfigTree: ReturnType<typeof getUnevaluatedDataTree> =
yield select(getUnevaluatedDataTree);

yield call(evaluateTreeSaga, unEvalAndConfigTree);
}

export default function* windowSaga() {
yield takeLatest(
ReduxActionTypes.UPDATE_WINDOW_DIMENSIONS,
handleWindowDimensionsUpdate,
);
}
6 changes: 5 additions & 1 deletion app/client/src/selectors/dataTreeSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
import "url-search-params-polyfill";
import type { DefaultRootState } from "react-redux";
import { getSelectedAppThemeProperties } from "./appThemingSelectors";
import { getWindowDimensions } from "./windowSelectors";
import type { LoadingEntitiesState } from "reducers/evaluationReducers/loadingEntitiesReducer";
import _, { get } from "lodash";
import type { EvaluationError } from "utils/DynamicBindingUtils";
Expand Down Expand Up @@ -149,6 +150,7 @@ export const getUnevaluatedDataTree = createSelector(
getMetaWidgetsFromUnevaluatedDataTree,
getAppData,
getSelectedAppThemeProperties,
getWindowDimensions,
getCurrentAppWorkspace,
getCurrentApplication,
getCurrentPageName,
Expand All @@ -160,6 +162,7 @@ export const getUnevaluatedDataTree = createSelector(
metaWidgets,
appData,
theme,
windowDimensions,
currentWorkspace,
currentApplication,
getCurrentPageName,
Expand All @@ -182,12 +185,13 @@ export const getUnevaluatedDataTree = createSelector(
// taking precedence in case the key is the same
store: appData.store,
theme,
ui: windowDimensions,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the structure of appsmith.ui to match the intended API.

The assignment ui: windowDimensions directly exposes the window dimensions object (with height and width properties) as appsmith.ui. Ensure this matches the desired public API structure and doesn't conflict with any existing ui property.


🏁 Script executed:

#!/bin/bash
# Search for existing references to appsmith.ui to verify structure expectations
rg -nP 'appsmith\.ui\b' --type=ts --type=tsx -C3

Length of output: 27



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for existing references to appsmith.ui with corrected type flags
rg -nP 'appsmith\.ui\b' --type=typescript -C3

Length of output: 34


🏁 Script executed:

#!/bin/bash
# Also search for ui property assignments and windowDimensions usage in dataTree context
rg -nP '(ui\s*:|windowDimensions)' app/client/src/selectors/dataTreeSelectors.ts -C5

Length of output: 691


🏁 Script executed:

#!/bin/bash
# Search for dataTree object construction to understand the structure
rg -nP 'dataTree\s*=\s*\{' --type=typescript -C10

Length of output: 34



---

🏁 Script executed:

```shell
#!/bin/bash
# Find the AppsmithEntity type definition
rg -nP 'type AppsmithEntity|interface AppsmithEntity' -C5

Length of output: 2717


🏁 Script executed:

#!/bin/bash
# Search for ui property references in the same file
rg -n 'ui:' app/client/src/selectors/dataTreeSelectors.ts

Length of output: 31


🏁 Script executed:

#!/bin/bash
# Find windowDimensions type/definition
rg -nP 'windowDimensions\s*[=:|]' -C3 | head -50

Length of output: 613


🏁 Script executed:

#!/bin/bash
# Search for appsmith.ui references without type flags
rg -n 'appsmith\.ui' -C2

Length of output: 3987



---

🏁 Script executed:

```shell
#!/bin/bash
# Get the complete AppsmithEntity interface definition
rg -n 'export interface AppsmithEntity' -A20 app/client/src/ce/entities/DataTree/types.ts

Length of output: 451


🏁 Script executed:

#!/bin/bash
# Also check if ui is defined in AppDataState (which AppsmithEntity extends from)
rg -n 'interface AppDataState|type AppDataState' -A15

Length of output: 1319



---

🏁 Script executed:

```shell
#!/bin/bash
# Check the complete selector function and type casting context around line 193
rg -n 'as AppsmithEntity' app/client/src/selectors/dataTreeSelectors.ts -B10 -A2

Length of output: 656


🏁 Script executed:

#!/bin/bash
# Search for any type extensions or augmentations of AppsmithEntity that add ui property
rg -n 'AppsmithEntity' app/client/src/ce/entities/DataTree/types.ts -A30

Length of output: 906


Add ui property to the AppsmithEntity interface definition.

The assignment of ui: windowDimensions at line 188 is being bypassed by the as AppsmithEntity cast at line 193, but the ui property is not declared in the interface. Production code in ExternalWidget and CustomWidget actively consumes appsmith.ui with a {width, height} structure, confirming this is intentional.

Update app/client/src/ce/entities/DataTree/types.ts to declare the property:

export interface AppsmithEntity extends Omit<AppDataState, "store"> {
  ENTITY_TYPE: typeof ENTITY_TYPE.APPSMITH;
  store: Record<string, unknown>;
  theme: AppTheme["properties"];
  ui: { width: number; height: number };
  currentPageName: string;
  workspaceName: string;
  appName: string;
  currentEnvironmentName: string;
}
🤖 Prompt for AI Agents
In app/client/src/ce/entities/DataTree/types.ts (update the AppsmithEntity
interface), add the missing ui property declaration so AppsmithEntity includes
ui: { width: number; height: number }; keep the rest of the interface as-is
(Omit<AppDataState, "store">, ENTITY_TYPE, store, theme, currentPageName,
workspaceName, appName, currentEnvironmentName) to match runtime usage where
code sets appsmith.ui = windowDimensions; ensure any necessary imports/types
referenced (e.g., AppTheme) remain unchanged.

currentPageName: getCurrentPageName,
workspaceName: currentWorkspace.name,
appName: currentApplication?.name,
currentEnvironmentName,
ENTITY_TYPE: ENTITY_TYPE.APPSMITH,
} as AppsmithEntity;
(dataTree.appsmith as AppsmithEntity).ENTITY_TYPE = ENTITY_TYPE.APPSMITH;
dataTree = { ...dataTree, ...metaWidgets.dataTree };
configTree = { ...configTree, ...metaWidgets.configTree };

Expand Down
12 changes: 12 additions & 0 deletions app/client/src/selectors/windowSelectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { DefaultRootState } from "react-redux";
import { selectFeatureFlagCheck } from "ee/selectors/featureFlagsSelectors";
import { FEATURE_FLAG } from "ee/entities/FeatureFlag";

export const getWindowDimensions = (state: DefaultRootState) => {
const isWindowDimensionsEnabled = selectFeatureFlagCheck(
state,
FEATURE_FLAG.release_window_dimensions_enabled,
);

return isWindowDimensionsEnabled ? state.ui.windowDimensions : undefined;
};
Loading