diff --git a/client/packages/lowcoder-design/src/components/Switch.tsx b/client/packages/lowcoder-design/src/components/Switch.tsx
index 576d304112..ae8a3e884d 100644
--- a/client/packages/lowcoder-design/src/components/Switch.tsx
+++ b/client/packages/lowcoder-design/src/components/Switch.tsx
@@ -52,6 +52,24 @@ const SwitchStyle: any = styled.input`
     border-radius: 20px;
     background-color: #ffffff;
   }
+
+  &:disabled {
+    background-color: #e0e0e0;
+    opacity: 0.6;
+    cursor: not-allowed;
+  }
+
+  &:disabled::before {
+    background-color: #cccccc;
+  }
+
+  &:disabled:checked {
+    background-color: #a0a0a0;
+  }
+
+  &:disabled:hover {
+    cursor: not-allowed;
+  }
 `;
 
 const SwitchDiv = styled.div<{
@@ -101,25 +119,31 @@ const JsIconGray = styled(jsIconGray)<Ishow>`
   ${IconCss}
 `;
 
-interface SwitchProps extends Omit<React.HTMLAttributes<HTMLInputElement>, "value" | "onChange"> {
+interface SwitchProps
+  extends Omit<React.HTMLAttributes<HTMLInputElement>, "value" | "onChange"> {
   value: boolean;
   onChange: (value: boolean) => void;
+  disabled?: boolean;
 }
 
 export const Switch = (props: SwitchProps) => {
-  const { value, onChange, ...inputChanges } = props;
+  const { value, onChange, disabled, ...inputChanges } = props;
   return (
     <SwitchStyle
       type="checkbox"
-      checked={props.value}
-      onClick={() => props.onChange(!props.value)}
+      checked={value}
+      onClick={() => onChange(!value)}
       onChange={() => {}}
+      disabled={disabled}
       {...inputChanges}
     />
   );
 };
 
-export const SwitchJsIcon = (props: { checked: boolean; onChange: (value: boolean) => void }) => {
+export const SwitchJsIcon = (props: {
+  checked: boolean;
+  onChange: (value: boolean) => void;
+}) => {
   const toggleShow = () => {
     props.onChange(!props.checked);
   };
@@ -154,15 +178,17 @@ export const SwitchWrapper = (props: {
 export function TacoSwitch(props: {
   label: string;
   checked: boolean;
-  onChange: (checked: boolean) => void;
+  disabled?: boolean;
+  onChange?: (checked: boolean) => void;
 }) {
   return (
     <SwitchWrapper label={props.label}>
       <Switch
         onChange={(value) => {
-          props.onChange(value);
+          props.onChange ? props.onChange(value) : null;
         }}
         value={props.checked}
+        disabled={props.disabled}
       />
     </SwitchWrapper>
   );
diff --git a/client/packages/lowcoder/src/api/applicationApi.ts b/client/packages/lowcoder/src/api/applicationApi.ts
index 2411b50d80..1aba07fbf4 100644
--- a/client/packages/lowcoder/src/api/applicationApi.ts
+++ b/client/packages/lowcoder/src/api/applicationApi.ts
@@ -12,7 +12,7 @@ import {
   SetAppEditingStatePayload,
   UpdateAppPermissionPayload,
 } from "redux/reduxActions/applicationActions";
-import {ApiResponse, GenericApiResponse} from "./apiResponses";
+import { ApiResponse, GenericApiResponse } from "./apiResponses";
 import { JSONObject, JSONValue } from "util/jsonTypes";
 import {
   ApplicationDetail,
@@ -24,7 +24,10 @@ import {
 } from "constants/applicationConstants";
 import { CommonSettingResponseData } from "./commonSettingApi";
 import { ResourceType } from "@lowcoder-ee/constants/queryConstants";
-import {fetchAppRequestType, GenericApiPaginationResponse} from "@lowcoder-ee/util/pagination/type";
+import {
+  fetchAppRequestType,
+  GenericApiPaginationResponse,
+} from "@lowcoder-ee/util/pagination/type";
 
 export interface HomeOrgMeta {
   id: string;
@@ -70,6 +73,11 @@ export interface ApplicationResp extends ApiResponse {
   data: ApplicationDetail;
 }
 
+export interface ApplicationPublishRequest {
+  commitMessage?: string;
+  tag: string;
+}
+
 interface GrantAppPermissionReq {
   applicationId: string;
   role: ApplicationRoleType;
@@ -82,38 +90,63 @@ class ApplicationApi extends Api {
   static fetchHomeDataURL = "/applications/home";
   static createApplicationURL = "/applications";
   static fetchAllMarketplaceAppsURL = "/applications/marketplace-apps";
-  static deleteApplicationURL = (applicationId: string) => `/applications/${applicationId}`;
-  static getAppPublishInfoURL = (applicationId: string) => `/applications/${applicationId}/view`;
-  static getAppEditingInfoURL = (applicationId: string) => `/applications/${applicationId}`;
-  static updateApplicationURL = (applicationId: string) => `/applications/${applicationId}`;
+  static deleteApplicationURL = (applicationId: string) =>
+    `/applications/${applicationId}`;
+  static getAppPublishInfoURL = (applicationId: string) =>
+    `/applications/${applicationId}/view`;
+  static getAppEditingInfoURL = (applicationId: string) =>
+    `/applications/${applicationId}`;
+  static updateApplicationURL = (applicationId: string) =>
+    `/applications/${applicationId}`;
   static getApplicationPermissionURL = (applicationId: string) =>
     `/applications/${applicationId}/permissions`;
   static grantAppPermissionURL = (applicationId: string) =>
     `/applications/${applicationId}/permissions`;
   static publishApplicationURL = (applicationId: string) =>
     `/applications/${applicationId}/publish`;
-  static updateAppPermissionURL = (applicationId: string, permissionId: string) =>
-    `/applications/${applicationId}/permissions/${permissionId}`;
+  static updateAppPermissionURL = (
+    applicationId: string,
+    permissionId: string
+  ) => `/applications/${applicationId}/permissions/${permissionId}`;
   static createFromTemplateURL = `/applications/createFromTemplate`;
-  static publicToAllURL = (applicationId: string) => `/applications/${applicationId}/public-to-all`;
-  static publicToMarketplaceURL = (applicationId: string) => `/applications/${applicationId}/public-to-marketplace`;
-  static getMarketplaceAppURL = (applicationId: string) => `/applications/${applicationId}/view_marketplace`;
-  static setAppEditingStateURL = (applicationId: string) => `/applications/editState/${applicationId}`;
+  static publicToAllURL = (applicationId: string) =>
+    `/applications/${applicationId}/public-to-all`;
+  static publicToMarketplaceURL = (applicationId: string) =>
+    `/applications/${applicationId}/public-to-marketplace`;
+  static getMarketplaceAppURL = (applicationId: string) =>
+    `/applications/${applicationId}/view_marketplace`;
+  static setAppEditingStateURL = (applicationId: string) =>
+    `/applications/editState/${applicationId}`;
   static serverSettingsURL = () => `/serverSettings`;
 
-  static fetchHomeData(request: HomeDataPayload): AxiosPromise<HomeDataResponse> {
+  static fetchHomeData(
+    request: HomeDataPayload
+  ): AxiosPromise<HomeDataResponse> {
     return Api.get(ApplicationApi.fetchHomeDataURL, request);
   }
 
-  static fetchAllApplications(request: HomeDataPayload): AxiosPromise<ApplicationMeta[]> {
-    return Api.get(ApplicationApi.newURLPrefix + "/list", { ...request, withContainerSize: false });
+  static fetchAllApplications(
+    request: HomeDataPayload
+  ): AxiosPromise<ApplicationMeta[]> {
+    return Api.get(ApplicationApi.newURLPrefix + "/list", {
+      ...request,
+      withContainerSize: false,
+    });
   }
 
-  static fetchAllApplicationsPagination(request: fetchAppRequestType): AxiosPromise<GenericApiPaginationResponse<ApplicationMeta[]>> {
-    return Api.get(ApplicationApi.newURLPrefix + "/list", { ...request, withContainerSize: false, applicationStatus: "RECYCLED" });
+  static fetchAllApplicationsPagination(
+    request: fetchAppRequestType
+  ): AxiosPromise<GenericApiPaginationResponse<ApplicationMeta[]>> {
+    return Api.get(ApplicationApi.newURLPrefix + "/list", {
+      ...request,
+      withContainerSize: false,
+      applicationStatus: "RECYCLED",
+    });
   }
 
-  static fetchAllModules(request: HomeDataPayload): AxiosPromise<ApplicationMeta[]> {
+  static fetchAllModules(
+    request: HomeDataPayload
+  ): AxiosPromise<ApplicationMeta[]> {
     return Api.get(ApplicationApi.newURLPrefix + "/list", {
       applicationType: AppTypeEnum.Module,
       applicationStatus: "NORMAL",
@@ -122,11 +155,15 @@ class ApplicationApi extends Api {
     });
   }
 
-  static fetchRecycleList(): AxiosPromise<GenericApiResponse<ApplicationMeta[]>> {
+  static fetchRecycleList(): AxiosPromise<
+    GenericApiResponse<ApplicationMeta[]>
+  > {
     return Api.get(ApplicationApi.newURLPrefix + "/recycle/list");
   }
 
-  static createApplication(request: CreateApplicationPayload): AxiosPromise<ApplicationResp> {
+  static createApplication(
+    request: CreateApplicationPayload
+  ): AxiosPromise<ApplicationResp> {
     return Api.post(ApplicationApi.createApplicationURL, {
       orgId: request.orgId,
       name: request.applicationName,
@@ -145,19 +182,25 @@ class ApplicationApi extends Api {
   static recycleApplication(
     request: RecycleApplicationPayload
   ): AxiosPromise<GenericApiResponse<Boolean>> {
-    return Api.put(ApplicationApi.newURLPrefix + `/recycle/${request.applicationId}`);
+    return Api.put(
+      ApplicationApi.newURLPrefix + `/recycle/${request.applicationId}`
+    );
   }
 
   static restoreApplication(
     request: RestoreApplicationPayload
   ): AxiosPromise<GenericApiResponse<Boolean>> {
-    return Api.put(ApplicationApi.newURLPrefix + `/restore/${request.applicationId}`);
+    return Api.put(
+      ApplicationApi.newURLPrefix + `/restore/${request.applicationId}`
+    );
   }
 
   static deleteApplication(
     request: DeleteApplicationPayload
   ): AxiosPromise<GenericApiResponse<Boolean>> {
-    return Api.delete(ApplicationApi.deleteApplicationURL(request.applicationId));
+    return Api.delete(
+      ApplicationApi.deleteApplicationURL(request.applicationId)
+    );
   }
 
   static updateApplication(request: {
@@ -170,11 +213,18 @@ class ApplicationApi extends Api {
     return Api.put(ApplicationApi.updateApplicationURL(applicationId), rest);
   }
 
-  static publishApplication(request: PublishApplicationPayload): AxiosPromise<ApiResponse> {
-    return Api.post(ApplicationApi.publishApplicationURL(request.applicationId));
+  static publishApplication(
+    request: PublishApplicationPayload
+  ): AxiosPromise<ApiResponse> {
+    return Api.post(
+      ApplicationApi.publishApplicationURL(request.applicationId),
+      request?.request
+    );
   }
 
-  static getApplicationDetail(request: FetchAppInfoPayload): AxiosPromise<ApplicationResp> {
+  static getApplicationDetail(
+    request: FetchAppInfoPayload
+  ): AxiosPromise<ApplicationResp> {
     const { type, applicationId } = request;
     const url =
       type === "published"
@@ -185,13 +235,20 @@ class ApplicationApi extends Api {
     return Api.get(url);
   }
 
-  static getApplicationPermissions(applicationId: string): AxiosPromise<AppPermissionResponse> {
+  static getApplicationPermissions(
+    applicationId: string
+  ): AxiosPromise<AppPermissionResponse> {
     return Api.get(ApplicationApi.getApplicationPermissionURL(applicationId));
   }
 
-  static grantAppPermission(request: GrantAppPermissionReq): AxiosPromise<AppPermissionResponse> {
+  static grantAppPermission(
+    request: GrantAppPermissionReq
+  ): AxiosPromise<AppPermissionResponse> {
     const { applicationId, ...requestParam } = request;
-    return Api.put(ApplicationApi.grantAppPermissionURL(applicationId), requestParam);
+    return Api.put(
+      ApplicationApi.grantAppPermissionURL(applicationId),
+      requestParam
+    );
   }
 
   static updateAppPermission(
@@ -208,7 +265,9 @@ class ApplicationApi extends Api {
     request: DeleteAppPermissionPayload
   ): AxiosPromise<AppPermissionResponse> {
     const { applicationId, permissionId } = request;
-    return Api.delete(ApplicationApi.updateAppPermissionURL(applicationId, permissionId));
+    return Api.delete(
+      ApplicationApi.updateAppPermissionURL(applicationId, permissionId)
+    );
   }
 
   static createFromTemplate(templateId: string): AxiosPromise<ApplicationResp> {
@@ -240,7 +299,9 @@ class ApplicationApi extends Api {
     return Api.get(ApplicationApi.getMarketplaceAppURL(appId));
   }
 
-  static setAppEditingState(request: SetAppEditingStatePayload): AxiosPromise<ApplicationResp> {
+  static setAppEditingState(
+    request: SetAppEditingStatePayload
+  ): AxiosPromise<ApplicationResp> {
     const { applicationId, editingFinished } = request;
     return Api.put(ApplicationApi.setAppEditingStateURL(applicationId), {
       editingFinished,
diff --git a/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx b/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx
index 3a22c96a43..e36e3d6ab3 100644
--- a/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx
+++ b/client/packages/lowcoder/src/components/PermissionDialog/AppPermissionDialog.tsx
@@ -12,6 +12,7 @@ import {
   fetchApplicationPermissions,
   updateAppPermission,
   updateAppPermissionInfo,
+  publishApplication,
 } from "../../redux/reduxActions/applicationActions";
 import { PermissionItemsType } from "./PermissionList";
 import { trans } from "../../i18n";
@@ -30,125 +31,263 @@ import { PermissionRole } from "./Permission";
 import { SHARE_TITLE } from "../../constants/apiConstants";
 import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
 import { default as Divider } from "antd/es/divider";
+import { default as Form } from "antd/es/form";
+import { Typography } from "antd";
+import StepModal from "../StepModal";
+import { AddIcon } from "icons";
+import { GreyTextColor } from "constants/style";
+import { VersionDataForm } from "@lowcoder-ee/pages/common/versionDataForm";
 
-export const AppPermissionDialog = React.memo((props: {
-  applicationId: string;
-  visible: boolean;
-  onVisibleChange: (visible: boolean) => void;
-}) => {
-  const { applicationId } = props;
-  const dispatch = useDispatch();
-  const appPermissionInfo = useSelector(getAppPermissionInfo);
+const BottomWrapper = styled.div`
+  margin: 12px 16px 0 16px;
+  display: flex;
+  justify-content: space-between;
+`;
 
-  const { appType } = useContext(ExternalEditorContext);
-  const isModule = appType === AppTypeEnum.Module;
+const AddPermissionButton = styled(TacoButton)`
+  &,
+  &:hover,
+  &:focus {
+    border: none;
+    box-shadow: none;
+    padding: 0;
+    display: flex;
+    align-items: center;
+    font-size: 14px;
+    line-height: 14px;
+    background: #ffffff;
+    transition: unset;
+  }
 
-  useEffect(() => {
-    dispatch(fetchApplicationPermissions({ applicationId: applicationId }));
-  }, [applicationId, dispatch]);
+  svg {
+    margin-right: 4px;
+  }
 
-  let permissions: PermissionItemsType = [];
-  if (appPermissionInfo) {
-    const creator = appPermissionInfo.permissions.find(
-      (p) => p.type === "USER" && p.id === appPermissionInfo.creatorId
-    );
+  &:hover {
+    color: #315efb;
 
-    permissions = [
-      {
-        permissionItem: {
-          permissionId: "orgAdmin",
-          id: "orgAdmin",
-          role: "owner",
-          name: trans("home.orgName", { orgName: appPermissionInfo.orgName }),
-          type: "ORG_ADMIN",
-        },
-      },
-      ...appPermissionInfo.permissions
-        .filter((p) => !(p.type === "USER" && p.id === appPermissionInfo.creatorId))
-        .map((p) => ({
-          permissionItem: p,
-        })),
-    ];
-    if (creator) {
-      permissions = [
-        {
-          isCreator: true,
-          permissionItem: creator,
-        },
-        ...permissions,
-      ];
+    svg g path {
+      fill: #315efb;
     }
   }
+`;
 
-  return (
-    <PermissionDialog
-      {...props}
-      title={SHARE_TITLE}
-      ownerLabel={trans("home.allPermissions")}
-      viewBodyRender={(list) => {
-        if (!appPermissionInfo) {
-          return <StyledLoading size={18} />;
-        }
-        return (
-          <>
-            <AppShareView
-              isModule={isModule}
-              applicationId={applicationId}
-              permissionInfo={appPermissionInfo!}
-            />
-            {list}
-          </>
-        );
-      }}
-      supportRoles={[
-        { label: trans("share.viewer"), value: PermissionRole.Viewer },
-        {
-          label: trans("share.editor"),
-          value: PermissionRole.Editor,
-        },
+export const AppPermissionDialog = React.memo(
+  (props: {
+    applicationId: string;
+    visible: boolean;
+    onVisibleChange: (visible: boolean) => void;
+  }) => {
+    const [form] = Form.useForm();
+    const { appType } = useContext(ExternalEditorContext);
+    const isModule = appType === AppTypeEnum.Module;
+    const { applicationId } = props;
+
+    const dispatch = useDispatch();
+    const appPermissionInfo = useSelector(getAppPermissionInfo);
+    const [activeStepKey, setActiveStepKey] = useState("permission");
+
+    useEffect(() => {
+      dispatch(fetchApplicationPermissions({ applicationId: applicationId }));
+    }, [applicationId, dispatch]);
+
+    let permissions: PermissionItemsType = [];
+    if (appPermissionInfo) {
+      const creator = appPermissionInfo.permissions.find(
+        (p) => p.type === "USER" && p.id === appPermissionInfo.creatorId
+      );
+
+      permissions = [
         {
-          label: trans("share.owner"),
-          value: PermissionRole.Owner,
+          permissionItem: {
+            permissionId: "orgAdmin",
+            id: "orgAdmin",
+            role: "owner",
+            name: trans("home.orgName", { orgName: appPermissionInfo.orgName }),
+            type: "ORG_ADMIN",
+          },
         },
-      ]}
-      permissionItems={permissions}
-      addPermission={(userIds, groupIds, role, onSuccess) =>
-        ApplicationApi.grantAppPermission({
-          applicationId: applicationId,
-          userIds: userIds,
-          groupIds: groupIds,
-          role: role as any,
-        })
-          .then((resp) => {
-            if (validateResponse(resp)) {
-              dispatch(fetchApplicationPermissions({ applicationId: applicationId }));
-              onSuccess();
-            }
-          })
-          .catch((e) => {
-            messageInstance.error(trans("home.addPermissionErrorMessage", { message: e.message }));
-          })
-      }
-      updatePermission={(permissionId, role) =>
-        dispatch(
-          updateAppPermission({
-            applicationId: applicationId,
-            role: role as ApplicationRoleType,
-            permissionId: permissionId,
-          })
-        )
-      }
-      deletePermission={(permissionId) =>
-        dispatch(
-          deleteAppPermission({
-            applicationId: applicationId,
-            permissionId: permissionId,
-          })
-        )
+        ...appPermissionInfo.permissions
+          .filter(
+            (p) => !(p.type === "USER" && p.id === appPermissionInfo.creatorId)
+          )
+          .map((p) => ({
+            permissionItem: p,
+          })),
+      ];
+      if (creator) {
+        permissions = [
+          {
+            isCreator: true,
+            permissionItem: creator,
+          },
+          ...permissions,
+        ];
       }
-    />
-  );
-});
+    }
+
+    return (
+      <StepModal
+        open={props.visible}
+        destroyOnClose
+        onCancel={() => {
+          setActiveStepKey("permission");
+          props.onVisibleChange(false);
+        }}
+        showOkButton={true}
+        showBackLink={true}
+        showCancelButton={true}
+        width="440px"
+        onStepChange={setActiveStepKey}
+        activeStepKey={activeStepKey}
+        steps={[
+          {
+            key: "permission",
+            titleRender: () => null,
+            bodyRender: (modalProps) => (
+              <PermissionDialog
+                {...props}
+                title={trans("home.managePermissions")}
+                ownerLabel={trans("home.allPermissions")}
+                viewBodyRender={(list) => {
+                  if (!appPermissionInfo) {
+                    return <StyledLoading size={18} />;
+                  }
+                  return <>{list}</>;
+                }}
+                supportRoles={[
+                  {
+                    label: trans("share.viewer"),
+                    value: PermissionRole.Viewer,
+                  },
+                  {
+                    label: trans("share.editor"),
+                    value: PermissionRole.Editor,
+                  },
+                  {
+                    label: trans("share.owner"),
+                    value: PermissionRole.Owner,
+                  },
+                ]}
+                permissionItems={permissions}
+                addPermission={(userIds, groupIds, role, onSuccess) =>
+                  ApplicationApi.grantAppPermission({
+                    applicationId: applicationId,
+                    userIds: userIds,
+                    groupIds: groupIds,
+                    role: role as any,
+                  })
+                    .then((resp) => {
+                      if (validateResponse(resp)) {
+                        dispatch(
+                          fetchApplicationPermissions({
+                            applicationId: applicationId,
+                          })
+                        );
+                        onSuccess();
+                      }
+                    })
+                    .catch((e) => {
+                      messageInstance.error(
+                        trans("home.addPermissionErrorMessage", {
+                          message: e.message,
+                        })
+                      );
+                    })
+                }
+                updatePermission={(permissionId, role) =>
+                  dispatch(
+                    updateAppPermission({
+                      applicationId: applicationId,
+                      role: role as ApplicationRoleType,
+                      permissionId: permissionId,
+                    })
+                  )
+                }
+                deletePermission={(permissionId) =>
+                  dispatch(
+                    deleteAppPermission({
+                      applicationId: applicationId,
+                      permissionId: permissionId,
+                    })
+                  )
+                }
+                viewFooterRender={(primaryModelProps, props) => (
+                  <BottomWrapper>
+                    <AddPermissionButton
+                      style={{ height: 28 }}
+                      icon={<AddIcon style={{ color: GreyTextColor }} />}
+                      onClick={() => {
+                        props.next();
+                      }}
+                    >
+                      {trans("home.addMember")}
+                    </AddPermissionButton>
+
+                    <TacoButton
+                      buttonType="primary"
+                      style={{ height: 28 }}
+                      onClick={() => {
+                        primaryModelProps.next();
+                      }}
+                    >
+                      {trans("event.next") + " "}
+                    </TacoButton>
+                  </BottomWrapper>
+                )}
+                primaryModelProps={modalProps}
+              />
+            ),
+            footerRender: () => null,
+          },
+          {
+            key: "versions",
+            titleRender: () => trans("home.versions"),
+            bodyRender: () => (
+              <AppShareView
+                isModule={isModule}
+                applicationId={applicationId}
+                permissionInfo={appPermissionInfo!}
+                form={form}
+              />
+            ),
+            footerRender: (modalProps) => (
+              <BottomWrapper>
+                <TacoButton
+                  buttonType="normal"
+                  style={{ height: 28 }}
+                  onClick={() => {
+                    modalProps.back();
+                  }}
+                >
+                  {trans("back")}
+                </TacoButton>
+                <TacoButton
+                  buttonType="primary"
+                  style={{ height: 28 }}
+                  onClick={() => {
+                    form.validateFields().then(() => {
+                      dispatch(
+                        publishApplication({
+                          applicationId: applicationId,
+                          request: form.getFieldsValue(),
+                        })
+                      );
+                      modalProps.back();
+                      props.onVisibleChange(false);
+                    });
+                  }}
+                >
+                  {trans("queryLibrary.publish")}
+                </TacoButton>
+              </BottomWrapper>
+            ),
+          },
+        ]}
+      />
+    );
+  }
+);
 
 const InviteInputBtn = styled.div`
   display: flex;
@@ -162,10 +301,13 @@ const InviteInputBtn = styled.div`
 `;
 
 const AppInviteView = (props: { appId: string }) => {
-  const inviteLink = window.location.origin + APPLICATION_VIEW_URL(props.appId, "view");
+  const inviteLink =
+    window.location.origin + APPLICATION_VIEW_URL(props.appId, "view");
   return (
     <>
-      <CommonTextLabel style={{ marginTop: "16px" }}>{trans("home.shareLink")}</CommonTextLabel>
+      <CommonTextLabel style={{ marginTop: "16px" }}>
+        {trans("home.shareLink")}
+      </CommonTextLabel>
       <InviteInputBtn>
         <TacoInput disabled value={inviteLink} />
         <TacoButton
@@ -195,10 +337,13 @@ function AppShareView(props: {
   applicationId: string;
   permissionInfo: AppPermissionInfo;
   isModule: boolean;
+  form: any;
 }) {
-  const { applicationId, permissionInfo, isModule } = props;
+  const { applicationId, permissionInfo, isModule, form } = props;
   const [isPublic, setPublic] = useState(permissionInfo.publicToAll);
-  const [isPublicToMarketplace, setPublicToMarketplace] = useState(permissionInfo.publicToMarketplace);
+  const [isPublicToMarketplace, setPublicToMarketplace] = useState(
+    permissionInfo.publicToMarketplace
+  );
   const dispatch = useDispatch();
   useEffect(() => {
     setPublic(permissionInfo.publicToAll);
@@ -206,9 +351,20 @@ function AppShareView(props: {
   useEffect(() => {
     setPublicToMarketplace(permissionInfo.publicToMarketplace);
   }, [permissionInfo.publicToMarketplace]);
+
   return (
     <div style={{ marginBottom: "22px" }}>
-      
+      <PermissionSwitchWrapper>
+        <TacoSwitch
+          checked={true}
+          disabled={true}
+          label={
+            isModule
+              ? trans("home.appMemberMessage")
+              : trans("home.moduleMemberMessage")
+          }
+        />
+      </PermissionSwitchWrapper>
       <PermissionSwitchWrapper>
         <TacoSwitch
           checked={isPublic}
@@ -223,10 +379,14 @@ function AppShareView(props: {
                 messageInstance.error(e.message);
               });
           }}
-          label={isModule ? trans("home.modulePublicMessage") : trans("home.appPublicMessage")}
+          label={
+            isModule
+              ? trans("home.modulePublicMessage")
+              : trans("home.appPublicMessage")
+          }
         />
       </PermissionSwitchWrapper>
-      {isPublic &&
+      {isPublic && (
         <PermissionSwitchWrapper>
           <TacoSwitch
             checked={isPublicToMarketplace}
@@ -235,19 +395,41 @@ function AppShareView(props: {
               ApplicationApi.publicToMarketplace(applicationId, checked)
                 .then((resp) => {
                   validateResponse(resp);
-                  dispatch(updateAppPermissionInfo({ publicToMarketplace: checked }));
+                  dispatch(
+                    updateAppPermissionInfo({ publicToMarketplace: checked })
+                  );
                 })
                 .catch((e) => {
                   messageInstance.error(e.message);
                 });
-            } }
-            label={isModule ? trans("home.moduleMarketplaceMessage") : trans("home.appMarketplaceMessage")} />
-        </PermissionSwitchWrapper> }
-        { isPublicToMarketplace && <><div style={{ margin: "10px 22px 22px 22px" }}>
-          {trans("home.marketplaceGoodPublishing")}
-        </div><Divider/></>}
+            }}
+            label={
+              isModule
+                ? trans("home.moduleMarketplaceMessage")
+                : trans("home.appMarketplaceMessage")
+            }
+          />
+        </PermissionSwitchWrapper>
+      )}
+      {isPublicToMarketplace && isPublic && (
+        <div style={{ marginTop: "16px" }}>
+          <Typography.Text type="secondary">
+            {trans("home.marketplaceGoodPublishing")}
+          </Typography.Text>
+          <Divider />
+        </div>
+      )}
 
       {isPublic && <AppInviteView appId={applicationId} />}
+      <Divider />
+
+      <VersionDataForm form={form} preserve={false} />
+
+      <div>
+        <Typography.Text type="secondary">
+          {trans("home.publishVersionDescription")}
+        </Typography.Text>
+      </div>
     </div>
   );
 }
diff --git a/client/packages/lowcoder/src/components/PermissionDialog/PermissionDialog.tsx b/client/packages/lowcoder/src/components/PermissionDialog/PermissionDialog.tsx
index 0834cf2d9e..274fb496ca 100644
--- a/client/packages/lowcoder/src/components/PermissionDialog/PermissionDialog.tsx
+++ b/client/packages/lowcoder/src/components/PermissionDialog/PermissionDialog.tsx
@@ -2,44 +2,7 @@ import React, { ReactNode, useState } from "react";
 import { PermissionItemsType, PermissionList } from "./PermissionList";
 import StepModal from "../StepModal";
 import { trans } from "../../i18n";
-import { TacoButton } from "components/button";
-import { AddIcon } from "icons";
-import { GreyTextColor } from "constants/style";
 import { Permission, PermissionRole } from "./Permission";
-import styled from "styled-components";
-
-const BottomWrapper = styled.div`
-  margin: 12px 16px 0 16px;
-  display: flex;
-`;
-
-const AddPermissionButton = styled(TacoButton)`
-  &,
-  &:hover,
-  &:focus {
-    border: none;
-    box-shadow: none;
-    padding: 0;
-    display: flex;
-    align-items: center;
-    font-size: 14px;
-    line-height: 14px;
-    background: #ffffff;
-    transition: unset;
-  }
-
-  svg {
-    margin-right: 4px;
-  }
-
-  &:hover {
-    color: #315efb;
-
-    svg g path {
-      fill: #315efb;
-    }
-  }
-`;
 
 export const PermissionDialog = (props: {
   title: string;
@@ -47,6 +10,7 @@ export const PermissionDialog = (props: {
   visible: boolean;
   onVisibleChange: (visible: boolean) => void;
   viewBodyRender?: (list: ReactNode) => ReactNode;
+  viewFooterRender?: (primaryModelProps: any, props: any) => ReactNode;
   permissionItems: PermissionItemsType;
   supportRoles: { label: string; value: PermissionRole }[];
   addPermission: (
@@ -57,9 +21,18 @@ export const PermissionDialog = (props: {
   ) => void;
   updatePermission: (permissionId: string, role: string) => void;
   deletePermission: (permissionId: string) => void;
+  primaryModelProps?: {};
 }) => {
-  const { supportRoles, permissionItems, visible, onVisibleChange, addPermission, viewBodyRender } =
-    props;
+  const {
+    supportRoles,
+    permissionItems,
+    visible,
+    onVisibleChange,
+    addPermission,
+    viewBodyRender,
+    viewFooterRender,
+    primaryModelProps,
+  } = props;
   const [activeStepKey, setActiveStepKey] = useState("view");
 
   return (
@@ -85,26 +58,10 @@ export const PermissionDialog = (props: {
             ) : (
               <PermissionList {...props} />
             ),
-          footerRender: (props) => (
-            <BottomWrapper>
-              <AddPermissionButton
-                style={{ height: 28 }}
-                icon={<AddIcon style={{ color: GreyTextColor }} />}
-                onClick={() => {
-                  props.next();
-                }}
-              >
-                {trans("home.addMember")}
-              </AddPermissionButton>
-              <TacoButton
-                buttonType="primary"
-                onClick={() => onVisibleChange(false)}
-                style={{ marginLeft: "auto", width: "76px", height: "28px" }}
-              >
-                {trans("finish") + " "}
-              </TacoButton>
-            </BottomWrapper>
-          ),
+          footerRender: (props) =>
+            viewFooterRender
+              ? viewFooterRender(primaryModelProps, props)
+              : null,
         },
         {
           key: "add",
@@ -119,7 +76,7 @@ export const PermissionDialog = (props: {
               }
             />
           ),
-          footerRender: (props) => null,
+          footerRender: () => null,
         },
       ]}
     />
diff --git a/client/packages/lowcoder/src/components/PermissionDialog/PermissionList.tsx b/client/packages/lowcoder/src/components/PermissionDialog/PermissionList.tsx
index 4fd3abb9ef..0f821cd8b5 100644
--- a/client/packages/lowcoder/src/components/PermissionDialog/PermissionList.tsx
+++ b/client/packages/lowcoder/src/components/PermissionDialog/PermissionList.tsx
@@ -1,5 +1,8 @@
 import { ASSETS_URI } from "constants/apiConstants";
-import { ApplicationPermissionType, ApplicationRoleType } from "constants/applicationConstants";
+import {
+  ApplicationPermissionType,
+  ApplicationRoleType,
+} from "constants/applicationConstants";
 import {
   CommonErrorLabel,
   CommonGrayLabel,
@@ -101,15 +104,22 @@ function PermissionLiItem(props: {
         side={32}
         userName={permissionItem.name}
         source={permissionItem.avatar && ASSETS_URI(permissionItem.avatar)}
-        svg={SvgIcon && <SvgIcon $color={getInitialsAndColorCode(permissionItem.name)[1]} />}
+        svg={
+          SvgIcon && (
+            <SvgIcon $color={getInitialsAndColorCode(permissionItem.name)[1]} />
+          )
+        }
       />
       <PermissionItemName title={permissionItem.name}>
-        {permissionItem.type === "GROUP" && trans("home.groupWithSquareBrackets")}
+        {permissionItem.type === "GROUP" &&
+          trans("home.groupWithSquareBrackets")}
         {permissionItem.name}
       </PermissionItemName>
       {isCreator && <CreatorTag>{trans("home.creator")}</CreatorTag>}
       {isCreator || permissionItem.type === "ORG_ADMIN" ? (
-        <CommonGrayLabel style={{ color: "color: #B8B9BF", margin: "0 24px 0 auto" }}>
+        <CommonGrayLabel
+          style={{ color: "color: #B8B9BF", margin: "0 24px 0 auto" }}
+        >
           {props.ownerLabel}
         </CommonGrayLabel>
       ) : (
@@ -145,7 +155,9 @@ function PermissionLiItem(props: {
             value="delete"
             permissionid={permissionItem.permissionId}
           >
-            <CommonErrorLabel $fontSize={13}>{trans("remove")}</CommonErrorLabel>
+            <CommonErrorLabel $fontSize={13}>
+              {trans("remove")}
+            </CommonErrorLabel>
           </CustomSelect.Option>
         </StyledRoleSelect>
       )}
@@ -153,7 +165,10 @@ function PermissionLiItem(props: {
   );
 }
 
-export type PermissionItemsType = { permissionItem: PermissionItem; isCreator?: boolean }[];
+export type PermissionItemsType = {
+  permissionItem: PermissionItem;
+  isCreator?: boolean;
+}[];
 export const PermissionList = (props: {
   ownerLabel: string;
   supportRoles: { label: string; value: PermissionRole }[];
@@ -163,7 +178,7 @@ export const PermissionList = (props: {
 }) => (
   <>
     <CommonTextLabel style={{ marginBottom: "4px" }}>
-      {trans("home.memberPermissionList")}
+      {`${trans("memberSettings.title")}:`}
     </CommonTextLabel>
     <UserPermissionUl $height={201}>
       {props.permissionItems.map((item, index) => (
diff --git a/client/packages/lowcoder/src/components/StepModal.tsx b/client/packages/lowcoder/src/components/StepModal.tsx
index 13d08319b3..2983b26628 100644
--- a/client/packages/lowcoder/src/components/StepModal.tsx
+++ b/client/packages/lowcoder/src/components/StepModal.tsx
@@ -25,7 +25,9 @@ export interface StepModalProps extends CustomModalProps {
 export default function StepModal(props: StepModalProps) {
   const { steps, activeStepKey, onStepChange, ...modalProps } = props;
   const [current, setCurrent] = useState(steps[0]?.key);
-  const currentStepIndex = steps.findIndex((i) => i.key === activeStepKey ?? current);
+  const currentStepIndex = steps.findIndex(
+    (i) => i.key === activeStepKey ?? current
+  );
   const currentStep = currentStepIndex >= 0 ? steps[currentStepIndex] : null;
 
   const handleChangeStep = (key: string) => {
diff --git a/client/packages/lowcoder/src/pages/common/header.tsx b/client/packages/lowcoder/src/pages/common/header.tsx
index 0b32ef3966..36cb352eeb 100644
--- a/client/packages/lowcoder/src/pages/common/header.tsx
+++ b/client/packages/lowcoder/src/pages/common/header.tsx
@@ -1,8 +1,8 @@
 import { default as Dropdown } from "antd/es/dropdown";
 import { default as Skeleton } from "antd/es/skeleton";
 import { default as Radio, RadioChangeEvent } from "antd/es/radio";
-import { default as Statistic} from "antd/es/statistic";
-import { default as Flex} from "antd/es/flex";
+import { default as Statistic } from "antd/es/statistic";
+import { default as Flex } from "antd/es/flex";
 import { default as Popover } from "antd/es/popover";
 import { default as Typography } from "antd/es/typography";
 import LayoutHeader from "components/layout/Header";
@@ -40,7 +40,10 @@ import {
   recoverSnapshotAction,
   setShowAppSnapshot,
 } from "redux/reduxActions/appSnapshotActions";
-import { currentApplication, isPublicApplication } from "redux/selectors/applicationSelector";
+import {
+  currentApplication,
+  isPublicApplication,
+} from "redux/selectors/applicationSelector";
 import {
   getSelectedAppSnapshot,
   showAppSnapshotSelector,
@@ -59,8 +62,8 @@ import { getBrandingConfig } from "../../redux/selectors/configSelectors";
 import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
 import { EditorContext } from "../../comps/editorState";
 import Tooltip from "antd/es/tooltip";
-import { LockOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
-import Avatar from 'antd/es/avatar';
+import { LockOutlined, ExclamationCircleOutlined } from "@ant-design/icons";
+import Avatar from "antd/es/avatar";
 import UserApi from "@lowcoder-ee/api/userApi";
 import { validateResponse } from "@lowcoder-ee/api/apiUtils";
 import ProfileImage from "./profileImage";
@@ -194,7 +197,7 @@ const GrayBtn = styled(TacoButton)`
       color: #ffffff;
       border: none;
     }
-    
+
     &[disabled] {
       cursor: not-allowed;
     }
@@ -314,10 +317,8 @@ const StyledRefreshIcon = styled(RefreshIcon)`
 // Add the lock icon logic for disabled options
 const DropdownMenuStyled = styled(DropdownMenu)`
   .ant-dropdown-menu-item:hover {
-    background: ${(props) =>
-      props.disabled ? 'inherit' : '#edf4fa'};
-    cursor: ${(props) =>
-      props.disabled ? 'not-allowed' : 'pointer'};
+    background: ${(props) => (props.disabled ? "inherit" : "#edf4fa")};
+    cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
   }
 `;
 
@@ -343,7 +344,7 @@ function HeaderProfile(props: { user: User }) {
   );
 }
 
-const setCountdown = () => dayjs().add(3, 'minutes').toISOString();
+const setCountdown = () => dayjs().add(3, "minutes").toISOString();
 
 export type PanelStatus = { left: boolean; bottom: boolean; right: boolean };
 export type TogglePanel = (panel?: keyof PanelStatus) => void;
@@ -373,27 +374,28 @@ export default function Header(props: HeaderProps) {
   const applicationId = useApplicationId();
   const dispatch = useDispatch();
   const showAppSnapshot = useSelector(showAppSnapshotSelector);
-  const {selectedSnapshot, isArchivedSnapshot} = useSelector(getSelectedAppSnapshot);
+  const { selectedSnapshot, isArchivedSnapshot } = useSelector(
+    getSelectedAppSnapshot
+  );
   const { appType } = useContext(ExternalEditorContext);
   const [editName, setEditName] = useState(false);
   const [editing, setEditing] = useState(false);
   const [permissionDialogVisible, setPermissionDialogVisible] = useState(false);
   const [editingUser, setEditingUser] = useState<CurrentUser>();
-  const [enableCheckEditingStatus, setEnableCheckEditingStatus] = useState<boolean>(false);
+  const [enableCheckEditingStatus, setEnableCheckEditingStatus] =
+    useState<boolean>(false);
   const editingCountdown = useRef(setCountdown());
 
   const isModule = appType === AppTypeEnum.Module;
 
   useEffect(() => {
-    if(blockEditing && application && Boolean(application?.editingUserId)) {
-      UserApi.getUserDetail(application.editingUserId!)
-        .then(resp => {
-          if (validateResponse(resp)) {
-
-            console.log('editing user', resp.data.data);
-            setEditingUser(resp.data.data);
-          }
-        });
+    if (blockEditing && application && Boolean(application?.editingUserId)) {
+      UserApi.getUserDetail(application.editingUserId!).then((resp) => {
+        if (validateResponse(resp)) {
+          console.log("editing user", resp.data.data);
+          setEditingUser(resp.data.data);
+        }
+      });
     }
   }, [blockEditing]);
 
@@ -424,7 +426,6 @@ export default function Header(props: HeaderProps) {
     editorState.setEditorModeStatus(value);
   };
 
-
   const headerStart = (
     <>
       <StyledLink onClick={() => history.push(ALL_APPLICATIONS_URL)}>
@@ -514,7 +515,7 @@ export default function Header(props: HeaderProps) {
                       application.applicationId,
                       selectedSnapshot.snapshotId,
                       selectedSnapshot.createTime,
-                      isArchivedSnapshot,
+                      isArchivedSnapshot
                     )
                   );
                 },
@@ -539,52 +540,65 @@ export default function Header(props: HeaderProps) {
         {/* Display a hint about who is editing the app */}
         {blockEditing && Boolean(applicationId) && (
           <>
-          <Popover
-            style={{ width: 200 }}
-            content={() => {
-              return (
-                <Flex vertical gap={10} align="center" style={{maxWidth : "250px"}}>
-                  <Text style={{textAlign : "center"}}> 
-                    {trans("header.AppEditingBlockedHint")}
-                  </Text>
-                  <StyledCountdown
-                    title={trans("header.AppEditingBlocked")}
-                    value={editingCountdown.current}
-                    onFinish={() => {
-                      setEnableCheckEditingStatus(true)
-                    }}
-                  />
-                  <Tooltip
-                    title={trans("header.AppEditingBlockedMessage")}
-                    placement="bottom"
+            <Popover
+              style={{ width: 200 }}
+              content={() => {
+                return (
+                  <Flex
+                    vertical
+                    gap={10}
+                    align="center"
+                    style={{ maxWidth: "250px" }}
                   >
-                    <TacoButton
-                      style={{width: '100%'}}
-                      buttonType="primary"
-                      disabled={blockEditing && !enableCheckEditingStatus}
-                      onClick={() => {
-                        fetchApplication?.();
-                        setEnableCheckEditingStatus(false);
-                        editingCountdown.current = setCountdown();
+                    <Text style={{ textAlign: "center" }}>
+                      {trans("header.AppEditingBlockedHint")}
+                    </Text>
+                    <StyledCountdown
+                      title={trans("header.AppEditingBlocked")}
+                      value={editingCountdown.current}
+                      onFinish={() => {
+                        setEnableCheckEditingStatus(true);
                       }}
+                    />
+                    <Tooltip
+                      title={trans("header.AppEditingBlockedMessage")}
+                      placement="bottom"
                     >
-                      <StyledRefreshIcon />
-                      <span>{trans("header.AppEditingBlockedCheckStatus")}</span>
-                    </TacoButton>
-                  </Tooltip>
-                </Flex>
-              )
-            }}
-            trigger="hover"
-          >
-            <EditingNoticeWrapper>
-              <ProfileImage source={user.avatarUrl} userName={user.username} side={24} />
-              <EditingHintText>
-                {`${editingUser?.email || trans("header.AppEditingBlockedSomeone")}` + " " + trans("header.AppEditingBlockedMessageSnipped")}
-              </EditingHintText>
-              <WarningIcon />
-            </EditingNoticeWrapper>
-          </Popover>
+                      <TacoButton
+                        style={{ width: "100%" }}
+                        buttonType="primary"
+                        disabled={blockEditing && !enableCheckEditingStatus}
+                        onClick={() => {
+                          fetchApplication?.();
+                          setEnableCheckEditingStatus(false);
+                          editingCountdown.current = setCountdown();
+                        }}
+                      >
+                        <StyledRefreshIcon />
+                        <span>
+                          {trans("header.AppEditingBlockedCheckStatus")}
+                        </span>
+                      </TacoButton>
+                    </Tooltip>
+                  </Flex>
+                );
+              }}
+              trigger="hover"
+            >
+              <EditingNoticeWrapper>
+                <ProfileImage
+                  source={user.avatarUrl}
+                  userName={user.username}
+                  side={24}
+                />
+                <EditingHintText>
+                  {`${editingUser?.email || trans("header.AppEditingBlockedSomeone")}` +
+                    " " +
+                    trans("header.AppEditingBlockedMessageSnipped")}
+                </EditingHintText>
+                <WarningIcon />
+              </EditingNoticeWrapper>
+            </Popover>
           </>
         )}
 
@@ -598,15 +612,18 @@ export default function Header(props: HeaderProps) {
           />
         )}
         {canManageApp(user, application) && (
-          <GrayBtn onClick={() => setPermissionDialogVisible(true)} disabled={blockEditing}>
-            {SHARE_TITLE}
+          <GrayBtn
+            onClick={() => setPermissionDialogVisible(true)}
+            disabled={blockEditing}
+          >
+            {trans("header.deploy")}
           </GrayBtn>
         )}
-  
+
         <PreviewBtn buttonType="primary" onClick={() => preview(applicationId)}>
           {trans("header.preview")}
         </PreviewBtn>
-  
+
         <Dropdown
           className="cypress-header-dropdown"
           placement="bottomRight"
@@ -616,31 +633,21 @@ export default function Header(props: HeaderProps) {
               style={{ minWidth: "110px", borderRadius: "4px" }}
               onClick={(e) => {
                 if (blockEditing) return; // Prevent clicks if the app is being edited by someone else
-                if (e.key === "deploy") {
-                  dispatch(publishApplication({ applicationId }));
-                } else if (e.key === "snapshot") {
+                if (e.key === "snapshot") {
                   dispatch(setShowAppSnapshot(true));
                 }
               }}
               items={[
-                {
-                  key: "deploy",
-                  label: (
-                    <div style={{ display: 'flex', alignItems: 'center' }}>
-                      {blockEditing && <LockOutlined style={{ marginRight: '8px' }} />}
-                      <CommonTextLabel style= {{color: blockEditing ? "#ccc" : "#222"}}>
-                        {trans("header.deploy")}
-                      </CommonTextLabel>
-                    </div>
-                  ),
-                  disabled: blockEditing,
-                },
                 {
                   key: "snapshot",
                   label: (
-                    <div style={{ display: 'flex', alignItems: 'center' }}>
-                      {blockEditing && <LockOutlined style={{ marginRight: '8px' }} />}
-                      <CommonTextLabel style= {{color: blockEditing ? "#ccc" : "#222"}}>
+                    <div style={{ display: "flex", alignItems: "center" }}>
+                      {blockEditing && (
+                        <LockOutlined style={{ marginRight: "8px" }} />
+                      )}
+                      <CommonTextLabel
+                        style={{ color: blockEditing ? "#ccc" : "#222" }}
+                      >
                         {trans("header.snapshot")}
                       </CommonTextLabel>
                     </div>
@@ -655,7 +662,7 @@ export default function Header(props: HeaderProps) {
             <PackUpIcon />
           </PackUpBtn>
         </Dropdown>
-  
+
         <HeaderProfile user={user} />
       </>
     );
diff --git a/client/packages/lowcoder/src/pages/common/versionDataForm.tsx b/client/packages/lowcoder/src/pages/common/versionDataForm.tsx
new file mode 100644
index 0000000000..c513ae8bd3
--- /dev/null
+++ b/client/packages/lowcoder/src/pages/common/versionDataForm.tsx
@@ -0,0 +1,35 @@
+import React from "react";
+import {
+  DatasourceForm,
+  FormInputItem,
+  FormRadioItem,
+  FormSection,
+} from "lowcoder-design";
+import { getVersionOptions } from "@lowcoder-ee/util/versionOptions";
+import { trans } from "../../i18n";
+
+export const VersionDataForm = (props: { form: any; preserve: boolean, latestVersion?: string }) => {
+  const { form, preserve, latestVersion } = props;
+  const versionOptions = getVersionOptions(latestVersion);
+
+  return (
+    <DatasourceForm
+      form={form}
+      preserve={preserve}
+      style={{ gap: "12px", marginBottom: "12px" }}
+    >
+      <FormSection>
+        <FormRadioItem
+          name={"tag"}
+          label={trans("queryLibrary.version")}
+          options={versionOptions}
+          initialValue={versionOptions[1].value}
+        />
+        <FormInputItem
+          name={"commitMessage"}
+          label={trans("queryLibrary.desc")}
+        />
+      </FormSection>
+    </DatasourceForm>
+  );
+};
diff --git a/client/packages/lowcoder/src/pages/queryLibrary/QueryLibraryEditor.tsx b/client/packages/lowcoder/src/pages/queryLibrary/QueryLibraryEditor.tsx
index 48e8f2e29b..55c267be4b 100644
--- a/client/packages/lowcoder/src/pages/queryLibrary/QueryLibraryEditor.tsx
+++ b/client/packages/lowcoder/src/pages/queryLibrary/QueryLibraryEditor.tsx
@@ -1,5 +1,8 @@
 import React, { useEffect, useMemo, useState } from "react";
-import { fetchDatasource, fetchDataSourceTypes } from "../../redux/reduxActions/datasourceActions";
+import {
+  fetchDatasource,
+  fetchDataSourceTypes,
+} from "../../redux/reduxActions/datasourceActions";
 import { useDispatch, useSelector } from "react-redux";
 import { getUser } from "../../redux/selectors/usersSelectors";
 import {
@@ -22,7 +25,7 @@ import { useCompInstance } from "../../comps/utils/useCompInstance";
 import { QueryLibraryComp } from "../../comps/comps/queryLibrary/queryLibraryComp";
 import { useSearchParam, useThrottle } from "react-use";
 import { Comp } from "lowcoder-core";
-import {LibraryQuery} from "../../api/queryLibraryApi";
+import { LibraryQuery } from "../../api/queryLibraryApi";
 import { NameGenerator } from "../../comps/utils";
 import { QueryLibraryHistoryView } from "./QueryLibraryHistoryView";
 import { default as Form } from "antd/es/form";
@@ -46,8 +49,10 @@ import { importQueryLibrary } from "./importQueryLibrary";
 import { registryDataSourcePlugin } from "constants/queryConstants";
 import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
 import { Helmet } from "react-helmet";
-import {fetchQLPaginationByOrg} from "@lowcoder-ee/util/pagination/axios";
+import { fetchQLPaginationByOrg } from "@lowcoder-ee/util/pagination/axios";
 import { isEmpty } from "lodash";
+import { getVersionOptions } from "@lowcoder-ee/util/versionOptions";
+import { VersionDataForm } from "../common/versionDataForm";
 
 const Wrapper = styled.div`
   display: flex;
@@ -68,7 +73,7 @@ interface ElementsState {
 
 function transformData(input: LibraryQuery[]) {
   const output: any = {};
-  input.forEach(item => {
+  input.forEach((item) => {
     output[item.id] = item;
   });
   return output;
@@ -84,11 +89,16 @@ export const QueryLibraryEditor = () => {
   const forwardQueryId = useSearchParam("forwardQueryId");
 
   const [isCreatePanelShow, showCreatePanel] = useState(false);
-  const [selectedQuery, setSelectedQuery] = useState<string>(forwardQueryId ?? "");
+  const [selectedQuery, setSelectedQuery] = useState<string>(
+    forwardQueryId ?? ""
+  );
   const [publishModalVisible, setPublishModalVisible] = useState(false);
   const [showHistory, setShowHistory] = useState(false);
   const [isDataSourceReady, setIsDataSourceReady] = useState(false);
-  const [elements, setElements] = useState<ElementsState>({ elements: [], total: 0 });
+  const [elements, setElements] = useState<ElementsState>({
+    elements: [],
+    total: 0,
+  });
   const [queryLibrary, setQueryLibrary] = useState<any>({});
   const [currentPage, setCurrentPage] = useState(1);
   const [pageSize, setPageSize] = useState(10);
@@ -119,29 +129,28 @@ export const QueryLibraryEditor = () => {
   useSaveQueryLibrary(libraryQuery, comp);
 
   useEffect(() => {
-        try {
-            fetchQLPaginationByOrg(
-                {
-                    name: searchValues,
-                    pageNum: currentPage,
-                    pageSize: pageSize,
-                }
-            ).then(result => {
-                if (result.success){
-                    setElements({elements: result.data || [], total: result.total || 1})
-                    setQueryLibrary(transformData(result.data || []));
-                }
-            });
-        } catch (error) {
-            console.error(error)
+    try {
+      fetchQLPaginationByOrg({
+        name: searchValues,
+        pageNum: currentPage,
+        pageSize: pageSize,
+      }).then((result) => {
+        if (result.success) {
+          setElements({
+            elements: result.data || [],
+            total: result.total || 1,
+          });
+          setQueryLibrary(transformData(result.data || []));
         }
-  }, [currentPage, pageSize, searchValues, modify])
+      });
+    } catch (error) {
+      console.error(error);
+    }
+  }, [currentPage, pageSize, searchValues, modify]);
 
-    useEffect( () => {
-            if (searchValues !== "")
-                setCurrentPage(1);
-        }, [searchValues]
-    );
+  useEffect(() => {
+    if (searchValues !== "") setCurrentPage(1);
+  }, [searchValues]);
 
   useEffect(() => {
     if (orgId) {
@@ -169,7 +178,7 @@ export const QueryLibraryEditor = () => {
   useEffect(() => {
     if (!forwardQueryId && !queryLibrary[selectedQuery]) {
       // @ts-ignore
-        setSelectedQuery(Object.values(queryLibrary)?.[0]?.id);
+      setSelectedQuery(Object.values(queryLibrary)?.[0]?.id);
     }
   }, [dispatch, Object.keys(queryLibrary).length]);
 
@@ -189,13 +198,13 @@ export const QueryLibraryEditor = () => {
     })
     .map((info) => info.datasource);
 
-    const recentlyUsed = Object.values(queryLibrary)
+  const recentlyUsed = Object.values(queryLibrary)
     .map((i: any) => i.libraryQueryDSL?.query.datasourceId)
     .map((id) => datasource.find((d) => d.id === id))
     .filter((i) => !!i) as Datasource[];
 
   const nameGenerator = new NameGenerator();
-    nameGenerator.init(Object.values(queryLibrary).map((t: any) => t.name));
+  nameGenerator.init(Object.values(queryLibrary).map((t: any) => t.name));
   const newName = nameGenerator.genItemName(trans("queryLibrary.unnamed"));
 
   const handleAdd = (type: BottomResTypeEnum, extraInfo?: any) => {
@@ -218,7 +227,6 @@ export const QueryLibraryEditor = () => {
             setModify(!modify);
           }, 200);
           setCurrentPage(Math.ceil(elements.total / pageSize));
-
         },
         () => {}
       )
@@ -229,7 +237,7 @@ export const QueryLibraryEditor = () => {
   return (
     <>
       <Helmet>{<title>{trans("home.queryLibrary")}</title>}</Helmet>
-        <Wrapper>
+      <Wrapper>
         <LeftNav
           selectedQuery={isCreatePanelShow ? undefined : selectedQuery}
           queryList={Object.values(queryLibrary)}
@@ -237,7 +245,7 @@ export const QueryLibraryEditor = () => {
           onSelect={(id) => {
             setSelectedQuery(id);
             showCreatePanel(false);
-          } }
+          }}
           setCurrentPage={setCurrentPage}
           setPageSize={setPageSize}
           currentPage={currentPage}
@@ -255,13 +263,14 @@ export const QueryLibraryEditor = () => {
             <QueryLibraryHistoryView
               libraryQueryId={selectedQuery}
               compContainer={container}
-              onClose={() => setShowHistory(false)} />
+              onClose={() => setShowHistory(false)}
+            />
           ) : (
             comp.propertyView({
               onPublish: () => setPublishModalVisible(true),
               onHistoryShow: () => setShowHistory(true),
               setModify: setModify,
-              modify: modify
+              modify: modify,
             })
           )}
 
@@ -272,26 +281,30 @@ export const QueryLibraryEditor = () => {
               onSelect={handleAdd}
               onClose={() => showCreatePanel(false)}
               placement={"queryLibrary"}
-              onImport={(options) => importQueryLibrary({
-                dispatch: dispatch,
-                options: options,
-                orgId: orgId,
-                onSuccess: (resp) => {
-                  setSelectedQuery(resp.data.data.id);
-                  showCreatePanel(false);
-                  setTimeout(() => {
-                    setModify(!modify);
-                  }, 200);
-                  setCurrentPage(Math.ceil(elements.total / pageSize));
-                },
-              })} />
+              onImport={(options) =>
+                importQueryLibrary({
+                  dispatch: dispatch,
+                  options: options,
+                  orgId: orgId,
+                  onSuccess: (resp) => {
+                    setSelectedQuery(resp.data.data.id);
+                    showCreatePanel(false);
+                    setTimeout(() => {
+                      setModify(!modify);
+                    }, 200);
+                    setCurrentPage(Math.ceil(elements.total / pageSize));
+                  },
+                })
+              }
+            />
           )}
         </RightContent>
         <PublishModal
           libraryQueryId={comp?.children.query.children.id.getView() || ""}
           visible={publishModalVisible}
           onClose={() => setPublishModalVisible(false)}
-          latestVersion={Object.values(selectedRecords)?.[0]?.tag} />
+          latestVersion={Object.values(selectedRecords)?.[0]?.tag}
+        />
       </Wrapper>
     </>
   );
@@ -319,7 +332,13 @@ const PublishModal = (props: {
       width="600px"
       title={trans("queryLibrary.publishNewVersion")}
       footer={
-        <div style={{ display: "flex", justifyContent: "flex-end", padding: "16px 16px 0" }}>
+        <div
+          style={{
+            display: "flex",
+            justifyContent: "flex-end",
+            padding: "16px 16px 0",
+          }}
+        >
           <TacoButton
             buttonType="primary"
             loading={loading}
@@ -333,7 +352,9 @@ const PublishModal = (props: {
                     onSuccessCallback: () => {
                       props.onClose();
                       setLoading(false);
-                      messageInstance.success(trans("queryLibrary.publishSuccess"));
+                      messageInstance.success(
+                        trans("queryLibrary.publishSuccess")
+                      );
                     },
                     onErrorCallback: () => setLoading(false),
                   })
@@ -346,45 +367,11 @@ const PublishModal = (props: {
         </div>
       }
     >
-      <DatasourceForm form={form} preserve={false} style={{ gap: "12px" }}>
-        <FormSection>
-          <FormRadioItem
-            name={"tag"}
-            label={trans("queryLibrary.version")}
-            options={versionOptions}
-            initialValue={versionOptions[1].value}
-          />
-          <FormInputItem name={"commitMessage"} label={trans("queryLibrary.desc")} />
-        </FormSection>
-      </DatasourceForm>
+      <VersionDataForm form={form} preserve={false} latestVersion={props?.latestVersion} />
     </CustomModal>
   );
 };
 
-function getVersionOptions(version?: string): Array<CheckboxOptionType> {
-  if (!version) {
-    return [
-      { label: "v1.0.0", value: "v1.0.0" },
-      { label: "v0.1.0", value: "v0.1.0" },
-    ];
-  }
-  const [major, minor, patch] = version.slice(1).split(".");
-  return [
-    {
-      label: ["v" + (Number(major) + 1), 0, 0].join("."),
-      value: ["v" + (Number(major) + 1), 0, 0].join("."),
-    },
-    {
-      label: ["v" + major, Number(minor) + 1, 0].join("."),
-      value: ["v" + major, Number(minor) + 1, 0].join("."),
-    },
-    {
-      label: ["v" + major, minor, Number(patch) + 1].join("."),
-      value: ["v" + major, minor, Number(patch) + 1].join("."),
-    },
-  ];
-}
-
 function useSaveQueryLibrary(
   query: LibraryQuery,
   instance: InstanceType<typeof QueryLibraryComp> | null
@@ -419,7 +406,7 @@ function useSaveQueryLibrary(
       return setPrevQueryId(queryId);
     }
     if (!Boolean(prevJsonStr) && Boolean(curJsonStr)) {
-      setPrevComp(comp)
+      setPrevComp(comp);
       return setPrevJsonStr(curJsonStr);
     }
     if (prevJsonStr === curJsonStr) {
diff --git a/client/packages/lowcoder/src/redux/reduxActions/applicationActions.ts b/client/packages/lowcoder/src/redux/reduxActions/applicationActions.ts
index 83be6cdbb1..eeb9a89d0a 100644
--- a/client/packages/lowcoder/src/redux/reduxActions/applicationActions.ts
+++ b/client/packages/lowcoder/src/redux/reduxActions/applicationActions.ts
@@ -8,6 +8,7 @@ import {
 } from "constants/applicationConstants";
 import { JSONValue } from "util/jsonTypes";
 import { CommonSettingResponseData } from "api/commonSettingApi";
+import { ApplicationPublishRequest } from "@lowcoder-ee/api/applicationApi";
 
 export interface HomeDataPayload {
   applicationType?: AppTypeEnum;
@@ -114,6 +115,7 @@ export const updateAppMetaAction = (payload: UpdateAppMetaPayload) => ({
 
 export type PublishApplicationPayload = {
   applicationId: string;
+  request: ApplicationPublishRequest;
 };
 export const publishApplication = (payload: PublishApplicationPayload) => ({
   type: ReduxActionTypes.PUBLISH_APPLICATION,
@@ -148,7 +150,9 @@ export const fetchApplicationInfo = (payload: FetchAppInfoPayload) => ({
 export type FetchAppPermissionPayload = {
   applicationId: string;
 };
-export const fetchApplicationPermissions = (payload: FetchAppPermissionPayload) => ({
+export const fetchApplicationPermissions = (
+  payload: FetchAppPermissionPayload
+) => ({
   type: ReduxActionTypes.FETCH_APP_PERMISSIONS,
   payload: payload,
 });
@@ -163,7 +167,9 @@ export const updateAppPermission = (payload: UpdateAppPermissionPayload) => ({
   payload: payload,
 });
 
-export const updateAppPermissionInfo = (payload: Partial<AppPermissionInfo>) => ({
+export const updateAppPermissionInfo = (
+  payload: Partial<AppPermissionInfo>
+) => ({
   type: ReduxActionTypes.UPDATE_APP_PERMISSION_INFO,
   payload: payload,
 });
diff --git a/client/packages/lowcoder/src/util/versionOptions.ts b/client/packages/lowcoder/src/util/versionOptions.ts
new file mode 100644
index 0000000000..44e43b730f
--- /dev/null
+++ b/client/packages/lowcoder/src/util/versionOptions.ts
@@ -0,0 +1,25 @@
+import { CheckboxOptionType } from "antd";
+
+export function getVersionOptions(version?: string): Array<CheckboxOptionType> {
+  if (!version) {
+    return [
+      { label: "v1.0.0", value: "v1.0.0" },
+      { label: "v0.1.0", value: "v0.1.0" },
+    ];
+  }
+  const [major, minor, patch] = version.slice(1).split(".");
+  return [
+    {
+      label: ["v" + (Number(major) + 1), 0, 0].join("."),
+      value: ["v" + (Number(major) + 1), 0, 0].join("."),
+    },
+    {
+      label: ["v" + major, Number(minor) + 1, 0].join("."),
+      value: ["v" + major, Number(minor) + 1, 0].join("."),
+    },
+    {
+      label: ["v" + major, minor, Number(patch) + 1].join("."),
+      value: ["v" + major, minor, Number(patch) + 1].join("."),
+    },
+  ];
+}
diff --git a/translations/locales/en.js b/translations/locales/en.js
index aafb078ed8..e63567cf4c 100644
--- a/translations/locales/en.js
+++ b/translations/locales/en.js
@@ -2949,11 +2949,14 @@ export const en = {
         "fileFormatError": "File format error",
         "groupWithSquareBrackets": "[Group] ",
         "allPermissions": "Owner",
+        "managePermissions": "Manage permissions",
         "shareLink": "Share link: ",
         "copyLink": "Copy link",
         "appPublicMessage": "Make the app public. Anyone can view.",
         "modulePublicMessage": "Make the module public. Anyone can view.",
         "marketplaceURL": "https://api-service.lowcoder.cloud",
+        "appMemberMessage": "All shared members can view this app.",
+        "moduleMemberMessage": "All shared members can view this module.",
         "appMarketplaceMessage": "Publish your App on the Public Marketplace. Anyone can view and copy it from there.",
         "moduleMarketplaceMessage": "Publish your Module on the Public Marketplace. Anyone can view and copy it from there.",
         "marketplaceGoodPublishing": "Please make sure your app is well-named and easy to use. Remove any sensitive information before publishing. Also, remove local datasources and replace by static built-in temporary data.",
@@ -2975,6 +2978,8 @@ export const en = {
         "createNavigation": "Create Navigation",
         "howToUseAPI": "How to use the Open Rest API",
         "support": "Support",
+        "versions": "Versions",
+        "publishVersionDescription": "By publishing, your users will see the current state of your app. Further editing will not be visible until you publish again",
     },
     "support": {
         "supportTitle": "Lowcoder Support",