diff --git a/README.md b/README.md
index 225c3f53d..b603176f7 100644
--- a/README.md
+++ b/README.md
@@ -3,13 +3,13 @@
 
 <div align="center">
     <h1 style="border-bottom: none; margin-bottom: 0">Lowcoder</h1>
-    <h3 style="margin-top: 0">The Open Source Retool, Tooljet and Appsmith Alternative</h3>
+    <h3 style="margin-top: 0">Lowcoder is the best Retool, Appsmith or Tooljet Alternative.</h3>
     <p>
-        Build internal and customer facing Apps fast, with no limitations
+        Create internal and external software applications for your Company and your Customers with minimal coding experience. 
     </p>
 </div>
 
-<img src="/docs/.gitbook/assets/Bu2fpz1h01.gif"/>
+<img src="https://1167272343-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FjNgeI0mUzgw6Re92iTOw%2Fuploads%2FnwXJC1XBqP2MvTQitPyo%2FApp%20Editor%20%7C%20Main%20Screeen%20clean.png?alt=media&token=e5fba81b-82a7-4c0e-a15d-baa781d5b13a"/>
 
 ## 📢 Use Lowcoder in 3 steps
 1. Connect to any data sources or APIs.
@@ -21,32 +21,42 @@ It's cumbersome to create a single app. You had to design user interfaces, write
 
 Low-code/No-code platforms are fast to get started with but quickly become unmaintainable and inflexible. This creates more problems than it solves.
 
-Retool-like solutions are great for their simplicity and flexibility, but they can also be limited in different ways compared to frameworks like React/Vue.
+NewGen Lowcode Platforms like Retool and others are great for their simplicity and flexibility - like Lowcoder too, but they can also be limited in different ways, especially when it comes to "external" applications for everyone.
 
-Lowcoder wants to take a step forward. More specifically, Lowcoder is
-- An all-in-one IDE to create internal or customer-facing apps.
+Lowcoder wants to take a step forward. More specifically, Lowcoder is:
+- An all-in-one IDE to create internal or customer-facing (external) apps.
 - A place to create, build and share building blocks of web applications.
-- A domain-specific language that UI-configurable block is the first-class citizen.
+- The tool and community to support your business, and lower the cost and time to develop interactive applications.
+- The only platform to embed Lowcode Apps natively in Websites (no iFrame!)
+- The only platform where you can build your own Meeting Tool - like Teams, Zoom or Google Meets, - just in the Lowcode way.
 
 ## 🪄 Features
-- **Visual UI builder** with 50+ built-in components.
+- **Visual UI builder** with 50+ built-in components. Save 90% of time to build apps.
 - **Modules** for reusable (!) component sets in the UI builder.
