diff --git a/client/VERSION b/client/VERSION
index b0f6bf0cd..e393c3c55 100644
--- a/client/VERSION
+++ b/client/VERSION
@@ -1 +1 @@
-2.4.10
+2.4.11
\ No newline at end of file
diff --git a/client/package.json b/client/package.json
index eaf135493..e82e56ed9 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,6 +1,6 @@
 {
   "name": "lowcoder-frontend",
-  "version": "2.4.9",
+  "version": "2.4.11",
   "type": "module",
   "private": true,
   "workspaces": [
diff --git a/client/packages/lowcoder-design/src/components/colorSelect/colorUtils.ts b/client/packages/lowcoder-design/src/components/colorSelect/colorUtils.ts
index ac24c5900..94621fcb1 100644
--- a/client/packages/lowcoder-design/src/components/colorSelect/colorUtils.ts
+++ b/client/packages/lowcoder-design/src/components/colorSelect/colorUtils.ts
@@ -4,18 +4,39 @@ import { generate } from "@ant-design/colors/es";
 
 extend([namesPlugin]);
 
+export const gradientColors = [
+  "linear-gradient(0deg, #fdfbfb 0%, #ebedee 100%)",
+  "linear-gradient(45deg, #cfd9df 0%, #e2ebf0 100%)",
+  "linear-gradient(90deg, #e3ffe7 0%, #d9e7ff 100%)",
+
+  "linear-gradient(135deg, #a8edea 0%, #fed6e3 100%)",
+  "linear-gradient(0deg, #fbc2eb 0%, #a6c1ee 100%)",
+  "linear-gradient(45deg, #efd5ff 0%, #515ada 100%)",
+  "linear-gradient(90deg, #4b6cb7 0%, #72afd3 100%)",
+  "linear-gradient(135deg, #72afd3 0%, #96e6a1 100%)",
+
+  "linear-gradient(90deg, #fa709a 0%, #fee140 100%)",
+  "linear-gradient(45deg, #d53369 0%, #daae51 100%)",
+  "linear-gradient(0deg, #f43b47 0%, #453a94 100%)",
+  
+  "linear-gradient(135deg, #00d2ff 0%, #3a47d5 100%)",
+  "linear-gradient(0deg, #f8ff00 0%, #3ad59f 100%)",
+  "linear-gradient(45deg, #fcff9e 0%, #c67700 100%)",
+  "linear-gradient(90deg, #fad0c4 0%, #ffd1ff 100%)",
+];
+
 // Color Palette
 export const constantColors = [
-  { id: 1, color: "#6D83F2" },
-  { id: 2, color: "#5589F2" },
-  { id: 3, color: "#36B389" },
-  { id: 4, color: "#E68E50" },
-  { id: 5, color: "#E67373" },
-  { id: 6, color: "#F5FFF7" },
-  { id: 7, color: "#F3FAFF" },
-  { id: 8, color: "#FFF6E6" },
-  { id: 9, color: "#F5F5F6" },
-  { id: 10, color: "#FFFFFF" },
+  "#6D83F2",
+  "#5589F2",
+  "#36B389",
+  "#E68E50",
+  "#E67373",
+  "#F5FFF7",
+  "#F3FAFF",
+  "#FFF6E6",
+  "#F5F5F6",
+  "#FFFFFF",
 ];
 
 export const chartColorPalette = [
@@ -40,7 +61,17 @@ const alphaOfRgba = (rgba: string) => {
   return colord(rgba).alpha().toString();
 };
 
-const isValidColor = (str: string) => {
+const isValidGradient = (color?: string) => {
+  if (!color) return false;
+
+  const linearGradientRegex = /^linear-gradient\((\d+deg|to\s+(top|right|bottom|left)(\s+(top|right|bottom|left))?)\s*,\s*((#[0-9a-fA-F]{3,6}|rgba?\(\d+,\s*\d+,\s*\d+(,\s*\d+(\.\d+)?)?\)|[a-zA-Z]+)(\s+\d+%?)?,?\s*)+\)$/i;
+  const radialGradientRegex = /^radial-gradient\(\s*(circle|ellipse)?\s*,\s*((#[0-9a-fA-F]{3,6}|rgba?\(\d+,\s*\d+,\s*\d+(,\s*\d+(\.\d+)?)?\)|[a-zA-Z]+)(\s+\d+%?)?,?\s*)+\)$/i;
+  
+  return linearGradientRegex.test(color) || radialGradientRegex.test(color);
+}
+
+const isValidColor = (str?: string) => {
+  if (!str) return false;
   return colord(str).isValid();
 };
 
@@ -91,4 +122,4 @@ export const darkenColor = (colorStr: string, intensity: number) => {
   return color.darken(intensity).toHex().toUpperCase();
 };
 
-export { toRGBA, toHex, alphaOfRgba, isValidColor };
+export { toRGBA, toHex, alphaOfRgba, isValidColor, isValidGradient };
diff --git a/client/packages/lowcoder-design/src/components/colorSelect/index.tsx b/client/packages/lowcoder-design/src/components/colorSelect/index.tsx
index 24c091256..52f2a206f 100644
--- a/client/packages/lowcoder-design/src/components/colorSelect/index.tsx
+++ b/client/packages/lowcoder-design/src/components/colorSelect/index.tsx
@@ -1,5 +1,5 @@
-import { RgbaStringColorPicker } from "react-colorful";
 import { default as Popover } from "antd/es/popover";
+import ColorPicker, {useColorPicker} from 'react-best-gradient-color-picker';
 import { ActionType } from '@rc-component/trigger/lib/interface';
 import {
   alphaOfRgba,
@@ -7,9 +7,11 @@ import {
   toHex,
   constantColors,
   isValidColor,
+  isValidGradient,
+  gradientColors,
 } from "components/colorSelect/colorUtils";
 import styled, { css } from "styled-components";
-import { useCallback, useRef, useState } from "react";
+import { useCallback, useRef, useState, useEffect, useMemo, } from "react";
 import { throttle } from "lodash";
 import { changeValueAction } from "lowcoder-core";
 
@@ -18,54 +20,65 @@ interface ColorSelectProps {
   trigger?: ActionType;
   dispatch?: (value: any) => void;
   changeColor?: (value: any) => void;
+  presetColors?: string[];
+  allowGradient?: boolean;
 }
 
 export const ColorSelect = (props: ColorSelectProps) => {
   const { color, trigger = "click", dispatch, changeColor } = props;
-  let pickerColor = useRef(toRGBA(color));
   const [visible, setVisible] = useState(false);
+  const [ selectedColor, setSelectedColor ] = useState(color);
+  const { getGradientObject } = useColorPicker(selectedColor, setSelectedColor);
+
+  const presetColors = useMemo(() => {
+    let colors = props.presetColors || [];
+    if (props.allowGradient) {
+      colors = colors.concat(gradientColors.slice(0, 16 - colors.length));
+    }
+    return colors;
+  }, [props.presetColors, selectedColor, props.allowGradient]);
+
   const throttleChange = useCallback(
     throttle((rgbaColor: string) => {
-      dispatch && dispatch(changeValueAction(toHex(rgbaColor), true));
-      changeColor && changeColor(toHex(rgbaColor));
+      dispatch && dispatch(changeValueAction(rgbaColor, true));
+      changeColor && changeColor(rgbaColor);
     }, 200),
     [dispatch,changeColor]
   );
+
+  useEffect(() => {
+    if (color !== selectedColor) {
+      const value = getGradientObject();
+      if (!value?.isGradient) {
+        return throttleChange(toHex(selectedColor));
+      }
+      throttleChange(selectedColor);
+    }
+  }, [selectedColor])
+  
   return (
     <Popover
       trigger={trigger}
+      placement="left"
       destroyTooltipOnHide={true}
       onOpenChange={(value) => {
-        pickerColor.current = toRGBA(color);
         setVisible(value);
       }}
       content={
         <PopoverContainer>
-          <div style={{ position: "relative" }}>
-            <RgbaStringColorPicker color={pickerColor.current} onChange={throttleChange} />
-            <AlphaDiv color={color?.substring(0, 7)}>
-              <BackDiv $color={alphaOfRgba(toRGBA(color))}></BackDiv>
-            </AlphaDiv>
-          </div>
-          <ConstantDiv>
-            {constantColors.map((item) => {
-              return (
-                <ConstantBlock
-                  color={item.color}
-                  key={item.id}
-                  onClick={() => {
-                    throttleChange(item.color);
-                    pickerColor.current = toRGBA(item.color);
-                  }}
-                />
-              );
-            })}
-          </ConstantDiv>
+          <StyledColorPicker
+            disableDarkMode
+            value={color}
+            onChange={setSelectedColor}
+            width={250}
+            height={160}
+            presets={presetColors}
+            $allowGradient={props.allowGradient}
+          />
         </PopoverContainer>
       }
     >
-      <ColorBlock $color={color?.substring(0, 7)}>
-        <BackDiv $color={alphaOfRgba(toRGBA(color))}></BackDiv>
+      <ColorBlock $color={color}>
       </ColorBlock>
     </Popover>
   );
@@ -139,7 +152,6 @@ const PopoverContainer = styled.div`
   display: flex;
   flex-direction: column;
   gap: 12px;
-  padding: 16px;
 `;
 // contrast block
 const AlphaDiv = styled.div.attrs((props) => ({
@@ -169,7 +181,11 @@ const BackDiv = styled.div.attrs<{ $color: string }>((props: { $color: string })
 `;
 // main block
 const ColorBlock = styled.div<{ $color: string }>`
-  background-color: ${(props) => (isValidColor(props.$color) ? props.$color : "#FFFFFF")};
+  background: ${(props) => (
+    isValidColor(props.$color) || isValidGradient(props.$color)
+    ? props.$color
+    : "#FFFFFF"
+  )};
   border: 1px solid rgba(0, 0, 0, 0.1);
   border-radius: 4px;
   height: 24px;
@@ -177,4 +193,18 @@ const ColorBlock = styled.div<{ $color: string }>`
   cursor: pointer;
   background-clip: content-box;
   overflow: hidden;
+`;
+
+const StyledColorPicker = styled(ColorPicker)<{$allowGradient?: boolean}>`
+  #rbgcp-wrapper > div:nth-child(2) > div:first-child > div:first-child {
+    ${props => !props.$allowGradient && `visibility: hidden`};
+  }
+  #rbgcp-wrapper > div:last-child > div:last-child {
+    justify-content: flex-start !important;
+    gap: 3px;
+  
+    > div {
+      border: 1px solid lightgray;
+    }
+  }
 `;
\ No newline at end of file
diff --git a/client/packages/lowcoder-design/src/icons/index.tsx b/client/packages/lowcoder-design/src/icons/index.tsx
index 785b43f1f..687d3516b 100644
--- a/client/packages/lowcoder-design/src/icons/index.tsx
+++ b/client/packages/lowcoder-design/src/icons/index.tsx
@@ -172,6 +172,8 @@ export { ReactComponent as LeftSettingIcon } from "./remix/tools-fill.svg";
 export { ReactComponent as LeftLayersIcon } from "./remix/stack-line.svg";
 export { ReactComponent as LeftHelpIcon } from "./v1/icon-left-help.svg";
 export { ReactComponent as LeftPreloadIcon } from "./v1/icon-left-preload.svg";
+export { ReactComponent as LeftColorPaletteIcon } from "./remix/palette-line.svg";
+export { ReactComponent as LeftJSSettingIcon } from "./remix/javascript-line.svg";
 
 
 export { ReactComponent as HomeSettingsIcon } from "./v1/icon-home-settings.svg";
diff --git a/client/packages/lowcoder/package.json b/client/packages/lowcoder/package.json
index 2f9133773..d5ef8d284 100644
--- a/client/packages/lowcoder/package.json
+++ b/client/packages/lowcoder/package.json
@@ -64,6 +64,7 @@
     "qrcode.react": "^3.1.0",
     "rc-trigger": "^5.3.1",
     "react": "^18.2.0",
+    "react-best-gradient-color-picker": "^3.0.10",
     "react-colorful": "^5.5.1",
     "react-documents": "^1.2.1",
     "react-dom": "^18.2.0",
diff --git a/client/packages/lowcoder/src/api/commonSettingApi.ts b/client/packages/lowcoder/src/api/commonSettingApi.ts
index 6b80e4527..510e67026 100644
--- a/client/packages/lowcoder/src/api/commonSettingApi.ts
+++ b/client/packages/lowcoder/src/api/commonSettingApi.ts
@@ -53,7 +53,16 @@ export interface ThemeDetail {
   chart?: string;
   margin?: string;
   padding?: string;
-  gridColumns?: string; //Added By Aqib Mirza
+  gridPaddingX?: number;
+  gridPaddingY?: number;
+  gridColumns?: string;
+  gridRowHeight?: string;
+  gridRowCount?: number;
+  gridBgImage?: string;
+  gridBgImageRepeat?: string;
+  gridBgImageSize?: string;
+  gridBgImagePosition?: string;
+  gridBgImageOrigin?: string;
   text?: string;
   textSize?: string;
   fontFamily?: string;
diff --git a/client/packages/lowcoder/src/components/ThemeSettingsSelector.tsx b/client/packages/lowcoder/src/components/ThemeSettingsSelector.tsx
index 54469195a..c81aa9542 100644
--- a/client/packages/lowcoder/src/components/ThemeSettingsSelector.tsx
+++ b/client/packages/lowcoder/src/components/ThemeSettingsSelector.tsx
@@ -1,7 +1,7 @@
 import _ from "lodash";
 import { useEffect, useState } from "react";
 import { ConfigItem, Radius, Margin, Padding, GridColumns, BorderWidth, BorderStyle } from "../pages/setting/theme/styledComponents";
-import { isValidColor, toHex } from "components/colorSelect/colorUtils";
+import { isValidColor, isValidGradient, toHex } from "components/colorSelect/colorUtils";
 import { ColorSelect } from "components/colorSelect";
 import { TacoInput } from "components/tacoInput";
 import { Slider, Switch } from "antd";
@@ -21,7 +21,6 @@ export type configChangeParams = {
   chart?: string;
   margin?: string;  
   padding?: string;
-  gridColumns?: string; // Added By Aqib Mirza
   borderStyle?: string;
   borderColor?: string;
   borderWidth?: string;
@@ -29,6 +28,16 @@ export type configChangeParams = {
   components?: Record<string, object>,
   showComponentLoadingIndicators?: boolean;
   showDataLoadingIndicators?: boolean;
+  gridColumns?: string;
+  gridRowHeight?: string;
+  gridRowCount?: number;
+  gridPaddingX?: number;
+  gridPaddingY?: number;
+  gridBgImage?: string;
+  gridBgImageRepeat?: string;
+  gridBgImageSize?: string;
+  gridBgImagePosition?: string;
+  gridBgImageOrigin?: string;
 };
 
 type ColorConfigProps = {
@@ -47,9 +56,18 @@ type ColorConfigProps = {
   fontFamily?: string;
   margin?: string;  
   padding?: string;
-  gridColumns?: string; // Added By Aqib Mirza
   showComponentLoadingIndicators?: boolean;
   showDataLoadingIndicators?: boolean;
+  gridColumns?: string;
+  gridRowHeight?: string;
+  gridRowCount?: number;
+  gridPaddingX?: number;
+  gridPaddingY?: number;
+  gridBgImage?: string;
+  gridBgImageRepeat?: string;
+  gridBgImageSize?: string;
+  gridBgImagePosition?: string;
+  gridBgImageOrigin?: string;
 };
 
 export default function ThemeSettingsSelector(props: ColorConfigProps) {
@@ -63,33 +81,54 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
     showVarName = true,
     margin: defaultMargin,  
     padding: defaultPadding,
-    gridColumns: defaultGridColumns,
     borderStyle: defaultBorderStyle,
     borderWidth: defaultBorderWidth,
     borderColor: defaultBorderColor,
     fontFamily: defaultFontFamily,
     showComponentLoadingIndicators: defaultShowComponentLoaders,
     showDataLoadingIndicators: defaultShowDataLoaders,
+    gridColumns: defaultGridColumns,
+    gridRowHeight: defaultGridRowHeight,
+    gridRowCount: defaultGridRowCount,
+    gridPaddingX: defaultGridPaddingX,
+    gridPaddingY: defaultGridPaddingY,
+    gridBgImage: defaultGridBgImage,
+    gridBgImageRepeat: defaultGridBgImageRepeat,
+    gridBgImageSize: defaultGridBgImageSize,
+    gridBgImagePosition: defaultGridBgImagePosition,
+    gridBgImageOrigin: defaultGridBgImageOrigin,
   } = props;
-  
+
   const configChangeWithDebounce = _.debounce(configChange, 0);
   const [color, setColor] = useState(defaultColor);
   const [radius, setRadius] = useState(defaultRadius);
   const [margin, setMargin] = useState(defaultMargin);  
   const [padding, setPadding] = useState(defaultPadding);
-  const [gridColumns, setGridColumns] = useState(defaultGridColumns); 
   const [borderStyle, setBorderStyle] = useState(defaultBorderStyle);
   const [borderWidth, setBorderWidth] = useState(defaultBorderWidth);
   const [borderColor, setBorderColor] = useState(defaultBorderColor);
   const [fontFamily, setFontFamily] = useState(defaultFontFamily);
   const [showComponentLoaders, setComponentLoaders] = useState(defaultShowComponentLoaders);
   const [showDataLoaders, setDataLoaders] = useState(defaultShowDataLoaders);
+  const [gridColumns, setGridColumns] = useState(defaultGridColumns); 
+  const [gridRowHeight, setGridRowHeight] = useState(defaultGridRowHeight); 
+  const [gridRowCount, setGridRowCount] = useState(defaultGridRowCount); 
+  const [gridPaddingX, setGridPaddingX] = useState(defaultGridPaddingX); 
+  const [gridPaddingY, setGridPaddingY] = useState(defaultGridPaddingY); 
+  const [gridBgImage, setGridBgImage] = useState(defaultGridBgImage); 
+  const [gridBgImageRepeat, setGridBgImageRepeat] = useState(defaultGridBgImageRepeat); 
+  const [gridBgImageSize, setGridBgImageSize] = useState(defaultGridBgImageSize); 
+  const [gridBgImagePosition, setGridBgImagePosition] = useState(defaultGridBgImagePosition); 
+  const [gridBgImageOrigin, setGridBgImageOrigin] = useState(defaultGridBgImageOrigin); 
 
   const varName = `(${themeSettingKey})`;
 
   const colorInputBlur = () => {
-    if (!color || !isValidColor(color)) {
+    if (!color || !isValidColor(color) || !isValidGradient(color)) {
       setColor(defaultColor);
+    } else if (isValidGradient(color)) {
+      setColor(color);
+      configChange({ themeSettingKey, color: color });
     } else {
       setColor(toHex(color));
       configChange({ themeSettingKey, color: toHex(color) });
@@ -136,20 +175,10 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
       result = padding;  
     } else {  
       result = "3px";  
-    }  
-    setPadding(result);  
-    configChange({ themeSettingKey, padding: result });  
-  };
-
-  const gridColumnsInputBlur = (gridColumns: string) => {
-    let result = "";
-    if (!gridColumns) {
-      result = "24";
-    } else {
-      result = gridColumns;
     }
-    setGridColumns(result);
-    configChange({ themeSettingKey, gridColumns: result });
+
+    setPadding(result);  
+    configChange({ themeSettingKey, padding: result });
   };
 
   const borderStyleInputBlur = (borderStyle: string) => {
@@ -194,9 +223,77 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
     configChange({ themeSettingKey, fontFamily: result });
   };
 
+  const gridSizeInputBlur = (value: string) => {
+    let result = "";
+    if (!value) {
+      result = themeSettingKey === 'gridRowHeight' ? '8' : '24';
+    } else {
+      result = value;
+    }
+    if (themeSettingKey === 'gridRowHeight') {
+      setGridRowHeight(result);
+      configChange({ themeSettingKey, gridRowHeight: result });
+      return;
+    }
+    setGridColumns(result);
+    configChange({ themeSettingKey, gridColumns: result });
+  };
+
+  const gridRowCountInputBlur = (value: string) => {  
+    let result = Infinity;
+    if (value !== '') {
+      result = Number(value);
+    }
+
+    setGridRowCount(result);  
+    configChange({ themeSettingKey, gridRowCount: result });
+  };
+
+  const gridPaddingInputBlur = (padding: string) => { 
+    let result = 20;  
+    if (padding !== '') {  
+      result = Number(padding);  
+    }
+
+    if (themeSettingKey === 'gridPaddingX') {
+      setGridPaddingX(result);  
+      configChange({ themeSettingKey, gridPaddingX: result });
+      return;
+    }
+    if (themeSettingKey === 'gridPaddingY') {
+      setGridPaddingY(result);  
+      configChange({ themeSettingKey, gridPaddingY: result });
+      return;
+    }
+  };
+
+  const gridBackgroundInputBlur = (value: string) => {
+    switch (themeSettingKey) {
+      case 'gridBgImage':
+        setGridBgImage(value);
+        configChange({ themeSettingKey, gridBgImage: value });
+        break;
+      case 'gridBgImageRepeat':
+        setGridBgImageRepeat(value || 'no-repeat');
+        configChange({ themeSettingKey, gridBgImageRepeat: value });
+        break;
+      case 'gridBgImageSize':
+        setGridBgImageSize(value || "cover");
+        configChange({ themeSettingKey, gridBgImageSize: value });
+        break;
+      case 'gridBgImagePosition':
+        setGridBgImagePosition(value || "center");
+        configChange({ themeSettingKey, gridBgImagePosition: value });
+        break;
+      case 'gridBgImageOrigin':
+        setGridBgImageOrigin(value || 'padding-box');
+        configChange({ themeSettingKey, gridBgImageOrigin: value });
+        break;
+    }
+  }
 
   useEffect(() => {
-    if (color && isValidColor(color)) {
+    if (color && (isValidColor(color) || isValidGradient(color))) {
       configChangeWithDebounce({ themeSettingKey, color });
     }
   }, [color]);
@@ -221,6 +318,10 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
     setGridColumns(defaultGridColumns);
   }, [defaultGridColumns]);
 
+  useEffect(() => {
+    setGridRowCount(defaultGridRowCount);
+  }, [defaultGridRowCount]);
+
   useEffect(() => {
     setBorderStyle(defaultBorderStyle);
   }, [defaultBorderStyle]);
@@ -245,6 +346,14 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
     setDataLoaders(defaultShowDataLoaders);
   }, [defaultShowDataLoaders]);
 
+  useEffect(() => {
+    setGridPaddingX(defaultGridPaddingX);
+  }, [defaultGridPaddingX]);
+
+  useEffect(() => {
+    setGridPaddingY(defaultGridPaddingY);
+  }, [defaultGridPaddingY]);
+
   return (
     <ConfigItem className={props.className}>
       {themeSettingKey !== "showDataLoadingIndicators"
@@ -261,20 +370,27 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
       {themeSettingKey !== "radius" &&  
         themeSettingKey !== "margin" &&  
         themeSettingKey !== "padding" && 
-        themeSettingKey !== "gridColumns" &&
         themeSettingKey !== "borderStyle" &&
         themeSettingKey !== "borderWidth" &&
         themeSettingKey !== "fontFamily" && 
         themeSettingKey !== "showComponentLoadingIndicators" && 
-        themeSettingKey !== "showDataLoadingIndicators" && (
+        themeSettingKey !== "showDataLoadingIndicators" &&
+        themeSettingKey !== "gridColumns" &&
+        themeSettingKey !== "gridRowHeight" &&
+        themeSettingKey !== "gridRowCount" &&
+        themeSettingKey !== "gridPaddingX" &&
+        themeSettingKey !== "gridPaddingY" &&
+        themeSettingKey !== "gridBgImage" &&
+        themeSettingKey !== "gridBgImageRepeat" &&
+        themeSettingKey !== "gridBgImageSize" &&
+        themeSettingKey !== "gridBgImagePosition" &&
+        themeSettingKey !== "gridBgImageOrigin" && (
         <div className="config-input">
           <ColorSelect
-            changeColor={_.debounce(setColor, 500, {
-              leading: true,
-              trailing: true,
-            })}
+            changeColor={setColor}
             color={color!}
             trigger="hover"
+            allowGradient={themeSettingKey === 'canvas' || themeSettingKey === 'primarySurface'}
           />
           <TacoInput
             value={color}
@@ -389,25 +505,6 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
         </div>
       )}
 
-      {themeSettingKey === "gridColumns" && (
-        <div className="config-input">
-          <GridColumns $gridColumns={defaultGridColumns || "24"}>
-            <div>
-              <TableCellsIcon title="" />
-            </div>
-          </GridColumns>
-
-          <Slider 
-            style={{ width: "90%", margin: "8px 5% 0 5%"}}
-            min={8}  // Define the minimum value for the slider
-            max={48} // Define the maximum value for the slider
-            value={parseInt(gridColumns || "24")}
-            onChange={(value) => setGridColumns(value.toString())}
-            onChangeComplete={(value) => gridColumnsInputBlur(value.toString())}
-          />
-        </div>
-      )}
-
       {themeSettingKey === "fontFamily" && (
         <div className="config-input">
           <TacoInput
@@ -453,6 +550,168 @@ export default function ThemeSettingsSelector(props: ColorConfigProps) {
           <span>{name}</span>
         </div>
       )}
+
+      {themeSettingKey === "gridColumns" && (
+        <div className="config-input">
+          <GridColumns $gridColumns={defaultGridColumns || "24"}>
+            <div><TableCellsIcon title="" /></div>
+          </GridColumns>
+
+          <Slider 
+            style={{ width: "90%", margin: "8px 5% 0 5%"}}
+            min={8}  // Define the minimum value for the slider
+            max={48} // Define the maximum value for the slider
+            value={parseInt(gridColumns || "24")}
+            onChange={(value) => setGridColumns(value.toString())}
+            onChangeComplete={(value) => gridSizeInputBlur(value.toString())}
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridRowHeight" && (
+        <div className="config-input">
+          <GridColumns $gridColumns={defaultGridColumns || "24"}>
+            <div><TableCellsIcon title="" /></div>
+          </GridColumns>
+
+          <Slider 
+            style={{ width: "90%", margin: "8px 5% 0 5%"}}
+            min={6}  // Define the minimum value for the slider
+            max={20} // Define the maximum value for the slider
+            value={parseInt(gridRowHeight || "8")}
+            onChange={(value) => setGridRowHeight(value.toString())}
+            onChangeComplete={(value) => gridSizeInputBlur(value.toString())}
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridRowCount" && (
+        <div className="config-input">
+          <GridColumns $gridColumns={defaultGridColumns || "24"}>
+            <div><TableCellsIcon title="" /></div>
+          </GridColumns>
+
+          <TacoInput
+            type="number"
+            min={0}
+            value={gridRowCount}
+            onChange={(e) => {
+              if (e.target.value === '') {
+                return setGridRowCount(Infinity);  
+              }
+              setGridRowCount(Number(e.target.value))
+            }}
+            onBlur={(e) => gridRowCountInputBlur(e.target.value)}
+            onKeyUp={(e) =>
+              e.nativeEvent.key === "Enter" &&
+              gridRowCountInputBlur(e.currentTarget.value)
+            }
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridPaddingX" && (
+        <div className="config-input">
+          <Padding $padding={"3px"}>
+            <div><CompressIcon title="" /></div>
+          </Padding>
+          <TacoInput
+            type="number"
+            min={0}
+            value={gridPaddingX}
+            onChange={(e) => {
+              if (e.target.value === '') {
+                return setGridPaddingX(undefined);  
+              }
+              setGridPaddingX(Number(e.target.value))
+            }}
+            onBlur={(e) => gridPaddingInputBlur(e.target.value)}
+            onKeyUp={(e) =>
+              e.nativeEvent.key === "Enter" &&
+              gridPaddingInputBlur(e.currentTarget.value)
+            }
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridPaddingY" && (
+        <div className="config-input">
+          <Padding $padding={"3px"}>
+            <div><CompressIcon title="" /></div>
+          </Padding>
+          <TacoInput
+            type="number"
+            min={0}
+            value={gridPaddingY}
+            onChange={(e) => {
+              if (e.target.value === '') {
+                return setGridPaddingY(undefined); 
+              }
+              setGridPaddingY(Number(e.target.value))
+            }}
+            onBlur={(e) => gridPaddingInputBlur(e.target.value)}
+            onKeyUp={(e) =>
+              e.nativeEvent.key === "Enter" &&
+              gridPaddingInputBlur(e.currentTarget.value)
+            }
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridBgImage" && (
+        <div className="config-input">
+          <TacoInput
+            value={gridBgImage}
+            onChange={(e) => setGridBgImage(e.target.value)}
+            onBlur={(e) => gridBackgroundInputBlur(e.target.value)}
+            onKeyUp={(e) => e.nativeEvent.key === "Enter" && gridBackgroundInputBlur(e.currentTarget.value)}
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridBgImageRepeat" && (
+        <div className="config-input">
+          <TacoInput
+            value={gridBgImageRepeat}
+            onChange={(e) => setGridBgImageRepeat(e.target.value)}
+            onBlur={(e) => gridBackgroundInputBlur(e.target.value)}
+            onKeyUp={(e) => e.nativeEvent.key === "Enter" && gridBackgroundInputBlur(e.currentTarget.value)}
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridBgImageSize" && (
+        <div className="config-input">
+          <TacoInput
+            value={gridBgImageSize}
+            onChange={(e) => setGridBgImageSize(e.target.value)}
+            onBlur={(e) => gridBackgroundInputBlur(e.target.value)}
+            onKeyUp={(e) => e.nativeEvent.key === "Enter" && gridBackgroundInputBlur(e.currentTarget.value)}
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridBgImagePosition" && (
+        <div className="config-input">
+          <TacoInput
+            value={gridBgImagePosition}
+            onChange={(e) => setGridBgImagePosition(e.target.value)}
+            onBlur={(e) => gridBackgroundInputBlur(e.target.value)}
+            onKeyUp={(e) => e.nativeEvent.key === "Enter" && gridBackgroundInputBlur(e.currentTarget.value)}
+          />
+        </div>
+      )}
+
+      {themeSettingKey === "gridBgImageOrigin" && (
+        <div className="config-input">
+          <TacoInput
+            value={gridBgImageOrigin}
+            onChange={(e) => setGridBgImageOrigin(e.target.value)}
+            onBlur={(e) => gridBackgroundInputBlur(e.target.value)}
+            onKeyUp={(e) => e.nativeEvent.key === "Enter" && gridBackgroundInputBlur(e.currentTarget.value)}
+          />
+        </div>
+      )}
     </ConfigItem>
   );
 }
diff --git a/client/packages/lowcoder/src/components/table/EditableCell.tsx b/client/packages/lowcoder/src/components/table/EditableCell.tsx
index f8f9ebe16..9fae29b50 100644
--- a/client/packages/lowcoder/src/components/table/EditableCell.tsx
+++ b/client/packages/lowcoder/src/components/table/EditableCell.tsx
@@ -1,7 +1,6 @@
 import { PresetStatusColorType } from "antd/es/_util/colors";
 import _ from "lodash";
 import { changeChildAction, DispatchType } from "lowcoder-core";
-import { constantColors } from "lowcoder-design/src/components/colorSelect/colorUtils";
 import React, { ReactNode, useCallback, useContext, useEffect, useMemo, useState } from "react";
 import styled from "styled-components";
 import { JSONValue } from "util/jsonTypes";
@@ -26,8 +25,8 @@ const EditableChip = styled.div`
   height: 0px;
   border: 4.5px solid transparent;
   border-radius: 2px;
-  border-top-color: ${constantColors[1].color};
-  border-right-color: ${constantColors[1].color};
+  border-top-color: #5589F2;
+  border-right-color: #5589F2;
 `;
 
 export interface CellProps {
diff --git a/client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx b/client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx
index bec7edb67..05e8eed96 100644
--- a/client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/appSettingsComp.tsx
@@ -2,9 +2,9 @@ import { ThemeDetail, ThemeType } from "api/commonSettingApi";
 import { RecordConstructorToComp } from "lowcoder-core";
 import { dropdownInputSimpleControl } from "comps/controls/dropdownInputSimpleControl";
 import { MultiCompBuilder, valueComp, withDefault } from "comps/generators";
-import { AddIcon, Dropdown } from "lowcoder-design";
+import { AddIcon, BaseSection, Dropdown } from "lowcoder-design";
 import { EllipsisSpan } from "pages/setting/theme/styledComponents";
-import { useEffect, useState } from "react";
+import { useContext, useEffect, useState } from "react";
 import { useSelector } from "react-redux";
 import { getDefaultTheme, getThemeList } from "redux/selectors/commonSettingSelectors";
 import styled, { css } from "styled-components";
@@ -14,7 +14,7 @@ import { default as Divider } from "antd/es/divider";
 import { THEME_SETTING } from "constants/routesURL";
 import { CustomShortcutsComp } from "./customShortcutsComp";
 import { DEFAULT_THEMEID } from "comps/utils/themeUtil";
-import { StringControl } from "comps/controls/codeControl";
+import { NumberControl, RangeControl, StringControl } from "comps/controls/codeControl";
 import { IconControl } from "comps/controls/iconControl";
 import { dropdownControl } from "comps/controls/dropdownControl";
 import { ApplicationCategoriesEnum } from "constants/applicationConstants";
@@ -22,6 +22,9 @@ import { BoolControl } from "../controls/boolControl";
 import { getNpmPackageMeta } from "../utils/remote";
 import { getPromiseAfterDispatch } from "@lowcoder-ee/util/promiseUtils";
 import type { AppState } from "@lowcoder-ee/redux/reducers";
+import { ColorControl } from "../controls/colorControl";
+import { DEFAULT_ROW_COUNT } from "@lowcoder-ee/layout/calculateUtils";
+import { AppSettingContext } from "../utils/appSettingContext";
 
 const TITLE = trans("appSetting.title");
 const USER_DEFINE = "__USER_DEFINE";
@@ -100,18 +103,41 @@ const SettingsStyled = styled.div`
 `;
 
 const DivStyled = styled.div`
+  margin: 0 16px;
+
   > div {
     flex-wrap: wrap;
     margin-bottom: 12px;
     
     > div {
       width: 100%;
+      flex: 0 0 100%;
       display: block;
+
+      .tooltipLabel {
+        width: 100%;
+      }
     }
 
     > div:first-child {
       margin-bottom: 6px;
     }
+    
+    > div:nth-child(2) {
+      > div {
+        width: 100%;
+        justify-content: flex-start;
+        > div:first-child {
+          flex: 0 0 24px;
+        }
+        > div:nth-child(2) {
+          flex: 1;
+          > div:nth-child(2) {
+            width: 100%;
+          }
+        }
+      }
+    }
 
   }
   // custom styles for icon selector
@@ -183,34 +209,152 @@ const childrenMap = {
   icon: IconControl,
   category: dropdownControl(AppCategories, ApplicationCategoriesEnum.BUSINESS),
   showHeaderInPublic: withDefault(BoolControl, true),
-  maxWidth: dropdownInputSimpleControl(OPTIONS, USER_DEFINE, "1920"),
   themeId: valueComp<string>(DEFAULT_THEMEID),
   preventAppStylesOverwriting: withDefault(BoolControl, true),
   customShortcuts: CustomShortcutsComp,
   disableCollision: valueComp<boolean>(false),
   lowcoderCompVersion: withDefault(StringControl, 'latest'),
+  maxWidth: dropdownInputSimpleControl(OPTIONS, USER_DEFINE, "1920"),
+  gridColumns: RangeControl.closed(8, 48, 24),
+  gridRowHeight: RangeControl.closed(6, 20, 8),
+  gridRowCount: withDefault(NumberControl, DEFAULT_ROW_COUNT),
+  gridPaddingX: withDefault(NumberControl, 20),
+  gridPaddingY: withDefault(NumberControl, 20),
+  gridBg: ColorControl,
+  gridBgImage: StringControl,
+  gridBgImageRepeat: StringControl,
+  gridBgImageSize: StringControl,
+  gridBgImagePosition: StringControl,
+  gridBgImageOrigin: StringControl,
 };
 type ChildrenInstance = RecordConstructorToComp<typeof childrenMap> & {
   themeList: ThemeType[];
   defaultTheme: string;
 };
 
-function AppSettingsModal(props: ChildrenInstance) {
+function AppGeneralSettingsModal(props: ChildrenInstance) {
   const lowcoderCompsMeta = useSelector((state: AppState) => state.npmPlugin.packageMeta['lowcoder-comps']);
   const [lowcoderCompVersions, setLowcoderCompVersions] = useState(['latest']);
   const {
-    themeList,
-    defaultTheme,
-    themeId,
-    maxWidth,
     title,
     description,
     icon,
     category,
     showHeaderInPublic,
-    preventAppStylesOverwriting,
     lowcoderCompVersion,
   } = props;
+  
+  useEffect(() => {
+    setLowcoderCompVersions([
+      'latest',
+      ...Object.keys(lowcoderCompsMeta?.versions || []).reverse()
+    ])
+  }, [lowcoderCompsMeta])
+
+  return (
+    <>
+      <BaseSection
+        name={trans("appSetting.title")}
+        width={288}
+        noMargin
+        style={{
+          borderTop: "1px solid #e1e3eb",
+          backgroundColor: "#fff",
+        }}
+      >
+        <DivStyled>
+          {title.propertyView({
+            label: trans("appSetting.appTitle"),
+            placeholder: trans("appSetting.appTitle")
+          })}
+          {description.propertyView({
+            label: trans("appSetting.appDescription"),
+            placeholder: trans("appSetting.appDescription")
+          })}
+          {category.propertyView({
+            label: trans("appSetting.appCategory"),
+          })}
+          <div className="app-icon">
+            {icon.propertyView({
+              label: trans("icon"),
+              tooltip: trans("aggregation.iconTooltip"),
+            })}
+          </div>
+          <div style={{ margin: '20px 0'}}>
+            {showHeaderInPublic.propertyView({
+              label: trans("appSetting.showPublicHeader"),
+            })}
+          </div>
+        </DivStyled>
+      </BaseSection>
+      <BaseSection
+        name={"Lowcoder Comps"}
+        width={288}
+        noMargin
+        style={{
+          borderTop: "1px solid #e1e3eb",
+          backgroundColor: "#fff",
+        }}
+      >
+        <DivStyled>
+          <Dropdown
+            defaultValue={lowcoderCompVersion.getView()}
+            placeholder={'Select version'}
+            options={
+              lowcoderCompVersions.map(version => ({label: version, value: version}))
+            }
+            label={'Current Version'}
+            placement="bottom"
+            onChange={async (value) => {
+              await getPromiseAfterDispatch(
+                lowcoderCompVersion.dispatch,
+                lowcoderCompVersion.changeValueAction(value), {
+                  autoHandleAfterReduce: true,
+                }
+              )
+              setTimeout(() => {
+                window.location.reload();
+              }, 1000);
+            }}
+          />
+        </DivStyled>
+      </BaseSection>
+      <BaseSection
+        name={"Shortcuts"}
+        width={288}
+        noMargin
+        style={{
+          borderTop: "1px solid #e1e3eb",
+          backgroundColor: "#fff",
+        }}
+      >
+        <DivStyled>
+          {props.customShortcuts.getPropertyView()}
+        </DivStyled>
+      </BaseSection>
+    </>
+  );
+}
+
+function AppCanvasSettingsModal(props: ChildrenInstance) {
+  const {
+    themeList,
+    defaultTheme,
+    themeId,
+    preventAppStylesOverwriting,
+    maxWidth,
+    gridColumns,
+    gridRowHeight,
+    gridRowCount,
+    gridPaddingX,
+    gridPaddingY,
+    gridBg,
+    gridBgImage,
+    gridBgImageRepeat,
+    gridBgImageSize,
+    gridBgImagePosition,
+    gridBgImageOrigin,
+  } = props;
 
   const THEME_OPTIONS = themeList?.map((theme) => ({
     label: theme.name,
@@ -230,14 +374,6 @@ function AppSettingsModal(props: ChildrenInstance) {
       themeId.dispatchChangeValueAction(themeWithDefault);
     }
   }, [themeWithDefault]);
-  
-  useEffect(() => {
-    setLowcoderCompVersions([
-      'latest',
-      ...Object.keys(lowcoderCompsMeta?.versions || []).reverse()
-    ])
-  }, [lowcoderCompsMeta])
-
 
   const DropdownItem = (params: { value: string }) => {
     const themeItem = themeList.find((theme) => theme.id === params.value);
@@ -252,104 +388,124 @@ function AppSettingsModal(props: ChildrenInstance) {
       </ItemSpan>
     );
   };
+
   return (
-    <SettingsStyled>
-      <Title>{TITLE}</Title>
-      <DivStyled>
-        {title.propertyView({
-          label: trans("appSetting.appTitle"),
-          placeholder: trans("appSetting.appTitle")
-        })}
-        {description.propertyView({
-          label: trans("appSetting.appDescription"),
-          placeholder: trans("appSetting.appDescription")
-        })}
-        {category.propertyView({
-          label: trans("appSetting.appCategory"),
-        })}
-        <div className="app-icon">
-          {icon.propertyView({
-            label: trans("icon"),
-            tooltip: trans("aggregation.iconTooltip"),
+    <>
+      <BaseSection
+        name={"Theme Settings"}
+        width={288}
+        noMargin
+        style={{
+          borderTop: "1px solid #e1e3eb",
+          backgroundColor: "#fff",
+        }}
+      >
+        <DivStyled>
+          <Dropdown
+            defaultValue={
+              themeWithDefault === ""
+                ? undefined
+                : themeWithDefault === DEFAULT_THEMEID
+                ? defaultTheme || undefined
+                : themeWithDefault
+            }
+            placeholder={trans("appSetting.themeSettingDefault")}
+            options={THEME_OPTIONS}
+            label={trans("appSetting.themeSetting")}
+            placement="bottom"
+            itemNode={(value) => <DropdownItem value={value} />}
+            preNode={() => (
+              <>
+                <CreateDiv onClick={() => window.open(THEME_SETTING)}>
+                  <StyledAddIcon />
+                  {trans("appSetting.themeCreate")}
+                </CreateDiv>
+                <DividerStyled />
+              </>
+            )}
+            allowClear
+            onChange={(value) => {
+              themeId.dispatchChangeValueAction(
+                value === defaultTheme ? DEFAULT_THEMEID : value || ""
+              );
+            }}
+          />
+          <div style={{ margin: '20px 0'}}>
+            {preventAppStylesOverwriting.propertyView({
+              label: trans("prop.preventOverwriting"),
+            })}
+          </div>
+        </DivStyled>
+      </BaseSection>
+      <BaseSection
+        name={trans("appSetting.canvas")}
+        width={288}
+        noMargin
+        style={{
+          borderTop: "1px solid #e1e3eb",
+          backgroundColor: "#fff",
+        }}
+      >
+        <DivStyled>
+          {maxWidth.propertyView({
+            dropdownLabel: trans("appSetting.canvasMaxWidth"),
+            inputLabel: trans("appSetting.userDefinedMaxWidth"),
+            inputPlaceholder: trans("appSetting.inputUserDefinedPxValue"),
+            placement: "bottom",
+            min: 350,
+            lastNode: <span>{trans("appSetting.maxWidthTip")}</span>,
           })}
-        </div>
-        <div style={{ margin: '20px 0'}}>
-          {showHeaderInPublic.propertyView({
-            label: trans("appSetting.showPublicHeader"),
+          {gridColumns.propertyView({
+            label: trans("appSetting.gridColumns"),
+            placeholder: '24',
           })}
-        </div>
-        {maxWidth.propertyView({
-          dropdownLabel: trans("appSetting.canvasMaxWidth"),
-          inputLabel: trans("appSetting.userDefinedMaxWidth"),
-          inputPlaceholder: trans("appSetting.inputUserDefinedPxValue"),
-          placement: "bottom",
-          min: 350,
-          lastNode: <span>{trans("appSetting.maxWidthTip")}</span>,
-        })}
-        <Dropdown
-          defaultValue={
-            themeWithDefault === ""
-              ? undefined
-              : themeWithDefault === DEFAULT_THEMEID
-              ? defaultTheme || undefined
-              : themeWithDefault
-          }
-          placeholder={trans("appSetting.themeSettingDefault")}
-          options={THEME_OPTIONS}
-          label={trans("appSetting.themeSetting")}
-          placement="bottom"
-          itemNode={(value) => <DropdownItem value={value} />}
-          preNode={() => (
-            <>
-              <CreateDiv onClick={() => window.open(THEME_SETTING)}>
-                <StyledAddIcon />
-                {trans("appSetting.themeCreate")}
-              </CreateDiv>
-              <DividerStyled />
-            </>
-          )}
-          allowClear
-          onChange={(value) => {
-            themeId.dispatchChangeValueAction(
-              value === defaultTheme ? DEFAULT_THEMEID : value || ""
-            );
-          }}
-        />
-        <div style={{ margin: '20px 0'}}>
-          {preventAppStylesOverwriting.propertyView({
-            label: trans("prop.preventOverwriting"),
+          {gridRowHeight.propertyView({
+            label: trans("appSetting.gridRowHeight"),
+            placeholder: '8',
           })}
-        </div>
-      </DivStyled>
-      <DividerStyled />
-      <DivStyled>
-        <Dropdown
-          defaultValue={lowcoderCompVersion.getView()}
-          placeholder={'Select version'}
-          options={
-            lowcoderCompVersions.map(version => ({label: version, value: version}))
-          }
-          label={'Lowcoder Comps Version'}
-          placement="bottom"
-          onChange={async (value) => {
-            await getPromiseAfterDispatch(
-              lowcoderCompVersion.dispatch,
-              lowcoderCompVersion.changeValueAction(value), {
-                autoHandleAfterReduce: true,
-              }
-            )
-            setTimeout(() => {
-              window.location.reload();
-            }, 1000);
-          }}
-        />
-      </DivStyled>
-      <DividerStyled />
-      {props.customShortcuts.getPropertyView()}
-    </SettingsStyled>
+          {gridRowCount.propertyView({
+            label: trans("appSetting.gridRowCount"),
+            placeholder: 'Infinity',
+          })}
+          {gridPaddingX.propertyView({
+            label: trans("appSetting.gridPaddingX"),
+            placeholder: '20',
+          })}
+          {gridPaddingY.propertyView({
+            label: trans("appSetting.gridPaddingY"),
+            placeholder: '20',
+          })}
+          {gridBg.propertyView({
+            label: trans("style.background"),
+            allowGradient: true,
+          })}
+          {gridBgImage.propertyView({
+            label: trans("appSetting.gridBgImage"),
+            placeholder: '',
+          })}
+          {gridBgImageRepeat.propertyView({
+            label: trans("appSetting.gridBgImageRepeat"),
+            placeholder: 'no-repeat',
+          })}
+          {gridBgImageSize.propertyView({
+            label: trans("appSetting.gridBgImageSize"),
+            placeholder: 'cover',
+          })}
+          {gridBgImagePosition.propertyView({
+            label: trans("appSetting.gridBgImagePosition"),
+            placeholder: 'center',
+          })}
+          {gridBgImageOrigin.propertyView({
+            label: trans("appSetting.gridBgImageOrigin"),
+            placeholder: 'no-padding',
+          })}
+        </DivStyled>
+      </BaseSection>
+    </>
   );
 }
 
+
 export const AppSettingsComp = new MultiCompBuilder(childrenMap, (props) => {
   return {
     ...props,
@@ -357,8 +513,12 @@ export const AppSettingsComp = new MultiCompBuilder(childrenMap, (props) => {
   };
 })
   .setPropertyViewFn((children) => {
+    const { settingType } = useContext(AppSettingContext);
     const themeList = useSelector(getThemeList) || [];
     const defaultTheme = (useSelector(getDefaultTheme) || "").toString();
-    return <AppSettingsModal {...children} themeList={themeList} defaultTheme={defaultTheme} />;
+
+    return settingType === 'canvas'
+      ? <AppCanvasSettingsModal {...children} themeList={themeList} defaultTheme={defaultTheme} />
+      : <AppGeneralSettingsModal {...children} themeList={themeList} defaultTheme={defaultTheme} />;
   })
   .build();
diff --git a/client/packages/lowcoder/src/comps/comps/autoCompleteComp/autoCompleteComp.tsx b/client/packages/lowcoder/src/comps/comps/autoCompleteComp/autoCompleteComp.tsx
index e97769184..4ab79a2ea 100644
--- a/client/packages/lowcoder/src/comps/comps/autoCompleteComp/autoCompleteComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/autoCompleteComp/autoCompleteComp.tsx
@@ -285,6 +285,7 @@ let AutoCompleteCompBase = (function () {
       labelStyle: props.labelStyle,
       inputFieldStyle:props.inputFieldStyle,
       animationStyle: props.animationStyle,
+      showValidationWhenEmpty: props.showValidationWhenEmpty,
       ...validateState,
     });
   })
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx
index e2517d306..f83b39939 100644
--- a/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/buttonComp/buttonCompConstants.tsx
@@ -27,7 +27,7 @@ export function getButtonStyle(buttonStyle: ButtonStyleType) {
         font-style: ${buttonStyle.fontStyle};
         text-transform:${buttonStyle.textTransform};
         text-decoration:${buttonStyle.textDecoration};
-        background-color: ${buttonStyle.background};
+        background: ${buttonStyle.background};
         border-radius: ${buttonStyle.radius};
         margin: ${buttonStyle.margin};
         padding: ${buttonStyle.padding};
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx
index 11e9bd402..1ce2b491e 100644
--- a/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/buttonComp/dropdownComp.tsx
@@ -52,7 +52,7 @@ const LeftButtonWrapper = styled.div<{ $buttonStyle: DropdownStyleType }>`
       ${(props) => `text-transform: ${props.$buttonStyle.textTransform};`}
       ${(props) => `font-weight: ${props.$buttonStyle.textWeight};`}
     }
-    ${(props) => `background-color: ${props.$buttonStyle.background};`}
+    ${(props) => `background: ${props.$buttonStyle.background};`}
     ${(props) => `color: ${props.$buttonStyle.text};`}
     ${(props) => `padding: ${props.$buttonStyle.padding};`}
     ${(props) => `font-size: ${props.$buttonStyle.textSize};`}
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/floatButtonComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/floatButtonComp.tsx
index 3ae02259b..223650ef4 100644
--- a/client/packages/lowcoder/src/comps/comps/buttonComp/floatButtonComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/buttonComp/floatButtonComp.tsx
@@ -37,7 +37,7 @@ const Wrapper = styled.div<{ $badgeStyle: BadgeStyleType, $style: FloatButtonSty
         inset-block-end: -8px;
     }
     .ant-float-btn-primary .ant-float-btn-body {
-    background-color: ${(props) => props.$style.background};
+    background: ${(props) => props.$style.background};
     border: ${(props) => props.$style.border};
     border-style: ${(props) => props.$style.borderStyle};
     border-width: ${(props) => props.$style.borderWidth};
diff --git a/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx b/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx
index a1f71169e..31ed59809 100644
--- a/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/buttonComp/linkComp.tsx
@@ -41,7 +41,7 @@ const Link = styled(Button)<{
     border-radius:${props.$style.radius ? props.$style.radius:'0px'};
     text-transform:${props.$style.textTransform ? props.$style.textTransform:''};
     text-decoration:${props.$style.textDecoration ? props.$style.textDecoration:''} !important;
-    background-color: ${props.$style.background};
+    background: ${props.$style.background};
     &:hover {
       color: ${props.$style.hoverText} !important;
     }
diff --git a/client/packages/lowcoder/src/comps/comps/carouselComp.tsx b/client/packages/lowcoder/src/comps/comps/carouselComp.tsx
index 3db1ccfb7..e4b5f26d0 100644
--- a/client/packages/lowcoder/src/comps/comps/carouselComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/carouselComp.tsx
@@ -29,7 +29,7 @@ const Container = styled.div<{$bg: string; $animationStyle:AnimationStyleType}>`
   &,
   .ant-carousel {
     height: 100%;
-    background-color: ${(props) => props.$bg};
+    background: ${(props) => props.$bg};
     ${props=>props.$animationStyle}
   }
 `;
diff --git a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
index df3fe5011..fa602fa6b 100644
--- a/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/columnLayout/columnLayout.tsx
@@ -41,6 +41,7 @@ import { EditorContext } from "comps/editorState";
 import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils";
 import { DisabledContext } from "comps/generators/uiCompBuilder";
 import SliderControl from "@lowcoder-ee/comps/controls/sliderControl";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const ContainWrapper = styled.div<{
   $style: ContainerStyleType & {
@@ -57,13 +58,13 @@ const ContainWrapper = styled.div<{
   column-gap: ${(props) => props.$style?.columnGap};
   row-gap: ${(props) => props.$style?.rowGap};
 
-  background-color: ${(props) => props.$style?.background} !important;
   border-radius: ${(props) => props.$style?.radius};
   border-width: ${(props) => props.$style?.borderWidth};
   border-color: ${(props) => props.$style?.border};
   border-style: ${(props) => props.$style?.borderStyle};
   margin: ${(props) => props.$style?.margin};
   padding: ${(props) => props.$style?.padding};
+  ${props => props.$style && getBackgroundStyle(props.$style)}
 `;
 
 const ColWrapper = styled(Col)<{
@@ -73,13 +74,13 @@ const ColWrapper = styled(Col)<{
 }>`
   > div {
     height: ${(props) => props.$matchColumnsHeight ? `calc(100% - ${props.$style?.padding || 0} - ${props.$style?.padding || 0})` : 'auto'};
-    background-color: ${(props) => props.$style?.background} !important;
     border-radius: ${(props) => props.$style?.radius};
     border-width: ${(props) => props.$style?.borderWidth};
     border-color: ${(props) => props.$style?.border};
     border-style: ${(props) => props.$style?.borderStyle};
     margin: ${(props) => props.$style?.margin};
     padding: ${(props) => props.$style?.padding};
+    ${props => props.$style && getBackgroundStyle(props.$style)}
   }
 `;
 
diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx
index 9e03ccb36..f9a14575f 100644
--- a/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/containerComp/cardComp.tsx
@@ -20,6 +20,7 @@ import { ButtonEventHandlerControl, CardEventHandlerControl, clickEvent, refresh
 import { optionsControl } from "comps/controls/optionsControl";
 import { dropdownControl } from "comps/controls/dropdownControl";
 import { styleControl } from "comps/controls/styleControl";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const { Meta } = Card;
 
@@ -34,7 +35,6 @@ const Wrapper = styled.div<{
   height: 100%;
   width: 100%;
   .ant-card-small >.ant-card-head {
-    background-color: ${props => props.$headerStyle?.background} !important;
     border: ${props => props.$headerStyle?.border};
     border-style: ${props => props.$headerStyle?.borderStyle};
     border-width: ${props => props.$headerStyle?.borderWidth};
@@ -49,6 +49,7 @@ const Wrapper = styled.div<{
     rotate: ${props => props.$headerStyle?.rotation};
     margin: ${props => props.$headerStyle?.margin};
     padding: ${props => props.$headerStyle?.padding};
+    ${props => getBackgroundStyle(props.$headerStyle)}
   }
   .ant-card-head-title{
     font-size: ${props => props.$headerStyle?.textSize};
@@ -61,10 +62,9 @@ const Wrapper = styled.div<{
     border-inline-end: 1px solid ${props => props.$style?.border};
   }
   .ant-card .ant-card-actions {
-    background-color: ${props => props.$style?.background};
+    ${props => props.$style && getBackgroundStyle(props.$style)}
   }
   .ant-card .ant-card-body {
-   background-color: ${props => props.$bodyStyle?.background} !important;
     border: ${props => props.$bodyStyle?.border};
     border-style: ${props => props.$bodyStyle?.borderStyle};
     border-width: ${props => props.$bodyStyle?.borderWidth};
@@ -72,6 +72,7 @@ const Wrapper = styled.div<{
     rotate: ${props => props.$bodyStyle?.rotation};
     margin: ${props => props.$bodyStyle?.margin};
     padding: ${props => props.$bodyStyle?.padding};
+    ${props => getBackgroundStyle(props.$bodyStyle)}
   }
   .ant-card {
     display: flex;
@@ -79,13 +80,13 @@ const Wrapper = styled.div<{
     justify-content: space-between;
     margin: ${props => props.$style?.margin};
     padding: ${props => props.$style?.padding};
-    background-color: ${props => props.$style?.background};
     border: ${props => props.$style?.border};
     rotate: ${props => props.$style?.rotation};
     border-style: ${props => props.$style?.borderStyle};
     border-radius: ${props => props.$style?.radius};
     border-width: ${props => props.$style?.borderWidth};
     box-shadow: ${props=>`${props.$style?.boxShadow} ${props.$style?.boxShadowColor}`};
+    ${props => props.$style && getBackgroundStyle(props.$style)}
     ${props=>props.$animationStyle}
   }
   .ant-card-body {
diff --git a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx
index ce9ddac21..1e47c5703 100644
--- a/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/containerComp/containerView.tsx
@@ -22,6 +22,7 @@ import {
   calcRowCount,
   calcRowHeight,
   DEFAULT_GRID_COLUMNS,
+  DEFAULT_ROW_COUNT,
   DEFAULT_ROW_HEIGHT,
 } from "layout/calculateUtils";
 import _, { isEqual } from "lodash";
@@ -342,20 +343,20 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => {
   const horizontalGridCells = props.horizontalGridCells ? String(props.horizontalGridCells) : undefined;
   const currentTheme = useContext(ThemeContext)?.theme;
   const [currentRowCount, setRowCount] = useState(rowCount || Infinity);
-  const [currentRowHeight, setRowHeight] = useState(DEFAULT_ROW_HEIGHT);
+  const [currentRowHeight, setRowHeight] = useState(positionParams.rowHeight || DEFAULT_ROW_HEIGHT);
   const editorState = useContext(EditorContext);
   const { readOnly } = useContext(ExternalEditorContext);
+  const appSettingsComp = editorState.getAppSettingsComp().getView();
+
+  const maxWidth = useMemo(() => appSettingsComp.maxWidth, [appSettingsComp.maxWidth]);
 
   // Falk: TODO: Here we can define the inner grid columns dynamically
-  //Added By Aqib Mirza
   const defaultGrid = useMemo(() => {
-    return horizontalGridCells ||
-    currentTheme?.gridColumns ||
-    defaultTheme?.gridColumns ||
-    "12";
-  }, [horizontalGridCells, currentTheme?.gridColumns, defaultTheme?.gridColumns]);
+    return horizontalGridCells
+      || String(positionParams.cols)
+      || String(DEFAULT_GRID_COLUMNS);
+  }, [horizontalGridCells, positionParams.cols]);
 
-  /////////////////////
   const isDroppable =
     useContext(IsDroppable) && (_.isNil(props.isDroppable) || props.isDroppable) && !readOnly;
   const isDraggable = !readOnly && (_.isNil(props.isDraggable) || props.isDraggable);
@@ -479,14 +480,12 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => {
 
   useEffect(() => {
     if (!isRowCountLocked) {
-      setRowHeight(DEFAULT_ROW_HEIGHT);
+      setRowHeight(positionParams.rowHeight || DEFAULT_ROW_HEIGHT);
       setRowCount(Infinity);
       onRowCountChange?.(0);
     }
   }, [isRowCountLocked, onRowCountChange]);
 
-  const maxWidth = editorState.getAppSettings().maxWidth;
-
   // log.info("rowCount:", currentRowCount, "rowHeight:", currentRowHeight);
 
   return (
@@ -543,6 +542,7 @@ export const InnerGrid = React.memo((props: ViewPropsWithSelect) => {
       onResizeStop={() => editorState.setDragging(false)}
       margin={[0, 0]}
       containerPadding={props.containerPadding}
+      fixedRowCount={props.emptyRows !== DEFAULT_ROW_COUNT}
       emptyRows={props.emptyRows}
       maxRows={currentRowCount}
       rowHeight={currentRowHeight}
diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx
index 89ff6a611..62f84bf84 100644
--- a/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/dateComp/dateComp.tsx
@@ -56,6 +56,7 @@ import { timeZoneOptions } from "./timeZone";
 const EventOptions = [changeEvent, focusEvent, blurEvent] as const;
 
 const validationChildren = {
+  showValidationWhenEmpty: BoolControl,
   required: BoolControl,
   minDate: StringControl,
   maxDate: StringControl,
@@ -224,6 +225,7 @@ export const datePickerControl = new UICompBuilder(childrenMap, (props) => {
         suffixIcon={hasIcon(props.suffixIcon) && props.suffixIcon}
       />
     ),
+    showValidationWhenEmpty: props.showValidationWhenEmpty,
     ...validate(props),
   });
 })
@@ -247,6 +249,9 @@ export const datePickerControl = new UICompBuilder(childrenMap, (props) => {
         {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (
           <><Section name={sectionNames.validation}>
             {requiredPropertyView(children)}
+            {children.showValidationWhenEmpty.propertyView({
+              label: trans("prop.showEmptyValidation")
+            })}
             {dateValidationFields(children)}
             {timeValidationFields(children)}
             {children.customRule.propertyView({})}
@@ -381,6 +386,7 @@ export const dateRangeControl = (function () {
       children: children,
       inputFieldStyle:props.inputFieldStyle,
       onMouseDown: (e) => e.stopPropagation(),
+      showValidationWhenEmpty: props.showValidationWhenEmpty,
       ...(startResult.validateStatus !== "success"
         ? startResult
         : endResult.validateStatus !== "success"
@@ -413,6 +419,9 @@ export const dateRangeControl = (function () {
           {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (
             <><Section name={sectionNames.validation}>
               {requiredPropertyView(children)}
+              {children.showValidationWhenEmpty.propertyView({
+                label: trans("prop.showEmptyValidation")
+              })}
               {dateValidationFields(children)}
               {timeValidationFields(children)}
               {children.customRule.propertyView({})}
diff --git a/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx b/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx
index e34ae15a8..4d9f0a6e5 100644
--- a/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/dateComp/timeComp.tsx
@@ -59,6 +59,7 @@ import { timeZoneOptions } from "./timeZone";
 const EventOptions = [changeEvent, focusEvent, blurEvent] as const;
 
 const validationChildren = {
+  showValidationWhenEmpty: BoolControl,
   required: BoolControl,
   minTime: StringControl,
   maxTime: StringControl,
@@ -116,7 +117,7 @@ function validate(
   }
 
   const current = dayjs(props.value.value, TimeParser);
-  if (props.required && !current.isValid()) {
+  if (props.required && (!Boolean(props.value.value) || !current.isValid())) {
     return { validateStatus: "error", help: trans("prop.required") };
   }
   return { validateStatus: "success" };
@@ -197,6 +198,7 @@ export const timePickerControl = new UICompBuilder(childrenMap, (props) => {
         onBlur={() => props.onEvent("blur")}
         suffixIcon={hasIcon(props.suffixIcon) && props.suffixIcon}      />
     ),
+    showValidationWhenEmpty: props.showValidationWhenEmpty,
     ...validate(props),
   });
 })
@@ -217,6 +219,9 @@ export const timePickerControl = new UICompBuilder(childrenMap, (props) => {
       {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (
         <><Section name={sectionNames.validation}>
           {requiredPropertyView(children)}
+          {children.showValidationWhenEmpty.propertyView({
+            label: trans("prop.showEmptyValidation")
+          })}
           {minTimePropertyView(children)}
           {maxTimePropertyView(children)}
           {children.customRule.propertyView({})}
@@ -338,6 +343,7 @@ export const timeRangeControl = (function () {
       animationStyle:props.animationStyle,
       children: children,
       onMouseDown: (e) => e.stopPropagation(),
+      showValidationWhenEmpty: props.showValidationWhenEmpty,
       ...(startResult.validateStatus !== "success"
         ? startResult
         : endResult.validateStatus !== "success"
@@ -366,6 +372,9 @@ export const timeRangeControl = (function () {
         {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (
           <><Section name={sectionNames.validation}>
             {requiredPropertyView(children)}
+            {children.showValidationWhenEmpty.propertyView({
+              label: trans("prop.showEmptyValidation")
+            })}
             {minTimePropertyView(children)}
             {maxTimePropertyView(children)}
             {children.customRule.propertyView({})}
diff --git a/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx b/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx
index 908721570..5ce11399e 100644
--- a/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/fileComp/fileComp.tsx
@@ -156,7 +156,7 @@ const getStyle = (style: FileStyleType) => {
 
     .ant-btn:not(:disabled) {
       border-color: ${style.border};
-      background-color: ${style.background};
+      background: ${style.background};
       color: ${style.text};
 
       &:hover,
diff --git a/client/packages/lowcoder/src/comps/comps/fileViewerComp.tsx b/client/packages/lowcoder/src/comps/comps/fileViewerComp.tsx
index 4b53dea3a..1c553bca9 100644
--- a/client/packages/lowcoder/src/comps/comps/fileViewerComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/fileViewerComp.tsx
@@ -22,7 +22,7 @@ const getStyle = (style: FileViewerStyleType) => {
     padding: ${style.padding};
 
     overflow: hidden;
-    background-color: ${style.background};
+    background: ${style.background};
     border: ${(props) => (style.borderWidth ? style.borderWidth : "1px")} solid ${style.border};
     border-radius: calc(min(${style.radius}, 20px));
   `;
diff --git a/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx b/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx
index cc655730d..4b0f769aa 100644
--- a/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/gridLayoutComp/canvasView.tsx
@@ -1,6 +1,6 @@
 import { EditorContext } from "comps/editorState";
 import { EditorContainer } from "pages/common/styledComponent";
-import React, { Profiler, useContext, useRef, useState } from "react";
+import React, { Profiler, useContext, useMemo, useRef, useState } from "react";
 import styled from "styled-components";
 import { profilerCallback } from "util/cacheUtils";
 import {
@@ -18,17 +18,36 @@ import { ThemeContext } from "comps/utils/themeContext";
 import { checkIsMobile } from "util/commonUtils";
 import { CanvasContainerID } from "constants/domLocators";
 import { CNRootContainer } from "constants/styleSelectors";
-import { ScrollBar } from "lowcoder-design";
+import { isValidColor, isValidGradient, ScrollBar } from "lowcoder-design";
 import { defaultTheme } from "@lowcoder-ee/constants/themeConstants";
 import { isEqual } from "lodash";
+import { DEFAULT_GRID_COLUMNS, DEFAULT_ROW_COUNT, DEFAULT_ROW_HEIGHT } from "@lowcoder-ee/layout/calculateUtils";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
-// min-height: 100vh;
-
-const UICompContainer = styled.div<{ $maxWidth?: number; readOnly?: boolean; $bgColor: string }>`
-  height: 100%;
+const UICompContainer = styled.div<{
+  $maxWidth?: number;
+  $rowCount?: number;
+  readOnly?: boolean;
+  $bgColor: string;
+  $bgImage?: string;
+  $bgImageSize?: string;
+  $bgImageRepeat?: string;
+  $bgImageOrigin?: string;
+  $bgImagePosition?: string;
+}>`
+  height: auto;
+  min-height: ${props => props.$rowCount === Infinity ? '100%' : 'auto'};
   margin: 0 auto;
   max-width: ${(props) => props.$maxWidth || 1600}px;
-  background-color: ${(props) => props.$bgColor};
+  
+  ${props => getBackgroundStyle({
+    background: props.$bgColor,
+    backgroundImage: props.$bgImage,
+    backgroundImageSize: props.$bgImageSize,
+    backgroundImageRepeat: props.$bgImageRepeat,
+    backgroundImageOrigin: props.$bgImageOrigin,
+    backgroundImagePosition: props.$bgImagePosition,
+  })}
 `;
 
 // modal/drawer container
@@ -74,55 +93,183 @@ function getDragSelectedNames(
 const EmptySet = new Set<string>();
 
 export const CanvasView = React.memo((props: ContainerBaseProps) => {
+  const currentTheme = useContext(ThemeContext)?.theme;
+  const isDefaultTheme = useContext(ThemeContext)?.themeId === 'default-theme-id';
+  const isPreviewTheme = useContext(ThemeContext)?.themeId === 'preview-theme';
   const editorState = useContext(EditorContext);
   const [dragSelectedComps, setDragSelectedComp] = useState(EmptySet);
   const scrollContainerRef = useRef(null);
-
+  const appSettings = editorState.getAppSettings();
   const maxWidthFromHook = useMaxWidth();
-  const maxWidth = editorState.getAppSettings().maxWidth ?? maxWidthFromHook;
+
+  const maxWidth = useMemo(
+    () => appSettings.maxWidth ?? maxWidthFromHook,
+    [appSettings, maxWidthFromHook]
+  );
+
+  const preventStylesOverwriting = useMemo(
+    () => appSettings.preventAppStylesOverwriting,
+    [appSettings]
+  );
+
   const isMobile = checkIsMobile(maxWidth);
-  const defaultContainerPadding = isMobile ? DEFAULT_MOBILE_PADDING : DEFAULT_CONTAINER_PADDING;
+  // const defaultContainerPadding = isMobile ? DEFAULT_MOBILE_PADDING : DEFAULT_CONTAINER_PADDING;
 
   const externalState = useContext(ExternalEditorContext);
   const {
     readOnly,
     appType,
     rootContainerExtraHeight = DEFAULT_EXTRA_HEIGHT,
-    rootContainerPadding = defaultContainerPadding,
+    rootContainerPadding,
     rootContainerOverflow,
   } = externalState;
 
   const isModule = appType === AppTypeEnum.Module;
-  const bgColor = (useContext(ThemeContext)?.theme || defaultTheme).canvas;
 
-  // Added By Aqib Mirza
-  const defaultGrid =
-    useContext(ThemeContext)?.theme?.gridColumns ||
-    defaultTheme?.gridColumns ||
-    "24";
+  const bgColor = useMemo(
+    () => {
+      if (isPreviewTheme) return currentTheme?.canvas ?? defaultTheme.canvas;
+
+      const themeGridBgColor = preventStylesOverwriting ? undefined : currentTheme?.canvas;
+      return themeGridBgColor || appSettings.gridBg || defaultTheme.canvas;
+    },
+    [preventStylesOverwriting, appSettings, currentTheme, defaultTheme]
+  );
+
+  const bgImage = useMemo(
+    () => {
+      if (isPreviewTheme) return currentTheme?.gridBgImage;
+
+      const themeGridBgImage = preventStylesOverwriting ? undefined : currentTheme?.gridBgImage;
+      return themeGridBgImage || appSettings.gridBgImage;
+    },
+    [preventStylesOverwriting, appSettings, currentTheme],
+  );
+
+  const bgImageRepeat = useMemo(
+    () => {
+      if (isPreviewTheme) return currentTheme?.gridBgImageRepeat ?? defaultTheme.gridBgImageRepeat;
+
+      const themeGridBgImageRepeat = preventStylesOverwriting ? undefined : currentTheme?.gridBgImageRepeat;
+      return themeGridBgImageRepeat || appSettings.gridBgImageRepeat || defaultTheme?.gridBgImageRepeat;
+    },
+    [preventStylesOverwriting, appSettings, currentTheme, defaultTheme],
+  );
+  const bgImageSize = useMemo(
+    () => {
+      if (isPreviewTheme) return currentTheme?.gridBgImageSize ?? defaultTheme.gridBgImageSize;
+
+      const themeGridBgImageSize = preventStylesOverwriting ? undefined : currentTheme?.gridBgImageSize;
+      return themeGridBgImageSize || appSettings.gridBgImageSize || defaultTheme?.gridBgImageSize;
+    },
+    [preventStylesOverwriting, appSettings, currentTheme, defaultTheme],
+  );
+  const bgImagePosition = useMemo(
+    () => {
+      if (isPreviewTheme) return currentTheme?.gridBgImagePosition ?? defaultTheme.gridBgImagePosition;
+
+      const themeGridBgImagePosition = preventStylesOverwriting ? undefined : currentTheme?.gridBgImagePosition;
+      return themeGridBgImagePosition || appSettings.gridBgImagePosition || defaultTheme?.gridBgImagePosition;
+    },
+    [preventStylesOverwriting, appSettings, currentTheme, defaultTheme],
+  );
+  const bgImageOrigin = useMemo(
+    () => {
+      if (isPreviewTheme) return currentTheme?.gridBgImageOrigin ?? defaultTheme.gridBgImageOrigin;
+
+      const themeGridBgImageOrigin = preventStylesOverwriting ? undefined : currentTheme?.gridBgImageOrigin;
+      return themeGridBgImageOrigin || appSettings.gridBgImageOrigin || defaultTheme?.gridBgImageOrigin;
+    },
+    [preventStylesOverwriting, appSettings, currentTheme, defaultTheme],
+  );
+
+  const defaultGrid = useMemo(() => {
+    if (isPreviewTheme) return currentTheme?.gridColumns ?? defaultTheme.gridColumns ?? String(DEFAULT_GRID_COLUMNS);
+
+    const themeGridColumns = preventStylesOverwriting ? undefined : currentTheme?.gridColumns;
+    return themeGridColumns
+      || String(appSettings?.gridColumns)
+      || defaultTheme?.gridColumns
+      || String(DEFAULT_GRID_COLUMNS);
+  }, [preventStylesOverwriting, appSettings, currentTheme, defaultTheme]);
+
+  const defaultRowHeight = useMemo(() => {
+    if (isPreviewTheme) return currentTheme?.gridRowHeight ?? defaultTheme.gridRowHeight ?? String(DEFAULT_ROW_HEIGHT);
+
+    const themeGridRowHeight = preventStylesOverwriting ? undefined : currentTheme?.gridRowHeight;
+    return themeGridRowHeight
+      || String(appSettings?.gridRowHeight)
+      || defaultTheme?.gridRowHeight
+      || String(DEFAULT_ROW_HEIGHT);
+  }, [preventStylesOverwriting, appSettings, currentTheme, defaultTheme]);
 
-  const positionParams = {
+  const defaultRowCount = useMemo(() => {
+    if (isPreviewTheme) return currentTheme?.gridRowCount ?? defaultTheme.gridRowCount;
+
+    const themeGridRowCount = preventStylesOverwriting ? undefined : currentTheme?.gridRowCount;
+    return themeGridRowCount
+      || appSettings?.gridRowCount
+      || defaultTheme?.gridRowCount
+      || DEFAULT_ROW_COUNT;
+  }, [preventStylesOverwriting, appSettings, currentTheme, defaultTheme]);
+
+  const defaultContainerPadding: [number, number] = useMemo(() => {
+    const DEFAULT_PADDING = isMobile ? DEFAULT_MOBILE_PADDING : DEFAULT_CONTAINER_PADDING;
+    
+    if (isPreviewTheme) {
+      return [
+        currentTheme?.gridPaddingX ?? defaultTheme.gridPaddingX ?? DEFAULT_PADDING[0],
+        currentTheme?.gridPaddingY ?? defaultTheme.gridPaddingY ?? DEFAULT_PADDING[1],
+      ];
+    }
+
+    const themeGridPaddingX = preventStylesOverwriting || isDefaultTheme ? undefined : currentTheme?.gridPaddingX;
+    const themeGridPaddingY = preventStylesOverwriting || isDefaultTheme ? undefined : currentTheme?.gridPaddingY;
+
+    let paddingX = themeGridPaddingX ?? appSettings?.gridPaddingX ?? defaultTheme?.gridPaddingX ?? DEFAULT_PADDING[0];
+    let paddingY = themeGridPaddingY ?? appSettings?.gridPaddingY ?? defaultTheme?.gridPaddingY ?? DEFAULT_PADDING[1];
+
+    return [paddingX, paddingY];
+  }, [preventStylesOverwriting, appSettings, isMobile, currentTheme, defaultTheme]);
+
+  const defaultMinHeight = useMemo(() => {
+    return defaultRowCount === DEFAULT_ROW_COUNT
+      ? `calc(100vh - ${TopHeaderHeight} - ${EditorContainerPadding} * 2)`
+      : undefined;
+  }, [defaultRowCount]);
+
+  const positionParams = useMemo(() => ({
     ...props.positionParams,
     cols: parseInt(defaultGrid),
-  };
-  //////////////////////
+    rowHeight: parseInt(defaultRowHeight),
+  }), [props.positionParams, defaultGrid, defaultRowHeight]);
+
   if (readOnly) {
     return (
       <UICompContainer
         $maxWidth={maxWidth}
+        $rowCount={defaultRowCount}
         readOnly={true}
         className={CNRootContainer}
         $bgColor={bgColor}
+        $bgImage={bgImage}
+        $bgImageSize={bgImageSize}
+        $bgImageRepeat={bgImageRepeat}
+        $bgImageOrigin={bgImageOrigin}
+        $bgImagePosition={bgImagePosition}
       >
         <Profiler id="Panel" onRender={profilerCallback}>
           <InnerGrid
-            containerPadding={rootContainerPadding}
+            containerPadding={defaultContainerPadding}
             overflow={rootContainerOverflow}
             {...props}
-            positionParams={positionParams} // Added By Aqib Mirza
+            positionParams={positionParams}
             {...gridLayoutCanvasProps}
             bgColor={bgColor}
             radius="0px"
+            emptyRows={defaultRowCount}
+            minHeight={defaultMinHeight}
+            extraHeight={defaultRowCount === DEFAULT_ROW_COUNT ? rootContainerExtraHeight : undefined }
           />
         </Profiler>
       </UICompContainer>
@@ -132,7 +279,17 @@ export const CanvasView = React.memo((props: ContainerBaseProps) => {
   return (
     <CanvasContainer $maxWidth={maxWidth} id={CanvasContainerID}>
       <EditorContainer ref={scrollContainerRef}>
-        <UICompContainer $maxWidth={maxWidth} className={CNRootContainer} $bgColor={bgColor}>
+        <UICompContainer
+          $maxWidth={maxWidth}
+          $rowCount={defaultRowCount}
+          className={CNRootContainer}
+          $bgColor={bgColor}
+          $bgImage={bgImage}
+          $bgImageSize={bgImageSize}
+          $bgImageRepeat={bgImageRepeat}
+          $bgImageOrigin={bgImageOrigin}
+          $bgImagePosition={bgImagePosition}
+        >
           <DragSelector
             onMouseDown={() => {
               setDragSelectedComp(EmptySet);
@@ -148,18 +305,20 @@ export const CanvasView = React.memo((props: ContainerBaseProps) => {
           >
             <Profiler id="Panel" onRender={profilerCallback}>
               <InnerGrid
-                containerPadding={rootContainerPadding}
-                extraHeight={rootContainerExtraHeight}
+                containerPadding={defaultContainerPadding}
                 overflow={rootContainerOverflow}
                 {...props}
                 {...gridLayoutCanvasProps}
                 dragSelectedComps={dragSelectedComps}
-                minHeight={`calc(100vh - ${TopHeaderHeight} - ${EditorContainerPadding} * 2)`}
                 scrollContainerRef={scrollContainerRef}
                 isDroppable={!isModule}
                 isDraggable={!isModule}
                 enableGridLines
                 bgColor={bgColor}
+                positionParams={positionParams}
+                emptyRows={defaultRowCount}
+                minHeight={defaultMinHeight}
+                extraHeight={defaultRowCount === DEFAULT_ROW_COUNT ? rootContainerExtraHeight : undefined }
               />
             </Profiler>
           </DragSelector>
diff --git a/client/packages/lowcoder/src/comps/comps/iconComp.tsx b/client/packages/lowcoder/src/comps/comps/iconComp.tsx
index ef1076b32..990f8531c 100644
--- a/client/packages/lowcoder/src/comps/comps/iconComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/iconComp.tsx
@@ -37,7 +37,7 @@ const Container = styled.div<{
   display: flex;
   align-items: center;
   justify-content: center;
-${props=>props.$animationStyle}
+  ${props=>props.$animationStyle}
   ${(props) =>
     props.$style &&
     css`
diff --git a/client/packages/lowcoder/src/comps/comps/iframeComp.tsx b/client/packages/lowcoder/src/comps/comps/iframeComp.tsx
index 955ceb1e3..889444883 100644
--- a/client/packages/lowcoder/src/comps/comps/iframeComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/iframeComp.tsx
@@ -30,7 +30,7 @@ ${props=>props.$animationStyle}
     width: 100%;
     height: 100%;
     display: block;
-    background-color: ${(props) => props.$style.background};
+    background: ${(props) => props.$style.background};
   }
 `;
 
diff --git a/client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx b/client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx
index 34d7ad607..89686dbe5 100644
--- a/client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/jsonComp/jsonLottieComp.tsx
@@ -115,7 +115,7 @@ let JsonLottieTmpComp = (function () {
             height: "100%",
             display: "flex",
             justifyContent: "center",
-            backgroundColor: `${props.container.background}`,
+            background: `${props.container.background}`,
             padding: `${props.container.padding}`,
             rotate: props.container.rotation,
           }}
diff --git a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx
index 5390db39b..71503d0c2 100644
--- a/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/listViewComp/listView.tsx
@@ -21,6 +21,7 @@ import { getCurrentItemParams, getData } from "./listViewUtils";
 import { useMergeCompStyles } from "@lowcoder-ee/util/hooks";
 import { childrenToProps } from "@lowcoder-ee/comps/generators/multi";
 import { AnimationStyleType } from "@lowcoder-ee/comps/controls/styleControlConstants";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const ListViewWrapper = styled.div<{ $style: any; $paddingWidth: string,$animationStyle:AnimationStyleType }>`
   height: 100%;
@@ -28,7 +29,7 @@ const ListViewWrapper = styled.div<{ $style: any; $paddingWidth: string,$animati
   border-radius: ${(props) => props.$style.radius};
   padding: 3px ${(props) => props.$paddingWidth};
   rotate: ${(props) => props.$style.rotation};
-  background-color: ${(props) => props.$style.background};
+  ${props => getBackgroundStyle(props.$style)}
   ${props=>props.$animationStyle}
   
 `;
diff --git a/client/packages/lowcoder/src/comps/comps/meetingComp/videobuttonCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/meetingComp/videobuttonCompConstants.tsx
index 2b74fd189..6bdf5c242 100644
--- a/client/packages/lowcoder/src/comps/comps/meetingComp/videobuttonCompConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/meetingComp/videobuttonCompConstants.tsx
@@ -27,7 +27,7 @@ export function getButtonStyle(buttonStyle: any) {
         --antd-wave-shadow-color: ${buttonStyle.border};
         border-color: ${buttonStyle.border};
         color: ${buttonStyle.text};
-        background-color: ${buttonStyle.background};
+        background: ${buttonStyle.background};
         border-radius: ${buttonStyle.radius};
         margin: ${buttonStyle.margin};
         padding: ${buttonStyle.padding};
diff --git a/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx b/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx
index 1f9e10dea..75b483072 100644
--- a/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/navComp/navComp.tsx
@@ -41,7 +41,7 @@ ${props=>props.$animationStyle}
   border-radius: ${(props) =>props.$borderRadius ? props.$borderRadius : '2px'};
   box-sizing: border-box;
   border: ${(props) => props.$borderWidth ? `${props.$borderWidth}` : '1px'} ${props=>props.$borderStyle} ${(props) => props.$borderColor};
-  background-color: ${(props) => props.$bgColor};
+  background: ${(props) => props.$bgColor};
 `;
 
 const NavInner = styled("div") <Pick<IProps, "$justify">>`
diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx
index 358235e56..4f8ad6bce 100644
--- a/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/numberInputComp.tsx
@@ -268,6 +268,7 @@ const childrenMap = {
   inputFieldStyle: styleControl(InputLikeStyle , 'inputFieldStyle'),
   // validation
   required: BoolControl,
+  showValidationWhenEmpty: BoolControl,
   min: UndefinedNumberControl,
   max: UndefinedNumberControl,
   customRule: CustomRuleControl,
@@ -389,6 +390,7 @@ let NumberInputTmpComp = (function () {
       labelStyle: props.labelStyle,
       inputFieldStyle:props.inputFieldStyle,
       animationStyle:props.animationStyle,
+      showValidationWhenEmpty: props.showValidationWhenEmpty,
       ...validate(props),
     });
   })
@@ -405,6 +407,7 @@ let NumberInputTmpComp = (function () {
         {(useContext(EditorContext).editorModeStatus === "logic" || useContext(EditorContext).editorModeStatus === "both") && (
           <><Section name={sectionNames.validation}>
             {requiredPropertyView(children)}
+            {children.showValidationWhenEmpty.propertyView({label: trans("prop.showEmptyValidation")})}
             {children.min.propertyView({ label: trans("prop.minimum") })}
             {children.max.propertyView({ label: trans("prop.maximum") })}
             {children.customRule.propertyView({})}
diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/rangeSliderComp.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/rangeSliderComp.tsx
index cac189864..0c17a5a7f 100644
--- a/client/packages/lowcoder/src/comps/comps/numberInputComp/rangeSliderComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/rangeSliderComp.tsx
@@ -22,7 +22,7 @@ const RangeSliderBasicComp = (function () {
       animationStyle:props.animationStyle,
       children: (
         <SliderWrapper
-          vertical={props.vertical}
+          $vertical={Boolean(props.vertical)}
           onMouseDown={(e: any) => {
             e.stopPropagation();
             return false;
@@ -35,7 +35,7 @@ const RangeSliderBasicComp = (function () {
             value={[props.start.value, props.end.value]}
             $style={props.inputFieldStyle}
             style={{ margin: 0 }}
-            vertical={props.vertical || false}
+            $vertical={Boolean(props.vertical) || false}
             onChange={([start, end]) => {
               props.start.onChange(start);
               props.end.onChange(end);
diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderComp.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderComp.tsx
index a73b39a47..cabda6634 100644
--- a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderComp.tsx
@@ -26,7 +26,7 @@ const SliderBasicComp = (function () {
       animationStyle:props.animationStyle,
       children: (
         <SliderWrapper
-          vertical={props.vertical}
+          $vertical={Boolean(props.vertical)}
           onMouseDown={(e: any) => {
             e.stopPropagation();
             return false;
@@ -38,7 +38,7 @@ const SliderBasicComp = (function () {
             value={props.value.value}
             $style={props.inputFieldStyle}
             style={{margin: 0}}
-            vertical={props.vertical || false}
+            $vertical={Boolean(props.vertical) || false}
             onChange={(e) => {
               props.value.onChange(e);
               props.onEvent("change");
diff --git a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx
index 771d0d73c..efee00caa 100644
--- a/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/numberInputComp/sliderCompConstants.tsx
@@ -60,11 +60,11 @@ const getStyle = (style: SliderStyleType, vertical: boolean) => {
   `;
 };
 
-export const SliderStyled = styled(Slider)<{ $style: SliderStyleType, vertical: boolean }>`
-  ${(props) => props.$style && getStyle(props.$style, props.vertical)}
+export const SliderStyled = styled(Slider)<{ $style: SliderStyleType, $vertical: boolean }>`
+  ${(props) => props.$style && getStyle(props.$style, props.$vertical)}
 `;
 
-export const SliderWrapper = styled.div<{ vertical: boolean }>`
+export const SliderWrapper = styled.div<{ $vertical: boolean }>`
   width: 100%;
   display: inline-flex;
   align-items: center;
diff --git a/client/packages/lowcoder/src/comps/comps/pageLayoutComp/pageLayout.tsx b/client/packages/lowcoder/src/comps/comps/pageLayoutComp/pageLayout.tsx
index 105164b0d..bfece6cf2 100644
--- a/client/packages/lowcoder/src/comps/comps/pageLayoutComp/pageLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/pageLayoutComp/pageLayout.tsx
@@ -11,6 +11,7 @@ import { ConfigProvider, Layout } from 'antd';
 import { contrastBackground, contrastText } from "comps/controls/styleControlConstants";
 import { useRef, useState } from "react";
 import { LowcoderAppView } from "appView/LowcoderAppView";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const { Header, Content, Footer, Sider } = Layout;
 
@@ -30,12 +31,7 @@ const getStyle = (style: ContainerStyleType) => {
     border-radius: ${style.radius};
     overflow: hidden;
     padding: ${style.padding};
-    ${style.background && `background-color: ${style.background};`}
-    ${style.backgroundImage && `background-image: url(${style.backgroundImage});`}
-    ${style.backgroundImageRepeat && `background-repeat: ${style.backgroundImageRepeat};`}
-    ${style.backgroundImageSize && `background-size: ${style.backgroundImageSize};`}
-    ${style.backgroundImagePosition && `background-position: ${style.backgroundImagePosition};`}
-    ${style.backgroundImageOrigin && `background-origin: ${style.backgroundImageOrigin};`}
+    ${style && getBackgroundStyle(style)}
   `;
 };
 
@@ -51,14 +47,30 @@ const Wrapper = styled.div<{ $style: ContainerStyleType,$animationStyle:Animatio
   #pageLayout::-webkit-scrollbar {
     display: ${(props) => props.$mainScrollbars ? "block" : "none"};
   }
+  
+  .ant-layout {
+    background: transparent;
+  }
 `;
 
 const HeaderInnerGrid = styled(InnerGrid)<{
-  $backgroundColor: string
+  $backgroundColor: string,
+  $headerBackgroundImage: string,
+  $headerBackgroundImageSize: string,
+  $headerBackgroundImageRepeat: string,
+  $headerBackgroundImageOrigin: string,
+  $headerBackgroundImagePosition: string,
  }>`
   overflow: visible;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$headerBackgroundImage,
+    backgroundImageSize: props.$headerBackgroundImageSize,
+    backgroundImageRepeat: props.$headerBackgroundImageRepeat,
+    backgroundImageOrigin: props.$headerBackgroundImageOrigin,
+    backgroundImagePosition: props.$headerBackgroundImagePosition,
+  })}
 `;
 
 const SiderInnerGrid = styled(InnerGrid)<{
@@ -70,25 +82,40 @@ const SiderInnerGrid = styled(InnerGrid)<{
   $siderBackgroundImageOrigin: string;
  }>`
   overflow: auto;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
-  ${(props) => props.$siderBackgroundImage && `background-image: url(${props.$siderBackgroundImage});`}
-  ${(props) => props.$siderBackgroundImageRepeat && `background-repeat: ${props.$siderBackgroundImageRepeat};`}
-  ${(props) => props.$siderBackgroundImageSize && `background-size: ${props.$siderBackgroundImageSize};`}
-  ${(props) => props.$siderBackgroundImagePosition && `background-position: ${props.$siderBackgroundImagePosition};`}
-  ${(props) => props.$siderBackgroundImageOrigin && `background-origin: ${props.$siderBackgroundImageOrigin};`}
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$siderBackgroundImage,
+    backgroundImageSize: props.$siderBackgroundImageSize,
+    backgroundImageRepeat: props.$siderBackgroundImageRepeat,
+    backgroundImageOrigin: props.$siderBackgroundImageOrigin,
+    backgroundImagePosition: props.$siderBackgroundImagePosition,
+  })}
 `;
 
 const BodyInnerGrid = styled(InnerGrid)<{
   $showBorder: boolean;
-  $backgroundColor: string;
   $borderColor: string;
   $borderWidth: string;
+  $backgroundColor: string;
+  $bodyBackgroundImage: string;
+  $bodyBackgroundImageRepeat: string;
+  $bodyBackgroundImageSize: string;
+  $bodyBackgroundImagePosition: string;
+  $bodyBackgroundImageOrigin: string;
 }>`
   border-top: ${(props) => `${props.$showBorder ? props.$borderWidth : 0} solid ${props.$borderColor}`};
   flex: 1;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
+
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$bodyBackgroundImage,
+    backgroundImageSize: props.$bodyBackgroundImageSize,
+    backgroundImageRepeat: props.$bodyBackgroundImageRepeat,
+    backgroundImageOrigin: props.$bodyBackgroundImageOrigin,
+    backgroundImagePosition: props.$bodyBackgroundImagePosition,
+  })}
 `;
 
 const FooterInnerGrid = styled(InnerGrid)<{
@@ -104,13 +131,15 @@ const FooterInnerGrid = styled(InnerGrid)<{
 }>`
   border-top: ${(props) => `${props.$showBorder ? props.$borderWidth : 0} solid ${props.$borderColor}`};
   overflow: visible;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
-  ${(props) => props.$footerBackgroundImage && `background-image: url(${props.$footerBackgroundImage});`}
-  ${(props) => props.$footerBackgroundImageRepeat && `background-repeat: ${props.$footerBackgroundImageRepeat};`}
-  ${(props) => props.$footerBackgroundImageSize && `background-size: ${props.$footerBackgroundImageSize};`}
-  ${(props) => props.$footerBackgroundImagePosition && `background-position: ${props.$footerBackgroundImagePosition};`}
-  ${(props) => props.$footerBackgroundImageOrigin && `background-origin: ${props.$footerBackgroundImageOrigin};`}
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$footerBackgroundImage,
+    backgroundImageSize: props.$footerBackgroundImageSize,
+    backgroundImageRepeat: props.$footerBackgroundImageRepeat,
+    backgroundImageOrigin: props.$footerBackgroundImageOrigin,
+    backgroundImagePosition: props.$footerBackgroundImagePosition,
+  })}
 `;
 
 export type LayoutProps = LayoutViewProps & {
@@ -205,10 +234,15 @@ export function PageLayout(props: LayoutProps & { siderCollapsed: boolean; setSi
                           horizontalGridCells={horizontalGridCells}
                           autoHeight={true}
                           emptyRows={5}
-                          minHeight="46px"
+                          minHeight="60px"
                           containerPadding={[0, 0]}
                           showName={{ bottom: showFooter ? 20 : 0 }}
                           $backgroundColor={headerStyle?.headerBackground || 'transparent'}
+                          $headerBackgroundImage={headerStyle?.headerBackgroundImage}
+                          $headerBackgroundImageRepeat={headerStyle?.headerBackgroundImageRepeat}
+                          $headerBackgroundImageSize={headerStyle?.headerBackgroundImageSize}
+                          $headerBackgroundImagePosition={headerStyle?.headerBackgroundImagePosition}
+                          $headerBackgroundImageOrigin={headerStyle?.headerBackgroundImageOrigin}
                           style={{ padding: headerStyle.containerHeaderPadding }} />
                       </Header>
                     </BackgroundColorContext.Provider>
@@ -271,6 +305,11 @@ export function PageLayout(props: LayoutProps & { siderCollapsed: boolean; setSi
                               containerPadding={[0, 0]}
                               hintPlaceholder={props.hintPlaceholder ?? HintPlaceHolder}
                               $backgroundColor={bodyStyle?.background || 'transparent'}
+                              $bodyBackgroundImage={bodyStyle?.backgroundImage}
+                              $bodyBackgroundImageRepeat={bodyStyle?.backgroundImageRepeat}
+                              $bodyBackgroundImageSize={bodyStyle?.backgroundImageSize}
+                              $bodyBackgroundImagePosition={bodyStyle?.backgroundImagePosition}
+                              $bodyBackgroundImageOrigin={bodyStyle?.backgroundImageOrigin}
                               $borderColor={style?.border}
                               $borderWidth={style?.borderWidth}
                               style={{ padding: bodyStyle.containerBodyPadding }} />
@@ -335,6 +374,11 @@ export function PageLayout(props: LayoutProps & { siderCollapsed: boolean; setSi
                           containerPadding={[0, 0]}
                           hintPlaceholder={props.hintPlaceholder ?? HintPlaceHolder}
                           $backgroundColor={bodyStyle?.background || 'transparent'}
+                          $bodyBackgroundImage={bodyStyle?.backgroundImage}
+                          $bodyBackgroundImageRepeat={bodyStyle?.backgroundImageRepeat}
+                          $bodyBackgroundImageSize={bodyStyle?.backgroundImageSize}
+                          $bodyBackgroundImagePosition={bodyStyle?.backgroundImagePosition}
+                          $bodyBackgroundImageOrigin={bodyStyle?.backgroundImageOrigin}
                           $borderColor={style?.border}
                           $borderWidth={style?.borderWidth}
                           style={{ padding: bodyStyle.containerBodyPadding }} />
diff --git a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
index c51ffb073..c37af57cd 100644
--- a/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/responsiveLayout/responsiveLayout.tsx
@@ -43,6 +43,7 @@ import { EditorContext } from "comps/editorState";
 import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils";
 import { DisabledContext } from "comps/generators/uiCompBuilder";
 import SliderControl from "@lowcoder-ee/comps/controls/sliderControl";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const RowWrapper = styled(Row)<{
   $style: ResponsiveLayoutRowStyleType;
@@ -56,13 +57,12 @@ const RowWrapper = styled(Row)<{
   border-color: ${(props) => props.$style?.border};
   border-style: ${(props) => props.$style?.borderStyle};
   padding: ${(props) => props.$style.padding};
-  background-color: ${(props) => props.$style.background};
   rotate: ${props=> props.$style.rotation}
   overflow: ${(props) => (props.$showScrollbar ? 'auto' : 'hidden')};
    ::-webkit-scrollbar {
     display: ${(props) => (props.$showScrollbar ? 'block' : 'none')};
-  }
-
+    }
+  ${props => getBackgroundStyle(props.$style)}
 `;
 
 const ColWrapper = styled(Col)<{
@@ -77,13 +77,13 @@ const ColWrapper = styled(Col)<{
 
   > div {
     height: ${(props) => props.$matchColumnsHeight ? '100%' : 'auto'};
-    background-color: ${(props) => props.$style?.background} !important;
     border-radius: ${(props) => props.$style?.radius};
     border-width: ${(props) => props.$style?.borderWidth}px;
     border-color: ${(props) => props.$style?.border};
     border-style: ${(props) => props.$style?.borderStyle};
     margin: ${(props) => props.$style?.margin};
     padding: ${(props) => props.$style?.padding};
+    ${props => props.$style && getBackgroundStyle(props.$style)}
   }
 `;
 
diff --git a/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx b/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx
index c0f25aad2..52829fd49 100644
--- a/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/richTextEditorComp.tsx
@@ -98,7 +98,7 @@ const commonStyle = (style: RichTextEditorStyleType, contentScrollBar: boolean)
     &.ql-container,
     &.ql-toolbar {
       border-color: ${style.border};
-      background-color: ${style.background};
+      background: ${style.background};
       
     }
   }
@@ -108,7 +108,7 @@ const commonStyle = (style: RichTextEditorStyleType, contentScrollBar: boolean)
   }
   & .ql-container {
     border-radius: 0 0 ${style.radius} ${style.radius};
-    background-color: ${style.background};
+    background: ${style.background};
     border-width: ${style.borderWidth ? style.borderWidth : "1px"};
   }
 `;
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx
index a1839f9cb..78724cab5 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/segmentedControl.tsx
@@ -5,7 +5,7 @@ import { ChangeEventHandlerControl } from "comps/controls/eventHandlerControl";
 import { LabelControl } from "comps/controls/labelControl";
 import { SelectOptionControl } from "comps/controls/optionsControl";
 import { styleControl } from "comps/controls/styleControl";
-import { AnimationStyle, SegmentStyle, SegmentStyleType } from "comps/controls/styleControlConstants";
+import { AnimationStyle, LabelStyle, SegmentStyle, SegmentStyleType } from "comps/controls/styleControlConstants";
 import styled, { css } from "styled-components";
 import { UICompBuilder } from "../../generators";
 import { CommonNameConfig, NameConfig, withExposingConfigs } from "../../generators/withExposing";
@@ -44,7 +44,7 @@ const getStyle = (style: SegmentStyleType) => {
       }
       .ant-segmented-item-selected,
       .ant-segmented-thumb {
-        background-color: ${style.indicatorBackground};
+        background: ${style.indicatorBackground};
       }
     }
 
@@ -77,6 +77,7 @@ const SegmentChildrenMap = {
   onEvent: ChangeEventHandlerControl,
   options: SelectOptionControl,
   style: styleControl(SegmentStyle, 'style'),
+  labelStyle: styleControl(LabelStyle , 'labelStyle'),
   animationStyle: styleControl(AnimationStyle, 'animationStyle'),
   viewRef: RefControl<HTMLDivElement>,
 
@@ -93,6 +94,7 @@ let SegmentedControlBasicComp = (function () {
     return props.label({
       required: props.required,
       style: props.style,
+      labelStyle: props.labelStyle,
       animationStyle: props.animationStyle,
       children: (
         <Segmented
@@ -143,6 +145,9 @@ let SegmentedControlBasicComp = (function () {
           <Section name={sectionNames.style}>
             {children.style.getPropertyView()}
           </Section>
+          <Section name={sectionNames.labelStyle}>
+            {children.labelStyle.getPropertyView()}
+          </Section>
           <Section name={sectionNames.animationStyle} hasTooltip={true}>
             {children.animationStyle.getPropertyView()}
             </Section>
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectComp.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectComp.tsx
index f20c66001..eef8cad60 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectComp.tsx
@@ -55,6 +55,7 @@ let SelectBasicComp = (function () {
           dispatch={dispatch}
         />
       ),
+      showValidationWhenEmpty: props.showValidationWhenEmpty,
       ...validateState,
     });
   })
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx
index 360a68460..6ad51d255 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectCompConstants.tsx
@@ -109,7 +109,7 @@ export const getStyle = (
       }
 
       .ant-select-selector {
-        background-color: ${style.background};
+        background: ${style.background};
         border-color: ${style.border};
         border-width:${(style as SelectStyleType).borderWidth};
         box-shadow:${(style as SelectStyleType).boxShadow} ${(style as SelectStyleType).boxShadowColor};
@@ -124,7 +124,7 @@ export const getStyle = (
 
       .ant-select-arrow,
       .ant-select-clear {
-        background-color: ${style.background};
+        // background: ${style.background};
         color: ${style.text === "#222222"
       ? "#8B8FA3"
       : isDarkColor(style.text)
@@ -142,7 +142,7 @@ export const getStyle = (
 
       &.ant-select-multiple .ant-select-selection-item {
         border: none;
-        background-color: ${(style as MultiSelectStyleType).tags};
+        background: ${(style as MultiSelectStyleType).tags};
         color: ${(style as MultiSelectStyleType).tagsText};
         border-radius: ${style.radius};
 
@@ -191,7 +191,7 @@ const Select = styled(AntdSelect) <{ $style: SelectStyleType & MultiSelectStyleT
 `;
 
 const DropdownStyled = styled.div<{ $style: ChildrenMultiSelectStyleType }>`
- background-color: ${props => props.$style?.background};
+    background: ${props => props.$style?.background};
     border: ${props => props.$style?.border};
     border-style: ${props => props.$style?.borderStyle};
     border-width: ${props => props.$style?.borderWidth};
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectInputConstants.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectInputConstants.tsx
index c69c35605..75f275c3c 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/selectInputConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/selectInputConstants.tsx
@@ -18,6 +18,7 @@ import { blurMethod, focusWithOptions } from "comps/utils/methodUtils";
 
 export const SelectInputValidationChildren = {
   required: BoolControl,
+  showValidationWhenEmpty: BoolControl,
   customRule: CustomRuleControl,
 };
 type ValidationComp = RecordConstructorToComp<typeof SelectInputValidationChildren>;
@@ -122,6 +123,9 @@ export const SelectInputInvalidConfig = depsConfig<
 export const SelectInputValidationSection = (children: ValidationComp) => (
   <Section name={sectionNames.validation}>
     {requiredPropertyView(children)}
+    {children.showValidationWhenEmpty.propertyView({
+      label: trans("prop.showEmptyValidation"),
+    })}
     {children.customRule.propertyView({})}
   </Section>
 );
diff --git a/client/packages/lowcoder/src/comps/comps/selectInputComp/stepControl.tsx b/client/packages/lowcoder/src/comps/comps/selectInputComp/stepControl.tsx
index 5db05650e..3dc80ed80 100644
--- a/client/packages/lowcoder/src/comps/comps/selectInputComp/stepControl.tsx
+++ b/client/packages/lowcoder/src/comps/comps/selectInputComp/stepControl.tsx
@@ -19,6 +19,7 @@ import { dropdownControl } from "comps/controls/dropdownControl";
 import { useContext, useState, useEffect } from "react";
 import { EditorContext } from "comps/editorState";
 import { AutoHeightControl } from "@lowcoder-ee/index.sdk";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const sizeOptions = [
   {
@@ -120,14 +121,9 @@ let StepControlBasicComp = (function () {
       margin: ${props.style.margin};
       rotate: ${props.style.rotation};
       padding: ${props.style.padding};
-      background-color: ${props.style.background};
       border: ${props.style.borderWidth} solid ${props.style.border};
       border-radius: ${props.style.radius};
-      background-image: url(${props.style.backgroundImage});
-      background-repeat: ${props.style.backgroundImageRepeat};
-      background-size: ${props.style.backgroundImageSize};
-      background-position: ${props.style.backgroundImagePosition};
-      background-origin: ${props.style.backgroundImageOrigin};
+      ${getBackgroundStyle(props.style)}
       .ant-steps-item { padding-top: 5px !important; }
       .ant-steps.ant-steps-label-vertical.ant-steps-small .ant-steps-item-icon { margin-top: 17px !important; }
       .ant-steps.ant-steps-label-vertical.ant-steps-default .ant-steps-item-icon { margin-top: 12px !important; }
diff --git a/client/packages/lowcoder/src/comps/comps/shapeComp/shapeTriContainer.tsx b/client/packages/lowcoder/src/comps/comps/shapeComp/shapeTriContainer.tsx
index 181b89887..6d083db1b 100644
--- a/client/packages/lowcoder/src/comps/comps/shapeComp/shapeTriContainer.tsx
+++ b/client/packages/lowcoder/src/comps/comps/shapeComp/shapeTriContainer.tsx
@@ -11,6 +11,7 @@ import {
 } from "../containerComp/containerView";
 import { TriContainerViewProps } from "../triContainerComp/triContainerCompBuilder";
 import { Coolshape } from "coolshapes-react";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const getStyle = (style: ContainerStyleType) => {
   return css`
@@ -19,16 +20,8 @@ const getStyle = (style: ContainerStyleType) => {
     border-radius: ${style.radius};
     overflow: hidden;
     padding: ${style.padding};
-    ${style.background && `background-color: ${style.background};`}
-    ${style.backgroundImage && `background-image: ${style.backgroundImage};`}
-    ${style.backgroundImageRepeat &&
-    `background-repeat: ${style.backgroundImageRepeat};`}
-    ${style.backgroundImageSize &&
-    `background-size: ${style.backgroundImageSize};`}
-    ${style.backgroundImagePosition &&
-    `background-position: ${style.backgroundImagePosition};`}
-    ${style.backgroundImageOrigin &&
-    `background-origin: ${style.backgroundImageOrigin};`}
+    
+    ${getBackgroundStyle(style)}
   `;
 };
 
diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx
index f68edddaf..a712fe7a8 100644
--- a/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableCompView.tsx
@@ -29,7 +29,7 @@ import { BackgroundColorContext } from "comps/utils/backgroundColorContext";
 import { PrimaryColor } from "constants/style";
 import { trans } from "i18n";
 import _ from "lodash";
-import { darkenColor, isDarkColor, ScrollBar } from "lowcoder-design";
+import { darkenColor, isDarkColor, isValidColor, ScrollBar } from "lowcoder-design";
 import React, { Children, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
 import { Resizable } from "react-resizable";
 import styled, { css } from "styled-components";
@@ -52,7 +52,7 @@ import { ThemeContext } from "@lowcoder-ee/comps/utils/themeContext";
 export const EMPTY_ROW_KEY = 'empty_row';
 
 function genLinerGradient(color: string) {
-  return `linear-gradient(${color}, ${color})`;
+  return isValidColor(color) ? `linear-gradient(${color}, ${color})` : color;
 }
 
 const getStyle = (
@@ -72,62 +72,53 @@ const getStyle = (
     }
     .ant-table-tbody {
       > tr:nth-of-type(2n + 1) {
-        &,
-        > td {
-          background: ${genLinerGradient(rowStyle.background)};
-          // border-bottom:${rowStyle.borderWidth} ${rowStyle.borderStyle} ${rowStyle.border} !important;
-          // border-right:${rowStyle.borderWidth} ${rowStyle.borderStyle} ${rowStyle.border} !important;
-        }
+        background: ${genLinerGradient(rowStyle.background)};
       }
 
       > tr:nth-of-type(2n) {
-        &,
-        > td {
-          background: ${alternateBackground};
-          // border-bottom:${rowStyle.borderWidth} ${rowStyle.borderStyle} ${rowStyle.border} !important;
-          // border-right:${rowStyle.borderWidth} ${rowStyle.borderStyle} ${rowStyle.border} !important;
-        }
+        background: ${alternateBackground};
       }
 
       // selected row
       > tr:nth-of-type(2n + 1).ant-table-row-selected {
-        > td {
-          background: ${selectedRowBackground}, ${rowStyle.background} !important;
+        background: ${selectedRowBackground}, ${rowStyle.background} !important;
+        > td.ant-table-cell {
+          background: transparent !important;
         }
 
-        > td.ant-table-cell-row-hover,
-        &:hover > td {
+        // > td.ant-table-cell-row-hover,
+        &:hover {
           background: ${hoverRowBackground}, ${selectedRowBackground}, ${rowStyle.background} !important;
         }
       }
 
       > tr:nth-of-type(2n).ant-table-row-selected {
-        > td {
-          background: ${selectedRowBackground}, ${alternateBackground} !important;
+        background: ${selectedRowBackground}, ${alternateBackground} !important;
+        > td.ant-table-cell {
+          background: transparent !important;
         }
 
-        > td.ant-table-cell-row-hover,
-        &:hover > td {
+        // > td.ant-table-cell-row-hover,
+        &:hover {
           background: ${hoverRowBackground}, ${selectedRowBackground}, ${alternateBackground} !important;
         }
       }
 
       // hover row
-      > tr:nth-of-type(2n + 1) > td.ant-table-cell-row-hover {
-        &,
-        > div:nth-of-type(2) {
-          background: ${hoverRowBackground}, ${rowStyle.background} !important;
+      > tr:nth-of-type(2n + 1):hover {
+        background: ${hoverRowBackground}, ${rowStyle.background} !important;
+        > td.ant-table-cell-row-hover {
+          background: transparent;
         }
       }
-
-      > tr:nth-of-type(2n) > td.ant-table-cell-row-hover {
-        &,
-        > div:nth-of-type(2) {
-          background: ${hoverRowBackground}, ${alternateBackground} !important;
+      > tr:nth-of-type(2n):hover {
+        background: ${hoverRowBackground}, ${alternateBackground} !important;
+        > td.ant-table-cell-row-hover {
+          background: transparent;
         }
       }
 
-      > tr.ant-table-expanded-row > td {
+      > tr.ant-table-expanded-row {
         background: ${background};
       }
     }
@@ -255,9 +246,11 @@ const TableWrapper = styled.div<{
               z-index: 99;
             `
           }
+          > tr {
+            background: ${(props) => props.$headerStyle.headerBackground}; 
+          }
           > tr > th {
-            background-color: ${(props) => props.$headerStyle.headerBackground};
-           
+            background: transparent;
             border-color: ${(props) => props.$headerStyle.border};
             border-width: ${(props) => props.$headerStyle.borderWidth};
             color: ${(props) => props.$headerStyle.headerText};
@@ -600,12 +593,10 @@ function TableCellView(props: {
       rowHeight: rowHeight,
     }
     let { background } = style;
-    if (rowContext.selected) {
-      background = genLinerGradient(handleToSelectedRow(background)) + "," + background;
-    }
     if (rowContext.hover) {
-      background = genLinerGradient(handleToHoverRow(background)) + "," + background;
+      background = 'transparent';
     }
+
     tdView = (
       <TableTd
         {...restProps}
@@ -982,6 +973,7 @@ export function TableCompView(props: {
       <TableSummary
         tableSize={size}
         istoolbarPositionBelow={toolbar.position === "below"}
+        multiSelectEnabled={compChildren.selection.children.mode.value === 'multiple'}
         expandableRows={Boolean(expansion.expandModalView)}
         summaryRows={parseInt(summaryRows)}
         columns={columns}
diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableSummaryComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableSummaryComp.tsx
index be47ae3bc..b6562a485 100644
--- a/client/packages/lowcoder/src/comps/comps/tableComp/tableSummaryComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableSummaryComp.tsx
@@ -9,7 +9,10 @@ import Tooltip from "antd/es/tooltip";
 
 const TableSummaryRow = styled(Table.Summary.Row)<{
   $istoolbarPositionBelow: boolean;
+  $background: string;
 }>`
+  ${props => `background: ${props.$background}`};
+
   td:last-child {
     border-right: unset !important;
   }
@@ -143,7 +146,7 @@ function TableSummaryCellView(props: {
   } = props;
 
   const style = {
-    background: cellColor || columnStyle.background || rowStyle.background,
+    background: cellColor || columnStyle.background,
     margin: columnStyle.margin || rowStyle.margin,
     text: columnStyle.text || rowStyle.text,
     border: columnStyle.border || rowStyle.border,
@@ -174,6 +177,7 @@ function TableSummaryCellView(props: {
 export function TableSummary(props: {
   tableSize: string;
   expandableRows: boolean;
+  multiSelectEnabled: boolean;
   summaryRows: number;
   columns: ColumnComp[];
   summaryRowStyle: TableSummaryRowStyleType;
@@ -185,19 +189,27 @@ export function TableSummary(props: {
     summaryRowStyle,
     tableSize,
     expandableRows,
+    multiSelectEnabled,
     istoolbarPositionBelow,
   } = props;
   let visibleColumns = columns.filter(col => !col.getView().hide);
   if (expandableRows) {
     visibleColumns.unshift(new ColumnComp({}));
   }
+  if (multiSelectEnabled) {
+    visibleColumns.unshift(new ColumnComp({}));
+  }
   
   if (!visibleColumns.length) return <></>;
 
   return (
     <Table.Summary>
       {Array.from(Array(summaryRows)).map((_, rowIndex) => (
-        <TableSummaryRow key={rowIndex} $istoolbarPositionBelow={istoolbarPositionBelow}>
+        <TableSummaryRow
+          key={rowIndex}
+          $istoolbarPositionBelow={istoolbarPositionBelow}
+          $background={summaryRowStyle.background}
+        >
           {visibleColumns.map((column, index) => {
             const summaryColumn = column.children.summaryColumns.getView()[rowIndex].getView();
             return (
diff --git a/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx b/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx
index 67b96cfe9..85703fd11 100644
--- a/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableComp/tableToolbarComp.tsx
@@ -54,7 +54,7 @@ const getStyle = (
   fixedToolbar: boolean,
 ) => {
   return css`
-    background-color: ${style.background};
+    background: ${style.background};
     // Implement horizontal scrollbar and vertical page number selection is not blocked
     padding: 13px 12px;
     position: sticky;
diff --git a/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx b/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx
index 0f3ebef67..a1fe29716 100644
--- a/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tabs/tabbedContainerComp.tsx
@@ -36,6 +36,7 @@ import { messageInstance } from "lowcoder-design/src/components/GlobalInstances"
 import { BoolControl } from "comps/controls/boolControl";
 import { PositionControl } from "comps/controls/dropdownControl";
 import SliderControl from "@lowcoder-ee/comps/controls/sliderControl";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const EVENT_OPTIONS = [
   {
@@ -82,25 +83,27 @@ const getStyle = (
       border: ${style.borderWidth} ${style.borderStyle} ${style.border};
       border-radius: ${style.radius};
       padding: ${style.padding};
-      background-color: ${style.background};
-      background-image: url(${style.backgroundImage});
-      background-repeat: ${style.backgroundImageRepeat};
-      background-size: ${style.backgroundImageSize};
-      background-position: ${style.backgroundImagePosition};
-      background-origin: ${style.backgroundImageOrigin};
+      ${getBackgroundStyle(style)}
 
       > .ant-tabs-content-holder > .ant-tabs-content > .ant-tabs-tabpane {
         height: 100%;
         .react-grid-layout {
           border-radius: 0;
-          background-color: ${bodyStyle.background || 'transparent'};
           padding: ${bodyStyle.containerBodyPadding};
+          ${getBackgroundStyle(bodyStyle)}
         }
       }
 
       > .ant-tabs-nav {
-        background-color: ${headerStyle.headerBackground || 'transparent'};
         padding: ${headerStyle.containerHeaderPadding};
+        ${getBackgroundStyle({
+          background: headerStyle.headerBackground,
+          backgroundImage: headerStyle.headerBackgroundImage,
+          backgroundImageSize: headerStyle.headerBackgroundImageSize,
+          backgroundImageRepeat: headerStyle.headerBackgroundImageRepeat,
+          backgroundImageOrigin: headerStyle.headerBackgroundImageOrigin,
+          backgroundImagePosition: headerStyle.headerBackgroundImagePosition,
+        })}
 
         .ant-tabs-tab {
           div {
@@ -159,7 +162,7 @@ const StyledTabs = styled(Tabs)<{
   .ant-tabs-nav {
     display: ${(props) => (props.$showHeader ? "block" : "none")};
     padding: 0 ${(props) => (props.$isMobile ? 16 : 24)}px;
-    background: white;
+    // background: white;
     margin: 0px;
   }
 
diff --git a/client/packages/lowcoder/src/comps/comps/textComp.tsx b/client/packages/lowcoder/src/comps/comps/textComp.tsx
index e8a695164..1dd20cae8 100644
--- a/client/packages/lowcoder/src/comps/comps/textComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textComp.tsx
@@ -35,7 +35,7 @@ const getStyle = (style: TextStyleType) => {
     color: ${style.text};
     text-transform:${style.textTransform} !important;
     text-decoration:${style.textDecoration} !important;
-    background-color: ${style.background};
+    background: ${style.background};
     .markdown-body a {
       color: ${style.links};
     }
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/inputComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/inputComp.tsx
index cb241a087..6222fb6de 100644
--- a/client/packages/lowcoder/src/comps/comps/textInputComp/inputComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textInputComp/inputComp.tsx
@@ -78,6 +78,7 @@ let InputBasicComp = new UICompBuilder(childrenMap, (props) => {
     labelStyle: props.labelStyle,
     inputFieldStyle:props.inputFieldStyle,
     animationStyle:props.animationStyle,
+    showValidationWhenEmpty: props.showValidationWhenEmpty,
     ...validateState,
   });
 })
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/passwordComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/passwordComp.tsx
index 151f4fc7f..846a81a7d 100644
--- a/client/packages/lowcoder/src/comps/comps/textInputComp/passwordComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textInputComp/passwordComp.tsx
@@ -82,6 +82,7 @@ let PasswordTmpComp = (function () {
       labelStyle: props.labelStyle,
       inputFieldStyle:props.inputFieldStyle,
       animationStyle:props.animationStyle,
+      showValidationWhenEmpty: props.showValidationWhenEmpty,
       ...validateState,
     });
   })
@@ -106,6 +107,7 @@ let PasswordTmpComp = (function () {
                 {children.prefixIcon.propertyView({ label: trans("button.prefixIcon") })}
               </Section><Section name={sectionNames.validation}>
                 {requiredPropertyView(children)}
+                {children.showValidationWhenEmpty.propertyView({label: trans("prop.showEmptyValidation")})}
                 {regexPropertyView(children)}
                 {minLengthPropertyView(children)}
                 {maxLengthPropertyView(children)}
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/textAreaComp.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/textAreaComp.tsx
index 2a559dee3..a41fed3a2 100644
--- a/client/packages/lowcoder/src/comps/comps/textInputComp/textAreaComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textInputComp/textAreaComp.tsx
@@ -99,6 +99,7 @@ let TextAreaTmpComp = (function () {
       style: props.style,
       labelStyle: props.labelStyle,
       animationStyle: props.animationStyle,
+      showValidationWhenEmpty: props.showValidationWhenEmpty,
       ...validateState,
     });
   })
diff --git a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
index 0cf64092a..9c3129746 100644
--- a/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
+++ b/client/packages/lowcoder/src/comps/comps/textInputComp/textInputConstants.tsx
@@ -144,6 +144,7 @@ export const textInputChildren = {
 
   // validation
   required: BoolControl,
+  showValidationWhenEmpty: BoolControl,
   minLength: NumberControl,
   maxLength: NumberControl,
   validationType: dropdownControl(TextInputValidationOptions, "Text"),
@@ -226,6 +227,7 @@ export const TextInputInteractionSection = (children: TextInputComp) => (
 export const TextInputValidationSection = (children: TextInputComp) => (
   <Section name={sectionNames.validation}>
     {requiredPropertyView(children)}
+    {children.showValidationWhenEmpty.propertyView({label: trans("prop.showEmptyValidation")})}
     {children.validationType.propertyView({ label: trans("prop.textType") })}
     {valueInfoMap[children.validationType.getView()]?.extra === undefined &&
       regexPropertyView(children)}
diff --git a/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx b/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx
index 5854058af..762ba516a 100644
--- a/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/timelineComp/timelineComp.tsx
@@ -36,6 +36,7 @@ import {
   heightCalculator,
   widthCalculator,
   marginCalculator,
+  TimeLineStyleType,
 } from "comps/controls/styleControlConstants";
 import { stateComp, valueComp } from "comps/generators/simpleGenerators";
 import {
@@ -47,6 +48,24 @@ import { timelineDate, timelineNode, TimelineDataTooltip } from "./timelineConst
 import { convertTimeLineData } from "./timelineUtils";
 import { default as Timeline } from "antd/es/timeline";
 import { EditorContext } from "comps/editorState";
+import { styled } from "styled-components";
+
+const TimelineWrapper = styled.div<{
+  $style: TimeLineStyleType
+}>`
+  ${props => `margin: ${props.$style.margin ?? '3px'}` };
+  ${props => `padding: ${props.$style.padding !== '3px' ? props.$style.padding : '20px 10px 0px 10px'}` };
+  ${props => `width: ${widthCalculator(props.$style.margin ?? '3px')}` };
+  ${props => `height: ${heightCalculator(props.$style.margin ?? '3px')}` };
+  ${props => `background: ${props.$style.background}` };
+  ${props => `border-radius: ${props.$style.radius}` };
+  overflow: auto;
+  overflow-x: hidden;
+
+  .ant-timeline .ant-timeline-item-head {
+    background-color: transparent;
+  }
+`;
 
 const EventOptions = [
   clickEvent,
@@ -139,18 +158,7 @@ const TimelineComp = (
 
   return (
     <ScrollBar hideScrollbar={!props.verticalScrollbar}>
-      <div
-        style={{
-          margin: style.margin ?? '3px',
-          padding: style.padding !== '3px' ? style.padding : '20px 10px 0px 10px',
-          width: widthCalculator(style.margin ?? '3px'),
-          height: heightCalculator(style.margin ?? '3px'),
-          background: style.background,
-          overflow: "auto",
-          overflowX: "hidden",
-          borderRadius: style.radius,
-        }}
-      >
+      <TimelineWrapper $style={style}>
         <Timeline
           mode={props?.mode || "left"}
           reverse={props?.reverse}
@@ -163,7 +171,7 @@ const TimelineComp = (
           }
           items={timelineItems}
         />
-      </div>
+      </TimelineWrapper>
     </ScrollBar>
   );
 };
diff --git a/client/packages/lowcoder/src/comps/comps/treeComp/treeComp.tsx b/client/packages/lowcoder/src/comps/comps/treeComp/treeComp.tsx
index aa117ff5c..dda409de1 100644
--- a/client/packages/lowcoder/src/comps/comps/treeComp/treeComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/treeComp/treeComp.tsx
@@ -130,6 +130,7 @@ const TreeCompView = (props: RecordConstructorToView<typeof childrenMap>) => {
         </ScrollBar>
       </Container>
     ),
+    showValidationWhenEmpty: props.showValidationWhenEmpty,
   });
 };
 
diff --git a/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx b/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx
index dbbf931c5..c1a3f8157 100644
--- a/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/treeComp/treeSelectComp.tsx
@@ -141,6 +141,7 @@ const TreeCompView = (
         onBlur={() => props.onEvent("blur")}
       />
     ),
+    showValidationWhenEmpty: props.showValidationWhenEmpty,
   });
 };
 
diff --git a/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainer.tsx b/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainer.tsx
index e9ba0b991..206864892 100644
--- a/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainer.tsx
+++ b/client/packages/lowcoder/src/comps/comps/triContainerComp/triContainer.tsx
@@ -7,6 +7,7 @@ import styled, { css } from "styled-components";
 import { checkIsMobile } from "util/commonUtils";
 import { gridItemCompToGridItems, InnerGrid } from "../containerComp/containerView";
 import { TriContainerViewProps } from "../triContainerComp/triContainerCompBuilder";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const getStyle = (style: ContainerStyleType) => {
   return css`
@@ -16,12 +17,7 @@ const getStyle = (style: ContainerStyleType) => {
     border-style: ${style.borderStyle};
     overflow: hidden;
     padding: ${style.padding};
-    ${style.background && `background-color: ${style.background};`}
-    ${style.backgroundImage && `background-image: url(${style.backgroundImage});`}
-    ${style.backgroundImageRepeat && `background-repeat: ${style.backgroundImageRepeat};`}
-    ${style.backgroundImageSize && `background-size: ${style.backgroundImageSize};`}
-    ${style.backgroundImagePosition && `background-position: ${style.backgroundImagePosition};`}
-    ${style.backgroundImageOrigin && `background-origin: ${style.backgroundImageOrigin};`}
+    ${getBackgroundStyle(style)}
   `;
 };
 
@@ -44,13 +40,15 @@ const HeaderInnerGrid = styled(InnerGrid)<{
   $headerBackgroundImageOrigin: string;
  }>`
   overflow: visible;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
-  ${(props) => props.$headerBackgroundImage && `background-image: url(${props.$headerBackgroundImage});`}
-  ${(props) => props.$headerBackgroundImageRepeat && `background-repeat: ${props.$headerBackgroundImageRepeat};`}
-  ${(props) => props.$headerBackgroundImageSize && `background-size: ${props.$headerBackgroundImageSize};`}
-  ${(props) => props.$headerBackgroundImagePosition && `background-position: ${props.$headerBackgroundImagePosition};`}
-  ${(props) => props.$headerBackgroundImageOrigin && `background-origin: ${props.$headerBackgroundImageOrigin};`}
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$headerBackgroundImage,
+    backgroundImageSize: props.$headerBackgroundImageSize,
+    backgroundImageRepeat: props.$headerBackgroundImageRepeat,
+    backgroundImageOrigin: props.$headerBackgroundImageOrigin,
+    backgroundImagePosition: props.$headerBackgroundImagePosition,
+  })}
 `;
 
 const BodyInnerGrid = styled(InnerGrid)<{
@@ -66,13 +64,15 @@ const BodyInnerGrid = styled(InnerGrid)<{
 }>`
   border-top: ${(props) => `${props.$showBorder ? props.$borderWidth : 0} solid ${props.$borderColor}`};
   flex: 1;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
-  ${(props) => props.$backgroundImage && `background-image: url(${props.$backgroundImage});`}
-  ${(props) => props.$backgroundImageRepeat && `background-repeat: ${props.$backgroundImageRepeat};`}
-  ${(props) => props.$backgroundImageSize && `background-size: ${props.$backgroundImageSize};`}
-  ${(props) => props.$backgroundImagePosition && `background-position: ${props.$backgroundImagePosition};`}
-  ${(props) => props.$backgroundImageOrigin && `background-origin: ${props.$backgroundImageOrigin};`}
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$backgroundImage,
+    backgroundImageSize: props.$backgroundImageSize,
+    backgroundImageRepeat: props.$backgroundImageRepeat,
+    backgroundImageOrigin: props.$backgroundImageOrigin,
+    backgroundImagePosition: props.$backgroundImagePosition,
+  })}
 `;
 
 const FooterInnerGrid = styled(InnerGrid)<{
@@ -88,13 +88,15 @@ const FooterInnerGrid = styled(InnerGrid)<{
 }>`
   border-top: ${(props) => `${props.$showBorder ? props.$borderWidth : 0} solid ${props.$borderColor}`};
   overflow: visible;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
-  ${(props) => props.$footerBackgroundImage && `background-image: url(${props.$footerBackgroundImage});`}
-  ${(props) => props.$footerBackgroundImageRepeat && `background-repeat: ${props.$footerBackgroundImageRepeat};`}
-  ${(props) => props.$footerBackgroundImageSize && `background-size: ${props.$footerBackgroundImageSize};`}
-  ${(props) => props.$footerBackgroundImagePosition && `background-position: ${props.$footerBackgroundImagePosition};`}
-  ${(props) => props.$footerBackgroundImageOrigin && `background-origin: ${props.$footerBackgroundImageOrigin};`}
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$footerBackgroundImage,
+    backgroundImageSize: props.$footerBackgroundImageSize,
+    backgroundImageRepeat: props.$footerBackgroundImageRepeat,
+    backgroundImageOrigin: props.$footerBackgroundImageOrigin,
+    backgroundImagePosition: props.$footerBackgroundImagePosition,
+  })}
 `;
 
 export type TriContainerProps = TriContainerViewProps & {
diff --git a/client/packages/lowcoder/src/comps/comps/triContainerComp/triFloatTextContainer.tsx b/client/packages/lowcoder/src/comps/comps/triContainerComp/triFloatTextContainer.tsx
index ee1fa3248..457a807ba 100644
--- a/client/packages/lowcoder/src/comps/comps/triContainerComp/triFloatTextContainer.tsx
+++ b/client/packages/lowcoder/src/comps/comps/triContainerComp/triFloatTextContainer.tsx
@@ -16,6 +16,7 @@ import {
   InnerGrid,
 } from "../containerComp/containerView";
 import { TriContainerViewProps } from "../triContainerComp/triContainerCompBuilder";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const getStyle = (style: TextContainerStyleType) => {
   return css`
@@ -28,7 +29,8 @@ const getStyle = (style: TextContainerStyleType) => {
     font-style:${style.fontStyle} !important;
     text-transform:${style.textTransform} !important;
     text-decoration:${style.textDecoration} !important;
-    background-color: ${style.background};
+    ${getBackgroundStyle(style)}
+
     .markdown-body a {
       color: ${style.links};
     }
@@ -64,6 +66,7 @@ const getStyle = (style: TextContainerStyleType) => {
           color: #000000;
         }
       }
+      background-color: transparent;
     }
   `;
   }
@@ -79,14 +82,9 @@ ${props=>props.$animationStyle&&props.$animationStyle}
   overflow-y: scroll;
   border: ${(props) => props.$style.borderWidth} ${(props) => (props.$style.borderStyle ? props.$style.borderStyle : "solid")} ${(props) => props.$style.border};
   border-radius: ${(props) => props.$style.radius};
-  background-color: ${(props) => props.$style.background};
   padding: ${(props) => props.$style.padding};
   margin: ${(props) => props.$style.margin};
-  ${(props) => props.$style.backgroundImage && `background-image: url(${props.$style.backgroundImage});`}
-  ${(props) => props.$style.backgroundImageRepeat && `background-repeat: ${props.$style.backgroundImageRepeat};`}
-  ${(props) => props.$style.backgroundImageSize && `background-size: ${props.$style.backgroundImageSize};`}
-  ${(props) => props.$style.backgroundImagePosition && `background-position: ${props.$style.backgroundImagePosition};`}
-  ${(props) => props.$style.backgroundImageOrigin && `background-origin: ${props.$style.backgroundImageOrigin};`}
+  ${props => getBackgroundStyle(props.$style)}
 `;
 
 const FloatTextWrapper = styled.div<{ $style: TextContainerStyleType, $horizontalAlignment : any }>`
@@ -97,23 +95,47 @@ const FloatTextWrapper = styled.div<{ $style: TextContainerStyleType, $horizonta
 `;
 
 const HeaderInnerGrid = styled(InnerGrid)<{
-  $backgroundColor: string
+  $backgroundColor: string,
+  $headerBackgroundImage: string,
+  $headerBackgroundImageSize: string,
+  $headerBackgroundImageRepeat: string,
+  $headerBackgroundImageOrigin: string,
+  $headerBackgroundImagePosition: string,
  }>`
   overflow: visible;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$headerBackgroundImage,
+    backgroundImageSize: props.$headerBackgroundImageSize,
+    backgroundImageRepeat: props.$headerBackgroundImageRepeat,
+    backgroundImageOrigin: props.$headerBackgroundImageOrigin,
+    backgroundImagePosition: props.$headerBackgroundImagePosition,
+  })}
 `;
 
 const BodyInnerGrid = styled(InnerGrid)<{
   $showBorder: boolean;
-  $backgroundColor: string;
   $borderColor: string;
   $borderWidth: string;
+  $backgroundColor: string;
+  $bodyBackgroundImage: string;
+  $bodyBackgroundImageRepeat: string;
+  $bodyBackgroundImageSize: string;
+  $bodyBackgroundImagePosition: string;
+  $bodyBackgroundImageOrigin: string;
 }>`
   border-top: ${(props) => `${props.$showBorder ? props.$borderWidth : 0} solid ${props.$borderColor}`};
   flex: 1;
-  ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$bodyBackgroundImage,
+    backgroundImageSize: props.$bodyBackgroundImageSize,
+    backgroundImageRepeat: props.$bodyBackgroundImageRepeat,
+    backgroundImageOrigin: props.$bodyBackgroundImageOrigin,
+    backgroundImagePosition: props.$bodyBackgroundImagePosition,
+  })}
 `;
 
 const FooterInnerGrid = styled(InnerGrid)<{
@@ -131,11 +153,14 @@ const FooterInnerGrid = styled(InnerGrid)<{
   overflow: visible;
   ${(props) => props.$backgroundColor && `background-color: ${props.$backgroundColor};`}
   border-radius: 0;
-  ${(props) => props.$footerBackgroundImage && `background-image: url(${props.$footerBackgroundImage});`}
-  ${(props) => props.$footerBackgroundImageRepeat && `background-repeat: ${props.$footerBackgroundImageRepeat};`}
-  ${(props) => props.$footerBackgroundImageSize && `background-size: ${props.$footerBackgroundImageSize};`}
-  ${(props) => props.$footerBackgroundImagePosition && `background-position: ${props.$footerBackgroundImagePosition};`}
-  ${(props) => props.$footerBackgroundImageOrigin && `background-origin: ${props.$footerBackgroundImageOrigin};`}
+  ${props => getBackgroundStyle({
+    background: props.$backgroundColor,
+    backgroundImage: props.$footerBackgroundImage,
+    backgroundImageSize: props.$footerBackgroundImageSize,
+    backgroundImageRepeat: props.$footerBackgroundImageRepeat,
+    backgroundImageOrigin: props.$footerBackgroundImageOrigin,
+    backgroundImagePosition: props.$footerBackgroundImagePosition,
+  })}
 `;
 
 export type TriContainerProps = TriContainerViewProps & {
@@ -190,6 +215,11 @@ export function TriContainer(props: TriContainerProps) {
             containerPadding={[0, 0]}
             showName={{ bottom: showFooter ? 20 : 0 }}
             $backgroundColor={headerStyle?.headerBackground || 'transparent'}
+            $headerBackgroundImage={headerStyle?.headerBackgroundImage}
+            $headerBackgroundImageRepeat={headerStyle?.headerBackgroundImageRepeat}
+            $headerBackgroundImageSize={headerStyle?.headerBackgroundImageSize}
+            $headerBackgroundImagePosition={headerStyle?.headerBackgroundImagePosition}
+            $headerBackgroundImageOrigin={headerStyle?.headerBackgroundImageOrigin}
             style={{ padding: headerStyle.containerHeaderPadding}} />
         </BackgroundColorContext.Provider>
       )}
@@ -208,6 +238,11 @@ export function TriContainer(props: TriContainerProps) {
                 containerPadding={[0, 0]}
                 hintPlaceholder={props.hintPlaceholder ?? HintPlaceHolder}
                 $backgroundColor={bodyStyle?.background || 'transparent'}
+                $bodyBackgroundImage={bodyStyle?.backgroundImage}
+                $bodyBackgroundImageRepeat={bodyStyle?.backgroundImageRepeat}
+                $bodyBackgroundImageSize={bodyStyle?.backgroundImageSize}
+                $bodyBackgroundImagePosition={bodyStyle?.backgroundImagePosition}
+                $bodyBackgroundImageOrigin={bodyStyle?.backgroundImageOrigin}
                 $borderColor={style?.border}
                 $borderWidth={style?.borderWidth}
                 style={{
diff --git a/client/packages/lowcoder/src/comps/controls/codeControl.tsx b/client/packages/lowcoder/src/comps/controls/codeControl.tsx
index 33103f911..2ae75d7a0 100644
--- a/client/packages/lowcoder/src/comps/controls/codeControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/codeControl.tsx
@@ -28,6 +28,7 @@ import {
 import {
   ControlPropertyViewWrapper,
   isValidColor,
+  isValidGradient,
   toHex,
   wrapperToControlItem,
 } from "lowcoder-design";
@@ -432,7 +433,7 @@ export function stringUnionControl<T extends readonly string[]>(
 export const ColorCodeControl = codeControl<string>(
   (value: unknown) => {
     const valueString = toString(value);
-
+    
     if (valueString === "") {
       return valueString;
     }
@@ -442,6 +443,9 @@ export const ColorCodeControl = codeControl<string>(
     if (isThemeColorKey(valueString)) {
       return valueString;
     }
+    if (isValidGradient(valueString)) {
+      return valueString;
+    }
     throw new Error(`the argument must be type CSS color`);
   },
   {
@@ -465,6 +469,9 @@ export const ColorOrBoolCodeControl = codeControl<string>(
     if (isThemeColorKey(valueString)) {
       return valueString;
     }
+    if (isValidGradient(valueString)) {
+      return valueString;
+    }
     throw new Error(`the argument must be type CSS color or Boolean`);
   },
   {
diff --git a/client/packages/lowcoder/src/comps/controls/colorControl.tsx b/client/packages/lowcoder/src/comps/controls/colorControl.tsx
index 7eb7a24d9..6b45a982d 100644
--- a/client/packages/lowcoder/src/comps/controls/colorControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/colorControl.tsx
@@ -1,9 +1,10 @@
 import { ColorCodeControl } from "./codeControl";
 import { ColorSelect, controlItem, ControlPropertyViewWrapper, IconDep } from "lowcoder-design";
 import styled from "styled-components";
-import React, { useEffect, useState } from "react";
+import React, { useContext, useEffect, useState } from "react";
 import { ControlParams } from "./controlParams";
 import { trans } from "i18n";
+import { useThemeColors } from "@lowcoder-ee/util/hooks";
 
 const ColorContainer = styled.div`
   display: inline-flex;
@@ -72,6 +73,7 @@ type PropertyViewParam = {
   isDep?: boolean;
   // auto-generated message?
   depMsg?: string;
+  allowGradient?: boolean;
 };
 
 export class ColorControl extends ColorCodeControl {
@@ -93,6 +95,8 @@ function ColorItem(props: {
   const [focus, setFocus] = useState(false);
   const inputRef = React.createRef<HTMLDivElement>();
   const containerRef = React.createRef<HTMLDivElement>();
+  
+  const currentThemeColors = useThemeColors(param.allowGradient);
 
   const input = propertyView.call(controlThis, {
     placeholder: param.panelDefaultColor,
@@ -112,6 +116,7 @@ function ColorItem(props: {
   }, [focus]);
 
   const color = controlThis.getView();
+
   useEffect(() => {
     setShowDep(param.isDep && !focus && !color);
   }, [color, focus, param.isDep]);
@@ -134,6 +139,8 @@ function ColorItem(props: {
         <ColorSelect
           dispatch={controlThis.dispatch}
           color={param.panelDefaultColor || color || DEFAULT_COLOR}
+          allowGradient={param.allowGradient}
+          presetColors={currentThemeColors}
         />
         <div style={{ display: "flex" }}>
           <DepStyle
diff --git a/client/packages/lowcoder/src/comps/controls/labelControl.tsx b/client/packages/lowcoder/src/comps/controls/labelControl.tsx
index 705ba099f..bfe9a42c0 100644
--- a/client/packages/lowcoder/src/comps/controls/labelControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/labelControl.tsx
@@ -26,6 +26,7 @@ type LabelViewProps = Pick<FormItemProps, "required" | "help" | "validateStatus"
   childrenInputFieldStyle?: Record<string, string>;
   animationStyle?: Record<string, string>;
   onMouseDown?: React.MouseEventHandler<HTMLDivElement>;
+  showValidationWhenEmpty?: boolean;
 };
 
 const StyledStarIcon = styled(StarIcon)`
@@ -77,7 +78,7 @@ const MainWrapper = styled.div<{
     props.$position === "column" && props.$hasLabel ? "calc(100% - 4px)" : "100%"};
   display: flex;
   align-items: ${(props) => (props.$position === "row" ? "center" : "start")};
-  flex-shrink: 0;
+  flex-shrink: 1;
 `;
 
 const LabelWrapper = styled.div<{
@@ -178,6 +179,12 @@ export const LabelControl = (function () {
 
   return new MultiCompBuilder(childrenMap, (props) => (args: LabelViewProps) => 
   {
+    const inputValue = (
+      ((args.children as ReactElement)?.props?.children as ReactElement)?.props?.value // text area comp
+      ?? (args.children as ReactElement)?.props.value?.value // number input comp
+      ?? (args.children as ReactElement)?.props.value
+    );
+
     return (
       <LabelViewWrapper
         $style={args.style}
@@ -245,7 +252,10 @@ export const LabelControl = (function () {
           </ChildrenWrapper>
         </MainWrapper>
 
-        {args.help && Boolean((args.children as ReactElement)?.props.value) && (
+        {args.help && (
+          args.showValidationWhenEmpty
+          || (!args.showValidationWhenEmpty && Boolean(inputValue))
+        ) && (
           <HelpWrapper
             $marginLeft={
               props.position === "column" || isEmpty(props.text) || props.hidden
diff --git a/client/packages/lowcoder/src/comps/controls/styleControl.tsx b/client/packages/lowcoder/src/comps/controls/styleControl.tsx
index a61df72f0..a9f8372b7 100644
--- a/client/packages/lowcoder/src/comps/controls/styleControl.tsx
+++ b/client/packages/lowcoder/src/comps/controls/styleControl.tsx
@@ -829,6 +829,48 @@ const ResetIcon = styled(IconReset)`
   }
 `;
 
+const useThemeStyles = (
+  styleKey: string,
+  props: Record<string, string | undefined>,
+) => {
+  const editorState = useContext(EditorContext);
+  const {comp, compType} = useContext(CompContext);
+  const theme = useContext(ThemeContext);
+  const bgColor = useContext(BackgroundColorContext);
+  const { themeId } = theme || {};
+  const isPreviewTheme = themeId === 'preview-theme';
+  const isDefaultTheme = themeId === 'default-theme-id';
+
+  const appSettingsComp = editorState?.getAppSettingsComp();
+  const preventAppStylesOverwriting = appSettingsComp?.getView()?.preventAppStylesOverwriting;
+  const { appliedThemeId, preventStyleOverwriting } = (comp?.comp || {});
+  const appTheme = isPreviewTheme || isDefaultTheme || (!preventStyleOverwriting && !preventAppStylesOverwriting)
+    ? theme?.theme
+    : defaultTheme;
+  let compTheme: JSONValue|undefined = {};
+  if (appliedThemeId !== themeId) {
+    compTheme = isPreviewTheme || isDefaultTheme || (compType && !preventStyleOverwriting && !preventAppStylesOverwriting)
+      ? {
+          ...(omit(defaultTheme, 'components', 'chart')),
+          ...defaultTheme.components?.[compType]?.[styleKey] as unknown as Record<string, string>,
+          ...(omit(theme?.theme, 'components', 'chart')),
+          ...theme?.theme?.components?.[compType]?.[styleKey] as unknown as Record<string, string>,
+        }
+      : defaultTheme.components?.[compType]?.[styleKey];
+  }
+  const styleProps = (!comp && !compType) || preventStyleOverwriting || preventAppStylesOverwriting || appliedThemeId === themeId
+    ? props
+    : {};
+
+  return {
+    appTheme,
+    styleProps,
+    bgColor,
+    compTheme,
+    compType,
+  }
+};
+
 export function styleControl<T extends readonly SingleColorConfig[]>(
   colorConfigs: T,
   styleKey: string = '',
@@ -888,39 +930,16 @@ export function styleControl<T extends readonly SingleColorConfig[]>(
   return new ControlItemCompBuilder(
     childrenMap as ToConstructor<{ [K in Names<T>]: ColorControl }>,
     (props) => {
-      // const compType = useContext(CompTypeContext);
-      const editorState = useContext(EditorContext);
-      const {comp, compType} = useContext(CompContext);
-      const theme = useContext(ThemeContext);
-      const bgColor = useContext(BackgroundColorContext);
-      const { themeId } = theme || {};
-      const isPreviewTheme = themeId === 'preview-theme';
-      const isDefaultTheme = themeId === 'default-theme-id';
-
-
-      const appSettingsComp = editorState?.getAppSettingsComp();
-      const preventAppStylesOverwriting = appSettingsComp?.getView()?.preventAppStylesOverwriting;
-      const { appliedThemeId, preventStyleOverwriting } = (comp?.comp || {});
-      const appTheme = isPreviewTheme || isDefaultTheme || (!preventStyleOverwriting && !preventAppStylesOverwriting)
-        ? theme?.theme
-        : defaultTheme;
-      let compTheme: JSONValue|undefined = {};
-      if (appliedThemeId !== themeId) {
-        compTheme = isPreviewTheme || isDefaultTheme || (compType && !preventStyleOverwriting && !preventAppStylesOverwriting)
-          ? {
-              ...(omit(defaultTheme, 'components', 'chart')),
-              ...defaultTheme.components?.[compType]?.[styleKey] as unknown as Record<string, string>,
-              ...(omit(theme?.theme, 'components', 'chart')),
-              ...theme?.theme?.components?.[compType]?.[styleKey] as unknown as Record<string, string>,
-            }
-          : defaultTheme.components?.[compType]?.[styleKey];
-      }
-      const styleProps = (!comp && !compType) || preventStyleOverwriting || preventAppStylesOverwriting || appliedThemeId === themeId
-        ? props as ColorMap
-        : {} as ColorMap;
+      const {
+        styleProps,
+        appTheme,
+        bgColor,
+        compTheme,
+        compType,
+      } = useThemeStyles(styleKey, props as Record<string, string>);
 
       return calcColors(
-        styleProps,
+        styleProps as ColorMap,
         colorConfigs,
         appTheme,
         bgColor,
@@ -932,21 +951,26 @@ export function styleControl<T extends readonly SingleColorConfig[]>(
   )
     .setControlItemData({ filterText: label, searchChild: true })
     .setPropertyViewFn((children) => {
-      const theme = useContext(ThemeContext);
-      const compType = useContext(CompTypeContext);
-      const bgColor = useContext(BackgroundColorContext);
       const isMobile = useIsMobile();
-      const compTheme = compType
-        ? theme?.theme?.components?.[compType]?.[styleKey]
-        : undefined;
+      const childrenProps = childrenToProps(children) as Record<string, string>;
+      const {
+        styleProps,
+        appTheme,
+        bgColor,
+        compTheme,
+        compType,
+      } = useThemeStyles(styleKey, childrenProps);
 
       const props = calcColors(
-        childrenToProps(children) as ColorMap,
+        styleProps as ColorMap,
         colorConfigs,
-        theme?.theme,
+        appTheme,
         bgColor,
         compTheme as Record<string, string> | undefined,
+        compType,
+        styleKey,
       );
+
       const showReset = Object.values(childrenToProps(children)).findIndex((item) => item) > -1;
       return (
         <>
@@ -1341,6 +1365,7 @@ export function styleControl<T extends readonly SingleColorConfig[]>(
                                                                   isDep: true,
                                                                   depMsg:
                                                                     depMsg,
+                                                                  allowGradient: config.name.includes('background'),
                                                                 })}
                   </div>
                 );
diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
index d8d6d2422..fca765ca3 100644
--- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
+++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
@@ -1353,7 +1353,6 @@ export const RadioStyle = [
 ] as const;
 
 export const SegmentStyle = [
-  LABEL,
   ...STYLING_FIELDS_SEQUENCE.filter(
     (style) => ["border", "borderWidth"].includes(style.name) === false
   ),
@@ -1986,6 +1985,7 @@ export const RichTextEditorStyle = [
 ] as const;
 
 export type QRCodeStyleType = StyleConfigType<typeof QRCodeStyle>;
+export type TimeLineStyleType = StyleConfigType<typeof TimeLineStyle>;
 export type AvatarStyleType = StyleConfigType<typeof AvatarStyle>;
 export type AvatarLabelStyleType = StyleConfigType<typeof avatarLabelStyle>;
 export type AvatarContainerStyleType = StyleConfigType<
diff --git a/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx b/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
index 43fa3e5b7..6c04fa8aa 100644
--- a/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/drawerComp.tsx
@@ -136,7 +136,7 @@ let TmpDrawerComp = (function () {
                 },
                 body: {
                   padding: 0,
-                  backgroundColor: props.style.background
+                  background: props.style.background
                 }
               }}
               title={props.title}
diff --git a/client/packages/lowcoder/src/comps/hooks/modalComp.tsx b/client/packages/lowcoder/src/comps/hooks/modalComp.tsx
index ed2710ed5..2a3235f9d 100644
--- a/client/packages/lowcoder/src/comps/hooks/modalComp.tsx
+++ b/client/packages/lowcoder/src/comps/hooks/modalComp.tsx
@@ -23,6 +23,7 @@ import { NameConfig, withExposingConfigs } from "../generators/withExposing";
 import { BoolControl } from "comps/controls/boolControl";
 import { withDefault } from "comps/generators";
 import SliderControl from "../controls/sliderControl";
+import { getBackgroundStyle } from "@lowcoder-ee/util/styleUtils";
 
 const EventOptions = [
   { label: trans("modalComp.open"), value: "open", description: trans("modalComp.openDesc") },
@@ -35,13 +36,9 @@ const getStyle = (style: ModalStyleType, modalScrollbar: boolean) => {
       border-radius: ${style.radius};
       border: ${style.borderWidth} solid ${style.border};
       overflow: hidden;
-      background-color: ${style.background};
-      ${style.backgroundImage ? `background-image: url(${style.backgroundImage}) !important; ` : ';'}
-      ${style.backgroundImageRepeat ? `background-repeat: ${style.backgroundImageRepeat};` : 'no-repeat;'}
-      ${style.backgroundImageSize ? `background-size: ${style.backgroundImageSize};` : 'cover'}
-      ${style.backgroundImagePosition ? `background-position: ${style.backgroundImagePosition};` : 'center;'}
-      ${style.backgroundImageOrigin ? `background-origin: ${style.backgroundImageOrigin};` : 'padding-box;'}
       margin: ${style.margin};
+      ${getBackgroundStyle(style)}
+      
       .ant-modal-body > .react-resizable > .react-grid-layout {
         background-color: ${style.background};
       }
diff --git a/client/packages/lowcoder/src/comps/utils/appSettingContext.tsx b/client/packages/lowcoder/src/comps/utils/appSettingContext.tsx
new file mode 100644
index 000000000..8b442b3d7
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/utils/appSettingContext.tsx
@@ -0,0 +1,7 @@
+import React from "react";
+
+export type AppSettingType = 'setting' | 'canvas';
+
+export const AppSettingContext = React.createContext<{
+  settingType: AppSettingType;
+}>({ settingType: 'setting' });
diff --git a/client/packages/lowcoder/src/constants/style.ts b/client/packages/lowcoder/src/constants/style.ts
index c7787ea77..e21136c11 100644
--- a/client/packages/lowcoder/src/constants/style.ts
+++ b/client/packages/lowcoder/src/constants/style.ts
@@ -1,5 +1,5 @@
 // brand color
-export const PrimaryColor = "#3377ff";
+export const PrimaryColor = "#b85fff";
 
 // module
 export const ModulePrimaryColor = "#F27A24";
@@ -21,10 +21,10 @@ export const DarkActiveTextColor = "#222222";
 export const ActiveTextColor = "#315efb";
 
 // icon
-export const NormalMenuIconColor = "#8B8FA3";
+export const NormalMenuIconColor = "#b480de";
 
 // Ui Comp
-export const UiCompBorderRadius = "2px";
+export const UiCompBorderRadius = "4px";
 
 // Tab
 export const TabActiveColor = "#222222";
diff --git a/client/packages/lowcoder/src/constants/themeConstants.ts b/client/packages/lowcoder/src/constants/themeConstants.ts
index 827602d7d..144486380 100644
--- a/client/packages/lowcoder/src/constants/themeConstants.ts
+++ b/client/packages/lowcoder/src/constants/themeConstants.ts
@@ -1,4 +1,5 @@
 import { ThemeDetail } from "@lowcoder-ee/api/commonSettingApi";
+import { DEFAULT_GRID_COLUMNS, DEFAULT_ROW_COUNT, DEFAULT_ROW_HEIGHT } from "@lowcoder-ee/layout/calculateUtils";
 
 const theme = {
   primary: "#3377FF",
@@ -13,7 +14,9 @@ const theme = {
   margin: "3px",
   padding: "3px",
   lineHeight: "18px",
-  gridColumns: "24",
+  gridColumns: String(DEFAULT_GRID_COLUMNS),
+  gridRowHeight: String(DEFAULT_ROW_HEIGHT),
+  gridRowCount: DEFAULT_ROW_COUNT,
   textSize: "14px",
   // text: "#222222",
   animation: "",
@@ -25,6 +28,10 @@ const theme = {
   animationIterationCount: "",
   showComponentLoadingIndicators: true,
   showDataLoadingIndicators: true,
+  gridBgImageSize: "cover",
+  gridBgImagePosition: "center",
+  gridBgImageRepeat: "no-repeat",
+  gridBgImageOrigin: "padding-box",
 };
 
 const text = {
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index 108d3f8d4..61450a61f 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -163,6 +163,7 @@ export const en = {
     "showSearch": "Searchable",
     "defaultValue": "Default Value",
     "required": "Required Field",
+    "showEmptyValidation": "Show Validation On Empty/Reset",
     "readOnly": "Read Only",
     "readOnlyTooltip": "Read-only components appear normal but cannot be modified.",
     "minimum": "Minimum",
@@ -2580,7 +2581,8 @@ export const en = {
     "chartClick": "Click",
     "chartVisit": "Visit",
     "chartQuery": "Query",
-    "chartBuy": "Buy"
+    "chartBuy": "Buy",
+    "canvas": "Canvas Settings",
   },
   "themeDetail": {
     "primary": "Brand Color",
@@ -2616,11 +2618,31 @@ export const en = {
     "paddingDesc": "Default padding typically used for most components",
     "containerHeaderPadding": "Header Padding",
     "containerheaderpaddingDesc": "Default header padding typically used for most components",
-    "gridColumns": "Canvas Grid Columns",
+    "gridColumns": "Grid Columns",
     "gridColumnsDesc": "Default number of columns typically used for most containers",
     "loadingIndicators": "Loading Indicators",
     "showComponentLoadingIndicators": "Show loading indicators when component load",
-    "showDataLoadingIndicators": "Show loading indicators when data load"
+    "showDataLoadingIndicators": "Show loading indicators when data load",
+    "background": "Background Styles",
+    "gridSettings": "Grid Settings",
+    "gridRowHeight": "Grid Row Height",
+    "gridRowHeightDesc": "Height of each row in grid",
+    "gridRowCount": "Grid Row Count",
+    "gridRowCountDesc": "Max. number of rows in grid",
+    "gridPaddingX": "Horizontal Padding",
+    "gridPaddingXDesc": "Canvas horizontal padding",
+    "gridPaddingY": "Vertical Padding",
+    "gridPaddingYDesc": "Canvas vertical padding",
+    "gridBgImage": "Background Image",
+    "gridBgImageDesc": "Canvas background image",
+    "gridBgImageRepeat": "Background Image Repeat",
+    "gridBgImageRepeatDesc": "Canvas background image repeat",
+    "gridBgImageSize": "Background Image Size",
+    "gridBgImageSizeDesc": "Canvas background image size",
+    "gridBgImagePosition": "Background Image Position",
+    "gridBgImagePositionDesc": "Canvas background image position",
+    "gridBgImageOrigin": "Background Image Origin",
+    "gridBgImageOriginDesc": "Canvas background image origin",
   },
   "pluginSetting": {
     "title": "Plugins",
@@ -2784,7 +2806,18 @@ export const en = {
     "appTitle": "Title",
     "appDescription": "Description",
     "appCategory": "Category",
-    "showPublicHeader": "Show header in public view"
+    "showPublicHeader": "Show header in public view",
+    "canvas": "Canvas Settings",
+    "gridColumns": "Grid Columns",
+    "gridRowHeight": "Grid Row Height",
+    "gridRowCount": "Grid Row Count",
+    "gridPaddingX": "Canvas Horizontal Padding",
+    "gridPaddingY": "Canvas Vertical Padding",
+    "gridBgImage": "Background Image",
+    "gridBgImageRepeat": "Background Image Repeat",
+    "gridBgImageSize": "Background Image Size",
+    "gridBgImagePosition": "Background Image Position",
+    "gridBgImageOrigin": "Background Image Origin"
   },
   "customShortcut": {
     "title": "Custom Shortcuts",
diff --git a/client/packages/lowcoder/src/layout/calculateUtils.tsx b/client/packages/lowcoder/src/layout/calculateUtils.tsx
index 587130f2d..1d919bd1b 100644
--- a/client/packages/lowcoder/src/layout/calculateUtils.tsx
+++ b/client/packages/lowcoder/src/layout/calculateUtils.tsx
@@ -7,18 +7,9 @@ export type PositionParams = Pick<
   "margin" | "containerPadding" | "containerWidth" | "cols" | "rowHeight" | "maxRows"
 >;
 
-// Added By Aqib Mirza
-let gridColumns: number;
-
-const getDefaultGridColumns = () => {
-  return gridColumns;
-};
-
-export { getDefaultGridColumns };
-export const DEFAULT_GRID_COLUMNS = getDefaultGridColumns() || 24;
-//////////////////////
-
+export const DEFAULT_GRID_COLUMNS = 24;
 export const DEFAULT_ROW_HEIGHT = 8;
+export const DEFAULT_ROW_COUNT = Infinity;
 
 export const DEFAULT_POSITION_PARAMS: PositionParams = {
   margin: [0, 0],
@@ -26,7 +17,7 @@ export const DEFAULT_POSITION_PARAMS: PositionParams = {
   containerWidth: 0,
   cols: DEFAULT_GRID_COLUMNS,
   rowHeight: DEFAULT_ROW_HEIGHT,
-  maxRows: Infinity,
+  maxRows: DEFAULT_ROW_COUNT,
 };
 
 // Helper for generating column width
@@ -102,6 +93,7 @@ export function calcGridItemSizePx(
   const width = calcGridItemWHPx(w, colWidth, margin[0], false);
 
   const isTouchSBound = top ? isTouchBound(maxRows, rowHeight, h, top) : false;
+  // console.log('positionParams',positionParams);
   const height = calcGridItemWHPx(h, rowHeight, margin[1], isTouchSBound);
   return { width, height };
 }
diff --git a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
index fd812a8b2..a0923d486 100644
--- a/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
+++ b/client/packages/lowcoder/src/layout/compSelectionWrapper.tsx
@@ -35,7 +35,7 @@ const NameDiv = styled.div<{
     }
     return "#B8B9BF";
   }};
-  border-radius: ${(props) => (props.$position === "top" ? "2px 2px 0 0" : "0 0 2px 2px")};
+  border-radius: ${(props) => (props.$position === "top" ? "4px 4px 0 0" : "0 0 4px 4px")};
   font-weight: 500;
   color: #ffffff;
   position: absolute;
@@ -89,13 +89,15 @@ function getLineStyle(
 
   return `
       border: ${GRID_ITEM_BORDER_WIDTH}px ${borderStyle} ${borderColor};
-      padding: ${isHidden || !isSelected ? 0 : padding[1] - GRID_ITEM_BORDER_WIDTH}px;
-      padding-left: ${padding[0] - GRID_ITEM_BORDER_WIDTH}px;
-      padding-right: ${padding[0] - GRID_ITEM_BORDER_WIDTH}px;
+      padding: 0px;
+      // padding: ${isHidden || !isSelected ? 0 : padding[1] - GRID_ITEM_BORDER_WIDTH}px;
+      // padding-left: ${padding[0] - GRID_ITEM_BORDER_WIDTH}px;
+      // padding-right: ${padding[0] - GRID_ITEM_BORDER_WIDTH}px;
   `;
 }
 
 // padding: ${props => props.hover || props.showDashline ? 3 : 4}px;
+
 const SelectableDiv = styled.div<{
   $hover?: boolean;
   $showDashLine: boolean;
@@ -173,7 +175,7 @@ const dragCss = (props: DragHandleProps, handle: ResizeHandleAxis) => css`
   height: 8px;
   width: 8px;
   border: 1px solid ${props.$compType === "module" ? ModulePrimaryColor : PrimaryColor};
-  border-radius: 2px;
+  border-radius: 4px;
   background-color: #f5f5f6;
   z-index: 11;
   pointer-events: none;
diff --git a/client/packages/lowcoder/src/layout/gridItem.tsx b/client/packages/lowcoder/src/layout/gridItem.tsx
index 432c9b453..963c1c056 100644
--- a/client/packages/lowcoder/src/layout/gridItem.tsx
+++ b/client/packages/lowcoder/src/layout/gridItem.tsx
@@ -103,15 +103,15 @@ const ResizableStyled = styled(Resizable)<{ $zIndex: number, isDroppable : boole
  * An individual item within a ReactGridLayout.
  */
 export const GridItem = React.memo((props: GridItemProps) => {
-  const position = useMemo(() =>
-    calcGridItemPosition({
+  const position = useMemo(() =>{
+    return calcGridItemPosition({
       margin: props.margin,
       containerPadding: props.containerPadding,
       containerWidth: props.containerWidth,
       cols: props.cols,
       rowHeight: props.rowHeight,
       maxRows: props.maxRows,
-    }, props.x, props.y, props.w, props.h),
+    }, props.x, props.y, props.w, props.h)},
     [
       props.margin,
       props.containerPadding,
@@ -126,6 +126,7 @@ export const GridItem = React.memo((props: GridItemProps) => {
       calcGridItemPosition,
     ]
   );
+
   const [resizing, setResizing] = useState<{ width: number; height: number } | undefined>();
   const [dragging, setDragging] = useState<{ top: number; left: number } | undefined>();
   const elementRef = useRef<HTMLDivElement>(null);
@@ -345,6 +346,7 @@ export const GridItem = React.memo((props: GridItemProps) => {
       Math.min(maxes.width, Infinity),
       Math.min(maxes.height, Infinity),
     ];
+
     return (
       <ResizableStyled // These are opts for the resize handle itself
         draggableOpts={{
diff --git a/client/packages/lowcoder/src/layout/gridLayout.tsx b/client/packages/lowcoder/src/layout/gridLayout.tsx
index 31f2054bf..47ce657ea 100644
--- a/client/packages/lowcoder/src/layout/gridLayout.tsx
+++ b/client/packages/lowcoder/src/layout/gridLayout.tsx
@@ -14,6 +14,7 @@ import {
   calcRowCount,
   calcWH,
   calcXY,
+  DEFAULT_ROW_COUNT,
   genPositionParams,
   PositionParams,
 } from "./calculateUtils";
@@ -212,14 +213,15 @@ class GridLayout extends React.Component<GridLayoutProps, GridLayoutState> {
    * @return {String} Container height in pixels.
    */
   containerHeight(): string {
-    const { margin, rowHeight } = this.props as Required<GridLayoutProps>;
+    const { margin, rowHeight, fixedRowCount } = this.props as Required<GridLayoutProps>;
     const { extraHeight, emptyRows } = this.props;
     const positionParams = genPositionParams(this.props);
+
     const { containerPadding } = positionParams;
     const layout = this.getUILayout(undefined, true);
     let nbRow = bottom(layout);
-    if (!_.isNil(emptyRows) && _.size(layout) === 0) {
-      nbRow = emptyRows;
+    if (!_.isNil(emptyRows) && (_.size(layout) === 0 || fixedRowCount)) {
+      nbRow = emptyRows;// === Infinity ? 0 : emptyRows;
     }
     const containerHeight = Math.max(
       nbRow * rowHeight + (nbRow - 1) * margin[1] + containerPadding[1] * 2
@@ -341,7 +343,7 @@ class GridLayout extends React.Component<GridLayoutProps, GridLayoutState> {
   onLayoutMaybeChanged(newLayout: Layout, oldLayout?: Layout) {
     // log.debug("layout: layoutMayBeChanged. oldLayout: ", oldLayout, " newLayout: ", newLayout);
     if (!oldLayout) oldLayout = this.state.layout;
-
+    
     if (!_.isEqual(oldLayout, newLayout)) {
       this.props.onLayoutChange?.(newLayout);
     }
@@ -1046,6 +1048,7 @@ class GridLayout extends React.Component<GridLayoutProps, GridLayoutState> {
         $radius={this.props.radius}
         $autoHeight={this.props.autoHeight}
         $overflow={this.props.overflow}
+        $maxRows={this.props.emptyRows}
         tabIndex={-1}
         onDrop={isDroppable ? this.onDrop : _.noop}
         onDragLeave={isDroppable ? this.onDragLeave : _.noop}
@@ -1081,15 +1084,19 @@ const LayoutContainer = styled.div<{
   $bgColor?: string;
   $autoHeight?: boolean;
   $overflow?: string;
+  $maxRows?: number;
   $radius?: string;
 }>`
   border-radius: ${(props) => props.$radius ?? "4px"};
-  background-color: ${(props) => props.$bgColor ?? "#f5f5f6"};
+  // background-color: ${(props) => props.$bgColor ?? "#f5f5f6"};
   /* height: 100%; */
   height: ${(props) => (props.$autoHeight ? "auto" : "100%")};
 
-  overflow: auto;
-  overflow: ${(props) => props.$overflow ?? "overlay"};
+  overflow: ${(props) =>
+    props.$maxRows !== DEFAULT_ROW_COUNT
+    ? 'hidden'
+    : props.$overflow ?? "overlay"
+  };
   ${(props) =>
     props.$autoHeight &&
     `::-webkit-scrollbar {
diff --git a/client/packages/lowcoder/src/layout/gridLayoutPropTypes.tsx b/client/packages/lowcoder/src/layout/gridLayoutPropTypes.tsx
index e7e2708d5..735b2ea94 100644
--- a/client/packages/lowcoder/src/layout/gridLayoutPropTypes.tsx
+++ b/client/packages/lowcoder/src/layout/gridLayoutPropTypes.tsx
@@ -34,6 +34,7 @@ export type GridLayoutProps = {
   containerPadding?: [number, number];
   rowHeight?: number;
   isRowCountLocked?: boolean;
+  fixedRowCount?: boolean;
   emptyRows?: number;
   maxRows?: number;
   isDraggable?: boolean;
diff --git a/client/packages/lowcoder/src/pages/editor/editorView.tsx b/client/packages/lowcoder/src/pages/editor/editorView.tsx
index 80a621514..d807771ef 100644
--- a/client/packages/lowcoder/src/pages/editor/editorView.tsx
+++ b/client/packages/lowcoder/src/pages/editor/editorView.tsx
@@ -14,6 +14,8 @@ import {
   LeftSettingIcon,
   LeftStateIcon,
   LeftLayersIcon,
+  LeftColorPaletteIcon,
+  LeftJSSettingIcon,
   ScrollBar,
 } from "lowcoder-design";
 import { useTemplateViewMode } from "util/hooks";
@@ -55,6 +57,7 @@ import { isAggregationApp } from "util/appUtils";
 import EditorSkeletonView from "./editorSkeletonView";
 import { getCommonSettings } from "@lowcoder-ee/redux/selectors/commonSettingSelectors";
 import { isEqual, noop } from "lodash";
+import { AppSettingContext, AppSettingType } from "@lowcoder-ee/comps/utils/appSettingContext";
 // import { BottomSkeleton } from "./bottom/BottomContent";
 
 const Header = lazy(
@@ -257,6 +260,8 @@ enum SiderKey {
   State = "state",
   Setting = "setting",
   Layout = "layout",
+  Canvas = "canvas",
+  JS = "js",
 }
 
 const standardSiderItems = [
@@ -268,6 +273,14 @@ const standardSiderItems = [
     key: SiderKey.Setting,
     icon: <LeftSettingIcon />,
   },
+  {
+    key: SiderKey.Canvas,
+    icon: <LeftColorPaletteIcon />,
+  },
+  {
+    key: SiderKey.JS,
+    icon: <LeftJSSettingIcon />,
+  },
   {
     key: SiderKey.Layout,
     icon: <LeftLayersIcon />,
@@ -536,40 +549,59 @@ function EditorView(props: EditorViewProps) {
               {panelStatus.left && editorModeStatus !== "layout" && (
                 <LeftPanel>
                   {menuKey === SiderKey.State && <LeftContent uiComp={uiComp} />}
-                  {menuKey === SiderKey.Setting && (
-                    <SettingsDiv>
-                      <ScrollBar>
-                        {application &&
-                          !isAggregationApp(
-                            AppUILayoutType[application.applicationType]
-                          ) && (
-                            <>
-                              {appSettingsComp.getPropertyView()}
-                              <Divider />
-                            </>
-                          )}
-                        <TitleDiv>{trans("leftPanel.toolbarTitle")}</TitleDiv>
-                        {props.preloadComp.getPropertyView()}
-                        <PreloadDiv
-                          onClick={() => dispatch(
-                            setEditorExternalStateAction({
-                              showScriptsAndStyleModal: true,
-                            })
-                          )}
-                        >
-                          <LeftPreloadIcon />
-                          {trans("leftPanel.toolbarPreload")}
-                        </PreloadDiv>
-                      </ScrollBar>
-
+                  <AppSettingContext.Provider value={{settingType: menuKey as AppSettingType}}>
+                    <>
+                      {menuKey === SiderKey.Setting && (
+                        <SettingsDiv>
+                          <ScrollBar>
+                            {application &&
+                              !isAggregationApp(
+                                AppUILayoutType[application.applicationType]
+                              ) && (
+                                <>
+                                  {appSettingsComp.getPropertyView()}
+                                </>
+                              )}
+                          </ScrollBar>
+                        </SettingsDiv>
+                      )}
+                      {menuKey === SiderKey.Canvas && (
+                        <SettingsDiv>
+                          <ScrollBar>
+                            {application &&
+                              !isAggregationApp(
+                                AppUILayoutType[application.applicationType]
+                              ) && (
+                                <>
+                                  {appSettingsComp.getPropertyView()}
+                                </>
+                              )}
+                          </ScrollBar>
+                        </SettingsDiv>
+                      )}
+                    </>
+                  </AppSettingContext.Provider>
+                  {menuKey === SiderKey.JS && (
+                    <>
+                      <TitleDiv>{trans("leftPanel.toolbarTitle")}</TitleDiv>
+                      {props.preloadComp.getPropertyView()}
+                      <PreloadDiv
+                        onClick={() => dispatch(
+                          setEditorExternalStateAction({
+                            showScriptsAndStyleModal: true,
+                          })
+                        )}
+                      >
+                        <LeftPreloadIcon />
+                        {trans("leftPanel.toolbarPreload")}
+                      </PreloadDiv>
+                      
                       {props.preloadComp.getJSLibraryPropertyView()}
-                    </SettingsDiv>
+                    </>
                   )}
-
                   {menuKey === SiderKey.Layout && (
                     <LeftLayersContent uiComp={uiComp} />
                   )}
-
                 </LeftPanel>
               )}
             </Suspense>
diff --git a/client/packages/lowcoder/src/pages/setting/theme/detail/index.tsx b/client/packages/lowcoder/src/pages/setting/theme/detail/index.tsx
index 8013a1b7c..a6e66f5e7 100644
--- a/client/packages/lowcoder/src/pages/setting/theme/detail/index.tsx
+++ b/client/packages/lowcoder/src/pages/setting/theme/detail/index.tsx
@@ -174,7 +174,7 @@ class ThemeDetailPage extends React.Component<ThemeDetailPageProps, ThemeDetailP
     this.setState({
       theme: {
         ...this.state.theme,
-        [params.themeSettingKey]: params.color || params.radius || params.chart || params.margin || params.padding  || params.gridColumns || params.borderWidth || params.borderStyle || params.fontFamily || params.showComponentLoadingIndicators || params.showDataLoadingIndicators,
+        [params.themeSettingKey]: params.color || params.radius || params.chart || params.margin || params.padding  || params.borderWidth || params.borderStyle || params.fontFamily || params.showComponentLoadingIndicators || params.showDataLoadingIndicators || params.gridColumns || params.gridRowHeight || params.gridRowCount || params.gridPaddingX || params.gridPaddingY || params.gridBgImage || params.gridBgImageRepeat || params.gridBgImageSize || params.gridBgImagePosition || params.gridBgImageOrigin,
       },
     });
   }
@@ -203,12 +203,6 @@ class ThemeDetailPage extends React.Component<ThemeDetailPageProps, ThemeDetailP
             desc: trans('themeDetail.primaryDesc'),
             color: this.state.theme?.primary,
           },
-          {
-            settingsKey: 'canvas',
-            name: trans('themeDetail.canvas'),
-            desc: trans('themeDetail.canvasDesc'),
-            color: this.state.theme?.canvas,
-          },
           {
             settingsKey: 'primarySurface',
             name: trans('themeDetail.primarySurface'),
@@ -302,13 +296,6 @@ class ThemeDetailPage extends React.Component<ThemeDetailPageProps, ThemeDetailP
             type: "padding",
             value: this.state.theme?.padding,
           },
-          {
-            settingsKey: 'gridColumns',
-            name: trans('themeDetail.gridColumns'),
-            desc: trans('themeDetail.gridColumnsDesc'),
-            type: "gridColumns",
-            value: this.state.theme?.gridColumns,
-          }
         ]
       },
       {
@@ -332,6 +319,101 @@ class ThemeDetailPage extends React.Component<ThemeDetailPageProps, ThemeDetailP
       },
     ];
 
+    const canvasSettings = [
+      {
+        title: trans('themeDetail.gridSettings'),
+        items: [
+          {
+            settingsKey: 'gridColumns',
+            name: trans('themeDetail.gridColumns'),
+            desc: trans('themeDetail.gridColumnsDesc'),
+            type: "gridColumns",
+            value: this.state.theme?.gridColumns,
+          },
+          {
+            settingsKey: 'gridRowHeight',
+            name: trans('themeDetail.gridRowHeight'),
+            desc: trans('themeDetail.gridRowHeightDesc'),
+            type: "gridRowHeight",
+            value: this.state.theme?.gridRowHeight,
+          },
+          {
+            settingsKey: 'gridRowCount',
+            name: trans('themeDetail.gridRowCount'),
+            desc: trans('themeDetail.gridRowCountDesc'),
+            type: "gridRowCount",
+            value: this.state.theme?.gridRowCount,
+          },
+        ]
+      },
+      {
+        title: trans('themeDetail.spacing'),
+        items: [
+          {
+            settingsKey: 'gridPaddingX',
+            name: trans('themeDetail.gridPaddingX'),
+            desc: trans('themeDetail.gridPaddingXDesc'),
+            type: "gridPaddingX",
+            value: this.state.theme?.gridPaddingX,
+          },
+          {
+            settingsKey: 'gridPaddingY',
+            name: trans('themeDetail.gridPaddingY'),
+            desc: trans('themeDetail.gridPaddingYDesc'),
+            type: "gridPaddingY",
+            value: this.state.theme?.gridPaddingY,
+          }
+        ]
+      },
+      {
+        title: trans('themeDetail.background'),
+        items: [
+          {
+            settingsKey: 'canvas',
+            type: "canvas",
+            name: trans('themeDetail.canvas'),
+            desc: trans('themeDetail.canvasDesc'),
+            color: this.state.theme?.canvas,
+          },
+          {
+            settingsKey: 'gridBgImage',
+            name: trans('themeDetail.gridBgImage'),
+            desc: trans('themeDetail.gridBgImageDesc'),
+            type: "gridBgImage",
+            value: this.state.theme?.gridBgImage,
+          },
+          {
+            settingsKey: 'gridBgImageRepeat',
+            name: trans('themeDetail.gridBgImageRepeat'),
+            desc: trans('themeDetail.gridBgImageRepeatDesc'),
+            type: "gridBgImageRepeat",
+            value: this.state.theme?.gridBgImageRepeat,
+          },
+          {
+            settingsKey: 'gridBgImageSize',
+            name: trans('themeDetail.gridBgImageSize'),
+            desc: trans('themeDetail.gridBgImageSizeDesc'),
+            type: "gridBgImageSize",
+            value: this.state.theme?.gridBgImageSize,
+          },
+          {
+            settingsKey: 'gridBgImagePosition',
+            name: trans('themeDetail.gridBgImagePosition'),
+            desc: trans('themeDetail.gridBgImagePositionDesc'),
+            type: "gridBgImagePosition",
+            value: this.state.theme?.gridBgImagePosition,
+          },
+          {
+            settingsKey: 'gridBgImageOrigin',
+            name: trans('themeDetail.gridBgImageOrigin'),
+            desc: trans('themeDetail.gridBgImageOriginDesc'),
+            type: "gridBgImageOrigin",
+            value: this.state.theme?.gridBgImageOrigin,
+          }
+        ]
+      },
+    ];
+
     if (!this.themeDefault) {
       return (
         <Flex align="center" justify="center" vertical style={{
@@ -468,6 +550,148 @@ class ThemeDetailPage extends React.Component<ThemeDetailPageProps, ThemeDetailP
                 </Flex>
               </Card>
             </ThemeSettingsView>
+            
+            <ThemeSettingsView>
+              <StyleThemeSettingsCover>
+                <PageLayoutCompIcon width={"36px"} style={{marginRight : "10px"}}/> <h2 style={{color: "#ffffff", marginTop : "8px"}}> {trans("theme.canvas")}</h2>
+              </StyleThemeSettingsCover>
+              <Card style={{ marginBottom: "20px", minHeight : "200px" }}>
+                <Flex gap={"middle"}>
+                  <List
+                    bordered
+                    dataSource={canvasSettings}
+                    renderItem={(item) => (
+                      <>
+                        {item.title && (
+                          <List.Item>
+                            <DetailTitle>{item.title}</DetailTitle>
+                          </List.Item>
+                        )}
+                        {item.items.map((canvasSettingItem) => (
+                          <Tooltip key={canvasSettingItem.settingsKey} title={canvasSettingItem.desc} placement="right">
+                            <List.Item key={canvasSettingItem.settingsKey}>
+                              {canvasSettingItem.type == "gridColumns" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridColumns={canvasSettingItem.value as string}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridRowHeight" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridRowHeight={canvasSettingItem.value as string}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridRowCount" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridRowCount={canvasSettingItem.value as number}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridPaddingX" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridPaddingX={canvasSettingItem.value as number}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridPaddingY" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridPaddingY={canvasSettingItem.value as number}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "canvas" &&
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  // desc={colorItem.desc}
+                                  color={(canvasSettingItem as any).color}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridBgImage" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridBgImage={canvasSettingItem.value as string}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridBgImageRepeat" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridBgImageRepeat={canvasSettingItem.value as string}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridBgImageSize" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridBgImageSize={canvasSettingItem.value as string}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridBgImagePosition" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridBgImagePosition={canvasSettingItem.value as string}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                              {canvasSettingItem.type == "gridBgImageOrigin" && 
+                                <ThemeSettingsSelector
+                                  themeSettingKey={canvasSettingItem.settingsKey}
+                                  name={canvasSettingItem.name}
+                                  gridBgImageOrigin={canvasSettingItem.value as string}
+                                  configChange={(params) => {
+                                    this.configChange(params);
+                                  }}
+                                />
+                              }
+                          </List.Item>
+                          </Tooltip>
+                        ))}
+                      </>
+                    )}
+                  />
+                  <Divider type="vertical" style={{height: "610px"}}/>
+                  <PreviewApp style={{marginTop: '3px', height: "620px", width: "100%"}} theme={this.state.theme!} dsl={dsl} />
+                </Flex>
+              </Card>
+            </ThemeSettingsView>
 
             <ThemeSettingsView>
               <StyleThemeSettingsCover>
@@ -538,23 +762,12 @@ class ThemeDetailPage extends React.Component<ThemeDetailPageProps, ThemeDetailP
                                   }}
                                 />
                               }
-                              {layoutSettingsItem.type == "gridColumns" && 
-                                <ThemeSettingsSelector
-                                  themeSettingKey={layoutSettingsItem.settingsKey}
-                                  name={layoutSettingsItem.name}
-                                  gridColumns={layoutSettingsItem.value as string}
-                                  configChange={(params) => {
-                                    this.configChange(params);
-                                  }}
-                                />
-                              }
                               {layoutSettingsItem.type == "showComponentLoadingIndicators" && 
                                 <ThemeSettingsSelector
                                   themeSettingKey={layoutSettingsItem.settingsKey}
                                   name={layoutSettingsItem.name}
                                   showComponentLoadingIndicators={layoutSettingsItem.value as boolean}
                                   configChange={(params) => {
-                                    console.log('configChange', params);
                                     this.configChange(params);
                                   }}
                                 />
diff --git a/client/packages/lowcoder/src/pages/setting/theme/detail/previewDsl.ts b/client/packages/lowcoder/src/pages/setting/theme/detail/previewDsl.ts
index a868cca8f..ffeb03feb 100644
--- a/client/packages/lowcoder/src/pages/setting/theme/detail/previewDsl.ts
+++ b/client/packages/lowcoder/src/pages/setting/theme/detail/previewDsl.ts
@@ -677,6 +677,7 @@ const dsl = {
   settings: {
     maxWidth: { dropdown: "3200", input: "3200" },
     themeId: "",
+    preventStylesOverwriting: false,
   },
   preload: { libs: [], script: "", css: "" },
 };
diff --git a/client/packages/lowcoder/src/util/hooks.ts b/client/packages/lowcoder/src/util/hooks.ts
index 3bc5a2ed6..57fa2baa4 100644
--- a/client/packages/lowcoder/src/util/hooks.ts
+++ b/client/packages/lowcoder/src/util/hooks.ts
@@ -19,9 +19,13 @@ import { getDataSourceStructures } from "redux/selectors/datasourceSelectors";
 import { DatasourceStructure } from "api/datasourceApi";
 import { loadAuthSearchParams } from "pages/userAuth/authUtils";
 import { ThemeContext } from "@lowcoder-ee/comps/utils/themeContext";
+import { defaultTheme } from "constants/themeConstants";
 import { CompTypeContext } from "@lowcoder-ee/comps/utils/compTypeContext";
 import { setInitialCompStyles } from "@lowcoder-ee/comps/utils/themeUtil";
 import { CompAction, changeChildAction } from "lowcoder-core";
+import { ThemeDetail } from "@lowcoder-ee/api/commonSettingApi";
+import { uniq } from "lodash";
+import { constantColors } from "components/colorSelect/colorUtils";
 
 export const ForceViewModeContext = React.createContext<boolean>(false);
 
@@ -168,7 +172,6 @@ export function useMetaData(datasourceId: string) {
   );
 }
 
-
 export function useMergeCompStyles(
   props: Record<string, any>,
   dispatch: (action: CompAction) => void
@@ -226,3 +229,31 @@ export function useMergeCompStyles(
     preventStyleOverwriting,
   ]);
 }
+
+type ColorKey = 'primary' | 'textDark' | 'textLight' | 'canvas' | 'primarySurface' | 'border';
+type ColorKeys = ColorKey[];
+
+export function useThemeColors(allowGradient?: boolean) {
+  const currentTheme = useContext(ThemeContext)?.theme ?? {} as ThemeDetail;
+  const colorKeys: ColorKeys = ['primary', 'textDark', 'textLight', 'canvas', 'primarySurface', 'border'];
+
+  return useMemo(() => {
+    let colors: string[] = [];
+
+    colorKeys.forEach(colorKey => {
+      if (Boolean(defaultTheme[colorKey])) {
+        colors.push(defaultTheme[colorKey] ?? '');
+      }
+      if (Boolean(currentTheme[colorKey])) {
+        colors.push(currentTheme[colorKey] ?? '');
+      }
+    })
+    if (!allowGradient) {
+      colors = colors.concat(constantColors);
+    }
+    return uniq(colors);
+  }, [
+    currentTheme,
+    defaultTheme,
+  ]);
+}
\ No newline at end of file
diff --git a/client/packages/lowcoder/src/util/styleUtils.tsx b/client/packages/lowcoder/src/util/styleUtils.tsx
new file mode 100644
index 000000000..e59a8fc25
--- /dev/null
+++ b/client/packages/lowcoder/src/util/styleUtils.tsx
@@ -0,0 +1,32 @@
+import { isValidColor, isValidGradient } from "components/colorSelect/colorUtils"
+import { css } from "styled-components";
+
+const getBackgroundStyle = (style: Record<string, string | undefined>) => {
+  return css`
+    ${isValidColor(style.background) && `background-color: ${style.background}`};
+    ${isValidGradient(style.background) && !Boolean(style.backgroundImage) && `background-image: ${style.background}`};
+    ${!isValidGradient(style.background) && Boolean(style.backgroundImage) && `background-image: ${style.backgroundImage}`};
+    ${isValidGradient(style.background) && Boolean(style.backgroundImage) && `background-image: url(${style.backgroundImage}), ${style.background}`};
+
+    ${style.backgroundImageRepeat && `background-repeat: ${style.backgroundImageRepeat};`}; 
+    ${style.backgroundImageSize && `background-size: ${style.backgroundImageSize};`}; 
+    ${style.backgroundImageOrigin && `background-origin: ${style.backgroundImageOrigin};`}; 
+    ${style.backgroundImagePosition && `background-position: ${style.backgroundImagePosition};`};
+  `;
+}
+
+const getTextStyle = (color?: string) => {
+  return css`
+    ${isValidColor(color) && `color: ${color};`}
+    ${isValidGradient(color) && `
+      background-image: -webkit-${color};
+      -webkit-background-clip: text;
+      -webkit-text-fill-color: transparent;
+    `}
+  `;
+}
+
+export {
+  getBackgroundStyle,
+  getTextStyle,
+}
\ No newline at end of file
diff --git a/client/packages/lowcoder/vite.config.mts b/client/packages/lowcoder/vite.config.mts
index e6618c6ee..1be2ebfd8 100644
--- a/client/packages/lowcoder/vite.config.mts
+++ b/client/packages/lowcoder/vite.config.mts
@@ -102,9 +102,9 @@ export const viteConfig: UserConfig = {
     preprocessorOptions: {
       less: {
         modifyVars: {
-          "@primary-color": "#3377FF",
+          "@primary-color": "#b480de",
           "@link-color": "#3377FF",
-          "@border-color-base": "#D7D9E0",
+          "@border-color-base": "#b480de",
           "@border-radius-base": "4px",
         },
         javascriptEnabled: true,
diff --git a/client/yarn.lock b/client/yarn.lock
index f3f30d79d..7825894fc 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -6655,6 +6655,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"base64-arraybuffer@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "base64-arraybuffer@npm:1.0.2"
+  checksum: 15e6400d2d028bf18be4ed97702b11418f8f8779fb8c743251c863b726638d52f69571d4cc1843224da7838abef0949c670bde46936663c45ad078e89fee5c62
+  languageName: node
+  linkType: hard
+
 "base64-js@npm:^1.3.1":
   version: 1.5.1
   resolution: "base64-js@npm:1.5.1"
@@ -7907,6 +7914,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
   languageName: node
   linkType: hard
 
+"css-line-break@npm:^2.1.0":
+  version: 2.1.0
+  resolution: "css-line-break@npm:2.1.0"
+  dependencies:
+    utrie: ^1.0.2
+  checksum: 37b1fe632b03be7a287cd394cef8b5285666343443125c510df9cfb6a4734a2c71e154ec8f7bbff72d7c339e1e5872989b1c52d86162aed27d6cc114725bb4d0
+  languageName: node
+  linkType: hard
+
 "css-loader@npm:^6.10.0":
   version: 6.11.0
   resolution: "css-loader@npm:6.11.0"
@@ -11285,6 +11301,16 @@ coolshapes-react@lowcoder-org/coolshapes-react:
   languageName: node
   linkType: hard
 
+"html2canvas@npm:^1.4.1":
+  version: 1.4.1
+  resolution: "html2canvas@npm:1.4.1"
+  dependencies:
+    css-line-break: ^2.1.0
+    text-segmentation: ^1.0.3
+  checksum: c134324af57f3262eecf982e436a4843fded3c6cf61954440ffd682527e4dd350e0c2fafd217c0b6f9a455fe345d0c67b4505689796ab160d4ca7c91c3766739
+  languageName: node
+  linkType: hard
+
 "http-cache-semantics@npm:^4.1.1":
   version: 4.1.1
   resolution: "http-cache-semantics@npm:4.1.1"
@@ -13598,6 +13624,13 @@ coolshapes-react@lowcoder-org/coolshapes-react:
   languageName: node
   linkType: hard
 
+"lodash.throttle@npm:^4.1.1":
+  version: 4.1.1
+  resolution: "lodash.throttle@npm:4.1.1"
+  checksum: 129c0a28cee48b348aef146f638ef8a8b197944d4e9ec26c1890c19d9bf5a5690fe11b655c77a4551268819b32d27f4206343e30c78961f60b561b8608c8c805
+  languageName: node
+  linkType: hard
+
 "lodash@npm:^3.9.1":
   version: 3.10.1
   resolution: "lodash@npm:3.10.1"
@@ -14000,6 +14033,7 @@ coolshapes-react@lowcoder-org/coolshapes-react:
     qrcode.react: ^3.1.0
     rc-trigger: ^5.3.1
     react: ^18.2.0
+    react-best-gradient-color-picker: ^3.0.10
     react-colorful: ^5.5.1
     react-documents: ^1.2.1
     react-dom: ^18.2.0
@@ -17485,6 +17519,24 @@ coolshapes-react@lowcoder-org/coolshapes-react:
   languageName: node
   linkType: hard
 
+"react-best-gradient-color-picker@npm:^3.0.10":
+  version: 3.0.10
+  resolution: "react-best-gradient-color-picker@npm:3.0.10"
+  dependencies:
+    html2canvas: ^1.4.1
+    lodash.throttle: ^4.1.1
+    tinycolor2: 1.4.2
+  peerDependencies:
+    "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+    react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+    react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+  peerDependenciesMeta:
+    "@types/react":
+      optional: true
+  checksum: 3f821beb164e7eed0c3247af99e492dc08bfa64b47f8d7f71d1543e5afbe1d9f01cde4a2917c9d794012dd1efbfe848ee58621b4f37801ea5655df8be806ea55
+  languageName: node
+  linkType: hard
+
 "react-colorful@npm:^5.5.1":
   version: 5.6.1
   resolution: "react-colorful@npm:5.6.1"
@@ -20198,6 +20250,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
   languageName: node
   linkType: hard
 
+"text-segmentation@npm:^1.0.3":
+  version: 1.0.3
+  resolution: "text-segmentation@npm:1.0.3"
+  dependencies:
+    utrie: ^1.0.2
+  checksum: 2e24632d59567c55ab49ac324815e2f7a8043e63e26b109636322ac3e30692cee8679a448fd5d0f0598a345f407afd0e34ba612e22524cf576d382d84058c013
+  languageName: node
+  linkType: hard
+
 "text-table@npm:^0.2.0":
   version: 0.2.0
   resolution: "text-table@npm:0.2.0"
@@ -20276,6 +20337,13 @@ coolshapes-react@lowcoder-org/coolshapes-react:
   languageName: node
   linkType: hard
 
+"tinycolor2@npm:1.4.2":
+  version: 1.4.2
+  resolution: "tinycolor2@npm:1.4.2"
+  checksum: 57ed262e08815a4ab0ed933edafdbc6555a17081781766149813b44a080ecbe58b3ee281e81c0e75b42e4d41679f138cfa98eabf043f829e0683c04adb12c031
+  languageName: node
+  linkType: hard
+
 "tmpl@npm:1.0.5":
   version: 1.0.5
   resolution: "tmpl@npm:1.0.5"
@@ -21217,6 +21285,15 @@ coolshapes-react@lowcoder-org/coolshapes-react:
   languageName: node
   linkType: hard
 
+"utrie@npm:^1.0.2":
+  version: 1.0.2
+  resolution: "utrie@npm:1.0.2"
+  dependencies:
+    base64-arraybuffer: ^1.0.2
+  checksum: c96fbb7d4d8855a154327da0b18e39b7511cc70a7e4bcc3658e24f424bb884312d72b5ba777500b8858e34d365dc6b1a921dc5ca2f0d341182519c6b78e280a5
+  languageName: node
+  linkType: hard
+
 "uuid@npm:^3.3.2":
   version: 3.4.0
   resolution: "uuid@npm:3.4.0"
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/ApplicationHistorySnapshotTS.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/ApplicationHistorySnapshotTS.java
new file mode 100644
index 000000000..1b6d86502
--- /dev/null
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/model/ApplicationHistorySnapshotTS.java
@@ -0,0 +1,23 @@
+package org.lowcoder.domain.application.model;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import org.lowcoder.sdk.models.HasIdAndAuditing;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.util.Map;
+
+@ToString(callSuper = true)
+@Document
+@Getter
+@Setter
+@NoArgsConstructor
+public class ApplicationHistorySnapshotTS extends HasIdAndAuditing {
+
+    private String applicationId;
+    private Map<String, Object> dsl;
+    private Map<String, Object> context;
+
+}
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistoryArchivedSnapshotRepository.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistoryArchivedSnapshotRepository.java
new file mode 100644
index 000000000..dded29c35
--- /dev/null
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistoryArchivedSnapshotRepository.java
@@ -0,0 +1,26 @@
+package org.lowcoder.domain.application.repository;
+
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.mongodb.repository.Query;
+import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
+import org.springframework.stereotype.Repository;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.time.Instant;
+
+@Repository
+public interface ApplicationHistoryArchivedSnapshotRepository extends ReactiveMongoRepository<ApplicationHistorySnapshot, String> {
+
+    @Query(value = "{ 'applicationId': ?0, $and: [" +
+            "{$or: [ { 'context.operations': { $elemMatch: { 'compName': ?1 } } }, { $expr: { $eq: [?1, null] } } ]}, " +
+            "{$or: [ { 'dsl.settings.themeId': ?2 }, { $expr: { $eq: [?2, null] } } ] }, " +
+            "{$or: [ { 'createdAt': { $gte: ?3} }, { $expr: { $eq: [?3, null] } } ] }, " +
+            "{$or: [ { 'createdAt': { $lte: ?4} }, { $expr: { $eq: [?4, null] } } ] } " +
+            "]}",
+            fields = "{applicationId : 1, context: 1, createdBy : 1, createdAt : 1}")
+    Flux<ApplicationHistorySnapshot> findAllByApplicationId(String applicationId, String compName, String theme, Instant createdAtFrom, Instant createdAtTo, Pageable pageable);
+
+    Mono<Long> countByApplicationId(String applicationId);
+}
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistorySnapshotRepository.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistorySnapshotRepository.java
index a8e9f3d02..809decfd6 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistorySnapshotRepository.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/repository/ApplicationHistorySnapshotRepository.java
@@ -1,19 +1,26 @@
 package org.lowcoder.domain.application.repository;
 
-import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.mongodb.repository.Query;
 import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
 import org.springframework.stereotype.Repository;
-
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
+import java.time.Instant;
+
 @Repository
-public interface ApplicationHistorySnapshotRepository extends ReactiveMongoRepository<ApplicationHistorySnapshot, String> {
+public interface ApplicationHistorySnapshotRepository extends ReactiveMongoRepository<ApplicationHistorySnapshotTS, String> {
 
-    @Query(fields = "{applicationId : 1, context: 1, createdBy : 1, createdAt : 1}")
-    Flux<ApplicationHistorySnapshot> findAllByApplicationId(String applicationId, Pageable pageable);
+    @Query(value = "{ 'applicationId': ?0, $and: [" +
+            "{$or: [ { 'context.operations': { $elemMatch: { 'compName': ?1 } } }, { $expr: { $eq: [?1, null] } } ]}, " +
+            "{$or: [ { 'dsl.settings.themeId': ?2 }, { $expr: { $eq: [?2, null] } } ] }, " +
+            "{$or: [ { 'createdAt': { $gte: ?3} }, { $expr: { $eq: [?3, null] } } ] }, " +
+            "{$or: [ { 'createdAt': { $lte: ?4} }, { $expr: { $eq: [?4, null] } } ] } " +
+            "]}",
+            fields = "{applicationId : 1, context: 1, createdBy : 1, createdAt : 1}")
+    Flux<ApplicationHistorySnapshotTS> findAllByApplicationId(String applicationId, String compName, String theme, Instant createdAtFrom, Instant createdAtTo, Pageable pageable);
 
     Mono<Long> countByApplicationId(String applicationId);
 }
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationHistorySnapshotService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationHistorySnapshotService.java
index a8845dd07..fd4a79f82 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationHistorySnapshotService.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/ApplicationHistorySnapshotService.java
@@ -1,22 +1,24 @@
 package org.lowcoder.domain.application.service;
 
-import java.util.List;
-import java.util.Map;
-
 import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
 import org.springframework.data.domain.PageRequest;
-
 import reactor.core.publisher.Mono;
 
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+
 public interface ApplicationHistorySnapshotService {
 
     Mono<Boolean> createHistorySnapshot(String applicationId, Map<String, Object> dsl, Map<String, Object> context, String userId);
 
-    Mono<List<ApplicationHistorySnapshot>> listAllHistorySnapshotBriefInfo(String applicationId, PageRequest pageRequest);
+    Mono<List<ApplicationHistorySnapshotTS>> listAllHistorySnapshotBriefInfo(String applicationId, String compName, String theme, Instant from, Instant to, PageRequest pageRequest);
+    Mono<List<ApplicationHistorySnapshot>> listAllHistorySnapshotBriefInfoArchived(String applicationId, String compName, String theme, Instant from, Instant to, PageRequest pageRequest);
 
     Mono<Long> countByApplicationId(String applicationId);
 
-    Mono<ApplicationHistorySnapshot> getHistorySnapshotDetail(String historySnapshotId);
+    Mono<ApplicationHistorySnapshotTS> getHistorySnapshotDetail(String historySnapshotId);
 
-    Mono<ApplicationHistorySnapshot> getLastSnapshotByApp(String applicationId);
+    Mono<ApplicationHistorySnapshot> getHistorySnapshotDetailArchived(String historySnapshotId);
 }
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/impl/ApplicationHistorySnapshotServiceImpl.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/impl/ApplicationHistorySnapshotServiceImpl.java
index 25a140c24..c47b39955 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/impl/ApplicationHistorySnapshotServiceImpl.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/application/service/impl/ApplicationHistorySnapshotServiceImpl.java
@@ -1,46 +1,53 @@
 package org.lowcoder.domain.application.service.impl;
 
-import static org.lowcoder.sdk.exception.BizError.INVALID_HISTORY_SNAPSHOT;
-import static org.lowcoder.sdk.util.ExceptionUtils.deferredError;
-import static org.lowcoder.sdk.util.ExceptionUtils.ofException;
-
-import java.time.Instant;
-import java.util.List;
-import java.util.Map;
-
 import lombok.RequiredArgsConstructor;
 import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
+import org.lowcoder.domain.application.repository.ApplicationHistoryArchivedSnapshotRepository;
 import org.lowcoder.domain.application.repository.ApplicationHistorySnapshotRepository;
 import org.lowcoder.domain.application.service.ApplicationHistorySnapshotService;
 import org.lowcoder.sdk.exception.BizError;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Sort.Direction;
 import org.springframework.stereotype.Service;
-
 import reactor.core.publisher.Mono;
 
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+
+import static org.lowcoder.sdk.exception.BizError.INVALID_HISTORY_SNAPSHOT;
+import static org.lowcoder.sdk.util.ExceptionUtils.deferredError;
+import static org.lowcoder.sdk.util.ExceptionUtils.ofException;
+
 @RequiredArgsConstructor
 @Service
 public class ApplicationHistorySnapshotServiceImpl implements ApplicationHistorySnapshotService {
 
     private final ApplicationHistorySnapshotRepository repository;
+    private final ApplicationHistoryArchivedSnapshotRepository repositoryArchived;
 
     @Override
     public Mono<Boolean> createHistorySnapshot(String applicationId, Map<String, Object> dsl, Map<String, Object> context, String userId) {
-        ApplicationHistorySnapshot applicationHistorySnapshot = new ApplicationHistorySnapshot();
-        applicationHistorySnapshot.setApplicationId(applicationId);
-        applicationHistorySnapshot.setDsl(dsl);
-        applicationHistorySnapshot.setContext(context);
-        return repository.save(applicationHistorySnapshot)
+        ApplicationHistorySnapshotTS applicationHistorySnapshotTS = new ApplicationHistorySnapshotTS();
+        applicationHistorySnapshotTS.setApplicationId(applicationId);
+        applicationHistorySnapshotTS.setDsl(dsl);
+        applicationHistorySnapshotTS.setContext(context);
+        return repository.save(applicationHistorySnapshotTS)
                 .thenReturn(true)
                 .onErrorReturn(false);
     }
 
     @Override
-    public Mono<List<ApplicationHistorySnapshot>> listAllHistorySnapshotBriefInfo(String applicationId, PageRequest pageRequest) {
-        return repository.findAllByApplicationId(applicationId, pageRequest.withSort(Direction.DESC, "id"))
+    public Mono<List<ApplicationHistorySnapshotTS>> listAllHistorySnapshotBriefInfo(String applicationId, String compName, String theme, Instant from, Instant to, PageRequest pageRequest) {
+        return repository.findAllByApplicationId(applicationId, compName, theme, from, to, pageRequest.withSort(Direction.DESC, "id"))
+                .collectList()
+                .onErrorMap(Exception.class, e -> ofException(BizError.FETCH_HISTORY_SNAPSHOT_FAILURE, "FETCH_HISTORY_SNAPSHOT_FAILURE"));
+    }
+
+    @Override
+    public Mono<List<ApplicationHistorySnapshot>> listAllHistorySnapshotBriefInfoArchived(String applicationId, String compName, String theme, Instant from, Instant to, PageRequest pageRequest) {
+        return repositoryArchived.findAllByApplicationId(applicationId, compName, theme, from, to, pageRequest.withSort(Direction.DESC, "id"))
                 .collectList()
                 .onErrorMap(Exception.class, e -> ofException(BizError.FETCH_HISTORY_SNAPSHOT_FAILURE, "FETCH_HISTORY_SNAPSHOT_FAILURE"));
     }
@@ -54,17 +61,15 @@ public Mono<Long> countByApplicationId(String applicationId) {
 
 
     @Override
-    public Mono<ApplicationHistorySnapshot> getHistorySnapshotDetail(String historySnapshotId) {
+    public Mono<ApplicationHistorySnapshotTS> getHistorySnapshotDetail(String historySnapshotId) {
         return repository.findById(historySnapshotId)
                 .switchIfEmpty(deferredError(INVALID_HISTORY_SNAPSHOT, "INVALID_HISTORY_SNAPSHOT", historySnapshotId));
     }
 
+
     @Override
-    public Mono<ApplicationHistorySnapshot> getLastSnapshotByApp(String applicationId) {
-        ApplicationHistorySnapshot _default = new ApplicationHistorySnapshot();
-        _default.setCreatedAt(Instant.ofEpochMilli(0));
-        _default.setCreatedBy("");
-        return repository.findAllByApplicationId(applicationId, PageRequest.of(0, 1).withSort(Direction.DESC, "createdAt"))
-                .switchIfEmpty(Mono.just(_default)).next();
+    public Mono<ApplicationHistorySnapshot> getHistorySnapshotDetailArchived(String historySnapshotId) {
+        return repositoryArchived.findById(historySnapshotId)
+                .switchIfEmpty(deferredError(INVALID_HISTORY_SNAPSHOT, "INVALID_HISTORY_SNAPSHOT", historySnapshotId));
     }
 }
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/authentication/AuthenticationServiceImpl.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/authentication/AuthenticationServiceImpl.java
index 65fdeb8b2..99a541274 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/authentication/AuthenticationServiceImpl.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/authentication/AuthenticationServiceImpl.java
@@ -53,15 +53,15 @@ private Mono<FindAuthConfig> findAuthConfig(String orgId, Function<AbstractAuthC
     public Flux<FindAuthConfig> findAllAuthConfigs(String orgId, boolean enableOnly) {
 
         Mono<FindAuthConfig> emailAuthConfigMono = orgMemberService.doesAtleastOneAdminExist()
-                .map(doesAtleastOneAdminExist -> {
+                .flatMap(doesAtleastOneAdminExist -> {
                     boolean shouldEnableRegister;
                     if(doesAtleastOneAdminExist) {
                         shouldEnableRegister = authProperties.getEmail().getEnableRegister();
                     } else {
                         shouldEnableRegister = Boolean.TRUE;
                     }
-                    return new FindAuthConfig
-                            (new EmailAuthConfig(AuthSourceConstants.EMAIL, authProperties.getEmail().isEnable(), shouldEnableRegister), null);
+                    if(orgId == null) return Mono.just(new FindAuthConfig(new EmailAuthConfig(AuthSourceConstants.EMAIL, authProperties.getEmail().isEnable(), shouldEnableRegister), null));
+                    else return organizationService.getById(orgId).map(organization -> new FindAuthConfig(new EmailAuthConfig(AuthSourceConstants.EMAIL, !organization.getIsEmailDisabled(), shouldEnableRegister), null));
                 });
 
 
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java
index 18d7d2423..80d338b48 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/organization/model/Organization.java
@@ -51,6 +51,13 @@ public class Organization extends HasIdAndAuditing implements BeforeMongodbWrite
 
     private String contactPhoneNumber;
 
+    private Boolean isEmailDisabled;
+
+    public Boolean getIsEmailDisabled() {
+        if(isEmailDisabled == null) return false;
+        else return isEmailDisabled;
+    }
+
     @JsonIgnore
     private String logoAssetId;
 
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java
index 19d1fde58..b7dc9326c 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserService.java
@@ -27,6 +27,7 @@ public interface UserService {
     Mono<Map<String, User>> getByIds(Collection<String> ids);
 
     Mono<User> findBySourceAndId(String connectionSource, String connectionSourceUuid);
+    Mono<User> findByEmailDeep(String email);
 
     Mono<Boolean> saveProfilePhoto(Part filePart, User t2);
 
diff --git a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserServiceImpl.java b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserServiceImpl.java
index 4ba0e9cd7..c1559b42d 100644
--- a/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserServiceImpl.java
+++ b/server/api-service/lowcoder-domain/src/main/java/org/lowcoder/domain/user/service/UserServiceImpl.java
@@ -112,6 +112,7 @@ public Mono<User> findByName(String rawUuid) {
         return repository.findByName(rawUuid);
     }
 
+    @Override
     public Mono<User> findByEmailDeep(String email) {
         if(StringUtils.isEmpty(email)) return Mono.empty();
         return repository.findByEmailOrConnections_Email(email, email).next();
diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/config/CommonConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/config/CommonConfig.java
index 011f321cf..b50d06935 100644
--- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/config/CommonConfig.java
+++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/config/CommonConfig.java
@@ -160,6 +160,7 @@ public static class Marketplace {
     @Setter
     public static class Query {
         private long readStructureTimeout = 15000;
+        private long appSnapshotKeepDuration = 30;
     }
 
     @Data
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java
index 2ed207aae..88d14e210 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiService.java
@@ -17,7 +17,7 @@
 public interface ApplicationApiService {
     Mono<ApplicationView> create(ApplicationEndpoints.CreateApplicationRequest createApplicationRequest);
 
-    Flux<ApplicationInfoView> getRecycledApplications();
+    Flux<ApplicationInfoView> getRecycledApplications(String name);
 
     Mono<ApplicationView> delete(String applicationId);
 
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiServiceImpl.java
index 1fceb04d5..25d772cdb 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiServiceImpl.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationApiServiceImpl.java
@@ -169,8 +169,8 @@ private Mono<Void> autoGrantPermissionsByFolderDefault(String applicationId, @Nu
     }
 
     @Override
-    public Flux<ApplicationInfoView> getRecycledApplications() {
-        return userHomeApiService.getAllAuthorisedApplications4CurrentOrgMember(null, ApplicationStatus.RECYCLED, false);
+    public Flux<ApplicationInfoView> getRecycledApplications(String name) {
+        return userHomeApiService.getAllAuthorisedApplications4CurrentOrgMember(null, ApplicationStatus.RECYCLED, false, name);
     }
 
     private Mono<Void> checkCurrentUserApplicationPermission(String applicationId, ResourceAction action) {
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java
index ffd88b337..e4807183b 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationController.java
@@ -77,8 +77,8 @@ public Mono<ResponseView<Boolean>> restore(@PathVariable String applicationId) {
     }
 
     @Override
-    public Mono<ResponseView<List<ApplicationInfoView>>> getRecycledApplications() {
-        return applicationApiService.getRecycledApplications()
+    public Mono<ResponseView<List<ApplicationInfoView>>> getRecycledApplications(@RequestParam(required = false) String name) {
+        return applicationApiService.getRecycledApplications(name)
                 .collectList()
                 .map(ResponseView::success);
     }
@@ -159,9 +159,10 @@ public Mono<ResponseView<UserHomepageView>> getUserHomePage(@RequestParam(requir
     @Override
     public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestParam(required = false) Integer applicationType,
             @RequestParam(required = false) ApplicationStatus applicationStatus,
-            @RequestParam(defaultValue = "true") boolean withContainerSize) {
+            @RequestParam(defaultValue = "true") boolean withContainerSize,
+            @RequestParam(required = false) String name) {
         ApplicationType applicationTypeEnum = applicationType == null ? null : ApplicationType.fromValue(applicationType);
-        return userHomeApiService.getAllAuthorisedApplications4CurrentOrgMember(applicationTypeEnum, applicationStatus, withContainerSize)
+        return userHomeApiService.getAllAuthorisedApplications4CurrentOrgMember(applicationTypeEnum, applicationStatus, withContainerSize, name)
                 .collectList()
                 .map(ResponseView::success);
     }
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java
index 0b29c1ebc..8db147302 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationEndpoints.java
@@ -71,7 +71,7 @@ public interface ApplicationEndpoints
 		    description = "List all the recycled Lowcoder Applications in the recycle bin where the authenticated or impersonated user has access."
 	)
     @GetMapping("/recycle/list")
-    public Mono<ResponseView<List<ApplicationInfoView>>> getRecycledApplications();
+    public Mono<ResponseView<List<ApplicationInfoView>>> getRecycledApplications(@RequestParam(required = false) String name);
 
 	@Operation(
 			tags = TAG_APPLICATION_MANAGEMENT,
@@ -165,7 +165,8 @@ public Mono<ResponseView<Boolean>> updateEditState(@PathVariable String applicat
     @GetMapping("/list")
     public Mono<ResponseView<List<ApplicationInfoView>>> getApplications(@RequestParam(required = false) Integer applicationType,
             @RequestParam(required = false) ApplicationStatus applicationStatus,
-            @RequestParam(defaultValue = "true") boolean withContainerSize);
+            @RequestParam(defaultValue = "true") boolean withContainerSize,
+			@RequestParam(required = false) String name);
 
 	@Operation(
 			tags = TAG_APPLICATION_MANAGEMENT,
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationHistorySnapshotController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationHistorySnapshotController.java
index f4c476e76..6b6d94a51 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationHistorySnapshotController.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationHistorySnapshotController.java
@@ -1,18 +1,14 @@
 package org.lowcoder.api.application;
 
-import static org.lowcoder.api.util.ViewBuilder.multiBuild;
-
-import java.time.Instant;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
+import com.google.common.collect.ImmutableMap;
+import lombok.RequiredArgsConstructor;
 import org.lowcoder.api.application.view.HistorySnapshotDslView;
 import org.lowcoder.api.framework.view.ResponseView;
 import org.lowcoder.api.home.SessionUserService;
 import org.lowcoder.api.util.Pagination;
 import org.lowcoder.domain.application.model.Application;
 import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
 import org.lowcoder.domain.application.service.ApplicationHistorySnapshotService;
 import org.lowcoder.domain.application.service.ApplicationService;
 import org.lowcoder.domain.permission.model.ResourceAction;
@@ -22,11 +18,14 @@
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
+import reactor.core.publisher.Mono;
 
-import com.google.common.collect.ImmutableMap;
+import java.time.Instant;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
-import lombok.RequiredArgsConstructor;
-import reactor.core.publisher.Mono;
+import static org.lowcoder.api.util.ViewBuilder.multiBuild;
 
 @RequiredArgsConstructor
 @RestController
@@ -55,15 +54,56 @@ public Mono<ResponseView<Boolean>> create(@RequestBody ApplicationHistorySnapsho
 
     @Override
     public Mono<ResponseView<Map<String, Object>>> listAllHistorySnapshotBriefInfo(@PathVariable String applicationId,
-            @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size) {
+            @RequestParam(defaultValue = "0") int page,
+            @RequestParam(defaultValue = "10") int size,
+            @RequestParam String compName,
+            @RequestParam String theme,
+            @RequestParam Instant from,
+            @RequestParam Instant to) {
+
+        Pagination pagination = Pagination.of(page, size).check();
+
+        return sessionUserService.getVisitorId()
+                .delayUntil(visitor -> resourcePermissionService.checkResourcePermissionWithError(visitor, applicationId,
+                        ResourceAction.EDIT_APPLICATIONS))
+                .flatMap(__ -> applicationHistorySnapshotService.listAllHistorySnapshotBriefInfo(applicationId, compName, theme, from, to, pagination.toPageRequest()))
+                .flatMap(snapshotList -> {
+                    Mono<List<ApplicationHistorySnapshotBriefInfo>> snapshotBriefInfoList = multiBuild(snapshotList,
+                            ApplicationHistorySnapshotTS::getCreatedBy,
+                            userService::getByIds,
+                            (applicationHistorySnapshotTS, user) -> new ApplicationHistorySnapshotBriefInfo(
+                                    applicationHistorySnapshotTS.getId(),
+                                    applicationHistorySnapshotTS.getContext(),
+                                    applicationHistorySnapshotTS.getCreatedBy(),
+                                    user.getName(),
+                                    user.getAvatarUrl(),
+                                    applicationHistorySnapshotTS.getCreatedAt().toEpochMilli()
+                            )
+                    );
+
+                    Mono<Long> applicationHistorySnapshotCount = applicationHistorySnapshotService.countByApplicationId(applicationId);
+
+                    return Mono.zip(snapshotBriefInfoList, applicationHistorySnapshotCount)
+                            .map(tuple -> ImmutableMap.of("list", tuple.getT1(), "count", tuple.getT2()));
+                })
+                .map(ResponseView::success);
+    }
+
+    @Override
+    public Mono<ResponseView<Map<String, Object>>> listAllHistorySnapshotBriefInfoArchived(@PathVariable String applicationId,
+                                                                                   @RequestParam(defaultValue = "0") int page,
+                                                                                   @RequestParam(defaultValue = "10") int size,
+                                                                                   @RequestParam String compName,
+                                                                                   @RequestParam String theme,
+                                                                                   @RequestParam Instant from,
+                                                                                   @RequestParam Instant to) {
 
         Pagination pagination = Pagination.of(page, size).check();
 
         return sessionUserService.getVisitorId()
                 .delayUntil(visitor -> resourcePermissionService.checkResourcePermissionWithError(visitor, applicationId,
                         ResourceAction.EDIT_APPLICATIONS))
-                .flatMap(__ -> applicationHistorySnapshotService.listAllHistorySnapshotBriefInfo(applicationId,
-                        pagination.toPageRequest()))
+                .flatMap(__ -> applicationHistorySnapshotService.listAllHistorySnapshotBriefInfoArchived(applicationId, compName, theme, from, to, pagination.toPageRequest()))
                 .flatMap(snapshotList -> {
                     Mono<List<ApplicationHistorySnapshotBriefInfo>> snapshotBriefInfoList = multiBuild(snapshotList,
                             ApplicationHistorySnapshot::getCreatedBy,
@@ -93,8 +133,30 @@ public Mono<ResponseView<HistorySnapshotDslView>> getHistorySnapshotDsl(@PathVar
                 .delayUntil(visitor -> resourcePermissionService.checkResourcePermissionWithError(visitor, applicationId,
                         ResourceAction.EDIT_APPLICATIONS))
                 .flatMap(__ -> applicationHistorySnapshotService.getHistorySnapshotDetail(snapshotId))
+                .map(ApplicationHistorySnapshotTS::getDsl)
+                .zipWhen(applicationService::getAllDependentModulesFromDsl)
+                .map(tuple -> {
+                    Map<String, Object> applicationDsl = tuple.getT1();
+                    List<Application> dependentModules = tuple.getT2();
+                    Map<String, Map<String, Object>> dependentModuleDsl = dependentModules.stream()
+                            .collect(Collectors.toMap(Application::getId, Application::getLiveApplicationDsl, (a, b) -> b));
+                    return HistorySnapshotDslView.builder()
+                            .applicationsDsl(applicationDsl)
+                            .moduleDSL(dependentModuleDsl)
+                            .build();
+                })
+                .map(ResponseView::success);
+    }
+
+    @Override
+    public Mono<ResponseView<HistorySnapshotDslView>> getHistorySnapshotDslArchived(@PathVariable String applicationId,
+                                                                            @PathVariable String snapshotId) {
+        return sessionUserService.getVisitorId()
+                .delayUntil(visitor -> resourcePermissionService.checkResourcePermissionWithError(visitor, applicationId,
+                        ResourceAction.EDIT_APPLICATIONS))
+                .flatMap(__ -> applicationHistorySnapshotService.getHistorySnapshotDetailArchived(snapshotId))
                 .map(ApplicationHistorySnapshot::getDsl)
-                .zipWhen(dsl -> applicationService.getAllDependentModulesFromDsl(dsl))
+                .zipWhen(applicationService::getAllDependentModulesFromDsl)
                 .map(tuple -> {
                     Map<String, Object> applicationDsl = tuple.getT1();
                     List<Application> dependentModules = tuple.getT2();
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationHistorySnapshotEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationHistorySnapshotEndpoints.java
index 9867ab3d1..32276f3a8 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationHistorySnapshotEndpoints.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/application/ApplicationHistorySnapshotEndpoints.java
@@ -1,7 +1,9 @@
 package org.lowcoder.api.application;
 
+import java.time.Instant;
 import java.util.Map;
 
+import jakarta.annotation.Nullable;
 import org.lowcoder.api.application.view.HistorySnapshotDslView;
 import org.lowcoder.api.framework.view.ResponseView;
 import org.lowcoder.infra.constant.NewUrl;
@@ -39,7 +41,23 @@ public interface ApplicationHistorySnapshotEndpoints
 	)
     @GetMapping("/{applicationId}")
     public Mono<ResponseView<Map<String, Object>>> listAllHistorySnapshotBriefInfo(@PathVariable String applicationId,
-            @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size);
+            @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size,
+			@RequestParam(required = false ) @Nullable String compName, @RequestParam(required = false ) @Nullable String theme,
+			@RequestParam(required = false ) @Nullable Instant from,
+			@RequestParam(required = false ) @Nullable Instant to);
+
+	@Operation(
+			tags = TAG_APPLICATION_HISTORY_MANAGEMENT,
+			operationId = "listApplicationSnapshotsArchived",
+			summary = "List Archived Application Snapshots",
+			description = "Retrieve a list of Archived Snapshots associated with a specific Application within Lowcoder."
+	)
+	@GetMapping("/archive/{applicationId}")
+	public Mono<ResponseView<Map<String, Object>>> listAllHistorySnapshotBriefInfoArchived(@PathVariable String applicationId,
+																				   @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "10") int size,
+																				   @RequestParam(required = false ) @Nullable String compName, @RequestParam(required = false ) @Nullable String theme,
+																				   @RequestParam(required = false ) @Nullable Instant from,
+																				   @RequestParam(required = false ) @Nullable Instant to);
 
 	@Operation(
 			tags = TAG_APPLICATION_HISTORY_MANAGEMENT,
@@ -51,6 +69,16 @@ public Mono<ResponseView<Map<String, Object>>> listAllHistorySnapshotBriefInfo(@
     public Mono<ResponseView<HistorySnapshotDslView>> getHistorySnapshotDsl(@PathVariable String applicationId,
             @PathVariable String snapshotId);
 
+	@Operation(
+			tags = TAG_APPLICATION_HISTORY_MANAGEMENT,
+			operationId = "getApplicationSnapshotArchived",
+			summary = "Retrieve Archived Application Snapshot",
+			description = "Retrieve a specific Archived Application Snapshot within Lowcoder using the Application and Snapshot IDs."
+	)
+	@GetMapping("/archive/{applicationId}/{snapshotId}")
+	public Mono<ResponseView<HistorySnapshotDslView>> getHistorySnapshotDslArchived(@PathVariable String applicationId,
+																			@PathVariable String snapshotId);
+
     public record ApplicationHistorySnapshotBriefInfo(String snapshotId, Map<String, Object> context,
                                                        String userId, String userName,
                                                        String userAvatar, long createTime) {
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/AuthenticationApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/AuthenticationApiServiceImpl.java
index 4b67de89c..7ca9fe3f9 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/AuthenticationApiServiceImpl.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/AuthenticationApiServiceImpl.java
@@ -246,9 +246,13 @@ public Mono<Boolean> enableAuthConfig(AuthConfigRequest authConfigRequest) {
                 .then(sessionUserService.getVisitorOrgMemberCache())
                 .flatMap(orgMember -> organizationService.getById(orgMember.getOrgId()))
                 .doOnNext(organization -> {
-                    boolean duplicateAuthType = addOrUpdateNewAuthConfig(organization, authConfigFactory.build(authConfigRequest, true));
-                    if(duplicateAuthType) {
-                        deferredError(DUPLICATE_AUTH_CONFIG_ADDITION, "DUPLICATE_AUTH_CONFIG_ADDITION");
+                    if(authConfigRequest.getId().equals("EMAIL")) {
+                        organization.setIsEmailDisabled(false);
+                    } else {
+                        boolean duplicateAuthType = addOrUpdateNewAuthConfig(organization, authConfigFactory.build(authConfigRequest, true));
+                        if (duplicateAuthType) {
+                            deferredError(DUPLICATE_AUTH_CONFIG_ADDITION, "DUPLICATE_AUTH_CONFIG_ADDITION");
+                        }
                     }
                 })
                 .flatMap(organization -> organizationService.update(organization.getId(), organization));
@@ -346,22 +350,15 @@ private Mono<Void> checkIfAdmin() {
      * If true, throw an exception to avoid disabling the last effective connection way.
      */
     private Mono<Void> checkIfOnlyEffectiveCurrentUserConnections(String authId) {
-        Mono<List<String>> userConnectionAuthConfigIdListMono = sessionUserService.getVisitor()
-                .flatMapIterable(User::getConnections)
-                .filter(connection -> StringUtils.isNotBlank(connection.getAuthId()))
-                .map(Connection::getAuthId)
-                .collectList();
-        Mono<List<String>> orgAuthIdListMono = authenticationService.findAllAuthConfigs(null, true)
-                .map(FindAuthConfig::authConfig)
-                .map(AbstractAuthConfig::getId)
-                .collectList();
-        return Mono.zip(userConnectionAuthConfigIdListMono, orgAuthIdListMono)
-                .delayUntil(tuple -> {
-                    List<String> userConnectionAuthConfigIds = tuple.getT1();
-                    List<String> orgAuthConfigIds = tuple.getT2();
-                    userConnectionAuthConfigIds.retainAll(orgAuthConfigIds);
-                    userConnectionAuthConfigIds.remove(authId);
-                    if (CollectionUtils.isEmpty(userConnectionAuthConfigIds)) {
+        return sessionUserService.getVisitorOrgMemberCache()
+                .map(OrgMember::getOrgId)
+                .flatMap(orgId -> authenticationService.findAllAuthConfigs(orgId, true)
+                        .map(FindAuthConfig::authConfig)
+                        .map(AbstractAuthConfig::getId)
+                        .collectList())
+                .delayUntil(orgAuthConfigIds -> {
+                    orgAuthConfigIds.remove(authId);
+                    if (CollectionUtils.isEmpty(orgAuthConfigIds)) {
                         return Mono.error(new BizException(DISABLE_AUTH_CONFIG_FORBIDDEN, "DISABLE_AUTH_CONFIG_FORBIDDEN"));
                     }
                     return Mono.empty();
@@ -370,26 +367,29 @@ private Mono<Void> checkIfOnlyEffectiveCurrentUserConnections(String authId) {
     }
 
     private void disableAuthConfig(Organization organization, String authId, boolean delete) {
-
-        Predicate<AbstractAuthConfig> authConfigPredicate = abstractAuthConfig -> Objects.equals(abstractAuthConfig.getId(), authId);
-
-        if(delete) {
-            List<AbstractAuthConfig> abstractAuthConfigs = Optional.of(organization)
-                    .map(Organization::getAuthConfigs)
-                    .orElse(Collections.emptyList());
-
-            abstractAuthConfigs.removeIf(authConfigPredicate);
-
-            organization.getOrganizationDomain().setConfigs(abstractAuthConfigs);
-
+        if(authId.equals("EMAIL")) {
+            organization.setIsEmailDisabled(true);
         } else {
-            Optional.of(organization)
-                    .map(Organization::getAuthConfigs)
-                    .orElse(Collections.emptyList()).stream()
-                    .filter(authConfigPredicate)
-                    .forEach(abstractAuthConfig -> {
-                        abstractAuthConfig.setEnable(false);
-                    });
+            Predicate<AbstractAuthConfig> authConfigPredicate = abstractAuthConfig -> Objects.equals(abstractAuthConfig.getId(), authId);
+
+            if (delete) {
+                List<AbstractAuthConfig> abstractAuthConfigs = Optional.of(organization)
+                        .map(Organization::getAuthConfigs)
+                        .orElse(Collections.emptyList());
+
+                abstractAuthConfigs.removeIf(authConfigPredicate);
+
+                organization.getOrganizationDomain().setConfigs(abstractAuthConfigs);
+
+            } else {
+                Optional.of(organization)
+                        .map(Organization::getAuthConfigs)
+                        .orElse(Collections.emptyList()).stream()
+                        .filter(authConfigPredicate)
+                        .forEach(abstractAuthConfig -> {
+                            abstractAuthConfig.setEnable(false);
+                        });
+            }
         }
     }
 
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceApiService.java
index f1331bd01..4df2cd16a 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceApiService.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceApiService.java
@@ -6,6 +6,7 @@
 import org.lowcoder.domain.permission.model.ResourceRole;
 import org.lowcoder.domain.plugin.client.dto.GetPluginDynamicConfigRequestDTO;
 import org.lowcoder.sdk.models.DatasourceTestResult;
+import org.springframework.web.bind.annotation.RequestParam;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
@@ -15,11 +16,11 @@
 public interface DatasourceApiService {
     Mono<Datasource> create(Datasource datasource);
 
-    Flux<Datasource> listJsDatasourcePlugins(String applicationId);
+    Flux<Datasource> listJsDatasourcePlugins(String applicationId, String name, String type);
 
-    Flux<DatasourceView> listAppDataSources(String appId);
+    Flux<DatasourceView> listAppDataSources(String appId, String name, String type);
 
-    Flux<DatasourceView> listOrgDataSources(String orgId);
+    Flux<DatasourceView> listOrgDataSources(String orgId, String name, String type);
 
     Mono<Datasource> update(String datasourceId, Datasource updatedDatasource);
 
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceApiServiceImpl.java
index 716c22892..16758884a 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceApiServiceImpl.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceApiServiceImpl.java
@@ -84,27 +84,33 @@ public Mono<Datasource> create(Datasource datasource) {
     }
 
     @Override
-    public Flux<Datasource> listJsDatasourcePlugins(String applicationId) {
+    public Flux<Datasource> listJsDatasourcePlugins(String applicationId, String name, String type) {
         return applicationService.findById(applicationId)
                 .delayUntil(application -> applicationApiService.checkPermissionWithReadableErrorMsg(applicationId, READ_APPLICATIONS))
                 .flatMapMany(application -> datasourceService.getByOrgId(application.getOrganizationId()))
                 .filter(datasource -> datasource.getDatasourceStatus() == DatasourceStatus.NORMAL)
-                .filter(datasource -> datasourceMetaInfoService.isJsDatasourcePlugin(datasource.getType()))
-                .delayUntil(datasource -> jsDatasourceHelper.processDynamicQueryConfig(datasource))
+                .filter(datasource -> datasourceMetaInfoService.isJsDatasourcePlugin(datasource.getType()) &&
+                        (name == null || StringUtils.containsIgnoreCase(datasource.getName(), name)) &&
+                        (type == null || datasource.getType().equals(type))
+                )
+                .delayUntil(jsDatasourceHelper::processDynamicQueryConfig)
                 .doOnNext(datasource -> datasource.setDetailConfig(null));
     }
 
     @Override
-    public Flux<DatasourceView> listAppDataSources(String appId) {
+    public Flux<DatasourceView> listAppDataSources(String appId, String name, String type) {
         return applicationService.findById(appId)
-                .flatMapMany(application -> listOrgDataSources(application.getOrganizationId()));
+                .flatMapMany(application -> listOrgDataSources(application.getOrganizationId(), name, type));
     }
 
     @Override
-    public Flux<DatasourceView> listOrgDataSources(String orgId) {
+    public Flux<DatasourceView> listOrgDataSources(String orgId, String name, String type) {
         // get datasource
         Flux<Datasource> datasourceFlux = datasourceService.getByOrgId(orgId)
-                .filter(datasource -> datasource.getDatasourceStatus() == DatasourceStatus.NORMAL)
+                .filter(datasource -> datasource.getDatasourceStatus() == DatasourceStatus.NORMAL &&
+                        (name == null || StringUtils.containsIgnoreCase(datasource.getName(), name)) &&
+                        (type == null || datasource.getType().equals(type))
+                )
                 .cache();
 
         // get user-datasource permissions
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceController.java
index 96cc761f9..f604a287b 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceController.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceController.java
@@ -116,9 +116,9 @@ public Mono<ResponseView<DatasourceStructure>> getStructure(@PathVariable String
      * name, type... and the plugin definition of it, excluding the detail configs such as the connection uri, password...
      */
     @Override
-    public Mono<ResponseView<List<Datasource>>> listJsDatasourcePlugins(@RequestParam("appId") String applicationId) {
+    public Mono<ResponseView<List<Datasource>>> listJsDatasourcePlugins(@RequestParam("appId") String applicationId, @RequestParam(required = false) String name, @RequestParam(required = false) String type) {
         String objectId = gidService.convertApplicationIdToObjectId(applicationId);
-        return datasourceApiService.listJsDatasourcePlugins(objectId)
+        return datasourceApiService.listJsDatasourcePlugins(objectId, name, type)
                 .collectList()
                 .map(ResponseView::success);
     }
@@ -139,24 +139,24 @@ public Mono<ResponseView<List<Object>>> getPluginDynamicConfig(
 
     @SneakyThrows
     @Override
-    public Mono<ResponseView<List<DatasourceView>>> listOrgDataSources(@RequestParam(name = "orgId") String orgId) {
+    public Mono<ResponseView<List<DatasourceView>>> listOrgDataSources(@RequestParam(name = "orgId") String orgId, @RequestParam(required = false) String name, @RequestParam(required = false) String type) {
         if (StringUtils.isBlank(orgId)) {
             return ofError(BizError.INVALID_PARAMETER, "ORG_ID_EMPTY");
         }
         String objectId = gidService.convertOrganizationIdToObjectId(orgId);
-        return datasourceApiService.listOrgDataSources(objectId)
+        return datasourceApiService.listOrgDataSources(objectId, name, type)
                 .collectList()
                 .map(ResponseView::success);
     }
 
     @Override
-    public Mono<ResponseView<List<DatasourceView>>> listAppDataSources(@RequestParam(name = "appId") String applicationId) {
+    public Mono<ResponseView<List<DatasourceView>>> listAppDataSources(@RequestParam(name = "appId") String applicationId, @RequestParam(required = false) String name, @RequestParam(required = false) String type) {
         if (StringUtils.isBlank(applicationId)) {
             return ofError(BizError.INVALID_PARAMETER, "INVALID_APP_ID");
         }
         String objectId = gidService.convertApplicationIdToObjectId(applicationId);
 
-        return datasourceApiService.listAppDataSources(objectId)
+        return datasourceApiService.listAppDataSources(objectId, name, type)
                 .collectList()
                 .map(ResponseView::success);
     }
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceEndpoints.java
index ec9c8512c..bffadd018 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceEndpoints.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/datasource/DatasourceEndpoints.java
@@ -99,7 +99,7 @@ public Mono<ResponseView<DatasourceStructure>> getStructure(@PathVariable String
 		    description = "Retrieve a list of node service plugins available within Lowcoder."
 	)
     @GetMapping("/jsDatasourcePlugins")
-    public Mono<ResponseView<List<Datasource>>> listJsDatasourcePlugins(@RequestParam("appId") String applicationId);
+    public Mono<ResponseView<List<Datasource>>> listJsDatasourcePlugins(@RequestParam("appId") String applicationId, @RequestParam(required = false) String name, @RequestParam(required = false) String type);
 
     /**
      * Proxy the request to the node service, besides, add the "extra" information from the data source config stored in the mongodb if exists to
@@ -123,7 +123,7 @@ public Mono<ResponseView<List<Object>>> getPluginDynamicConfig(
 	)
     @JsonView(JsonViews.Public.class)
     @GetMapping("/listByOrg")
-    public Mono<ResponseView<List<DatasourceView>>> listOrgDataSources(@RequestParam(name = "orgId") String orgId);
+    public Mono<ResponseView<List<DatasourceView>>> listOrgDataSources(@RequestParam(name = "orgId") String orgId, @RequestParam String name, @RequestParam String type);
 
 	@Operation(
 			tags = TAG_DATASOURCE_MANAGEMENT,
@@ -134,7 +134,7 @@ public Mono<ResponseView<List<Object>>> getPluginDynamicConfig(
     @Deprecated
     @JsonView(JsonViews.Public.class)
     @GetMapping("/listByApp")
-    public Mono<ResponseView<List<DatasourceView>>> listAppDataSources(@RequestParam(name = "appId") String applicationId);
+    public Mono<ResponseView<List<DatasourceView>>> listAppDataSources(@RequestParam(name = "appId") String applicationId, @RequestParam String name, @RequestParam String type);
 
 	@Operation(
 			tags = TAG_DATASOURCE_PERMISSIONS,
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/configuration/PluginConfiguration.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/configuration/PluginConfiguration.java
index c7feaae0f..92dd32b73 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/configuration/PluginConfiguration.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/configuration/PluginConfiguration.java
@@ -1,28 +1,18 @@
 package org.lowcoder.api.framework.configuration;
 
-import java.util.ArrayList;
-
 import org.lowcoder.api.framework.plugin.LowcoderPluginManager;
 import org.lowcoder.api.framework.plugin.endpoint.PluginEndpointHandler;
-// Falk: eventually not needed
-import org.lowcoder.api.framework.plugin.security.PluginAuthorizationManager;
-import org.lowcoder.plugin.api.EndpointExtension;
-import org.springframework.aop.Advisor;
-import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
-import org.springframework.beans.factory.config.BeanDefinition;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.DependsOn;
-import org.springframework.context.annotation.Role;
-import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;
-import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;
 import org.springframework.web.reactive.function.server.RequestPredicates;
 import org.springframework.web.reactive.function.server.RouterFunction;
 import org.springframework.web.reactive.function.server.RouterFunctions;
 import org.springframework.web.reactive.function.server.ServerResponse;
-
 import reactor.core.publisher.Mono;
 
+import java.util.ArrayList;
+
 @Configuration
 public class PluginConfiguration
 {
@@ -43,15 +33,4 @@ RouterFunction<?> pluginEndpoints(LowcoderPluginManager pluginManager, PluginEnd
         
         return (endpoints == null) ? pluginsList : pluginsList.andOther(endpoints);
     }
-    
-    // Falk: eventually not needed
-    @Bean
-    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
-    Advisor protectPluginEndpoints(PluginAuthorizationManager pluginAauthManager)
-    {
-    	AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(EndpointExtension.class, true);
-    	AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(pointcut, pluginAauthManager);
-    	interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder() -1);
-    	return interceptor;
-    }
 }
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/UserSessionPersistenceFilter.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/UserSessionPersistenceFilter.java
index 589aae782..75d28bd46 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/UserSessionPersistenceFilter.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/filter/UserSessionPersistenceFilter.java
@@ -122,6 +122,7 @@ private Mono<User> refreshOauthToken(Triple<User, Connection, List<String>> trip
 
             return authenticationService
                     .findAllAuthConfigs(orgId, true)
+                    .onErrorResume(e -> Flux.empty())
                     .filter(findAuthConfig -> findAuthConfig.authConfig().getId().equals(connection.getAuthId()))
                     .switchIfEmpty(Mono.empty())
                     .flatMap(findAuthConfig -> {
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/endpoint/PluginEndpointHandlerImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/endpoint/PluginEndpointHandlerImpl.java
index 214252827..1e1b3c8e3 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/endpoint/PluginEndpointHandlerImpl.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/framework/plugin/endpoint/PluginEndpointHandlerImpl.java
@@ -1,5 +1,6 @@
 package org.lowcoder.api.framework.plugin.endpoint;
 
+import static org.lowcoder.sdk.exception.BizError.NOT_AUTHORIZED;
 import static org.springframework.web.reactive.function.server.RequestPredicates.DELETE;
 import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
 import static org.springframework.web.reactive.function.server.RequestPredicates.OPTIONS;
@@ -8,22 +9,30 @@
 import static org.springframework.web.reactive.function.server.RequestPredicates.PUT;
 import static org.springframework.web.reactive.function.server.RouterFunctions.route;
 
+import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
+import org.aopalliance.intercept.MethodInvocation;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.lowcoder.api.framework.plugin.data.PluginServerRequest;
+import org.lowcoder.api.framework.plugin.security.PluginAuthorizationManager;
 import org.lowcoder.api.framework.plugin.security.SecuredEndpoint;
 import org.lowcoder.plugin.api.EndpointExtension;
 import org.lowcoder.plugin.api.PluginEndpoint;
 import org.lowcoder.plugin.api.data.EndpointRequest;
 import org.lowcoder.plugin.api.data.EndpointResponse;
 import org.lowcoder.sdk.exception.BaseException;
+import org.lowcoder.sdk.exception.BizException;
 import org.springframework.aop.TargetSource;
 import org.springframework.aop.framework.ProxyFactoryBean;
+import org.springframework.aop.framework.ReflectiveMethodInvocation;
 import org.springframework.aop.target.SimpleBeanTargetSource;
 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
 import org.springframework.context.ApplicationContext;
@@ -31,7 +40,11 @@
 import org.springframework.core.ResolvableType;
 import org.springframework.http.ResponseCookie;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.ReactiveSecurityContextHolder;
+import org.springframework.security.core.context.SecurityContext;
 import org.springframework.stereotype.Component;
 import org.springframework.web.reactive.function.server.RequestPredicate;
 import org.springframework.web.reactive.function.server.RouterFunction;
@@ -52,6 +65,7 @@ public class PluginEndpointHandlerImpl implements PluginEndpointHandler
 	
 	private final ApplicationContext applicationContext;
 	private final DefaultListableBeanFactory beanFactory;
+	private final PluginAuthorizationManager pluginAuthorizationManager;
 	
 	@Override
 	public void registerEndpoints(String pluginUrlPrefix, List<PluginEndpoint> endpoints) 
@@ -101,26 +115,69 @@ private void registerEndpointHandler(String urlPrefix, PluginEndpoint endpoint,
 
 		log.info("Registered endpoint: {} -> {}: {}", endpoint.getClass().getSimpleName(), endpointMeta.method(), urlPrefix + endpointMeta.uri());
 	}
-	
-	@SecuredEndpoint
+
 	public Mono<ServerResponse> runPluginEndpointMethod(PluginEndpoint endpoint, EndpointExtension endpointMeta, Method handler, ServerRequest request)
 	{
-		Mono<ServerResponse> result = null;
-		try
-		{
-			log.info("Running plugin endpoint method {}\nRequest: {}", handler.getName(), request);
+		log.info("Running plugin endpoint method {}\nRequest: {}", handler.getName(), request);
 
-			EndpointResponse response = (EndpointResponse)handler.invoke(endpoint, PluginServerRequest.fromServerRequest(request));
-			result = createServerResponse(response);
-		}
-		catch (IllegalAccessException | InvocationTargetException cause) 
-		{
-			throw new BaseException("Error running handler for [ " + endpointMeta.method() + ": " + endpointMeta.uri() + "] !");
-		}
-		return result; 		
+		Mono<Authentication> monoAuthentication = ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication).cache();
+		Mono<AuthorizationDecision> decisionMono = monoAuthentication.flatMap(authentication -> {
+			MethodInvocation methodInvocation = null;
+			try {
+				methodInvocation = getMethodInvocation(endpointMeta, authentication);
+			} catch (NoSuchMethodException e) {
+				return Mono.error(new RuntimeException(e));
+			}
+			return pluginAuthorizationManager.check(monoAuthentication, methodInvocation);
+		});
+
+		return decisionMono.<EndpointResponse>handle((authorizationDecision, sink) -> {
+			if(!authorizationDecision.isGranted()) sink.error(new BizException(NOT_AUTHORIZED, "NOT_AUTHORIZED"));
+			try {
+				sink.next((EndpointResponse) handler.invoke(endpoint, PluginServerRequest.fromServerRequest(request)));
+			} catch (IllegalAccessException | InvocationTargetException e) {
+				sink.error(new RuntimeException(e));
+			}
+		}).flatMap(this::createServerResponse);
 	}
-	
-	
+
+	private static @NotNull MethodInvocation getMethodInvocation(EndpointExtension endpointMeta, Authentication authentication) throws NoSuchMethodException {
+		Method method = Authentication.class.getMethod("isAuthenticated");
+		Object[] arguments = new Object[]{"someString", endpointMeta};
+        return new MethodInvocation() {
+			@NotNull
+			@Override
+			public Method getMethod() {
+				return method;
+			}
+
+			@NotNull
+			@Override
+			public Object[] getArguments() {
+				return arguments;
+			}
+
+			@Nullable
+			@Override
+			public Object proceed() throws Throwable {
+				return null;
+			}
+
+			@Nullable
+			@Override
+			public Object getThis() {
+				return authentication;
+			}
+
+			@NotNull
+			@Override
+			public AccessibleObject getStaticPart() {
+				return null;
+			}
+		};
+	}
+
+
 	private void registerRouterFunctionMapping(String endpointName, RouterFunction<ServerResponse> routerFunction)
 	{
 		String beanName = "pluginEndpoint_" + endpointName + "_" + System.currentTimeMillis();
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderApiService.java
index 721fc015f..de161bb19 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderApiService.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderApiService.java
@@ -27,7 +27,7 @@ public interface FolderApiService {
 
     Mono<Void> upsertLastViewTime(@Nullable String folderId);
 
-    Flux<?> getElements(@Nullable String folderId, @Nullable ApplicationType applicationType);
+    Flux<?> getElements(@Nullable String folderId, @Nullable ApplicationType applicationType, @Nullable String name);
 
     Mono<Void> grantPermission(String folderId, Set<String> userIds, Set<String> groupIds, ResourceRole role);
 
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderApiServiceImpl.java
index 0517fa935..79fafda96 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderApiServiceImpl.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderApiServiceImpl.java
@@ -233,8 +233,8 @@ public Mono<Void> upsertLastViewTime(@Nullable String folderId) {
      * @return flux of {@link ApplicationInfoView} or {@link FolderInfoView}
      */
     @Override
-    public Flux<?> getElements(@Nullable String folderId, @Nullable ApplicationType applicationType) {
-        return buildApplicationInfoViewTree(applicationType)
+    public Flux<?> getElements(@Nullable String folderId, @Nullable ApplicationType applicationType, @Nullable String name) {
+        return buildApplicationInfoViewTree(applicationType, name)
                 .flatMap(tree -> {
                     FolderNode<ApplicationInfoView, FolderInfoView> folderNode = tree.get(folderId);
                     if (folderNode == null) {
@@ -278,13 +278,13 @@ private Mono<Tree<Object, Folder>> buildFolderTree(String orgId) {
                 .map(folders -> new Tree<>(folders, Folder::getId, Folder::getParentFolderId, Collections.emptyList(), null, null));
     }
 
-    private Mono<Tree<ApplicationInfoView, FolderInfoView>> buildApplicationInfoViewTree(@Nullable ApplicationType applicationType) {
+    private Mono<Tree<ApplicationInfoView, FolderInfoView>> buildApplicationInfoViewTree(@Nullable ApplicationType applicationType, @Nullable String name) {
 
         Mono<OrgMember> orgMemberMono = sessionUserService.getVisitorOrgMemberCache()
                 .cache();
 
         Flux<ApplicationInfoView> applicationInfoViewFlux =
-                userHomeApiService.getAllAuthorisedApplications4CurrentOrgMember(applicationType, ApplicationStatus.NORMAL, false)
+                userHomeApiService.getAllAuthorisedApplications4CurrentOrgMember(applicationType, ApplicationStatus.NORMAL, false, null)
                         .cache();
 
         Mono<Map<String, String>> application2FolderMapMono = applicationInfoViewFlux
@@ -294,6 +294,9 @@ private Mono<Tree<ApplicationInfoView, FolderInfoView>> buildApplicationInfoView
                 .collectMap(FolderElement::elementId, FolderElement::folderId);
 
         Flux<Folder> folderFlux = orgMemberMono.flatMapMany(orgMember -> folderService.findByOrganizationId(orgMember.getOrgId()))
+                .filter(folder -> name == null || StringUtils.containsIgnoreCase(folder.getName(), name)
+                        || StringUtils.containsIgnoreCase(folder.getType(), name)
+                        || StringUtils.containsIgnoreCase(folder.getDescription(), name))
                 .cache();
 
         Mono<Map<String, Instant>> folderId2LastViewTimeMapMono = orgMemberMono
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderController.java
index f0be4d535..f75b73d9d 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderController.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderController.java
@@ -69,9 +69,10 @@ public Mono<ResponseView<FolderInfoView>> update(@RequestBody Folder folder) {
      */
     @Override
     public Mono<ResponseView<List<?>>> getElements(@RequestParam(value = "id", required = false) String folderId,
-            @RequestParam(value = "applicationType", required = false) ApplicationType applicationType) {
+            @RequestParam(value = "applicationType", required = false) ApplicationType applicationType,
+            @RequestParam(required = false) String name) {
         String objectId = gidService.convertFolderIdToObjectId(folderId);
-        return folderApiService.getElements(objectId, applicationType)
+        return folderApiService.getElements(objectId, applicationType, name)
                 .collectList()
                 .delayUntil(__ -> folderApiService.upsertLastViewTime(objectId))
                 .map(ResponseView::success);
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderEndpoints.java
index 420bfca3a..337048d96 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderEndpoints.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/FolderEndpoints.java
@@ -69,7 +69,8 @@ public interface FolderEndpoints
 	)
     @GetMapping("/elements")
     public Mono<ResponseView<List<?>>> getElements(@RequestParam(value = "id", required = false) String folderId,
-            @RequestParam(value = "applicationType", required = false) ApplicationType applicationType);
+            @RequestParam(value = "applicationType", required = false) ApplicationType applicationType,
+			@RequestParam(required = false) String name);
 
 	@Operation(
 			tags = TAG_FOLDER_MANAGEMENT,
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java
index e89962605..e711304a4 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiService.java
@@ -24,7 +24,7 @@ public interface UserHomeApiService {
     Mono<UserHomepageView> getUserHomePageView(ApplicationType applicationType);
 
     Flux<ApplicationInfoView> getAllAuthorisedApplications4CurrentOrgMember(@Nullable ApplicationType applicationType,
-                                                                            @Nullable ApplicationStatus applicationStatus, boolean withContainerSize);
+                                                                            @Nullable ApplicationStatus applicationStatus, boolean withContainerSize, @Nullable String name);
 
     Flux<BundleInfoView> getAllAuthorisedBundles4CurrentOrgMember(@Nullable BundleStatus bundleStatus);
 
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java
index b125f82fc..ae5f22fcf 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/home/UserHomeApiServiceImpl.java
@@ -157,7 +157,7 @@ public Mono<UserHomepageView> getUserHomePageView(ApplicationType applicationTyp
                     }
 
                     return organizationService.getById(currentOrgId)
-                            .zipWith(folderApiService.getElements(null, applicationType).collectList())
+                            .zipWith(folderApiService.getElements(null, applicationType, null).collectList())
                             .map(tuple2 -> {
                                 Organization organization = tuple2.getT1();
                                 List<?> list = tuple2.getT2();
@@ -189,7 +189,7 @@ public Mono<UserHomepageView> getUserHomePageView(ApplicationType applicationTyp
 
     @Override
     public Flux<ApplicationInfoView> getAllAuthorisedApplications4CurrentOrgMember(@Nullable ApplicationType applicationType,
-                                                                                   @Nullable ApplicationStatus applicationStatus, boolean withContainerSize) {
+                                                                                   @Nullable ApplicationStatus applicationStatus, boolean withContainerSize, @Nullable String name) {
 
         return sessionUserService.getVisitorOrgMemberCache()
                 .flatMapMany(orgMember -> {
@@ -202,8 +202,9 @@ public Flux<ApplicationInfoView> getAllAuthorisedApplications4CurrentOrgMember(@
                                 }
                                 return applicationService.findByOrganizationIdWithoutDsl(currentOrgId);
                             })
-                            .filter(application -> isNull(applicationType) || application.getApplicationType() == applicationType.getValue())
-                            .filter(application -> isNull(applicationStatus) || application.getApplicationStatus() == applicationStatus)
+                            .filter(application -> (isNull(applicationType) || application.getApplicationType() == applicationType.getValue())
+                                    && (isNull(applicationStatus) || application.getApplicationStatus() == applicationStatus)
+                                    && (isNull(name) || StringUtils.containsIgnoreCase(application.getName(), name)))
                             .cache()
                             .collectList()
                             .flatMapIterable(Function.identity());
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java
index 5f0d37690..19f5a50fb 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiService.java
@@ -9,7 +9,7 @@
 import java.util.List;
 
 public interface LibraryQueryApiService {
-    Mono<List<LibraryQueryView>> listLibraryQueries();
+    Mono<List<LibraryQueryView>> listLibraryQueries(String name);
 
     Mono<LibraryQueryView> create(LibraryQuery libraryQuery);
 
@@ -20,7 +20,7 @@ public interface LibraryQueryApiService {
     Mono<LibraryQueryRecordMetaView> publish(String libraryQueryId, LibraryQueryPublishRequest libraryQueryPublishRequest);
 
     @SuppressWarnings("ConstantConditions")
-    Mono<List<LibraryQueryAggregateView>> dropDownList();
+    Mono<List<LibraryQueryAggregateView>> dropDownList(String name);
 
     Mono<QueryExecutionResult> executeLibraryQueryFromJs(ServerWebExchange exchange, LibraryQueryRequestFromJs request);
 
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiServiceImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiServiceImpl.java
index 57505fca0..572f7ccdd 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiServiceImpl.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryApiServiceImpl.java
@@ -71,10 +71,11 @@ public class LibraryQueryApiServiceImpl implements LibraryQueryApiService {
     private int port;
 
     @Override
-    public Mono<List<LibraryQueryView>> listLibraryQueries() {
+    public Mono<List<LibraryQueryView>> listLibraryQueries(String name) {
         return orgDevChecker.checkCurrentOrgDev()
                 .then(sessionUserService.getVisitorOrgMemberCache())
                 .flatMapMany(orgMember -> getByOrgIdWithDatasourcePermissions(orgMember.getOrgId()))
+                .filter(libraryQuery -> StringUtils.containsIgnoreCase(libraryQuery.getName(), name))
                 .collectList()
                 .flatMap(libraryQueries -> ViewBuilder.multiBuild(libraryQueries,
                         LibraryQuery::getCreatedBy,
@@ -152,9 +153,10 @@ public Mono<LibraryQueryRecordMetaView> publish(String libraryQueryId, LibraryQu
 
     @Override
     @SuppressWarnings("ConstantConditions")
-    public Mono<List<LibraryQueryAggregateView>> dropDownList() {
+    public Mono<List<LibraryQueryAggregateView>> dropDownList(String name) {
         Mono<List<LibraryQuery>> libraryQueryListMono = sessionUserService.getVisitorOrgMemberCache()
                 .flatMapMany(orgMember -> getByOrgIdWithDatasourcePermissions(orgMember.getOrgId()))
+                .filter(libraryQuery -> StringUtils.containsIgnoreCase(libraryQuery.getName(), name))
                 .collectList()
                 .cache();
 
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryController.java
index dfbf317f5..dae78359c 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryController.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryController.java
@@ -16,6 +16,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import reactor.core.publisher.Mono;
@@ -34,14 +35,14 @@ public class LibraryQueryController implements LibraryQueryEndpoints
     private GidService gidService;
 
     @Override
-    public Mono<ResponseView<List<LibraryQueryAggregateView>>> dropDownList() {
-        return libraryQueryApiService.dropDownList()
+    public Mono<ResponseView<List<LibraryQueryAggregateView>>> dropDownList(@RequestParam(required = false) String name) {
+        return libraryQueryApiService.dropDownList(name)
                 .map(ResponseView::success);
     }
 
     @Override
-    public Mono<ResponseView<List<LibraryQueryView>>> list() {
-        return libraryQueryApiService.listLibraryQueries()
+    public Mono<ResponseView<List<LibraryQueryView>>> list(@RequestParam(required = false) String name) {
+        return libraryQueryApiService.listLibraryQueries(name)
                 .map(ResponseView::success);
     }
 
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryEndpoints.java
index 9d493ddf0..97f6947d8 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryEndpoints.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/query/LibraryQueryEndpoints.java
@@ -11,14 +11,7 @@
 import org.lowcoder.api.query.view.LibraryQueryView;
 import org.lowcoder.api.query.view.UpsertLibraryQueryRequest;
 import org.lowcoder.domain.query.model.LibraryQuery;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 
 import io.swagger.v3.oas.annotations.Operation;
 import reactor.core.publisher.Mono;
@@ -36,7 +29,7 @@ public interface LibraryQueryEndpoints
 		    description = "Retrieve Library Queries in a dropdown format within Lowcoder, suitable for selection in user interfaces."
 	)
     @GetMapping("/dropDownList")
-    public Mono<ResponseView<List<LibraryQueryAggregateView>>> dropDownList();
+    public Mono<ResponseView<List<LibraryQueryAggregateView>>> dropDownList(@RequestParam(required = false) String name);
 
 	@Operation(
 			tags = TAG_LIBRARY_QUERY_MANAGEMENT,
@@ -45,7 +38,7 @@ public interface LibraryQueryEndpoints
 		    description = "Retrieve a list of Library Queries for a specific Organization within Lowcoder."
 	)
     @GetMapping("/listByOrg")
-    public Mono<ResponseView<List<LibraryQueryView>>> list();
+    public Mono<ResponseView<List<LibraryQueryView>>> list(@RequestParam(required = false) String name);
 
 	@Operation(
 			tags = TAG_LIBRARY_QUERY_MANAGEMENT,
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationController.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationController.java
index 846b2483c..562d072f9 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationController.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationController.java
@@ -17,6 +17,7 @@
 import org.lowcoder.domain.organization.service.OrganizationService;
 import org.lowcoder.domain.plugin.DatasourceMetaInfo;
 import org.lowcoder.domain.plugin.service.DatasourceMetaInfoService;
+import org.lowcoder.domain.user.service.UserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.codec.multipart.Part;
 import org.springframework.web.bind.annotation.*;
@@ -41,10 +42,12 @@ public class OrganizationController implements OrganizationEndpoints
     private OrgMemberService orgMemberService;
     @Autowired
     private OrganizationService organizationService;
+    @Autowired
+    private UserService userService;
 
     @Override
-    public Mono<ResponseView<List<OrgView>>> getOrganizationByUser(@PathVariable String userId) {
-        return orgMemberService.getAllActiveOrgs(userId)
+    public Mono<ResponseView<List<OrgView>>> getOrganizationByUser(@PathVariable String email) {
+        return userService.findByEmailDeep(email).flux().flatMap(user -> orgMemberService.getAllActiveOrgs(user.getId()))
                 .flatMap(orgMember -> organizationService.getById(orgMember.getOrgId()))
                 .map(OrgView::new)
                 .collectList()
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationEndpoints.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationEndpoints.java
index a2c3dac6c..5ceaa5bd4 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationEndpoints.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/usermanagement/OrganizationEndpoints.java
@@ -43,8 +43,8 @@ public interface OrganizationEndpoints
 		    summary = "Get a list of specified user's organization",
 		    description = "Get a list of specified user's organization"
 	)
-    @GetMapping("/byuser/{userId}")
-    public Mono<ResponseView<List<OrgView>>> getOrganizationByUser(@PathVariable String userId);
+    @GetMapping("/byuser/{email}")
+    public Mono<ResponseView<List<OrgView>>> getOrganizationByUser(@PathVariable String email);
 
 	@Operation(
 			tags = TAG_ORGANIZATION_MANAGEMENT,
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/runner/migrations/DatabaseChangelog.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/runner/migrations/DatabaseChangelog.java
index ba6b2d8c3..e533b42e5 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/runner/migrations/DatabaseChangelog.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/runner/migrations/DatabaseChangelog.java
@@ -7,6 +7,8 @@
 import lombok.extern.slf4j.Slf4j;
 import org.bson.Document;
 import org.lowcoder.domain.application.model.Application;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
 import org.lowcoder.domain.bundle.model.Bundle;
 import org.lowcoder.domain.datasource.model.Datasource;
 import org.lowcoder.domain.datasource.model.DatasourceStructureDO;
@@ -27,9 +29,11 @@
 import org.lowcoder.runner.migrations.job.AddSuperAdminUser;
 import org.lowcoder.runner.migrations.job.CompleteAuthType;
 import org.lowcoder.runner.migrations.job.MigrateAuthConfigJob;
+import org.lowcoder.sdk.config.CommonConfig;
 import org.springframework.context.annotation.Profile;
 import org.springframework.data.domain.Sort;
 import org.springframework.data.mongodb.UncategorizedMongoDbException;
+import org.springframework.data.mongodb.core.CollectionOptions;
 import org.springframework.data.mongodb.core.DocumentCallbackHandler;
 import org.springframework.data.mongodb.core.index.CompoundIndexDefinition;
 import org.springframework.data.mongodb.core.index.Index;
@@ -39,6 +43,8 @@
 import org.springframework.data.mongodb.core.query.Update;
 
 import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
 import java.util.Set;
 
 import static org.lowcoder.domain.util.QueryDslUtils.fieldName;
@@ -273,7 +279,7 @@ public void processDocument(Document document) {
     @ChangeSet(order = "025", id = "add-gid-indexes-unique", author = "")
     public void addGidIndexesUnique(MongockTemplate mongoTemplate) {
         // collections to add gid
-        String[] collectionNames = {"group", "organization"};
+        String[] collectionNames = {"group", "organization", "application", "bundle", "datasource", "libraryQuery", "folder"};
 
         // Get the list of existing collections
         Set<String> existingCollections = mongoTemplate.getCollectionNames();
@@ -295,6 +301,35 @@ public void addGidIndexesUnique(MongockTemplate mongoTemplate) {
         ensureIndexes(mongoTemplate, LibraryQuery.class, makeIndex("gid").unique());
     }
 
+    @ChangeSet(order = "026", id = "add-time-series-snapshot-history", author = "")
+    public void addTimeSeriesSnapshotHistory(MongockTemplate mongoTemplate, CommonConfig commonConfig) {
+        // Create the time-series collection if it doesn't exist
+        if (!mongoTemplate.collectionExists(ApplicationHistorySnapshotTS.class)) {
+            mongoTemplate.createCollection(ApplicationHistorySnapshotTS.class, CollectionOptions.empty().timeSeries("createdAt"));
+        }
+        Instant thresholdDate = Instant.now().minus(commonConfig.getQuery().getAppSnapshotKeepDuration(), ChronoUnit.DAYS);
+        List<ApplicationHistorySnapshot> snapshots = mongoTemplate.find(new Query().addCriteria(Criteria.where("createdAt").gte(thresholdDate)), ApplicationHistorySnapshot.class);
+        snapshots.forEach(snapshot -> {
+            ApplicationHistorySnapshotTS applicationHistorySnapshotTS = new ApplicationHistorySnapshotTS();
+            applicationHistorySnapshotTS.setApplicationId(snapshot.getApplicationId());
+            applicationHistorySnapshotTS.setDsl(snapshot.getDsl());
+            applicationHistorySnapshotTS.setContext(snapshot.getContext());
+            applicationHistorySnapshotTS.setCreatedAt(snapshot.getCreatedAt());
+            applicationHistorySnapshotTS.setCreatedBy(snapshot.getCreatedBy());
+            applicationHistorySnapshotTS.setModifiedBy(snapshot.getModifiedBy());
+            applicationHistorySnapshotTS.setUpdatedAt(snapshot.getUpdatedAt());
+            applicationHistorySnapshotTS.setId(snapshot.getId());
+            mongoTemplate.insert(applicationHistorySnapshotTS);
+            mongoTemplate.remove(snapshot);
+        });
+
+        // Ensure indexes if needed
+        ensureIndexes(mongoTemplate, ApplicationHistorySnapshotTS.class,
+                makeIndex("applicationId"),
+                makeIndex("createdAt")
+        );
+    }
+
     private void addGidField(MongockTemplate mongoTemplate, String collectionName) {
         // Create a query to match all documents
         Query query = new Query();
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/runner/task/ArchiveSnapshotTask.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/runner/task/ArchiveSnapshotTask.java
new file mode 100644
index 000000000..2fa516379
--- /dev/null
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/runner/task/ArchiveSnapshotTask.java
@@ -0,0 +1,46 @@
+package org.lowcoder.runner.task;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
+import org.lowcoder.sdk.config.CommonConfig;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@RequiredArgsConstructor
+@Component
+public class ArchiveSnapshotTask {
+
+    private final CommonConfig commonConfig;
+    private final MongoTemplate mongoTemplate;
+
+    @Scheduled(initialDelay = 1, fixedRate = 1, timeUnit = TimeUnit.DAYS)
+    public void archive() {
+        Instant thresholdDate = Instant.now().minus(commonConfig.getQuery().getAppSnapshotKeepDuration(), ChronoUnit.DAYS);
+        List<ApplicationHistorySnapshotTS> snapshots = mongoTemplate.find(new Query().addCriteria(Criteria.where("createdAt").lte(thresholdDate)), ApplicationHistorySnapshotTS.class);
+        snapshots.forEach(snapshot -> {
+            ApplicationHistorySnapshot applicationHistorySnapshot = new ApplicationHistorySnapshot();
+            applicationHistorySnapshot.setApplicationId(snapshot.getApplicationId());
+            applicationHistorySnapshot.setDsl(snapshot.getDsl());
+            applicationHistorySnapshot.setContext(snapshot.getContext());
+            applicationHistorySnapshot.setCreatedAt(snapshot.getCreatedAt());
+            applicationHistorySnapshot.setCreatedBy(snapshot.getCreatedBy());
+            applicationHistorySnapshot.setModifiedBy(snapshot.getModifiedBy());
+            applicationHistorySnapshot.setUpdatedAt(snapshot.getUpdatedAt());
+            applicationHistorySnapshot.setId(snapshot.getId());
+            mongoTemplate.insert(applicationHistorySnapshot);
+            mongoTemplate.remove(snapshot);
+        });
+    }
+
+}
diff --git a/server/api-service/lowcoder-server/src/main/resources/application-debug.yaml b/server/api-service/lowcoder-server/src/main/resources/application-debug.yaml
index 5e17368b5..5f16fdace 100644
--- a/server/api-service/lowcoder-server/src/main/resources/application-debug.yaml
+++ b/server/api-service/lowcoder-server/src/main/resources/application-debug.yaml
@@ -47,6 +47,8 @@ common:
   notifications-email-sender: ${LOWCODER_EMAIL_NOTIFICATIONS_SENDER:info@localhost}
   cookie:
     max-age-in-hours: ${LOWCODER_COOKIE_MAX_AGE:24}
+  query:
+    app-snapshot-keep-duration: ${LOWCODER_APP_SNAPSHOT_RETENTIONTIME:30}
 
 debug: true
 
diff --git a/server/api-service/lowcoder-server/src/main/resources/application.yaml b/server/api-service/lowcoder-server/src/main/resources/application.yaml
index 4184806b2..1ca536930 100644
--- a/server/api-service/lowcoder-server/src/main/resources/application.yaml
+++ b/server/api-service/lowcoder-server/src/main/resources/application.yaml
@@ -88,6 +88,8 @@ common:
   notifications-email-sender: ${LOWCODER_EMAIL_NOTIFICATIONS_SENDER:info@localhost}
   cookie:
     max-age-in-hours: ${LOWCODER_COOKIE_MAX_AGE:24}
+  query:
+    app-snapshot-keep-duration: ${LOWCODER_APP_SNAPSHOT_RETENTIONTIME:30}
 
 material:
   mongodb-grid-fs:
diff --git a/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/datasource/DatasourceApiServiceIntegrationTest.java b/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/datasource/DatasourceApiServiceIntegrationTest.java
index f11b74c04..27f0e56ee 100644
--- a/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/datasource/DatasourceApiServiceIntegrationTest.java
+++ b/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/datasource/DatasourceApiServiceIntegrationTest.java
@@ -54,7 +54,7 @@ public void testListOrgDatasource() {
                         .flatMap(permissionItemView -> datasourceApiService.updatePermission(permissionItemView.getPermissionId(), VIEWER))
                         // create mysql05
                         .then(datasourceApiService.create(buildMysqlDatasource("mysql05")))
-                        .then(datasourceApiService.listOrgDataSources("org01").collectList());
+                        .then(datasourceApiService.listOrgDataSources("org01", null, null).collectList());
 
         StepVerifier.create(datasourceListMono)
                 .assertNext(datasourceViews -> {
diff --git a/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/query/LibraryQueryApiServiceIntegrationTest.java b/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/query/LibraryQueryApiServiceIntegrationTest.java
index 461811478..f4886ca5b 100644
--- a/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/query/LibraryQueryApiServiceIntegrationTest.java
+++ b/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/query/LibraryQueryApiServiceIntegrationTest.java
@@ -47,7 +47,7 @@ public void beforeEach() {
     public void testListLibraryQueries() {
         Mono<List<LibraryQueryView>> listMono = datasourceApiService.create(DatasourceApiServiceIntegrationTest.buildMysqlDatasource("mysql06"))
                 .flatMap(datasource -> libraryQueryApiService.create(buildLibraryQuery("query01", datasource.getId())))
-                .then(libraryQueryApiService.listLibraryQueries());
+                .then(libraryQueryApiService.listLibraryQueries(null));
 
         StepVerifier.create(listMono)
                 .assertNext(libraryQueryViews -> {
diff --git a/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/service/FolderApiServiceTest.java b/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/service/FolderApiServiceTest.java
index 1413e01a2..c470c11d0 100644
--- a/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/service/FolderApiServiceTest.java
+++ b/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/service/FolderApiServiceTest.java
@@ -137,7 +137,7 @@ public void updateByGid() {
     public void move() {
 
         Mono<? extends List<?>> mono = folderApiService.move("app01", "folder02")
-                .then(folderApiService.getElements("folder02", null).collectList());
+                .then(folderApiService.getElements("folder02", null, null).collectList());
 
         StepVerifier.create(mono)
                 .assertNext(list -> {
diff --git a/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/service/impl/ApplicationHistorySnapshotServiceTest.java b/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/service/impl/ApplicationHistorySnapshotServiceTest.java
index d728dfc54..fb7109134 100644
--- a/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/service/impl/ApplicationHistorySnapshotServiceTest.java
+++ b/server/api-service/lowcoder-server/src/test/java/org/lowcoder/api/service/impl/ApplicationHistorySnapshotServiceTest.java
@@ -2,10 +2,9 @@
 
 import com.google.common.collect.ImmutableMap;
 import lombok.extern.slf4j.Slf4j;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.lowcoder.domain.application.model.ApplicationHistorySnapshot;
+import org.lowcoder.domain.application.model.ApplicationHistorySnapshotTS;
 import org.lowcoder.domain.application.service.ApplicationHistorySnapshotService;
 import org.lowcoder.sdk.models.HasIdAndAuditing;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -44,12 +43,12 @@ public void testServiceMethods() {
                 .verifyComplete();
 
 
-        StepVerifier.create(service.listAllHistorySnapshotBriefInfo(applicationId, PageRequest.of(0, 5)))
+        StepVerifier.create(service.listAllHistorySnapshotBriefInfo(applicationId, null, null, null, null, PageRequest.of(0, 5)))
                 .assertNext(list -> {
                     assertEquals(2, list.size());
 
-                    ApplicationHistorySnapshot first = list.get(0);
-                    ApplicationHistorySnapshot second = list.get(1);
+                    ApplicationHistorySnapshotTS first = list.get(0);
+                    ApplicationHistorySnapshotTS second = list.get(1);
                     assertTrue(first.getCreatedAt().isAfter(second.getCreatedAt()));
 
                     assertNull(first.getDsl());
@@ -64,10 +63,10 @@ public void testServiceMethods() {
                 })
                 .verifyComplete();
 
-        StepVerifier.create(service.listAllHistorySnapshotBriefInfo(applicationId, PageRequest.of(1, 1)))
+        StepVerifier.create(service.listAllHistorySnapshotBriefInfo(applicationId, null, null, null, null, PageRequest.of(1, 1)))
                 .assertNext(list -> {
                     assertEquals(1, list.size());
-                    ApplicationHistorySnapshot one = list.get(0);
+                    ApplicationHistorySnapshotTS one = list.get(0);
                     assertNull(one.getDsl());
                     assertEquals(ImmutableMap.of("context", "context1"), one.getContext());
                     assertEquals(applicationId, one.getApplicationId());
@@ -75,7 +74,7 @@ public void testServiceMethods() {
                 .verifyComplete();
 
 
-        StepVerifier.create(service.listAllHistorySnapshotBriefInfo(applicationId, PageRequest.of(0, 5))
+        StepVerifier.create(service.listAllHistorySnapshotBriefInfo(applicationId, null, null, null, null, PageRequest.of(0, 5))
                         .map(it -> it.get(0))
                         .map(HasIdAndAuditing::getId)
                         .flatMap(id -> service.getHistorySnapshotDetail(id)))
diff --git a/server/api-service/pom.xml b/server/api-service/pom.xml
index 10c35fd82..200dfebf5 100644
--- a/server/api-service/pom.xml
+++ b/server/api-service/pom.xml
@@ -12,7 +12,7 @@
 
 
 	<properties>
-		<revision>2.4.9</revision>
+		<revision>2.4.11</revision>
 		<java.version>17</java.version>
 		<maven.compiler.source>${java.version}</maven.compiler.source>
 		<maven.compiler.target>${java.version}</maven.compiler.target>
diff --git a/server/node-service/package.json b/server/node-service/package.json
index 1152337c5..9827f6799 100644
--- a/server/node-service/package.json
+++ b/server/node-service/package.json
@@ -1,6 +1,6 @@
 {
   "name": "lowcoder-node-server",
-  "version": "2.4.9",
+  "version": "2.4.11",
   "private": true,
   "engines": {
     "node": "^14.18.0 || >=16.0.0"
diff --git a/server/node-service/src/plugins/firebase/dataSourceConfig.ts b/server/node-service/src/plugins/firebase/dataSourceConfig.ts
index 86fe2cb5d..5cf3ce099 100644
--- a/server/node-service/src/plugins/firebase/dataSourceConfig.ts
+++ b/server/node-service/src/plugins/firebase/dataSourceConfig.ts
@@ -25,7 +25,7 @@ const dataSourceConfig = {
       label: "Private Key",
       type: "password",
       tooltip:
-        "The [document](https://firebase.google.com/docs/admin/setup) on how to obtain the private key.",
+        "The JSON [document](https://firebase.google.com/docs/admin/setup) on how to obtain the private key.",
     },
     {
       label: "Spec Version",
diff --git a/server/node-service/src/plugins/firebase/queryConfig.ts b/server/node-service/src/plugins/firebase/queryConfig.ts
index 3e1dbae37..ddf4128ba 100644
--- a/server/node-service/src/plugins/firebase/queryConfig.ts
+++ b/server/node-service/src/plugins/firebase/queryConfig.ts
@@ -108,6 +108,12 @@ const queryConfig = {
               type: "numberInput",
               defaultValue: 10,
             },
+            {
+              key: "startAt",
+              label: "Start at",
+              type: "textInput",
+              tooltip: "Start the query at a specific document.",
+            }
           ],
         },
         {
diff --git a/server/node-service/src/plugins/firebase/run.ts b/server/node-service/src/plugins/firebase/run.ts
index 258405627..d837bc075 100644
--- a/server/node-service/src/plugins/firebase/run.ts
+++ b/server/node-service/src/plugins/firebase/run.ts
@@ -87,11 +87,22 @@ export async function runFirebasePlugin(
       const data = await withFirestoreCollection(async (ref) => {
         let query;
         if (actionData.orderBy) {
+          console.log("orderBy", actionData.orderBy);
           query = ref.orderBy(
             actionData.orderBy,
             (actionData.orderDirection || "asc") as OrderByDirection
           );
         }
+        // Apply startAt if specified (for pagination)
+        if (actionData.startAt) {
+          if (Array.isArray(actionData.startAt)) {
+              // If startAt is an array, pass it as is
+              query = (query || ref).startAt(...actionData.startAt);
+          } else {
+              // If startAt is a single value, use it directly
+              query = (query || ref).startAt(actionData.startAt);
+          }
+        }
         if (actionData.limit > 0) {
           query = (query || ref).limit(actionData.limit);
         }