-- **Embed Lowcoder Apps as native React component** instead of iFrame (!). [Demo](https://github.com/lowcoder-org/lowcoder-sdk-demo)
+- **Embed Lowcoder Apps as native parts of any Website** instead of iFrame (!). [Demo](https://github.com/lowcoder-org/lowcoder-sdk-demo)
+- **Video Meeting Components** to create your own individual Web-Meeting tool.
 - **Query Library** for reusable data queries of your data sources.
 - **Custom components** to develop own components and use them in the UI builder.
 - **Native Data connections** to PostgreSQL, MongoDB, MySQL, Redis, Elasticsearch, REST API, SMTP, etc.
+- **Stream Data connections** to Websockets for realtime data updates & collaboration
 - **JavaScript supported everywhere** to transform data, control components, etc.
 - **Role-based access control (RBAC)** for granular permission management.
 - **Auto-saved and restorable history** for release and version control.
 - **App Themes and Theme Editor** to precisely align with your company's brand guidelines.
 
-- **Self Hosting** to use Lowcoder in your internal company network.
+- **Self Hosting** to use Lowcoder in your internal company network, even behind the firewall.
 - **Free Community Cloud** to start within a minute and build your first Apps. [Start here](https://app.lowcoder.cloud)
 
 ## 🏆 Comparisons
+### Lowcoder vs Teams, Google Meets, Zoom
+- build a Meeting tool with peace in mind. Blue buttons - ok. Red corners or circle Videostream - ok too.
+- embedd applications in your Video-Meetings, so attendees can enjoy collaborative "anything". From shopping to working and gaming...
+### Lowcoder vs Powerapps
+- build a apps way faster than in Power Apps. Save up to 50& of the time at least.
+- Use self-hosting to keep all apps and data under your control for example at the own baremetals.
 ### Lowcoder vs Retool
 - Lowcoder is open-source. You don't need to worry about vendor lock-in or being stuck with an outdated version of the software.
-- In Lowcoder, developers can create and use their own components instead of depending on official updates.
+- In Lowcoder, developers can build truly responsive apps - not as cumbersome as the "Desktop / Mobile switch" in Retool
 - Lowcoder is free and you can contribute!
 - The EE Version of Lowcoder comes with a much better pricing model, so you have no "per-user costs".
 ### Lowcoder vs Appsmith, Tooljet
@@ -55,15 +65,17 @@ Lowcoder wants to take a step forward. More specifically, Lowcoder is
 - In Lowcoder, you can reuse common structures when building apps with modules and query library features.
 ### Lowcoder vs Mendix, Outsystems, Pega
 - Lowcoder is modern. The codebase is fresh and uses modern standards.
-- Lowcoder Apps do not need a compile and deployment. Just publish and use.
+- Lowcoder Apps do not need a compile and deployment. Just publish and use. Within seconds!
 - Lowcoder Apps can get embedded natively in websites and apps, even in mobile apps.
 ### Lowcoder vs internal Tool platforms
 - Lowcoder supports internal tools like admin panels perfectly, but also customer-facing apps can get developed and published.
 - The Lowcoder UI builder is straightforward and better to use than Bubble.
 - App release cycles and updates can be done nearly daily without service downtimes for customers and users.
 
+
 ## 👐 Support and Community
 If you have any questions, please feel free to contact us or share them with our community. Our team is here ready to help.
+And we mean it... Day by day!
 
 📮 Best way is to chat with us on [Discord](https://discord.gg/qMG9uTmAx2)
 
@@ -72,10 +84,16 @@ If you have any questions, please feel free to contact us or share them with our
 🔎 Submit an issue here on [GitHub](https://github.com/lowcoder-org/lowcoder/issues)
 
 ## 💻 Deployment Options
-You can access Lowcoder from [cloud-hosted version](https://www.lowcoder.cloud/) at any time, or use the following resources for deploying Lowcoder on different platforms:
-- [Docker](docs/self-hosting/README.md)
+You can access Lowcoder from [cloud-hosted version](https://app.lowcoder.cloud/) at any time, or use the following resources for deploying Lowcoder on different platforms:
+- [Docker](https://docs.lowcoder.cloud/lowcoder-documentation/setup-and-run/self-hosting)
 
 ## 💪 Contributing
 - Language support: If you have experience with a language that isn't currently supported by our product, send us a pull request.
 - Create and share components or demos: If you've created something that might be useful to others, add the link here.
 - [Frontend contributing guide](https://github.com/lowcoder-org/lowcoder/tree/develop/client)
+
+## 🥇 Sponsors
+Accelerate the growth of Lowcoder and unleash its potential with your Sponsorship – together, we're shaping the future of Lowcode for everyone!
+[Be a Sponsor](https://github.com/sponsors/lowcoder-org)
+
+Like ... @CHSchuepfer. Thank you very much!
\ No newline at end of file
diff --git a/app.json b/app.json
index 0889503a9..5d6a647c0 100644
--- a/app.json
+++ b/app.json
@@ -1,11 +1,16 @@
 {
   "name": "lowcoder",
-  "description": "Lowcoder is a developer-friendly open-source low code platform to build internal apps within minutes.",
+  "description": "An all-in-one IDE to create internal or customer-facing apps. · Visual UI builder with 50+ built-in components",
   "repository": "https://github.com/lowcoder-org/lowcoder",
-  "logo": "https://cdn-files.openblocks.dev/logo.png",
+  "logo": "https://lowcoder.cloud/images/webclip.png",
   "keywords": [
-    "low code",
-    "develop tool"
+    "LowCode",
+    "Low code",
+    "develop tool",
+    "Fast Application Development",
+    "Rapid development",
+    "Collaboration tool",
+    "Video conferencing"
   ],
   "stack": "container",
   "formation": {
diff --git a/client/VERSION b/client/VERSION
index cd57a8b95..9671f9a9b 100644
--- a/client/VERSION
+++ b/client/VERSION
@@ -1 +1 @@
-2.1.5
+2.1.7
\ No newline at end of file
diff --git a/client/packages/lowcoder-design/src/components/Section.tsx b/client/packages/lowcoder-design/src/components/Section.tsx
index a0c18134f..da58f9588 100644
--- a/client/packages/lowcoder-design/src/components/Section.tsx
+++ b/client/packages/lowcoder-design/src/components/Section.tsx
@@ -142,5 +142,5 @@ export const sectionNames = {
   validation: trans("prop.validation"),
   layout: trans("prop.layout"),
   style: trans("prop.style"),
-  meetings : trans("prop.meetings"),
+  meetings : trans("prop.meetings"), // added by Falk Wolsky
 };
diff --git a/client/packages/lowcoder-design/src/icons/icon-autocomplete-comp.svg b/client/packages/lowcoder-design/src/icons/icon-autocomplete-comp.svg
index dd882963a..505c59adb 100644
--- a/client/packages/lowcoder-design/src/icons/icon-autocomplete-comp.svg
+++ b/client/packages/lowcoder-design/src/icons/icon-autocomplete-comp.svg
@@ -1 +1,20 @@
-<svg t="1690794551698" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="41410" width="200" height="200"><path d="M826.515614 829.65762H94.474185C42.378083 829.65762 0 787.273538 0 735.179436V288.816564C0 236.722462 42.378083 194.34038 94.474185 194.34038h835.05363c52.094102 0 94.474185 42.382083 94.474185 94.476184v326.288637c0 15.58003-12.634025 28.212055-28.212055 28.212055-15.58003 0-28.212055-12.634025-28.212055-28.212055V288.816564c0-20.982041-17.068033-38.050074-38.048075-38.050074H94.474185c-20.978041-0.002-38.048074 17.068033-38.048075 38.050074V735.181436c0 20.982041 17.068033 38.050074 38.048075 38.050074h732.041429c15.58003 0 28.212055 12.634025 28.212055 28.212055 0 15.58203-12.634025 28.214055-28.212055 28.214055z" fill="#d7d9e0" p-id="41411" data-spm-anchor-id="a313x.7781069.0.i11" class=""></path><path d="M913.357784 739.061443c-7.222014 0-14.440028-2.756005-19.950039-8.264016l-95.760187-95.756187c-11.018022-11.018022-11.018022-28.882056 0-39.902078 11.020022-11.014022 28.878056-11.014022 39.902078 0l95.760187 95.756187c11.018022 11.018022 11.018022 28.882056 0 39.902078a28.132055 28.132055 0 0 1-19.952039 8.264016z" fill="#d7d9e0" p-id="41412" data-spm-anchor-id="a313x.7781069.0.i13" class=""></path><path d="M749.529464 547.043068m-122.72024 0a122.72024 122.72024 0 1 0 245.44048 0 122.72024 122.72024 0 1 0-245.44048 0Z" fill="#3377ff" p-id="41413" data-spm-anchor-id="a313x.7781069.0.i15" class="selected"></path><path d="M749.539464 697.963363c-40.316079 0-78.220153-15.698031-106.726209-44.206086-28.506056-28.508056-44.206086-66.41013-44.206086-106.724209 0-40.316079 15.698031-78.220153 44.206086-106.726208 28.508056-28.506056 66.41013-44.206086 106.726209-44.206086s78.220153 15.698031 106.726208 44.206086c58.846115 58.846115 58.846115 154.602302 0 213.448417-28.508056 28.512056-66.41013 44.208086-106.726208 44.208086z m0-245.436479c-25.242049 0-48.974096 9.830019-66.828131 27.682054-17.850035 17.848035-27.682054 41.580081-27.682054 66.82813 0 25.242049 9.830019 48.974096 27.682054 66.824131 17.850035 17.850035 41.582081 27.682054 66.828131 27.682054 25.244049 0 48.974096-9.830019 66.82813-27.682054 36.848072-36.848072 36.848072-96.804189 0-133.650261-17.854035-17.854035-41.586081-27.684054-66.82813-27.684054zM513.349003 635.277241H183.258358c-15.58003 0-28.212055-12.634025-28.212055-28.212055 0-15.58003 12.634025-28.212055 28.212055-28.212055h330.088645c15.58003 0 28.212055 12.634025 28.212055 28.212055 0.002 15.58003-12.628025 28.212055-28.210055 28.212055z" fill="#d7d9e0" p-id="41414" data-spm-anchor-id="a313x.7781069.0.i12" class=""></path></svg>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#D7D9E0;}
+	.st1{fill:#3377FF;}
+</style>
+<path class="st0" d="M37.8,37.9H5.7c-2.3,0-4.1-1.9-4.1-4.1V14.2c0-2.3,1.9-4.1,4.1-4.1h36.6c2.3,0,4.1,1.9,4.1,4.1v14.3
+	c0,0.7-0.6,1.2-1.2,1.2c-0.7,0-1.2-0.6-1.2-1.2V14.2c0-0.9-0.7-1.7-1.7-1.7H5.7c-0.9,0-1.7,0.7-1.7,1.7v19.6c0,0.9,0.7,1.7,1.7,1.7
+	h32.1c0.7,0,1.2,0.6,1.2,1.2C39,37.4,38.5,37.9,37.8,37.9z"/>
+<path class="st0" d="M41.6,34c-0.3,0-0.6-0.1-0.9-0.4l-4.2-4.2c-0.5-0.5-0.5-1.3,0-1.7c0.5-0.5,1.3-0.5,1.7,0l4.2,4.2
+	c0.5,0.5,0.5,1.3,0,1.7C42.2,33.8,41.9,34,41.6,34L41.6,34z"/>
+<path class="st1" d="M29,25.5c0,3,2.4,5.4,5.4,5.4s5.4-2.4,5.4-5.4s-2.4-5.4-5.4-5.4S29,22.6,29,25.5z"/>
+<path class="st0" d="M34.4,32.2c-1.8,0-3.4-0.7-4.7-1.9c-1.2-1.2-1.9-2.9-1.9-4.7c0-1.8,0.7-3.4,1.9-4.7c1.2-1.2,2.9-1.9,4.7-1.9
+	s3.4,0.7,4.7,1.9c2.6,2.6,2.6,6.8,0,9.4C37.8,31.5,36.2,32.2,34.4,32.2L34.4,32.2z M34.4,21.4c-1.1,0-2.1,0.4-2.9,1.2
+	c-0.8,0.8-1.2,1.8-1.2,2.9c0,1.1,0.4,2.1,1.2,2.9c0.8,0.8,1.8,1.2,2.9,1.2c1.1,0,2.1-0.4,2.9-1.2c1.6-1.6,1.6-4.2,0-5.9
+	C36.5,21.8,35.5,21.4,34.4,21.4L34.4,21.4z M24,29.4H9.6c-0.7,0-1.2-0.6-1.2-1.2c0-0.7,0.6-1.2,1.2-1.2H24c0.7,0,1.2,0.6,1.2,1.2
+	C25.3,28.9,24.7,29.4,24,29.4z"/>
+</svg>
diff --git a/client/packages/lowcoder-design/src/icons/icon-comment-comp.svg b/client/packages/lowcoder-design/src/icons/icon-comment-comp.svg
index b6828e6a0..5a737ee86 100644
--- a/client/packages/lowcoder-design/src/icons/icon-comment-comp.svg
+++ b/client/packages/lowcoder-design/src/icons/icon-comment-comp.svg
@@ -1 +1,13 @@
-<svg t="1690270225287" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5882" width="200" height="200"><path d="M781.3 188.6H269.7c-59.6 0-108 51.1-108 113.9v312.8c0 64.8 50.3 119.5 109.9 119.5h120.2c30.4 32.8 109.8 117 109.8 117 6.2 6.7 14.9 10.5 23.9 10.5s17.6-3.8 24.6-11.3c0.6-0.7 62.1-72 105.6-116.2h123.7c59.6 0 110-54.7 110-119.5V302.6c-0.1-62.9-48.6-114-108.1-114z m-431.4 321c-25.2 0-45.6-20.4-45.6-45.6s20.4-45.6 45.6-45.6 45.6 20.4 45.6 45.6-20.4 45.6-45.6 45.6z m179.4 0c-25.2 0-45.6-20.4-45.6-45.6s20.4-45.6 45.6-45.6 45.6 20.4 45.6 45.6-20.4 45.6-45.6 45.6z m179.4 0c-25.2 0-45.6-20.4-45.6-45.6s20.4-45.6 45.6-45.6 45.6 20.4 45.6 45.6-20.4 45.6-45.6 45.6z" fill="#B2B2B2" p-id="5883"></path></svg>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 200 200" style="enable-background:new 0 0 200 200;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#4575F6;}
+</style>
+<path class="st0" d="M152.6,36.8H52.7c-11.6,0-21.1,10-21.1,22.2v61.1c0,12.7,9.8,23.3,21.5,23.3h23.5c5.9,6.4,21.4,22.9,21.4,22.9
+	c1.2,1.3,2.9,2.1,4.7,2.1s3.4-0.7,4.8-2.2c0.1-0.1,12.1-14.1,20.6-22.7h24.2c11.6,0,21.5-10.7,21.5-23.3V59.1
+	C173.7,46.8,164.2,36.8,152.6,36.8z M68.3,99.5c-4.9,0-8.9-4-8.9-8.9s4-8.9,8.9-8.9s8.9,4,8.9,8.9S73.3,99.5,68.3,99.5z M103.4,99.5
+	c-4.9,0-8.9-4-8.9-8.9s4-8.9,8.9-8.9c4.9,0,8.9,4,8.9,8.9S108.3,99.5,103.4,99.5z M138.4,99.5c-4.9,0-8.9-4-8.9-8.9s4-8.9,8.9-8.9
+	c4.9,0,8.9,4,8.9,8.9S143.3,99.5,138.4,99.5z"/>
+</svg>
diff --git a/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg b/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg
index 4c04c61e2..5b311e0b4 100644
--- a/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg
+++ b/client/packages/lowcoder-design/src/icons/icon-mention-comp.svg
@@ -1 +1,20 @@
-<svg t="1690851406312" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2494" width="200" height="200"><path d="M981.333333 938.666667h-384A469.333333 469.333333 0 0 1 128 469.333333a384 384 0 0 1 768 0v192a64 64 0 0 1-128 0V256a42.666667 42.666667 0 0 0-85.333333 0 209.92 209.92 0 0 0-128-42.666667h-42.666667a256 256 0 0 0 0 512h42.666667a213.333333 213.333333 0 0 0 128-45.226666 148.906667 148.906667 0 0 0 298.666666-18.773334V469.333333a469.333333 469.333333 0 0 0-938.666666 0 554.666667 554.666667 0 0 0 554.666666 554.666667h384a42.666667 42.666667 0 0 0 0-85.333333z m-426.666666-298.666667h-42.666667a170.666667 170.666667 0 0 1 0-341.333333h42.666667a128 128 0 0 1 128 128v85.333333a128 128 0 0 1-128 128z" p-id="2495" fill="#d7d9e0"></path></svg>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#4575F6;}
+</style>
+<g>
+	<path class="st0" d="M14,38.5c-2.2-1-3.9-2.5-5-4.3c-1.1-1.9-1.7-4-1.7-6.6c0-3.6,0.9-6.9,2.6-9.9c1.8-3,4.2-5.3,7.2-7
+		c3.1-1.7,6.5-2.5,10.2-2.5c4.3,0,7.7,1,10.2,2.9c2.5,1.9,3.8,4.6,3.8,8.1c0,2.5-0.5,4.7-1.6,6.8c-1.1,2.1-2.6,3.8-4.4,5
+		s-3.9,1.8-6.1,1.8c-1.1,0-1.9-0.3-2.6-0.8c-0.7-0.5-1-1.2-1-2.2c-0.8,0.9-1.7,1.6-2.8,2.2c-1.1,0.5-2.3,0.8-3.5,0.8
+		c-1.6,0-2.8-0.4-3.6-1.2c-0.8-0.8-1.2-2-1.2-3.5c0-2.1,0.6-4.2,1.7-6.2c1.1-2,2.6-3.7,4.3-4.9c1.8-1.3,3.6-1.9,5.6-1.9
+		c1,0,1.8,0.2,2.5,0.7s1.1,1.1,1.4,1.9l1-2h3.6l-5.6,11.7c-0.2,0.4-0.3,0.7-0.3,1c0,0.6,0.4,0.8,1.3,0.8c1.2,0,2.3-0.5,3.4-1.4
+		c1-0.9,1.9-2.1,2.5-3.6c0.6-1.5,0.9-3.1,0.9-4.8c0-2.6-0.9-4.5-2.7-5.7c-1.8-1.2-4.3-1.8-7.4-1.8c-2.9,0-5.5,0.6-7.7,1.9
+		c-2.2,1.3-4,3-5.2,5.3c-1.2,2.3-1.8,4.9-1.8,7.8c0,2.9,0.9,5.1,2.7,6.6c1.8,1.5,4.5,2.3,8,2.3c1.8,0,3.4-0.2,4.9-0.5
+		c1.5-0.3,2.9-0.9,4.2-1.7c0.5-0.2,0.9-0.4,1.2-0.4c0.5,0,1,0.2,1.4,0.5s0.7,0.8,1,1.4c-1.8,1.6-3.9,2.8-6.2,3.5
+		C27,39.6,24.6,40,22.1,40C18.9,40,16.2,39.5,14,38.5z M23.6,28c1-1,1.9-2.2,2.6-3.6c0.7-1.4,1.1-2.8,1.1-4.1c0-1.2-0.6-1.9-1.9-1.9
+		c-1,0-2.1,0.5-3.1,1.4c-1.1,0.9-2,2.1-2.7,3.5s-1.1,2.8-1.1,4.1c0,1.3,0.7,2,2.2,2C21.6,29.4,22.6,28.9,23.6,28z"/>
+</g>
+</svg>
diff --git a/client/packages/lowcoder-design/src/icons/icon-timeline-comp.svg b/client/packages/lowcoder-design/src/icons/icon-timeline-comp.svg
index 329690d6d..e47f5fc79 100644
--- a/client/packages/lowcoder-design/src/icons/icon-timeline-comp.svg
+++ b/client/packages/lowcoder-design/src/icons/icon-timeline-comp.svg
@@ -1 +1,23 @@
-<svg t="1689814019529" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2710" data-spm-anchor-id="a313x.7781069.0.i26" width="200" height="200"><path d="M896 618.666667H443.733333c-10.666667 0-21.333333-4.266667-29.866666-12.8l-78.933334-78.933334c-8.533333-8.533333-8.533333-21.333333 0-29.866666l78.933334-78.933334c8.533333-8.533333 19.2-12.8 29.866666-12.8H896c12.8 0 21.333333 8.533333 21.333333 21.333334v170.666666c0 12.8-8.533333 21.333333-21.333333 21.333334z" fill="#3377ff" p-id="2711" data-spm-anchor-id="a313x.7781069.0.i22" class="selected"></path><path d="M192 128h42.666667v768H192z" fill="#CFD8DC" p-id="2712"></path><path d="M213.333333 213.333333m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#90A4AE" p-id="2713"></path><path d="M213.333333 512m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#90A4AE" p-id="2714"></path><path d="M213.333333 810.666667m-64 0a64 64 0 1 0 128 0 64 64 0 1 0-128 0Z" fill="#90A4AE" p-id="2715"></path><path d="M725.333333 917.333333H443.733333c-10.666667 0-21.333333-4.266667-29.866666-12.8l-78.933334-78.933333c-8.533333-8.533333-8.533333-21.333333 0-29.866667l78.933334-78.933333c8.533333-8.533333 19.2-12.8 29.866666-12.8H725.333333c12.8 0 21.333333 8.533333 21.333334 21.333333v170.666667c0 12.8-8.533333 21.333333-21.333334 21.333333z" fill="#dbdbdb" p-id="2716" data-spm-anchor-id="a313x.7781069.0.i23" class=""></path><path d="M746.666667 320H443.733333c-10.666667 0-21.333333-4.266667-29.866666-12.8l-78.933334-78.933333c-8.533333-8.533333-8.533333-21.333333 0-29.866667l78.933334-78.933333c8.533333-8.533333 19.2-12.8 29.866666-12.8H746.666667c12.8 0 21.333333 8.533333 21.333333 21.333333v170.666667c0 12.8-8.533333 21.333333-21.333333 21.333333z" fill="#dbdbdb" p-id="2717" data-spm-anchor-id="a313x.7781069.0.i24" class=""></path></svg>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#3377FF;}
+	.st1{fill:#CFD8DC;}
+	.st2{fill:#90A4AE;}
+	.st3{fill:#DBDBDB;}
+</style>
+<path class="st0" d="M43.6,29.5H20.4c-0.5,0-1.1-0.2-1.5-0.7l-4-4c-0.4-0.4-0.4-1.1,0-1.5l4-4c0.4-0.4,1-0.7,1.5-0.7h23.2
+	c0.7,0,1.1,0.4,1.1,1.1v8.7C44.7,29,44.2,29.5,43.6,29.5z"/>
+<path class="st1" d="M7.5,4.3h2.2v39.3H7.5V4.3z"/>
+<path class="st2" d="M5.3,8.7c0,1.8,1.5,3.3,3.3,3.3c1.8,0,3.3-1.5,3.3-3.3c0,0,0,0,0,0c0-1.8-1.5-3.3-3.3-3.3S5.3,6.9,5.3,8.7
+	L5.3,8.7z"/>
+<path class="st2" d="M5.3,24c0,1.8,1.5,3.3,3.3,3.3s3.3-1.5,3.3-3.3c0,0,0,0,0,0c0-1.8-1.5-3.3-3.3-3.3S5.3,22.2,5.3,24L5.3,24z"/>
+<path class="st2" d="M5.3,39.3c0,1.8,1.5,3.3,3.3,3.3s3.3-1.5,3.3-3.3c0,0,0,0,0,0c0-1.8-1.5-3.3-3.3-3.3S5.3,37.5,5.3,39.3
+	L5.3,39.3z"/>
+<path class="st3" d="M34.8,44.8H20.4c-0.5,0-1.1-0.2-1.5-0.7l-4-4c-0.4-0.4-0.4-1.1,0-1.5l4-4c0.4-0.4,1-0.7,1.5-0.7h14.4
+	c0.7,0,1.1,0.4,1.1,1.1v8.7C35.9,44.3,35.5,44.8,34.8,44.8z"/>
+<path class="st3" d="M35.9,14.2H20.4c-0.5,0-1.1-0.2-1.5-0.7l-4-4c-0.4-0.4-0.4-1.1,0-1.5l4-4c0.4-0.4,1-0.7,1.5-0.7h15.5
+	c0.7,0,1.1,0.4,1.1,1.1v8.7C37,13.7,36.6,14.2,35.9,14.2L35.9,14.2z"/>
+</svg>
diff --git a/client/packages/lowcoder-design/src/icons/icon-undo.svg b/client/packages/lowcoder-design/src/icons/icon-undo.svg
index e7296453f..a1199d1bb 100644
--- a/client/packages/lowcoder-design/src/icons/icon-undo.svg
+++ b/client/packages/lowcoder-design/src/icons/icon-undo.svg
@@ -1,3 +1,10 @@
-<svg t="1675856776421" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2686" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
-  <path d="M70.494 316.086c-3.484 3.173-3.484 8.656 0 11.829l193.119 175.883c5.137 4.679 13.387 1.034 13.387-5.915V368h395c114.875 0 208 93.125 208 208 0 114.876-93.125 208-208 208H256v80h416c159.058 0 288-128.942 288-288S831.058 288 672 288H277V146.117c0-6.948-8.25-10.593-13.387-5.914L70.494 316.086z" fill="#707070" p-id="2687"></path>
-</svg>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 27.9.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#707070;}
+</style>
+<path class="st0" d="M3.4,15.4c-0.2,0.1-0.2,0.4,0,0.5l9,8.2c0.2,0.2,0.6,0,0.6-0.3v-6h18.4c5.3,0,9.7,4.3,9.7,9.7
+	c0,5.3-4.3,9.7-9.7,9.7H12v3.7h19.3c7.4,0,13.4-6,13.4-13.4s-6-13.4-13.4-13.4H13V7.5c0-0.3-0.4-0.5-0.6-0.3L3.4,15.4z"/>
+</svg>
diff --git a/client/packages/lowcoder/site.webmanifest b/client/packages/lowcoder/site.webmanifest
new file mode 100644
index 000000000..91352b1a9
--- /dev/null
+++ b/client/packages/lowcoder/site.webmanifest
@@ -0,0 +1 @@
+{"name":"Lowcoder.cloud","short_name":"Lowcoder","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
diff --git a/client/packages/lowcoder/src/assets/images/android-chrome-192x192.png b/client/packages/lowcoder/src/assets/images/android-chrome-192x192.png
new file mode 100644
index 000000000..23d6e525e
Binary files /dev/null and b/client/packages/lowcoder/src/assets/images/android-chrome-192x192.png differ
diff --git a/client/packages/lowcoder/src/assets/images/android-chrome-512x512.png b/client/packages/lowcoder/src/assets/images/android-chrome-512x512.png
new file mode 100644
index 000000000..c690d113c
Binary files /dev/null and b/client/packages/lowcoder/src/assets/images/android-chrome-512x512.png differ
diff --git a/client/packages/lowcoder/src/assets/images/apple-touch-icon.png b/client/packages/lowcoder/src/assets/images/apple-touch-icon.png
new file mode 100644
index 000000000..6fff652aa
Binary files /dev/null and b/client/packages/lowcoder/src/assets/images/apple-touch-icon.png differ
diff --git a/client/packages/lowcoder/src/assets/images/favicon-16x16.png b/client/packages/lowcoder/src/assets/images/favicon-16x16.png
new file mode 100644
index 000000000..a87cf442d
Binary files /dev/null and b/client/packages/lowcoder/src/assets/images/favicon-16x16.png differ
diff --git a/client/packages/lowcoder/src/assets/images/favicon-32x32.png b/client/packages/lowcoder/src/assets/images/favicon-32x32.png
new file mode 100644
index 000000000..127dc44cb
Binary files /dev/null and b/client/packages/lowcoder/src/assets/images/favicon-32x32.png differ
diff --git a/client/packages/lowcoder/src/assets/images/favicon.ico b/client/packages/lowcoder/src/assets/images/favicon.ico
index 91f699e41..91f1543d0 100644
Binary files a/client/packages/lowcoder/src/assets/images/favicon.ico and b/client/packages/lowcoder/src/assets/images/favicon.ico differ
diff --git a/client/packages/lowcoder/src/components/CompName.tsx b/client/packages/lowcoder/src/components/CompName.tsx
index cf63dbab4..2f0b26b8b 100644
--- a/client/packages/lowcoder/src/components/CompName.tsx
+++ b/client/packages/lowcoder/src/components/CompName.tsx
@@ -11,6 +11,7 @@ import { GreyTextColor } from "constants/style";
 import { UICompType } from "comps/uiCompRegistry";
 import { trans } from "i18n";
 import { getComponentDocUrl } from "comps/utils/compDocUtil";
+import { getComponentPlaygroundUrl } from "comps/utils/compDocUtil";
 import { parseCompType } from "comps/utils/remote";
 
 const CompDiv = styled.div<{ width?: number; hasSearch?: boolean; showSearch?: boolean }>`
@@ -78,6 +79,7 @@ export const CompName = (props: Iprops) => {
   const compType = selectedComp.children.compType.getView() as UICompType;
   const compInfo = parseCompType(compType);
   const docUrl = getComponentDocUrl(compType);
+  const playgroundUrl = getComponentPlaygroundUrl(compType);
 
   const items: EditPopoverItemType[] = [];
 
@@ -99,6 +101,16 @@ export const CompName = (props: Iprops) => {
     });
   }
 
+  if (playgroundUrl) {
+    items.push({
+      text: trans("comp.menuViewPlayground"),
+      onClick: () => {
+        window.open(playgroundUrl, "_blank");
+      },
+    });
+  }
+
+
   if (compInfo.isRemote) {
     items.push({
       text: trans("comp.menuUpgradeToLatest"),
diff --git a/client/packages/lowcoder/src/comps/comps/layout/layoutMenuItemComp.tsx b/client/packages/lowcoder/src/comps/comps/layout/layoutMenuItemComp.tsx
index 62b55a7da..0999a4012 100644
--- a/client/packages/lowcoder/src/comps/comps/layout/layoutMenuItemComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/layout/layoutMenuItemComp.tsx
@@ -38,15 +38,13 @@ export class LayoutMenuItemComp extends MultiBaseComp<ChildrenType> {
   }
 
   override getPropertyView(): ReactNode {
-    const isLeaf = this.children.items.getView().length === 0;
     return (
       <>
-        {isLeaf &&
-          this.children.action.propertyView({
-            onAppChange: (label) => {
-              label && this.children.label.dispatchChangeValueAction(label);
-            },
-          })}
+        {this.children.action.propertyView({
+          onAppChange: (label) => {
+            label && this.children.label.dispatchChangeValueAction(label);
+          },
+        })}
         {this.children.label.propertyView({ label: trans("label") })}
         {this.children.icon.propertyView({
           label: trans("icon"),
@@ -98,12 +96,17 @@ const LayoutMenuItemCompMigrate = migrateOldData(LayoutMenuItemComp, (oldData: a
 export class LayoutMenuItemListComp extends list(LayoutMenuItemCompMigrate) {
   addItem(value?: any) {
     const data = this.getView();
+
     this.dispatch(
       this.pushAction(
-        value || {
-          label: trans("menuItem") + " " + (data.length + 1),
-          itemKey: genRandomKey(),
-        }
+        value
+          ? {
+            ...value,
+            itemKey: value.itemKey || genRandomKey(),
+          } : {
+            label: trans("menuItem") + " " + (data.length + 1),
+            itemKey: genRandomKey(),
+          }
       )
     );
   }
diff --git a/client/packages/lowcoder/src/comps/comps/layout/navLayout.tsx b/client/packages/lowcoder/src/comps/comps/layout/navLayout.tsx
index 5e8d47320..368e459a9 100644
--- a/client/packages/lowcoder/src/comps/comps/layout/navLayout.tsx
+++ b/client/packages/lowcoder/src/comps/comps/layout/navLayout.tsx
@@ -1,4 +1,4 @@
-import { Layout, Menu as AntdMenu, MenuProps } from "antd";
+import { Layout, Menu as AntdMenu, MenuProps, Segmented } from "antd";
 import MainContent from "components/layout/MainContent";
 import { LayoutMenuItemComp, LayoutMenuItemListComp } from "comps/comps/layout/layoutMenuItemComp";
 import { menuPropertyView } from "comps/comps/navComp/components/MenuItemList";
@@ -8,12 +8,38 @@ import { withDispatchHook } from "comps/generators/withDispatchHook";
 import { NameAndExposingInfo } from "comps/utils/exposingTypes";
 import { ALL_APPLICATIONS_URL } from "constants/routesURL";
 import { TopHeaderHeight } from "constants/style";
-import { Section } from "lowcoder-design";
+import { Section, controlItem, sectionNames } from "lowcoder-design";
 import { trans } from "i18n";
 import { EditorContainer, EmptyContent } from "pages/common/styledComponent";
 import { useCallback, useEffect, useMemo, useState } from "react";
 import styled from "styled-components";
 import { isUserViewMode, useAppPathParam } from "util/hooks";
+import { StringControl, jsonControl } from "comps/controls/codeControl";
+import { styleControl } from "comps/controls/styleControl";
+import {
+  NavLayoutStyle,
+  NavLayoutItemStyle,
+  NavLayoutItemStyleType,
+  NavLayoutItemHoverStyle,
+  NavLayoutItemHoverStyleType,
+  NavLayoutItemActiveStyle,
+  NavLayoutItemActiveStyleType,
+} from "comps/controls/styleControlConstants";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import _ from "lodash";
+import { check } from "util/convertUtils";
+import { genRandomKey } from "comps/utils/idGenerator";
+import history from "util/history";
+import {
+  DataOption,
+  DataOptionType,
+  ModeOptions,
+  jsonMenuItems,
+  menuItemStyleOptions
+} from "./navLayoutConstants";
+
+const DEFAULT_WIDTH = 240;
+type MenuItemStyleOptionValue = "normal" | "hover" | "active";
 
 const StyledSide = styled(Layout.Sider)`
   max-height: calc(100vh - ${TopHeaderHeight});
@@ -39,22 +65,192 @@ const ContentWrapper = styled.div`
   }
 `;
 
+const StyledMenu = styled(AntdMenu)<{
+  $navItemStyle?: NavLayoutItemStyleType & { width: string},
+  $navItemHoverStyle?: NavLayoutItemHoverStyleType,
+  $navItemActiveStyle?: NavLayoutItemActiveStyleType,
+}>`
+  .ant-menu-item {
+    height: auto;
+    width: ${(props) => props.$navItemStyle?.width};
+    background-color: ${(props) => props.$navItemStyle?.background};
+    color: ${(props) => props.$navItemStyle?.text};
+    border-radius: ${(props) => props.$navItemStyle?.radius} !important;
+    border: ${(props) => `1px solid ${props.$navItemStyle?.border}`};
+    margin: ${(props) => props.$navItemStyle?.margin};
+    padding: ${(props) => props.$navItemStyle?.padding};
+
+  }
+  .ant-menu-item-active {
+    background-color: ${(props) => props.$navItemHoverStyle?.background} !important;
+    color: ${(props) => props.$navItemHoverStyle?.text} !important;
+    border: ${(props) => `1px solid ${props.$navItemHoverStyle?.border}`};
+  }
+
+  .ant-menu-item-selected {
+    background-color: ${(props) => props.$navItemActiveStyle?.background} !important;
+    color: ${(props) => props.$navItemActiveStyle?.text} !important;
+    border: ${(props) => `1px solid ${props.$navItemActiveStyle?.border}`};
+  }
+
+  .ant-menu-submenu {
+    margin: ${(props) => props.$navItemStyle?.margin};
+    width: ${(props) => props.$navItemStyle?.width};
+
+    .ant-menu-submenu-title {
+      width: 100%;
+      height: auto !important;
+      background-color: ${(props) => props.$navItemStyle?.background};
+      color: ${(props) => props.$navItemStyle?.text};
+      border-radius: ${(props) => props.$navItemStyle?.radius} !important;
+      border: ${(props) => `1px solid ${props.$navItemStyle?.border}`};
+      margin: 0;
+      padding: ${(props) => props.$navItemStyle?.padding};
+
+    }
+
+    .ant-menu-item {
+      width: 100%;
+    }
+
+    &.ant-menu-submenu-active {
+      >.ant-menu-submenu-title {
+        width: 100%;
+        background-color: ${(props) => props.$navItemHoverStyle?.background} !important;
+        color: ${(props) => props.$navItemHoverStyle?.text} !important;
+        border: ${(props) => `1px solid ${props.$navItemHoverStyle?.border}`};
+      }
+    }
+    &.ant-menu-submenu-selected {
+      >.ant-menu-submenu-title {
+        width: 100%;
+        background-color: ${(props) => props.$navItemActiveStyle?.background} !important;
+        color: ${(props) => props.$navItemActiveStyle?.text} !important;
+        border: ${(props) => `1px solid ${props.$navItemActiveStyle?.border}`};
+      }
+    }
+  }
+
+`;
+
+const StyledImage = styled.img`
+  height: 1em;
+  color: currentColor;
+`;
+
+const defaultStyle = {
+  radius: '0px',
+  margin: '0px',
+  padding: '0px',
+}
+
+type UrlActionType = {
+  url?: string;
+  newTab?: boolean;
+}
+
+export type MenuItemNode = {
+  label: string;
+  key: string;
+  hidden?: boolean;
+  icon?: any;
+  action?: UrlActionType,
+  children?: MenuItemNode[];
+}
+
+function checkDataNodes(value: any, key?: string): MenuItemNode[] | undefined {
+  return check(value, ["array", "undefined"], key, (node, k) => {
+    check(node, ["object"], k);
+    check(node["label"], ["string"], "label");
+    check(node["hidden"], ["boolean", "undefined"], "hidden");
+    check(node["icon"], ["string", "undefined"], "icon");
+    check(node["action"], ["object", "undefined"], "action");
+    checkDataNodes(node["children"], "children");
+    return node;
+  });
+}
+
+function convertTreeData(data: any) {
+  return data === "" ? [] : checkDataNodes(data) ?? [];
+}
+
 let NavTmpLayout = (function () {
   const childrenMap = {
+    dataOptionType: dropdownControl(DataOptionType, DataOption.Manual),
     items: withDefault(LayoutMenuItemListComp, [
       {
         label: trans("menuItem") + " 1",
+        itemKey: genRandomKey(),
       },
     ]),
+    jsonItems: jsonControl(convertTreeData, jsonMenuItems),
+    width: withDefault(StringControl, DEFAULT_WIDTH),
+    backgroundImage: withDefault(StringControl, ""),
+    mode: dropdownControl(ModeOptions, "inline"),
+    navStyle: withDefault(styleControl(NavLayoutStyle), defaultStyle),
+    navItemStyle: withDefault(styleControl(NavLayoutItemStyle), defaultStyle),
+    navItemHoverStyle: withDefault(styleControl(NavLayoutItemHoverStyle), {}),
+    navItemActiveStyle: withDefault(styleControl(NavLayoutItemActiveStyle), {}),
   };
   return new MultiCompBuilder(childrenMap, (props) => {
     return null;
   })
     .setPropertyViewFn((children) => {
+      const [styleSegment, setStyleSegment] = useState('normal')
+
       return (
-        <>
-          <Section name={trans("menu")}>{menuPropertyView(children.items)}</Section>
-        </>
+        <div style={{overflowY: 'auto'}}>
+          <Section name={trans("menu")}>
+            {children.dataOptionType.propertyView({
+              radioButton: true,
+              type: "oneline",
+            })}
+            {
+              children.dataOptionType.getView() === DataOption.Manual
+                ? menuPropertyView(children.items)
+                : children.jsonItems.propertyView({
+                  label: "Json Data",
+                })
+            }
+          </Section>
+          <Section name={sectionNames.layout}>
+            { children.width.propertyView({
+                label: trans("navLayout.width"),
+                tooltip: trans("navLayout.widthTooltip"),
+                placeholder: DEFAULT_WIDTH + "",
+            })}
+            { children.mode.propertyView({
+              label: trans("labelProp.position"),
+              radioButton: true
+            })}
+            {children.backgroundImage.propertyView({
+              label: `Background Image`,
+              placeholder: 'https://temp.im/350x400',
+            })}
+          </Section>
+          <Section name={trans("navLayout.navStyle")}>
+            { children.navStyle.getPropertyView() }
+          </Section>
+          <Section name={trans("navLayout.navItemStyle")}>
+            {controlItem({}, (
+              <Segmented
+                block
+                options={menuItemStyleOptions}
+                value={styleSegment}
+                onChange={(k) => setStyleSegment(k as MenuItemStyleOptionValue)}
+              />
+            ))}
+            {styleSegment === 'normal' && (
+              children.navItemStyle.getPropertyView()
+            )}
+            {styleSegment === 'hover' && (
+              children.navItemHoverStyle.getPropertyView()
+            )}
+            {styleSegment === 'active' && (
+              children.navItemActiveStyle.getPropertyView()
+            )}
+          </Section>
+        </div>
       );
     })
     .build();
@@ -64,13 +260,98 @@ NavTmpLayout = withViewFn(NavTmpLayout, (comp) => {
   const pathParam = useAppPathParam();
   const isViewMode = isUserViewMode(pathParam);
   const [selectedKey, setSelectedKey] = useState("");
-  const items = useMemo(() => comp.children.items.getView(), [comp.children.items]);
-
+  const items = comp.children.items.getView();
+  const navWidth = comp.children.width.getView();
+  const navMode = comp.children.mode.getView();
+  const navStyle = comp.children.navStyle.getView();
+  const navItemStyle = comp.children.navItemStyle.getView();
+  const navItemHoverStyle = comp.children.navItemHoverStyle.getView();
+  const navItemActiveStyle = comp.children.navItemActiveStyle.getView();
+  const backgroundImage = comp.children.backgroundImage.getView();
+  const jsonItems = comp.children.jsonItems.getView();
+  const dataOptionType = comp.children.dataOptionType.getView();
+  
   // filter out hidden. unauthorised items filtered by server
   const filterItem = useCallback((item: LayoutMenuItemComp): boolean => {
     return !item.children.hidden.getView();
   }, []);
 
+  const generateItemKeyRecord = useCallback(
+    (items: LayoutMenuItemComp[] | MenuItemNode[]) => {
+      const result: Record<string, LayoutMenuItemComp | MenuItemNode> = {};
+      if(dataOptionType === DataOption.Manual) {
+        (items as LayoutMenuItemComp[])?.forEach((item) => {
+          const subItems = item.children.items.getView();
+          if (subItems.length > 0) {
+            Object.assign(result, generateItemKeyRecord(subItems))
+          }
+          result[item.getItemKey()] = item;
+        });
+      }
+      if(dataOptionType === DataOption.Json) {
+        (items as MenuItemNode[])?.forEach((item) => {
+          if (item.children?.length) {
+            Object.assign(result, generateItemKeyRecord(item.children))
+          }
+          result[item.key] = item;
+        })
+      }
+      return result;
+    }, [dataOptionType]
+  )
+
+  const itemKeyRecord = useMemo(() => {
+    if(dataOptionType === DataOption.Json) {
+      return generateItemKeyRecord(jsonItems)
+    }
+    return generateItemKeyRecord(items)
+  }, [dataOptionType, jsonItems, items, generateItemKeyRecord]);
+
+  const onMenuItemClick = useCallback(({key}: {key: string}) => {
+    const itemComp = itemKeyRecord[key]
+  
+    const url = [
+      ALL_APPLICATIONS_URL,
+      pathParam.applicationId,
+      pathParam.viewMode,
+      key,
+    ].join("/");
+
+    // handle manual menu item action
+    if(dataOptionType === DataOption.Manual) {
+      (itemComp as LayoutMenuItemComp).children.action.act(url);
+      return;
+    }
+    // handle json menu item action
+    if((itemComp as MenuItemNode).action?.newTab) {
+      return window.open((itemComp as MenuItemNode).action?.url, '_blank')
+    }
+    history.push(url);
+  }, [pathParam.applicationId, pathParam.viewMode, dataOptionType, itemKeyRecord])
+
+  const getJsonMenuItem = useCallback(
+    (items: MenuItemNode[]): MenuProps["items"] => {
+      return items?.map((item: MenuItemNode) => {
+        const {
+          label,
+          key,
+          hidden,
+          icon,
+          children,
+        } = item;
+        return {
+          label,
+          key,
+          hidden,
+          icon: <StyledImage src={icon} />,
+          onTitleClick: onMenuItemClick,
+          onClick: onMenuItemClick,
+          ...(children?.length && { children: getJsonMenuItem(children) }),
+        }
+      })
+    }, [onMenuItemClick]
+  )
+
   const getMenuItem = useCallback(
     (itemComps: LayoutMenuItemComp[]): MenuProps["items"] => {
       return itemComps.filter(filterItem).map((item) => {
@@ -81,14 +362,20 @@ NavTmpLayout = withViewFn(NavTmpLayout, (comp) => {
           title: label,
           key: item.getItemKey(),
           icon: <span>{item.children.icon.getView()}</span>,
+          onTitleClick: onMenuItemClick,
+          onClick: onMenuItemClick,
           ...(subItems.length > 0 && { children: getMenuItem(subItems) }),
         };
       });
     },
-    [filterItem]
+    [onMenuItemClick, filterItem]
   );
 
-  const menuItems = useMemo(() => getMenuItem(items), [items, getMenuItem]);
+  const menuItems = useMemo(() => {
+    if(dataOptionType === DataOption.Json) return getJsonMenuItem(jsonItems)
+
+    return getMenuItem(items)
+  }, [dataOptionType, jsonItems, getJsonMenuItem, items, getMenuItem]);
 
   // Find by path itemKey
   const findItemPathByKey = useCallback(
@@ -134,22 +421,60 @@ NavTmpLayout = withViewFn(NavTmpLayout, (comp) => {
     [filterItem]
   );
 
-  const itemKeyRecord = useMemo(() => {
-    const result: Record<string, LayoutMenuItemComp> = {};
-    items.forEach((item) => {
-      const subItems = item.children.items.getView();
-      if (subItems.length > 0) {
-        item.children.items
-          .getView()
-          .forEach((subItem) => (result[subItem.getItemKey()] = subItem));
-      } else {
-        result[item.getItemKey()] = item;
+  // Find by path itemKey
+  const findItemPathByKeyJson = useCallback(
+    (itemComps: MenuItemNode[], itemKey: string): string[] => {
+      for (let item of itemComps) {
+        const subItems = item.children;
+        if (subItems?.length) {
+          // have subMenus
+          const childPath = findItemPathByKeyJson(subItems, itemKey);
+          if (childPath.length > 0) {
+            return [item.key, ...childPath];
+          }
+        } else {
+          if (item.key === itemKey) {
+            return [item.key];
+          }
+        }
+      }
+      return [];
+    },
+    []
+  );
+
+  // Get the first visible menu
+  const findFirstItemPathJson = useCallback(
+    (itemComps: MenuItemNode[]): string[] => {
+      for (let item of itemComps) {
+        if (!item.hidden) {
+          const subItems = item.children;
+          if (subItems?.length) {
+            // have subMenus
+            const childPath = findFirstItemPathJson(subItems);
+            if (childPath.length > 0) {
+              return [item.key, ...childPath];
+            }
+          } else {
+            return [item.key];
+          }
+        }
       }
-    });
-    return result;
-  }, [items]);
+      return [];
+    }, []
+  );
 
   const defaultOpenKeys = useMemo(() => {
+    if(dataOptionType === DataOption.Json) {
+      let itemPath: string[];
+      if (pathParam.appPageId) {
+        itemPath = findItemPathByKeyJson(jsonItems, pathParam.appPageId);
+      } else {
+        itemPath = findFirstItemPathJson(jsonItems);
+      }
+      return itemPath.slice(0, itemPath.length - 1);
+    }
+
     let itemPath: string[];
     if (pathParam.appPageId) {
       itemPath = findItemPathByKey(items, pathParam.appPageId);
@@ -170,34 +495,79 @@ NavTmpLayout = withViewFn(NavTmpLayout, (comp) => {
     setSelectedKey(selectedKey);
   }, [pathParam.appPageId]);
 
-  let pageView = <EmptyContent text="" style={{ height: "100%" }} />;
-  const selectedItem = itemKeyRecord[selectedKey];
-  if (selectedItem && !selectedItem.children.hidden.getView()) {
-    const compView = selectedItem.children.action.getView();
-    if (compView) {
-      pageView = compView;
+  const pageView = useMemo(() => {
+    let pageView = <EmptyContent text="" style={{ height: "100%" }} />;
+    
+    if(dataOptionType === DataOption.Manual) {
+      const selectedItem = (itemKeyRecord[selectedKey] as LayoutMenuItemComp);
+      if (selectedItem && !selectedItem.children.hidden.getView()) {
+        const compView = selectedItem.children.action.getView();
+        if (compView) {
+          pageView = compView;
+        }
+      }
+    }
+    if(dataOptionType === DataOption.Json) {
+      const item = (itemKeyRecord[selectedKey] as MenuItemNode)
+      if(item?.action?.url) {
+        pageView =  <iframe
+          title={item?.action?.url}
+          src={item?.action?.url}
+          width="100%"
+          height="100%"
+          style={{ border: "none", marginBottom: "-6px" }}
+        />
+      }
     }
+    return pageView;
+  }, [dataOptionType, itemKeyRecord, selectedKey])
+
+  const getVerticalMargin = (margin: string[]) => {
+    if(margin.length === 1) return `${margin[0]}`;
+    if(margin.length === 2) return `(${margin[0]} + ${margin[0]})`;
+    if(margin.length === 3 || margin.length === 4)
+      return `(${margin[0]} + ${margin[2]})`;
+
+    return '0px';
+  }
+  const getHorizontalMargin = (margin: string[]) => {
+    if(margin.length === 1) return `(${margin[0]} + ${margin[0]})`;
+    if(margin.length === 2) return `(${margin[1]} + ${margin[1]})`;
+    if(margin.length === 3 || margin.length === 4)
+      return `(${margin[1]} + ${margin[3]})`;
+
+    return '0px';
+  }
+
+  let backgroundStyle = navStyle.background;
+  if(!_.isEmpty(backgroundImage))  {
+    backgroundStyle = `center / cover url('${backgroundImage}') no-repeat, ${backgroundStyle}`;
   }
 
   let content = (
     <Layout>
-      <StyledSide theme="light" width={240}>
-        <AntdMenu
+      <StyledSide theme="light" width={navWidth}>
+        <StyledMenu
           items={menuItems}
-          mode="inline"
-          style={{ height: "100%" }}
+          mode={navMode}
+          style={{
+            height: `calc(100% - ${getVerticalMargin(navStyle.margin.split(' '))})`,
+            width: `calc(100% - ${getHorizontalMargin(navStyle.margin.split(' '))})`,
+            borderRight: `1px solid ${navStyle.border}`,
+            borderRadius: navStyle.radius,
+            color: navStyle.text,
+            margin: navStyle.margin,
+            padding: navStyle.padding,
+            background: backgroundStyle,
+          }}
           defaultOpenKeys={defaultOpenKeys}
           selectedKeys={[selectedKey]}
-          onClick={(e) => {
-            const itemComp = itemKeyRecord[e.key];
-            const url = [
-              ALL_APPLICATIONS_URL,
-              pathParam.applicationId,
-              pathParam.viewMode,
-              itemComp.getItemKey(),
-            ].join("/");
-            itemComp.children.action.act(url);
+          $navItemStyle={{
+            width: `calc(100% - ${getHorizontalMargin(navItemStyle.margin.split(' '))})`,
+            ...navItemStyle,
           }}
+          $navItemHoverStyle={navItemHoverStyle}
+          $navItemActiveStyle={navItemActiveStyle}
         />
       </StyledSide>
       <MainContent>{pageView}</MainContent>
diff --git a/client/packages/lowcoder/src/comps/comps/layout/navLayoutConstants.ts b/client/packages/lowcoder/src/comps/comps/layout/navLayoutConstants.ts
new file mode 100644
index 000000000..e8fc23c0b
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/layout/navLayoutConstants.ts
@@ -0,0 +1,77 @@
+import { trans } from "i18n";
+
+export const ModeOptions = [
+  { label: trans("navLayout.modeInline"), value: "inline" },
+  { label: trans("navLayout.modeVertical"), value: "vertical" },
+] as const;
+
+export const DataOption = {
+  Manual: 'manual',
+  Json: 'json',
+}
+export const DataOptionType = [
+  {
+    label: trans("prop.manual"),
+    value: DataOption.Manual,
+  },
+  {
+    label: trans("prop.json"),
+    value: DataOption.Json,
+  },
+];
+
+export const menuItemStyleOptions = [
+  {
+    value: "normal",
+    label: "Normal",
+  },
+  {
+    value: "hover",
+    label: "Hover",
+  },
+  {
+    value: "active",
+    label: "Active",
+  }
+];
+
+export const jsonMenuItems = [
+  {
+    label: "Menu Item 1",
+    key: 'menu-item-1',
+    icon: "https://cdn-icons-png.flaticon.com/128/149/149338.png",
+    action: {
+      url: "https://www.lowcoder.cloud",
+      newTab: false,
+    },
+    children: [
+      {
+        label: "Submenu Item 1",
+        key: 'submenu-item-11',
+        icon: "",
+        action: {
+          url: "https://www.lowcoder.cloud",
+          newTab: false,
+        },
+      },
+      {
+        label: "Submenu Item 2",
+        key: 'submenu-item-12',
+        icon: "",
+        action: {
+          url: "https://www.lowcoder.cloud",
+          newTab: false,
+        },
+      },
+    ]
+  },
+  {
+    label: "Menu Item 2",
+    key: 'menu-item-2',
+    icon: "https://cdn-icons-png.flaticon.com/128/149/149206.png",
+    action: {
+      url: "https://www.lowcoder.cloud",
+      newTab: true,
+    },
+  }
+]
\ No newline at end of file
diff --git a/client/packages/lowcoder/src/comps/comps/meetingComp/videoMeetingControllerComp.tsx b/client/packages/lowcoder/src/comps/comps/meetingComp/videoMeetingControllerComp.tsx
index 49a5f2cbb..be45aede9 100644
--- a/client/packages/lowcoder/src/comps/comps/meetingComp/videoMeetingControllerComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/meetingComp/videoMeetingControllerComp.tsx
@@ -41,7 +41,7 @@ import { useUserViewMode } from "util/hooks";
 import { isNumeric } from "util/stringUtils";
 import { NameConfig, withExposingConfigs } from "../../generators/withExposing";
 
-import { v4 as uuidv4 } from 'uuid';
+import { v4 as uuidv4 } from "uuid";
 
 // import axios from "axios";
 
@@ -147,6 +147,7 @@ const shareScreen = async (sharing: boolean) => {
   try {
     if (sharing === false) {
       await client.unpublish(screenShareStream);
+      screenShareStream.close();
       await client.publish(videoTrack);
       videoTrack.play(userId + "");
     } else {
@@ -165,11 +166,16 @@ const shareScreen = async (sharing: boolean) => {
   }
 };
 const leaveChannel = async () => {
+  //stops local sharing video
+  screenShareStream.close();
+
+  //stops local video streaming and puts off the camera
   if (videoTrack) {
     await client.unpublish(videoTrack);
     await turnOnCamera(false);
   }
 
+  //mutes and stops locla audio stream
   if (audioTrack) {
     await turnOnMicrophone(false);
   }
@@ -183,12 +189,12 @@ const publishVideo = async (
   rtmToken: string,
   rtcToken: string
 ) => {
-    // initializing the Agora Meeting Client
-    await turnOnCamera(true);
-    await client.join(appId, channel, rtcToken, userId);
-    await client.publish(videoTrack);
-    // initializing the Agora RTM Client
-    await rtmInit(appId, userId, rtmToken, channel);
+  // initializing the Agora Meeting Client
+  await turnOnCamera(true);
+  await client.join(appId, channel, rtcToken, userId);
+  await client.publish(videoTrack);
+  // initializing the Agora RTM Client
+  await rtmInit(appId, userId, rtmToken, channel);
 };
 
 const sendMessageRtm = (message: any) => {
@@ -231,8 +237,14 @@ export const meetingControllerChildren = {
   participants: stateComp<JSONValue>([]),
   usersScreenShared: stateComp<JSONValue>([]),
   localUser: jsonObjectExposingStateControl(""),
-  localUserID : withDefault(stringStateControl(trans("meeting.localUserID")), uuidv4() + ""),
-  meetingName: withDefault(stringStateControl(trans("meeting.meetingName")), uuidv4() + ""),
+  localUserID: withDefault(
+    stringStateControl(trans("meeting.localUserID")),
+    uuidv4() + ""
+  ),
+  meetingName: withDefault(
+    stringStateControl(trans("meeting.meetingName")),
+    uuidv4() + ""
+  ),
   rtmToken: stringStateControl(trans("meeting.rtmToken")),
   rtcToken: stringStateControl(trans("meeting.rtcToken")),
   messages: stateComp<JSONValue>([]),
@@ -265,7 +277,8 @@ let MTComp = (function () {
       });
       const [rtmMessages, setRtmMessages] = useState<any>([]);
       const [localUserSpeaking, setLocalUserSpeaking] = useState<any>(false);
-      const [localUserVideo, setLocalUserVideo] = useState<IAgoraRTCRemoteUser>();
+      const [localUserVideo, setLocalUserVideo] =
+        useState<IAgoraRTCRemoteUser>();
       const [userJoined, setUserJoined] = useState<IAgoraRTCRemoteUser>();
       const [userLeft, setUserLeft] = useState<IAgoraRTCRemoteUser>();
 
@@ -323,6 +336,8 @@ let MTComp = (function () {
         }
       }, [userLeft]);
 
+      console.log("sharing", props.sharing);
+
       useEffect(() => {
         if (updateVolume.userid) {
           let prevUsers: [] = props.participants as [];
@@ -342,6 +357,28 @@ let MTComp = (function () {
         }
       }, [updateVolume]);
 
+      useEffect(() => {
+        let prevUsers: [] = props.participants as [];
+        const updatedItems = prevUsers.map((userInfo: any) => {
+          if (userInfo.user === localUserVideo?.uid) {
+            return { ...userInfo, streamingSharing: props.sharing.value };
+          }
+          return userInfo;
+        });
+        dispatch(
+          changeChildAction("participants", getData(updatedItems).data, false)
+        );
+
+        let localObject = {
+          user: userId + "",
+          audiostatus: props.audioControl.value,
+          streamingVideo: props.videoControl.value,
+          streamingSharing: props.sharing.value,
+          speaking: localUserSpeaking,
+        };
+        props.localUser.onChange(localObject);
+      }, [props.sharing.value]);
+
       useEffect(() => {
         let prevUsers: [] = props.participants as [];
         const updatedItems = prevUsers.map((userInfo: any) => {
@@ -378,10 +415,33 @@ let MTComp = (function () {
       useEffect(() => {
         if (rtmChannelResponse) {
           rtmClient.on("MessageFromPeer", function (message, peerId) {
-            setRtmMessages(message.text);
+            setRtmMessages((prevMessages: any[]) => {
+              // Check if the messages array exceeds the maximum limit
+              if (prevMessages.length >= 500) {
+                prevMessages.pop(); // Remove the oldest message
+              }
+              return [
+                ...prevMessages,
+                { peermessage: JSON.parse(message.text + ""), from: peerId },
+              ];
+            });
           });
+
           rtmChannelResponse.on("ChannelMessage", function (message, memberId) {
-            setRtmMessages(message.text);
+            setRtmMessages((prevMessages: any[]) => {
+              // Check if the messages array exceeds the maximum limit
+              if (prevMessages.length >= 500) {
+                prevMessages.pop(); // Remove the oldest message
+              }
+              return [
+                ...prevMessages,
+                {
+                  channelmessage: JSON.parse(message.text + ""),
+                  from: memberId,
+                },
+              ];
+            });
+
             dispatch(
               changeChildAction("messages", getData(rtmMessages).data, false)
             );
@@ -391,19 +451,24 @@ let MTComp = (function () {
 
       useEffect(() => {
         if (client) {
+          //Enable Agora to send audio bytes
           client.enableAudioVolumeIndicator();
+          //user activity listeners
           client.on("user-joined", (user: IAgoraRTCRemoteUser) => {
             setUserJoined(user);
           });
           client.on("user-left", (user: IAgoraRTCRemoteUser, reason: any) => {
             setUserLeft(user);
           });
+
+          //listen to user speaking,
           client.on("volume-indicator", (volumeInfos: any) => {
-            if (volumeInfos.length == 0) return;
+            if (volumeInfos.length === 0) return;
             volumeInfos.map((volumeInfo: any) => {
+              //when the volume is above 30, user is probably speaking
               const speaking = volumeInfo.level >= 30;
               if (
-                volumeInfo.uid == userId &&
+                volumeInfo.uid === userId &&
                 props.localUser.value.speaking != speaking
               ) {
                 setLocalUserSpeaking(speaking);
@@ -521,8 +586,8 @@ let MTComp = (function () {
           })}
         </Section>
         <Section name={sectionNames.meetings}>
-          {children.appId.propertyView({ 
-            label: trans("meeting.appid") 
+          {children.appId.propertyView({
+            label: trans("meeting.appid"),
           })}
           {children.meetingName.propertyView({
             label: trans("meeting.meetingName"),
@@ -633,7 +698,10 @@ MTComp = withMethodExposing(MTComp, [
     },
     execute: async (comp, values) => {
       if (comp.children.meetingActive.getView().value) return;
-      userId = comp.children.localUserID.getView().value === "" ? uuidv4() : comp.children.localUserID.getView().value;
+      userId =
+        comp.children.localUserID.getView().value === ""
+          ? uuidv4()
+          : comp.children.localUserID.getView().value;
       comp.children.localUser.change({
         user: userId + "",
         audiostatus: false,
@@ -656,7 +724,9 @@ MTComp = withMethodExposing(MTComp, [
       comp.children.videoControl.change(true);
       await publishVideo(
         comp.children.appId.getView(),
-        comp.children.meetingName.getView().value === "" ? uuidv4() : comp.children.meetingName.getView().value, 
+        comp.children.meetingName.getView().value === ""
+          ? uuidv4()
+          : comp.children.meetingName.getView().value,
         comp.children.rtmToken.getView().value,
         comp.children.rtcToken.getView().value
       );
@@ -671,21 +741,20 @@ MTComp = withMethodExposing(MTComp, [
     },
     execute: async (comp, values) => {
       if (!comp.children.meetingActive.getView().value) return;
-      let otherData =
-        values !== undefined && values[1] !== undefined ? values[1] : "";
-      let toUsers: any =
+      let messagedata =
         values !== undefined && values[0] !== undefined ? values[0] : "";
+      let toUsers: any =
+        values !== undefined && values[1] !== undefined ? values[1] : "";
 
       let message: any = {
         time: Date.now(),
-        from: comp.children.localUser.getView().value,
+        message: messagedata,
       };
-      message["data"] = otherData;
 
       if (toUsers.length > 0 && toUsers[0] !== undefined) {
-        let peers = toUsers?.map((u: any) => u.user);
-        peers.forEach((p: any) => {
-          sendPeerMessageRtm(message, String(p));
+        toUsers.forEach((peer: any) => {
+          message.to = peer;
+          sendPeerMessageRtm(message, String(peer));
         });
       } else {
         sendMessageRtm(message);
@@ -778,7 +847,7 @@ export const VideoMeetingControllerComp = withExposingConfigs(MTComp, [
   new NameConfig("meetingActive", trans("meeting.meetingActive")),
   new NameConfig("meetingName", trans("meeting.meetingName")),
   new NameConfig("localUserID", trans("meeting.localUserID")),
-  new NameConfig("messages", trans("meeting.messages")), 
+  new NameConfig("messages", trans("meeting.messages")),
   new NameConfig("rtmToken", trans("meeting.rtmToken")),
   new NameConfig("rtcToken", trans("meeting.rtcToken")),
 ]);
diff --git a/client/packages/lowcoder/src/comps/comps/meetingComp/videoMeetingStreamComp.tsx b/client/packages/lowcoder/src/comps/comps/meetingComp/videoMeetingStreamComp.tsx
index 5fbc372b7..fe868117a 100644
--- a/client/packages/lowcoder/src/comps/comps/meetingComp/videoMeetingStreamComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/meetingComp/videoMeetingStreamComp.tsx
@@ -137,7 +137,6 @@ const typeOptions = [
 
 export const meetingStreamChildren = {
   autoHeight: withDefault(AutoHeightControl, "fixed"),
-  shareScreen: withDefault(BoolShareVideoControl, false),
   profilePadding: withDefault(StringControl, "0px"),
   profileBorderRadius: withDefault(StringControl, "0px"),
   videoAspectRatio: withDefault(StringControl, "1 / 1"),
@@ -236,6 +235,9 @@ let VideoCompBuilder = (function (props) {
       }
     }, [props.userId.value]);
 
+    console.log(props.userId);
+    
+
     return (
       <EditorContext.Consumer>
         {(editorState) => (
@@ -264,7 +266,7 @@ let VideoCompBuilder = (function (props) {
                     borderRadius: props.style.radius,
                     width: "auto",
                   }}
-                  id={props.shareScreen ? "share-screen" : userId}
+                  id={userId}
                 ></VideoContainer>
               ) : (
                 <></>
@@ -300,9 +302,6 @@ let VideoCompBuilder = (function (props) {
         <Section name={sectionNames.basic}>
           {children.userId.propertyView({ label: trans("meeting.videoId") })}
           {children.autoHeight.getPropertyView()}
-          {children.shareScreen.propertyView({
-            label: trans("meeting.shareScreen"),
-          })}
           {children.profileImageUrl.propertyView({
             label: trans("meeting.profileImageUrl"),
             placeholder: "https://via.placeholder.com/120",
diff --git a/client/packages/lowcoder/src/comps/comps/meetingComp/videoSharingStreamComp.tsx b/client/packages/lowcoder/src/comps/comps/meetingComp/videoSharingStreamComp.tsx
new file mode 100644
index 000000000..424d04853
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/meetingComp/videoSharingStreamComp.tsx
@@ -0,0 +1,345 @@
+import { BoolCodeControl } from "comps/controls/codeControl";
+import { dropdownControl } from "comps/controls/dropdownControl";
+// import { IconControl } from "comps/controls/iconControl";
+import { CompNameContext, EditorContext, EditorState } from "comps/editorState";
+import { withDefault } from "comps/generators";
+import { UICompBuilder } from "comps/generators/uiCompBuilder";
+import ReactResizeDetector from "react-resize-detector";
+// import _ from "lodash";
+import {
+  CommonBlueLabel,
+  controlItem,
+  Dropdown,
+  Section,
+  sectionNames,
+} from "lowcoder-design";
+import { trans } from "i18n";
+
+import styled, { css } from "styled-components";
+import {
+  CommonNameConfig,
+  NameConfig,
+  withExposingConfigs,
+} from "../../generators/withExposing";
+import { IForm } from "../formComp/formDataConstants";
+import { SimpleNameComp } from "../simpleNameComp";
+import { ButtonStyleControl } from "./videobuttonCompConstants";
+import { RefControl } from "comps/controls/refControl";
+import { useEffect, useRef, useState } from "react";
+
+import { AutoHeightControl } from "comps/controls/autoHeightControl";
+import { client } from "./videoMeetingControllerComp";
+
+import { IAgoraRTCRemoteUser } from "agora-rtc-sdk-ng";
+
+import {
+  MeetingEventHandlerControl,
+  StringControl,
+  StringStateControl,
+  hiddenPropertyView,
+  stringExposingStateControl,
+} from "@lowcoder-ee/index.sdk";
+import { BoolShareVideoControl } from "./meetingControlerUtils";
+
+const FormLabel = styled(CommonBlueLabel)`
+  font-size: 13px;
+  margin-right: 4px;
+`;
+
+function getFormOptions(editorState: EditorState) {
+  return editorState
+    .uiCompInfoList()
+    .filter((info) => info.type === "form")
+    .map((info) => ({
+      label: info.name,
+      value: info.name,
+    }));
+}
+
+const VideoContainer = styled.video`
+  height: 100%;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+`;
+
+function getForm(editorState: EditorState, formName: string) {
+  const comp = editorState?.getUICompByName(formName);
+  if (comp && comp.children.compType.getView() === "form") {
+    return comp.children.comp as unknown as IForm;
+  }
+}
+
+function getFormEventHandlerPropertyView(
+  editorState: EditorState,
+  formName: string
+) {
+  const form = getForm(editorState, formName);
+  if (!form) {
+    return undefined;
+  }
+
+  return (
+    <CompNameContext.Provider value={formName}>
+      {form.onEventPropertyView(
+        <>
+          <FormLabel
+            onClick={() =>
+              editorState.setSelectedCompNames(
+                new Set([formName]),
+                "rightPanel"
+              )
+            }
+          >
+            {formName}
+          </FormLabel>
+          {trans("button.formButtonEvent")}
+        </>
+      )}
+    </CompNameContext.Provider>
+  );
+}
+
+class SelectFormControl extends SimpleNameComp {
+  override getPropertyView() {
+    const label = trans("button.formToSubmit");
+    return controlItem(
+      { filterText: label },
+      <EditorContext.Consumer>
+        {(editorState) => (
+          <>
+            <Dropdown
+              label={label}
+              value={this.value}
+              options={getFormOptions(editorState)}
+              onChange={(value) => this.dispatchChangeValueAction(value)}
+              allowClear={true}
+            />
+            {getFormEventHandlerPropertyView(editorState, this.value)}
+          </>
+        )}
+      </EditorContext.Consumer>
+    );
+  }
+}
+
+const typeOptions = [
+  {
+    label: trans("button.default"),
+    value: "",
+  },
+  {
+    label: trans("button.submit"),
+    value: "submit",
+  },
+] as const;
+
+export const meetingStreamChildren = {
+  autoHeight: withDefault(AutoHeightControl, "fixed"),
+  profilePadding: withDefault(StringControl, "0px"),
+  profileBorderRadius: withDefault(StringControl, "0px"),
+  videoAspectRatio: withDefault(StringControl, "1 / 1"),
+  type: dropdownControl(typeOptions, ""),
+  onEvent: MeetingEventHandlerControl,
+  disabled: BoolCodeControl,
+  loading: BoolCodeControl,
+  form: SelectFormControl,
+  // prefixIcon: IconControl,
+  // suffixIcon: IconControl,
+  style: ButtonStyleControl,
+  viewRef: RefControl<HTMLElement>,
+  userId: stringExposingStateControl(""),
+  profileImageUrl: withDefault(
+    StringStateControl,
+    "https://api.dicebear.com/7.x/fun-emoji/svg?seed=Peanut&radius=50&backgroundColor=transparent&randomizeIds=true&eyes=wink,sleepClose"
+  ),
+  noVideoText: stringExposingStateControl("No Video"),
+};
+
+let SharingCompBuilder = (function (props) {
+  return new UICompBuilder(meetingStreamChildren, (props) => {
+    const videoRef = useRef<HTMLVideoElement>(null);
+    const conRef = useRef<HTMLDivElement>(null);
+    const [userId, setUserId] = useState();
+    const [userName, setUsername] = useState("");
+    const [showVideoSharing, setVideoSharing] = useState(true);
+
+    useEffect(() => {
+      if (props.userId.value !== "") {
+        let userData = JSON.parse(props.userId?.value);
+        client.on(
+          "user-published",
+          async (user: IAgoraRTCRemoteUser, mediaType: "video" | "audio") => {
+            if (mediaType === "video") {
+              const remoteTrack = await client.subscribe(user, mediaType);
+              let userId = user.uid + "";
+              if (
+                user.hasVideo &&
+                user.uid + "" !== userData.user &&
+                userData.user !== ""
+              ) {
+                props.onEvent("videoOn");
+              }
+              const element = document.getElementById(userId);
+
+              if (element) {
+                remoteTrack.play(userId);
+              }
+            }
+            if (mediaType === "audio") {
+              const remoteTrack = await client.subscribe(user, mediaType);
+              if (
+                user.hasAudio &&
+                user.uid + "" !== userData.user &&
+                userData.user !== ""
+              ) {
+                userData.audiostatus = user.hasVideo;
+
+                props.onEvent("audioUnmuted");
+              }
+              remoteTrack.play();
+            }
+          }
+        );
+        client.on(
+          "user-unpublished",
+          (user: IAgoraRTCRemoteUser, mediaType: "video" | "audio") => {
+            console.log("user-unpublished");
+
+            if (mediaType === "audio") {
+              if (
+                !user.hasAudio &&
+                user.uid + "" !== userData.user &&
+                userData.user !== ""
+              ) {
+                userData.audiostatus = user.hasVideo;
+                props.onEvent("audioMuted");
+              }
+            }
+            if (mediaType === "video") {
+              if (videoRef.current && videoRef.current?.id === user.uid + "") {
+                videoRef.current.srcObject = null;
+              }
+              if (
+                !user.hasVideo &&
+                user.uid + "" !== userData.user &&
+                userData.user !== ""
+              ) {
+                props.onEvent("videoOff");
+              }
+            }
+          }
+        );
+
+        setUserId(userData.user);
+        setUsername(userData.userName);
+        setVideoSharing(userData.streamingSharing);
+      }
+    }, [props.userId.value]);
+
+    return (
+      <EditorContext.Consumer>
+        {(editorState) => (
+          <ReactResizeDetector>
+            <div
+              ref={conRef}
+              style={{
+                display: "flex",
+                alignItems: "center",
+                height: "100%",
+                overflow: "hidden",
+                borderRadius: props.style.radius,
+                aspectRatio: props.videoAspectRatio,
+                backgroundColor: props.style.background,
+                padding: props.style.padding,
+                margin: props.style.margin,
+              }}
+            >
+              {userId ? (
+                <VideoContainer
+                  onClick={() => props.onEvent("videoClicked")}
+                  ref={videoRef}
+                  style={{
+                    display: `${showVideoSharing ? "flex" : "none"}`,
+                    aspectRatio: props.videoAspectRatio,
+                    borderRadius: props.style.radius,
+                    width: "auto",
+                  }}
+                  id="share-screen"
+                ></VideoContainer>
+              ) : (
+                <></>
+              )}
+              <div
+                style={{
+                  flexDirection: "column",
+                  alignItems: "center",
+                  display: `${!showVideoSharing || userId ? "flex" : "none"}`,
+                  margin: "0 auto",
+                  padding: props.profilePadding,
+                }}
+              >
+                <img
+                  alt=""
+                  style={{
+                    borderRadius: props.profileBorderRadius,
+                    width: "100%",
+                    overflow: "hidden",
+                  }}
+                  src={props.profileImageUrl.value}
+                />
+                <p style={{ margin: "0" }}>{userName ?? ""}</p>
+              </div>
+            </div>
+          </ReactResizeDetector>
+        )}
+      </EditorContext.Consumer>
+    );
+  })
+    .setPropertyViewFn((children) => (
+      <>
+        <Section name={sectionNames.basic}>
+          {children.userId.propertyView({ label: trans("meeting.videoId") })}
+          {children.autoHeight.getPropertyView()}
+          {children.profileImageUrl.propertyView({
+            label: trans("meeting.profileImageUrl"),
+            placeholder: "https://via.placeholder.com/120",
+          })}
+        </Section>
+
+        <Section name={sectionNames.interaction}>
+          {children.onEvent.getPropertyView()}
+        </Section>
+        <Section name={sectionNames.layout}>
+          {hiddenPropertyView(children)}
+        </Section>
+        <Section name={sectionNames.style}>
+          {children.profilePadding.propertyView({
+            label: "Profile Image Padding",
+          })}
+          {children.profileBorderRadius.propertyView({
+            label: "Profile Image Border Radius",
+          })}
+          {children.videoAspectRatio.propertyView({
+            label: "Video Aspect Ratio",
+          })}
+          {children.style.getPropertyView()}
+        </Section>
+      </>
+    ))
+    .build();
+})();
+
+SharingCompBuilder = class extends SharingCompBuilder {
+  override autoHeight(): boolean {
+    return this.children.autoHeight.getView();
+  }
+};
+
+export const VideoSharingStreamComp = withExposingConfigs(SharingCompBuilder, [
+  new NameConfig("loading", trans("button.loadingDesc")),
+  new NameConfig("profileImageUrl", trans("meeting.profileImageUrl")),
+
+  ...CommonNameConfig,
+]);
diff --git a/client/packages/lowcoder/src/comps/comps/navComp/components/DroppableMenuItem.tsx b/client/packages/lowcoder/src/comps/comps/navComp/components/DroppableMenuItem.tsx
index 44c342101..c4f22191a 100644
--- a/client/packages/lowcoder/src/comps/comps/navComp/components/DroppableMenuItem.tsx
+++ b/client/packages/lowcoder/src/comps/comps/navComp/components/DroppableMenuItem.tsx
@@ -1,10 +1,12 @@
 import { useDraggable, useDroppable } from "@dnd-kit/core";
 import { trans } from "i18n";
-import { Fragment } from "react";
+import { Fragment, useEffect } from "react";
 import styled from "styled-components";
 import DroppablePlaceholder from "./DroppablePlaceHolder";
 import MenuItem, { ICommonItemProps } from "./MenuItem";
 import { IDragData, IDropData } from "./types";
+import { LayoutMenuItemComp } from "comps/comps/layout/layoutMenuItemComp";
+import { genRandomKey } from "comps/utils/idGenerator";
 
 const DraggableMenuItemWrapper = styled.div`
   position: relative;
@@ -63,6 +65,22 @@ export default function DraggableMenuItem(props: IDraggableMenuItemProps) {
     disabled: isDragging || disabled || disableDropIn,
     data: dropData,
   });
+
+  // TODO: Remove this later.
+  // Set ItemKey for previously added sub-menus
+  useEffect(() => {
+    if(!items.length) return;
+    if(!(items[0] instanceof LayoutMenuItemComp)) return;
+
+    return items.forEach(item  => {
+      const subItem = item as LayoutMenuItemComp;
+      const itemKey = subItem.children.itemKey.getView();
+      if(itemKey === '') {
+        subItem.children.itemKey.dispatchChangeValueAction(genRandomKey())
+      }
+    })
+  }, [items])
+
   return (
     <>
       <DraggableMenuItemWrapper>
@@ -99,7 +117,7 @@ export default function DraggableMenuItem(props: IDraggableMenuItemProps) {
                 item={subItem}
                 level={0}
                 disabled={disabled || isDragging || disableDropIn}
-                // onAddSubMenu={onAddSubMenu}
+                onAddSubMenu={onAddSubMenu}
                 onDelete={onDelete}
                 parentDragging={isDragging}
               />
diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
index 0344f21c9..58b706771 100644
--- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
+++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx
@@ -821,7 +821,7 @@ export const TreeStyle = [
 
 export const TreeSelectStyle = [...multiSelectCommon, ...ACCENT_VALIDATE] as const;
 
-export const DrawerStyle = [getBackground()] as const;
+export const DrawerStyle = [getBackground()] as const
 
 export const JsonEditorStyle = [LABEL] as const;
 
@@ -928,6 +928,59 @@ export const ResponsiveLayoutColStyle = [
   PADDING,
 ] as const;
 
+export const NavLayoutStyle = [
+  ...getBgBorderRadiusByBg(),
+  {
+    name: "text",
+    label: trans("text"),
+    depName: "background",
+    // depTheme: "primary",
+    depType: DEP_TYPE.CONTRAST_TEXT,
+    transformer: contrastText,
+  },
+  MARGIN,
+  PADDING,
+] as const;
+
+export const NavLayoutItemStyle = [
+  getBackground("primarySurface"),
+  getStaticBorder('transparent'),
+  RADIUS,
+  {
+    name: "text",
+    label: trans("text"),
+    depName: "background",
+    depType: DEP_TYPE.CONTRAST_TEXT,
+    transformer: contrastText,
+  },
+  MARGIN,
+  PADDING,
+] as const;
+
+export const NavLayoutItemHoverStyle = [
+  getBackground("canvas"),
+  getStaticBorder('transparent'),
+  {
+    name: "text",
+    label: trans("text"),
+    depName: "background",
+    depType: DEP_TYPE.CONTRAST_TEXT,
+    transformer: contrastText,
+  },
+] as const;
+
+export const NavLayoutItemActiveStyle = [
+  getBackground("primary"),
+  getStaticBorder('transparent'),
+  {
+    name: "text",
+    label: trans("text"),
+    depName: "background",
+    depType: DEP_TYPE.CONTRAST_TEXT,
+    transformer: contrastText,
+  },
+] as const;
+
 export const CarouselStyle = [getBackground("canvas")] as const;
 
 export const RichTextEditorStyle = [getStaticBorder(), RADIUS] as const;
@@ -968,6 +1021,10 @@ export type CarouselStyleType = StyleConfigType<typeof CarouselStyle>;
 export type RichTextEditorStyleType = StyleConfigType<typeof RichTextEditorStyle>;
 export type ResponsiveLayoutRowStyleType = StyleConfigType<typeof ResponsiveLayoutRowStyle>;
 export type ResponsiveLayoutColStyleType = StyleConfigType<typeof ResponsiveLayoutColStyle>;
+export type NavLayoutStyleType = StyleConfigType<typeof NavLayoutStyle>;
+export type NavLayoutItemStyleType = StyleConfigType<typeof NavLayoutItemStyle>;
+export type NavLayoutItemHoverStyleType = StyleConfigType<typeof NavLayoutItemHoverStyle>;
+export type NavLayoutItemActiveStyleType = StyleConfigType<typeof NavLayoutItemActiveStyle>;
 
 export function widthCalculator(margin: string) {
   const marginArr = margin?.trim().replace(/\s+/g,' ').split(" ") || "";
diff --git a/client/packages/lowcoder/src/comps/index.tsx b/client/packages/lowcoder/src/comps/index.tsx
index 8aa9386b7..a14e4e682 100644
--- a/client/packages/lowcoder/src/comps/index.tsx
+++ b/client/packages/lowcoder/src/comps/index.tsx
@@ -142,6 +142,7 @@ import { ResponsiveLayoutComp } from "./comps/responsiveLayout";
 import { VideoMeetingStreamComp } from "./comps/meetingComp/videoMeetingStreamComp";
 import { ControlButton } from "./comps/meetingComp/controlButton";
 import { VideoMeetingControllerComp } from "./comps/meetingComp/videoMeetingControllerComp";
+import { VideoSharingStreamComp } from "./comps/meetingComp/videoSharingStreamComp";
 
 type Registry = {
   [key in UICompType]?: UICompManifest;
@@ -560,7 +561,17 @@ const uiCompMap: Registry = {
     },
     defaultDataFn: defaultContainerData,
   },
-
+  //ADDED BY FRED
+  sharingcomponent: {
+    name: trans("meeting.sharingCompName"),
+    enName: "Sharing",
+    description: trans("meeting.sharingCompName"),
+    categories: ["meeting"],
+    icon: VideoCompIcon,
+    keywords: trans("meeting.meetingCompKeywords"),
+    comp: VideoSharingStreamComp,
+    withoutLoading: true,
+  },
   videocomponent: {
     name: trans("meeting.videoCompName"),
     enName: "Video",
@@ -581,6 +592,7 @@ const uiCompMap: Registry = {
     comp: ControlButton,
     withoutLoading: true,
   },
+  //END
   tabbedContainer: {
     name: trans("uiComp.tabbedContainerCompName"),
     enName: "Tabbed Container",
@@ -931,7 +943,7 @@ const uiCompMap: Registry = {
     layoutInfo: {
       w: 13,
       h: 55,
-    }
+    },
   },
   mention: {
     name: trans("uiComp.mentionCompName"),
diff --git a/client/packages/lowcoder/src/comps/uiCompRegistry.ts b/client/packages/lowcoder/src/comps/uiCompRegistry.ts
index c48be999e..54d0f5d2c 100644
--- a/client/packages/lowcoder/src/comps/uiCompRegistry.ts
+++ b/client/packages/lowcoder/src/comps/uiCompRegistry.ts
@@ -58,6 +58,7 @@ export type UICompType =
   | "chart"
   | "meeting"
   | "videocomponent"
+  | "sharingcomponent"
   | "controlButton"
   | "imageEditor"
   | "calendar"
diff --git a/client/packages/lowcoder/src/comps/utils/compDocUtil.ts b/client/packages/lowcoder/src/comps/utils/compDocUtil.ts
index 0f82307f4..edb1bb42b 100644
--- a/client/packages/lowcoder/src/comps/utils/compDocUtil.ts
+++ b/client/packages/lowcoder/src/comps/utils/compDocUtil.ts
@@ -12,3 +12,14 @@ export function getComponentDocUrl(compType: UICompType) {
       return trans("docUrls.components", { compType });
   }
 }
+export function getComponentPlaygroundUrl(compType: UICompType) {
+  if (!compType) {
+    return "";
+  }
+  switch (compType) {
+    case "module":
+      return trans("docUrls.module");
+    default:
+      return trans("playground.url", { compType });
+  }
+}
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index 40138fb17..73756da19 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -177,6 +177,7 @@ export const en = {
       "If the result is non-empty string, it is an error message. If empty or null, the validation passes. Example: ",
     manual: "Manual",
     map: "Mapped",
+    json: "JSON",
     use12Hours: "Use 12-hours",
     hourStep: "Hour step",
     minuteStep: "Minute step",
@@ -318,6 +319,7 @@ export const en = {
     validate: "Validation message",
     border: "Border",
     borderRadius: "Border radius",
+    borderwidth: "Border width",
     background: "Background",
     headerBackground: "Header background",
     footerBackground: "Footer background",
@@ -859,6 +861,7 @@ export const en = {
     audioCompDesc: "Audio component",
     audioCompKeywords: "",
     videoCompName: "Video",
+    sharingCompName: "Sharing",
     videoCompDesc: "Video component",
     videoCompKeywords: "",
     drawerCompName: "Drawer",
@@ -905,6 +908,7 @@ export const en = {
   },
   comp: {
     menuViewDocs: "View documentation",
+    menuViewPlayground: "View playground",
     menuUpgradeToLatest: "Upgrade to latest version",
     nameNotEmpty: "Can not be empty",
     nameRegex:
@@ -1463,8 +1467,8 @@ export const en = {
     meetingName: "Meeting Name",
     localUserID: "Host User Id",
     userName: "Host User Name",
-    rtmToken : "Agora RTM Token",
-    rtcToken : "Agora RTC Token",
+    rtmToken: "Agora RTM Token",
+    rtcToken: "Agora RTC Token",
     videoCompText: "No video Text",
     profileImageUrl: "Profile Image Url",
     right: "Right",
@@ -1481,18 +1485,21 @@ export const en = {
     actionBtnDesc: "Action Button",
     broadCast: "BroadCast Messages",
     title: "Meeting Title",
-    meetingCompName: "Meeting Controller",
-    videoCompName: "Video Stream",
-    videoSharingCompName: "Screen Sharing",
-    meetingControlCompName: "Controls Buttons",
-    meetingCompDesc: "Meeting component",
-    meetingCompControls: "Meeting control",
-    meetingCompKeywords: "",
+    //ADDED BY FRED
+    meetingCompName: "Agora Meeting Controller",
+    sharingCompName: "Screen share Stream",
+    videoCompName: "Camera Stream",
+    videoSharingCompName: "Screen share Stream",
+    meetingControlCompName: "Control Button",
+    meetingCompDesc: "Meeting Component",
+    meetingCompControls: "Meeting Control",
+    meetingCompKeywords: "Agora Meeting, Web Meeting, Collaboration",
+    //END
     iconSize: "Icon Size",
     userId: "userId",
     roomId: "roomId",
-    meetingActive :  "Ongoing Meeting",
-    messages : "Broadcasted Messages",
+    meetingActive: "Ongoing Meeting",
+    messages: "Broadcasted Messages",
   },
   settings: {
     title: "Settings",
@@ -1841,6 +1848,8 @@ export const en = {
     preloadLibsEmpty: "No JavaScript libraries were added",
     preloadLibsAddBtn: "Add a library",
     saveSuccess: "Saved successfully",
+    AuthOrgTitle: "Workspace welcome Screen",
+    AuthOrgDescrition: "The URL for your users to Sign in to the current workspace.",
   },
   branding: {
     title: "Branding",
@@ -2272,13 +2281,13 @@ export const en = {
   },
   docUrls: {
     docHome: "https://docs.lowcoder.cloud/",
-    components: "https://app.lowcoder.cloud/components?n={compType}",
-    module: "",
+    components: "https://app.lowcoder.cloud/components/{compType}",
+    module: "https://docs.lowcoder.cloud/lowcoder-documentation/build-applications/create-a-new-app/modules",
     optionList: "",
-    terms: "",
-    privacy: "",
-    aboutUs: "",
-    changeLog: "",
+    terms: "https://lowcoder.cloud/terms",
+    privacy: "https://lowcoder.cloud/privacy",
+    aboutUs: "https://lowcoder.cloud/about",
+    changeLog: "https://github.com/lowcoder-org/lowcoder/releases",
     introVideo: "",
     devNpmPlugin:
       "https://docs.lowcoder.cloud/lowcoder-extension/develop-data-source-plugins",
@@ -2535,6 +2544,7 @@ export const en = {
     justify: "Justify both ends",
   },
   playground: {
+    url: "https://app.lowcoder.cloud/playground/{compType}/1",
     data: "Data",
     preview: "Preview",
     property: "Properties",
@@ -2710,4 +2720,13 @@ export const en = {
     rowLayout: "Row Layout",
     columnsLayout: "Columns Layout",
   },
+  navLayout: {
+    mode: "Mode",
+    modeInline: "Inline",
+    modeVertical: "Vertical",
+    width: "Width",
+    widthTooltip: "Number or percentage, e.g. 520, 60%",
+    navStyle: "Menu Style",
+    navItemStyle: "Menu Item Style",
+  }
 };
diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts
index 53b922a9e..6b97f4805 100644
--- a/client/packages/lowcoder/src/i18n/locales/zh.ts
+++ b/client/packages/lowcoder/src/i18n/locales/zh.ts
@@ -167,6 +167,7 @@ prop: {
     customRuleTooltip: "如果结果是非空字符串,则为错误消息.如果为空或null,则验证通过.\n示例:",
     manual: "手动",
     map: "映射",
+    json: "JSON",
     use12Hours: "使用12小时制",
     hourStep: "小时步长",
     minuteStep: "分钟步长",
@@ -852,6 +853,7 @@ uiComp: {
 },
 comp: {
     menuViewDocs: "查看文档",
+    menuViewPlayground: "查看组件游乐场",
     menuUpgradeToLatest: "升级到最新版本",
     nameNotEmpty: "不能为空",
     nameRegex: "必须以字母开头,只能包含字母、数字和下划线(_)",
@@ -2106,7 +2108,7 @@ toggleButton: {
 },
 docUrls: {
     docHome: "https://docs.lowcoder.cloud/",
-    components: "https://app.lowcoder.cloud/components?n={compType}",
+    components: "https://app.lowcoder.cloud/components/{compType}",
     module: "",
     optionList: "",
     terms: "",
@@ -2391,6 +2393,7 @@ componentDoc: {
     justify: "两端对齐",
 },
 playground: {
+    url: "https://app.lowcoder.cloud/playground/{compType}/1",
     data: "数据",
     preview: "预览",
     property: "属性",
@@ -2557,6 +2560,15 @@ timeLine: {
         matchColumnsHeight: "匹配列高度",
         rowLayout: "行布局",
         columnsLayout: "栏目布局",
+    },
+    navLayout: {
+        mode: "模式",
+        modeInline: "排队",
+        modeVertical: "垂直的",
+        width: "宽度",
+        widthTooltip: "数字或百分比,例如 520,60%",
+        navStyle: "菜单风格",
+        navItemStyle: "菜单项样式",
     }
 };
 
diff --git a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
index 8baf2944d..2820e14c9 100644
--- a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
+++ b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
@@ -88,6 +88,7 @@ export const CompStateIcon: {
   meeting: <LeftMeeting />,
   mermaid: <LeftChart />,
   videocomponent: <LeftMeeting />,
+  sharingcomponent: <LeftMeeting />,
   controlButton: <LeftButton />,
   tabbedContainer: <LeftContainer />,
   modal: <LeftModal />,
diff --git a/client/packages/lowcoder/src/pages/setting/idSource/idSourceConstants.ts b/client/packages/lowcoder/src/pages/setting/idSource/idSourceConstants.ts
index d4b09e173..d7e0dcbe2 100644
--- a/client/packages/lowcoder/src/pages/setting/idSource/idSourceConstants.ts
+++ b/client/packages/lowcoder/src/pages/setting/idSource/idSourceConstants.ts
@@ -48,7 +48,7 @@ export const authConfig = {
     sourceValue: AuthType.Ory,
     form: {
       ...clientIdandSecretConfig,
-      instanceId: "Instance ID",
+      baseUrl: "Base URL",
       scope: "Scope",
     },
   },
@@ -57,7 +57,7 @@ export const authConfig = {
     sourceValue: AuthType.KeyCloak,
     form: {
       ...clientIdandSecretConfig,
-      instanceId: "Instance ID",
+      baseUrl: "Base URL",
       realm: "Realm",
       scope: "Scope",
     },
@@ -107,5 +107,7 @@ export type FormItemType = {
   authServerId?: string;
   publicKey?: ItemType;
   domain?: string;
+  baseUrl?: string;
   realm?: string;
+  scope?: string;
 };
diff --git a/client/packages/lowcoder/src/pages/setting/idSource/list.tsx b/client/packages/lowcoder/src/pages/setting/idSource/list.tsx
index 62f121ac0..353fba1a4 100644
--- a/client/packages/lowcoder/src/pages/setting/idSource/list.tsx
+++ b/client/packages/lowcoder/src/pages/setting/idSource/list.tsx
@@ -34,6 +34,7 @@ import { messageInstance, AddIcon } from "lowcoder-design";
 import { currentOrgAdmin } from "../../../util/permissionUtils";
 import CreateModal from "./createModal";
 import _ from "lodash";
+import { HelpText } from "components/HelpText";
 
 export const IdSourceList = (props: any) => {
   const user = useSelector(getUser);
@@ -44,6 +45,18 @@ export const IdSourceList = (props: any) => {
   const [modalVisible, setModalVisible] = useState(false);
   const enableEnterpriseLogin = useSelector(selectSystemConfig)?.featureFlag?.enableEnterpriseLogin;
 
+  let protocol = window.location.protocol;
+  const port = window.location.port;
+  let currentDomain = window.location.hostname;
+
+  // Show port only if it is not a standard port
+  if (port && port !== '80' && port !== '443') {
+    currentDomain += `:${port}`;
+  }
+
+  const redirectUrl = encodeURIComponent(`${protocol}//${currentDomain}/apps`);
+  const loginUrl = `${protocol}//${currentDomain}/org/${currentOrgId}/auth/login?redirectUrl=${encodeURIComponent(redirectUrl)}`;
+
   useEffect(() => {
     if (!currentOrgId) {
       return;
@@ -154,6 +167,11 @@ export const IdSourceList = (props: any) => {
             )}
           />
         </TableStyled>
+
+        <div style={{ marginTop: 20, marginLeft: 12 }} className="section-title">{trans("advanced.AuthOrgTitle")}</div>
+        <HelpText style={{ marginBottom: 12, marginLeft: 12 }}>{trans("advanced.AuthOrgDescrition") + ": "}</HelpText>
+        <HelpText style={{ marginBottom: 12, marginLeft: 12 }}><a href={loginUrl} target="blank">{loginUrl}</a></HelpText> 
+
       </Level1SettingPageContentWithList>
       <CreateModal
         modalVisible={modalVisible}
@@ -167,3 +185,4 @@ export const IdSourceList = (props: any) => {
     </>
   );
 };
+
diff --git a/deploy/docker/Dockerfile b/deploy/docker/Dockerfile
index b638e62c8..c618771a9 100644
--- a/deploy/docker/Dockerfile
+++ b/deploy/docker/Dockerfile
@@ -66,12 +66,17 @@ CMD [ "sh" , "/lowcoder/api-service/entrypoint.sh" ]
 ##
 FROM ubuntu:jammy as build-node-service
 
-RUN apt update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y curl ca-certificates build-essential
+RUN apt update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y curl ca-certificates build-essential gnupg
+
+# Add nodejs repo and keys
+RUN mkdir -p /etc/apt/keyrings \
+  && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
+  && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
 
 # Download nodejs and install yarn
-RUN curl -sL https://deb.nodesource.com/setup_19.x | bash - \
-&& apt-get install --no-install-recommends -y nodejs \
-&& npm install -g yarn
+RUN apt-get update \
+  && apt-get install --no-install-recommends -y nodejs \
+  && npm install -g yarn
 
 # Copy and build the node-service app
 COPY server/node-service/ /lowcoder/node-service/app/
@@ -93,9 +98,16 @@ RUN chmod +x /lowcoder/node-service/*.sh
 FROM ubuntu:jammy as lowcoder-ce-node-service
 LABEL maintainer="lowcoder"
 
-RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y curl ca-certificates \
-  && curl -sL https://deb.nodesource.com/setup_19.x | bash - \
-  && apt-get install --no-install-recommends -y nodejs gosu \
+RUN apt update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y curl ca-certificates gnupg
+
+# Add nodejs repo and keys
+RUN mkdir -p /etc/apt/keyrings \
+  && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
+  && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
+
+# Download nodejs and install yarn
+RUN apt-get update \
+  && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y nodejs gosu \
   && npm install -g yarn \
   && rm -rf /var/cache/apt/lists \
   && addgroup --system --gid 9001 lowcoder \
@@ -167,13 +179,20 @@ EXPOSE 3443
 FROM lowcoder-ce-frontend
 LABEL maintainer="lowcoder"
 
+RUN apt update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y curl ca-certificates gnupg
+
+# Add nodejs repo and keys
+RUN mkdir -p /etc/apt/keyrings \
+  && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
+  && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
+
+
 # Install required packages
 RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y bash gnupg curl lsb-release \
   && curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg \
   && echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb bullseye main" | tee /etc/apt/sources.list.d/redis.list \
   && curl -fsSL https://www.mongodb.org/static/pgp/server-4.4.asc | gpg --dearmor -o /usr/share/keyrings/mongodb-archive-keyring.gpg \
   && echo "deb [signed-by=/usr/share/keyrings/mongodb-archive-keyring.gpg arch=amd64,arm64] http://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-4.4.list \
-  && curl -sL https://deb.nodesource.com/setup_19.x | bash - \
   && if [ "$(dpkg --print-architecture)" = "amd64" ] || [ "$(dpkg --print-architecture)" = "i386" ]; then \
     curl -sL http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_$(dpkg --print-architecture).deb --output libssl1.1_1.1.1f-1ubuntu2_$(dpkg --print-architecture).deb; \
   else \
diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2KeycloakAuthConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2KeycloakAuthConfig.java
index a6395f972..33e718d58 100644
--- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2KeycloakAuthConfig.java
+++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2KeycloakAuthConfig.java
@@ -1,7 +1,8 @@
 package org.lowcoder.sdk.auth;
 
-import static org.lowcoder.sdk.auth.constants.Oauth2Constants.INSTANCE_ID_PLACEHOLDER;
+import static org.lowcoder.sdk.auth.constants.Oauth2Constants.BASE_URL_PLACEHOLDER;
 import static org.lowcoder.sdk.auth.constants.Oauth2Constants.REALM_PLACEHOLDER;
+import static org.lowcoder.sdk.auth.constants.Oauth2Constants.SCOPE_PLACEHOLDER;
 
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
@@ -15,8 +16,9 @@
 @Getter
 public class Oauth2KeycloakAuthConfig extends Oauth2SimpleAuthConfig 
 {
-	protected String instanceId;
+	protected String baseUrl;
 	protected String realm;
+	protected String scope;
 	
 	@JsonCreator
 	public Oauth2KeycloakAuthConfig(
@@ -27,13 +29,15 @@ public Oauth2KeycloakAuthConfig(
 			@JsonProperty("sourceName") String sourceName,
 			@JsonProperty("clientId") String clientId, 
 			@JsonProperty("clientSecret") String clientSecret,
-			@JsonProperty("instanceId") String instanceId,
+			@JsonProperty("baseUrl") String baseUrl,
 			@JsonProperty("realm") String realm,
+			@JsonProperty("scope") String scope,
 			@JsonProperty("authType") String authType) 
 	{
 		super(id, enable, enableRegister, source, sourceName, clientId, clientSecret, authType);
-		this.instanceId = instanceId;
+		this.baseUrl = baseUrl;
 		this.realm = realm;
+		this.scope = scope;
 	}
 
 
@@ -42,8 +46,9 @@ public Oauth2KeycloakAuthConfig(
 	public String replaceAuthUrlClientIdPlaceholder(String url) 
 	{
 		return super.replaceAuthUrlClientIdPlaceholder(url)
-				.replace(INSTANCE_ID_PLACEHOLDER, instanceId)
-				.replace(REALM_PLACEHOLDER, realm);
+				.replace(BASE_URL_PLACEHOLDER, baseUrl)
+				.replace(REALM_PLACEHOLDER, realm)
+				.replace(SCOPE_PLACEHOLDER, scope);
 	}
 	
 	
diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2OryAuthConfig.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2OryAuthConfig.java
index 345e05b96..d30715ca6 100644
--- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2OryAuthConfig.java
+++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/Oauth2OryAuthConfig.java
@@ -1,6 +1,7 @@
 package org.lowcoder.sdk.auth;
 
-import static org.lowcoder.sdk.auth.constants.Oauth2Constants.INSTANCE_ID_PLACEHOLDER;
+import static org.lowcoder.sdk.auth.constants.Oauth2Constants.BASE_URL_PLACEHOLDER;
+import static org.lowcoder.sdk.auth.constants.Oauth2Constants.SCOPE_PLACEHOLDER;
 
 import javax.annotation.Nullable;
 
@@ -14,7 +15,8 @@
 @Getter
 public class Oauth2OryAuthConfig extends Oauth2SimpleAuthConfig {
 
-    protected String instanceId;
+    protected String baseUrl;
+    protected String scope;
 
     @JsonCreator
     public Oauth2OryAuthConfig(
@@ -25,14 +27,18 @@ public Oauth2OryAuthConfig(
             String sourceName,
             String clientId,
             String clientSecret,
-            String instanceId,
+            String baseUrl,
+            String scope,
             String authType) {
         super(id, enable, enableRegister, source, sourceName, clientId, clientSecret, authType);
-        this.instanceId = instanceId;
+        this.baseUrl = baseUrl;
+        this.scope = scope;
     }
 
     @Override
     public String replaceAuthUrlClientIdPlaceholder(String url) {
-        return super.replaceAuthUrlClientIdPlaceholder(url).replace(INSTANCE_ID_PLACEHOLDER, instanceId);
+        return super.replaceAuthUrlClientIdPlaceholder(url)
+        		.replace(BASE_URL_PLACEHOLDER, baseUrl)
+        		.replace(SCOPE_PLACEHOLDER, scope);
     }
 }
diff --git a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java
index 6313af520..38b7eea68 100644
--- a/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java
+++ b/server/api-service/lowcoder-sdk/src/main/java/org/lowcoder/sdk/auth/constants/Oauth2Constants.java
@@ -8,7 +8,8 @@ public class Oauth2Constants {
     public static final String STATE_PLACEHOLDER = "$STATE";
     public static final String REALM_PLACEHOLDER = "$REALM";
 
-    public static final String INSTANCE_ID_PLACEHOLDER = "$INSTANCE_ID";
+    public static final String BASE_URL_PLACEHOLDER = "$BASE_URL";
+    public static final String SCOPE_PLACEHOLDER = "$SCOPE";
 
     // authorize url
     public static final String GITHUB_AUTHORIZE_URL = "https://github.com/login/oauth/authorize"
@@ -27,17 +28,17 @@ public class Oauth2Constants {
             + "&scope=openid email profile"
             + "&prompt=select_account";
 
-    public static final String ORY_AUTHORIZE_URL = "https://" + INSTANCE_ID_PLACEHOLDER +  "/oauth2/auth"
+    public static final String ORY_AUTHORIZE_URL = BASE_URL_PLACEHOLDER +  "/oauth2/auth"
             + "?response_type=code"
             + "&client_id=" + CLIENT_ID_PLACEHOLDER
             + "&redirect_uri=" + REDIRECT_URL_PLACEHOLDER
             + "&state=" + STATE_PLACEHOLDER
-            + "&scope=openid email profile offline_access";
+            + "&scope=" + SCOPE_PLACEHOLDER;
 
-    public static final String KEYCLOAK_AUTHORIZE_URL = "https://" + INSTANCE_ID_PLACEHOLDER + "/realms/" + REALM_PLACEHOLDER + "/protocol/openid-connect/auth"
+    public static final String KEYCLOAK_AUTHORIZE_URL = BASE_URL_PLACEHOLDER + "/realms/" + REALM_PLACEHOLDER + "/protocol/openid-connect/auth"
             + "?response_type=code"
             + "&client_id=" + CLIENT_ID_PLACEHOLDER
             + "&redirect_uri=" + REDIRECT_URL_PLACEHOLDER
             + "&state=" + STATE_PLACEHOLDER
-            + "&scope=openid email profile";
+            + "&scope=" + SCOPE_PLACEHOLDER;
 }
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java
index 70a7cc776..8a2d1a281 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/request/oauth2/Oauth2DefaultSource.java
@@ -1,7 +1,6 @@
 package org.lowcoder.api.authentication.request.oauth2;
 
 import org.lowcoder.sdk.auth.constants.Oauth2Constants;
-import org.springframework.security.config.oauth2.client.CommonOAuth2Provider;
 
 public enum Oauth2DefaultSource implements Oauth2Source {
 
@@ -43,17 +42,17 @@ public String refresh() {
     ORY {
         @Override
         public String accessToken() {
-            return "https://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/oauth2/token";
+            return Oauth2Constants.BASE_URL_PLACEHOLDER + "/oauth2/token";
         }
 
         @Override
         public String userInfo() {
-            return "https://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/userinfo";
+            return Oauth2Constants.BASE_URL_PLACEHOLDER + "/userinfo";
         }
 
         @Override
         public String refresh() {
-            return "https://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/oauth2/token";
+            return Oauth2Constants.BASE_URL_PLACEHOLDER + "/oauth2/token";
         }
 
     },
@@ -62,17 +61,17 @@ public String refresh() {
 
         @Override
         public String accessToken() {
-            return "http://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/token";
+            return Oauth2Constants.BASE_URL_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/token";
         }
 
         @Override
         public String userInfo() {
-            return "http://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/userinfo";
+            return Oauth2Constants.BASE_URL_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/userinfo";
         }
 
         @Override
         public String refresh() {
-        	return "http://" + Oauth2Constants.INSTANCE_ID_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/token";
+        	return Oauth2Constants.BASE_URL_PLACEHOLDER + "/realms/" + Oauth2Constants.REALM_PLACEHOLDER + "/protocol/openid-connect/token";
         }
     	
     }
diff --git a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/factory/AuthConfigFactoryImpl.java b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/factory/AuthConfigFactoryImpl.java
index 82a8b331a..e2f256f47 100644
--- a/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/factory/AuthConfigFactoryImpl.java
+++ b/server/api-service/lowcoder-server/src/main/java/org/lowcoder/api/authentication/service/factory/AuthConfigFactoryImpl.java
@@ -71,7 +71,8 @@ private Oauth2SimpleAuthConfig buildOauth2OryAuthConfig(AuthConfigRequest authCo
                 org.lowcoder.sdk.constants.AuthSourceConstants.ORY_NAME,
                 requireNonNull(authConfigRequest.getClientId(), "clientId can not be null."),
                 authConfigRequest.getClientSecret(),
-                authConfigRequest.getInstanceId(),
+                authConfigRequest.getString("baseUrl"),
+                authConfigRequest.getString("scope"),
                 authConfigRequest.getAuthType());
     }
     
@@ -84,8 +85,9 @@ private Oauth2SimpleAuthConfig buildOauth2KeycloakAuthConfig(AuthConfigRequest a
                 org.lowcoder.sdk.constants.AuthSourceConstants.KEYCLOAK_NAME,
                 requireNonNull(authConfigRequest.getClientId(), "clientId can not be null."),
                 authConfigRequest.getClientSecret(),
-                authConfigRequest.getInstanceId(),
+                authConfigRequest.getString("baseUrl"),
                 authConfigRequest.getString("realm"),
+                authConfigRequest.getString("scope"),
                 authConfigRequest.getAuthType());
     }