This is an automated email from the ASF dual-hosted git repository.

abeizn pushed a commit to branch release-v1.0
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git

commit db544eafdc544f45039b09fc42edffbfd55beb0a
Author: 青湛 <[email protected]>
AuthorDate: Mon Mar 25 16:55:49 2024 +1300

    feat: add onboard process (#7199)
---
 config-ui/package.json                             |   4 +-
 config-ui/public/onboard/assets/github/1.png       | Bin 0 -> 18549 bytes
 config-ui/public/onboard/assets/github/2.png       | Bin 0 -> 25492 bytes
 config-ui/public/onboard/assets/github/3.png       | Bin 0 -> 53740 bytes
 config-ui/public/onboard/assets/gitlab/1.png       | Bin 0 -> 22729 bytes
 config-ui/public/onboard/assets/gitlab/2.png       | Bin 0 -> 40874 bytes
 config-ui/public/onboard/assets/gitlab/3.png       | Bin 0 -> 23739 bytes
 config-ui/public/onboard/step-1/azuredevops.md     |  25 +
 config-ui/public/onboard/step-1/bitbucket.md       |  25 +
 config-ui/public/onboard/step-1/default.md         |  21 +
 config-ui/public/onboard/step-1/github.md          |  25 +
 config-ui/public/onboard/step-1/gitlab.md          |  25 +
 config-ui/public/onboard/step-2/azuredevops.md     |  16 +
 config-ui/public/onboard/step-2/bitbucket.md       |  16 +
 config-ui/public/onboard/step-2/github.md          |  44 +
 config-ui/public/onboard/step-2/gitlab.md          |  16 +
 config-ui/public/onboard/step-3/azuredevops.md     |  16 +
 config-ui/public/onboard/step-3/bitbucket.md       |  16 +
 config-ui/public/onboard/step-3/github.md          |  34 +
 config-ui/public/onboard/step-3/gitlab.md          |  16 +
 config-ui/src/api/index.ts                         |   4 +-
 .../project/home/styled.ts => api/store/index.ts}  |  17 +-
 config-ui/src/app/routrer.tsx                      |  23 +-
 .../components/connection-form/fields/password.tsx |   1 -
 .../components/connection-form/fields/token.tsx    |   1 -
 .../components/connection-form/fields/username.tsx |  18 -
 .../plugins/components/connection-select/index.tsx |  71 ++
 .../data-scope-remote/data-scope-remote.tsx        |  56 +-
 .../components/data-scope-remote/search-local.tsx  |   4 +-
 .../components/data-scope-remote/search-remote.tsx |   5 +-
 config-ui/src/plugins/components/index.ts          |   1 +
 .../register/github/connection-fields/token.tsx    |   2 +-
 config-ui/src/routes/index.ts                      |   1 +
 config-ui/src/routes/layout/config.tsx             |  10 +-
 config-ui/src/routes/layout/layout.tsx             |   3 +-
 config-ui/src/routes/layout/loader.ts              |   7 +
 config-ui/src/routes/onboard/card.tsx              |  54 ++
 .../home/styled.ts => onboard/context.tsx}         |  38 +-
 config-ui/src/routes/onboard/index.tsx             | 121 +++
 config-ui/src/routes/onboard/step-0.tsx            | 106 +++
 config-ui/src/routes/onboard/step-1.tsx            | 126 +++
 config-ui/src/routes/onboard/step-2.tsx            | 183 ++++
 config-ui/src/routes/onboard/step-3.tsx            | 161 ++++
 .../{project/home/styled.ts => onboard/step-4.tsx} |  39 +-
 config-ui/src/routes/onboard/styled.ts             | 149 ++++
 config-ui/src/routes/project/home/index.tsx        |  61 +-
 config-ui/vite.config.ts                           |   2 +-
 config-ui/yarn.lock                                | 934 ++++++++++++++++++++-
 48 files changed, 2360 insertions(+), 137 deletions(-)

diff --git a/config-ui/package.json b/config-ui/package.json
index a5aa294f2..d3d2ee115 100644
--- a/config-ui/package.json
+++ b/config-ui/package.json
@@ -34,15 +34,17 @@
     "dayjs": "^1.11.10",
     "file-saver": "^2.0.5",
     "lodash": "^4.17.21",
-    "miller-columns-select": "1.3.2",
+    "miller-columns-select": "1.4.0",
     "react": "^18.2.0",
     "react-copy-to-clipboard": "^5.1.0",
     "react-dom": "^18.2.0",
     "react-is": "^18.2.0",
+    "react-markdown": "^9.0.1",
     "react-redux": "^9.1.0",
     "react-router-dom": "^6.22.2",
     "react-transition-group": "^4.4.5",
     "redux": "^5.0.1",
+    "rehype-raw": "^7.0.0",
     "styled-components": "^6.1.8"
   },
   "devDependencies": {
diff --git a/config-ui/public/onboard/assets/github/1.png 
b/config-ui/public/onboard/assets/github/1.png
new file mode 100644
index 000000000..f1766e453
Binary files /dev/null and b/config-ui/public/onboard/assets/github/1.png differ
diff --git a/config-ui/public/onboard/assets/github/2.png 
b/config-ui/public/onboard/assets/github/2.png
new file mode 100644
index 000000000..568440f29
Binary files /dev/null and b/config-ui/public/onboard/assets/github/2.png differ
diff --git a/config-ui/public/onboard/assets/github/3.png 
b/config-ui/public/onboard/assets/github/3.png
new file mode 100644
index 000000000..2e0f94a39
Binary files /dev/null and b/config-ui/public/onboard/assets/github/3.png differ
diff --git a/config-ui/public/onboard/assets/gitlab/1.png 
b/config-ui/public/onboard/assets/gitlab/1.png
new file mode 100644
index 000000000..11504a763
Binary files /dev/null and b/config-ui/public/onboard/assets/gitlab/1.png differ
diff --git a/config-ui/public/onboard/assets/gitlab/2.png 
b/config-ui/public/onboard/assets/gitlab/2.png
new file mode 100644
index 000000000..1c950ebba
Binary files /dev/null and b/config-ui/public/onboard/assets/gitlab/2.png differ
diff --git a/config-ui/public/onboard/assets/gitlab/3.png 
b/config-ui/public/onboard/assets/gitlab/3.png
new file mode 100644
index 000000000..483286d6c
Binary files /dev/null and b/config-ui/public/onboard/assets/gitlab/3.png differ
diff --git a/config-ui/public/onboard/step-1/azuredevops.md 
b/config-ui/public/onboard/step-1/azuredevops.md
new file mode 100644
index 000000000..6018ef364
--- /dev/null
+++ b/config-ui/public/onboard/step-1/azuredevops.md
@@ -0,0 +1,25 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+##### Azure DevOps tells you:
+
+1. How much code have you submitted?
+   ![1](/onboard/assets/gitlab/1.png)
+2. How are your Pull Requests handled?
+   ![2](/onboard/assets/gitlab/2.png)
+3. How fast is your Code Review process?
+   ![3](/onboard/assets/gitlab/3.png)
diff --git a/config-ui/public/onboard/step-1/bitbucket.md 
b/config-ui/public/onboard/step-1/bitbucket.md
new file mode 100644
index 000000000..745c454e7
--- /dev/null
+++ b/config-ui/public/onboard/step-1/bitbucket.md
@@ -0,0 +1,25 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+##### Bitbucket tells you:
+
+1. How much code have you submitted?
+   ![1](/onboard/assets/gitlab/1.png)
+2. How are your Pull Requests handled?
+   ![2](/onboard/assets/gitlab/2.png)
+3. How fast is your Code Review process?
+   ![3](/onboard/assets/gitlab/3.png)
diff --git a/config-ui/public/onboard/step-1/default.md 
b/config-ui/public/onboard/step-1/default.md
new file mode 100644
index 000000000..f2183b8ba
--- /dev/null
+++ b/config-ui/public/onboard/step-1/default.md
@@ -0,0 +1,21 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+##### What is a data connection?
+
+- On a high level, a project can be viewed as a real-world project or product 
line. It represents a specific initiative or endeavor within the software 
development domain.
+- On a lower level, a project is a way of organizing and grouping data from 
different domains.
diff --git a/config-ui/public/onboard/step-1/github.md 
b/config-ui/public/onboard/step-1/github.md
new file mode 100644
index 000000000..154fe51bd
--- /dev/null
+++ b/config-ui/public/onboard/step-1/github.md
@@ -0,0 +1,25 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+##### GitHub tells you:
+
+1. What is your issue lead time?
+   ![1](/onboard/assets/github/1.png)
+2. How fast is your Code Review process?
+   ![2](/onboard/assets/github/2.png)
+3. How is your CI/CD process?
+   ![3](/onboard/assets/github/3.png)
diff --git a/config-ui/public/onboard/step-1/gitlab.md 
b/config-ui/public/onboard/step-1/gitlab.md
new file mode 100644
index 000000000..1878dc998
--- /dev/null
+++ b/config-ui/public/onboard/step-1/gitlab.md
@@ -0,0 +1,25 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+##### GitLab tells you:
+
+1. How much code have you submitted?
+   ![1](/onboard/assets/gitlab/1.png)
+2. How are your Merge Requests handled?
+   ![2](/onboard/assets/gitlab/2.png)
+3. How fast is your Code Review process?
+   ![3](/onboard/assets/gitlab/3.png)
diff --git a/config-ui/public/onboard/step-2/azuredevops.md 
b/config-ui/public/onboard/step-2/azuredevops.md
new file mode 100644
index 000000000..449e8a88c
--- /dev/null
+++ b/config-ui/public/onboard/step-2/azuredevops.md
@@ -0,0 +1,16 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
diff --git a/config-ui/public/onboard/step-2/bitbucket.md 
b/config-ui/public/onboard/step-2/bitbucket.md
new file mode 100644
index 000000000..449e8a88c
--- /dev/null
+++ b/config-ui/public/onboard/step-2/bitbucket.md
@@ -0,0 +1,16 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
diff --git a/config-ui/public/onboard/step-2/github.md 
b/config-ui/public/onboard/step-2/github.md
new file mode 100644
index 000000000..74ac669c5
--- /dev/null
+++ b/config-ui/public/onboard/step-2/github.md
@@ -0,0 +1,44 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+##### Q1. How to create a classic GitHub personal access token?
+
+1. Log in to [github.com](https://github.com).
+2. In the upper-right corner of any page, click your profile photo, then click 
Settings.
+3. In the left sidebar, click <> Developer settings.
+4. In the left sidebar, under 'Personal access tokens', click Tokens (classic).
+5. Choose the following scopes: repo:status, repo:deployment, read:user and 
read:org.
+
+Check [this 
doc](https://devlake.apache.org/docs/Configuration/GitHub/#personal-access-tokens)
 for more details.
+
+##### Q2. What token scopes should I choose?
+
+Normally, only the following scopes are required:
+`repo:status` `repo:deployment` `read:user` `read:org`
+
+However, if you want to collect data from private repositories, you need to 
give full permission to repo:
+`repo` `read:user` `read:org`
+
+##### Q3. Can I connect to the GitHub server?
+
+Sure, you could
+
+1. Go to the Connections page.
+2. Click Create a New Connection.
+3. Choose 'GitHub server' and finish the connection.
+
+GitHub server is not supported to simplify this onboard process.
diff --git a/config-ui/public/onboard/step-2/gitlab.md 
b/config-ui/public/onboard/step-2/gitlab.md
new file mode 100644
index 000000000..449e8a88c
--- /dev/null
+++ b/config-ui/public/onboard/step-2/gitlab.md
@@ -0,0 +1,16 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
diff --git a/config-ui/public/onboard/step-3/azuredevops.md 
b/config-ui/public/onboard/step-3/azuredevops.md
new file mode 100644
index 000000000..449e8a88c
--- /dev/null
+++ b/config-ui/public/onboard/step-3/azuredevops.md
@@ -0,0 +1,16 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
diff --git a/config-ui/public/onboard/step-3/bitbucket.md 
b/config-ui/public/onboard/step-3/bitbucket.md
new file mode 100644
index 000000000..449e8a88c
--- /dev/null
+++ b/config-ui/public/onboard/step-3/bitbucket.md
@@ -0,0 +1,16 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
diff --git a/config-ui/public/onboard/step-3/github.md 
b/config-ui/public/onboard/step-3/github.md
new file mode 100644
index 000000000..388751d2f
--- /dev/null
+++ b/config-ui/public/onboard/step-3/github.md
@@ -0,0 +1,34 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+##### Q1. What GitHub data will be collected?
+
+`issues` `repos` `commits` `branches` `pull requests` `pr comments` `workflow 
runs` `job runs` `deployments` `users`
+
+Check [this 
doc](https://devlake.apache.org/docs/Overview/SupportedDataSources/#data-collection-scope-by-each-plugin)
 for more details.
+
+##### Q2. The data from which time range will be collected?
+
+Only the data from last 14 days will be collected to speed up the sync up 
time. You can always change the time range in the project details page later.
+
+##### Q3. Can I do transformations on the collected data?
+
+Yes. You can do transformations by adding a Scope Config to the repositories 
you choose later.
+
+##### Q4. What is the frequency to sync up data?
+
+The data will be synced daily, you can change it in the project details page 
later.
diff --git a/config-ui/public/onboard/step-3/gitlab.md 
b/config-ui/public/onboard/step-3/gitlab.md
new file mode 100644
index 000000000..449e8a88c
--- /dev/null
+++ b/config-ui/public/onboard/step-3/gitlab.md
@@ -0,0 +1,16 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
diff --git a/config-ui/src/api/index.ts b/config-ui/src/api/index.ts
index 60456a198..f211e5640 100644
--- a/config-ui/src/api/index.ts
+++ b/config-ui/src/api/index.ts
@@ -26,6 +26,7 @@ import plugin from './plugin';
 import * as project from './project';
 import * as scope from './scope';
 import * as scopeConfig from './scope-config';
+import * as store from './store';
 import * as task from './task';
 
 const migrate = () => request('/proceed-db-migration');
@@ -39,8 +40,9 @@ export const API = {
   pipeline,
   plugin,
   project,
-  scopeConfig,
   scope,
+  scopeConfig,
+  store,
   task,
   migrate,
   ping,
diff --git a/config-ui/src/routes/project/home/styled.ts 
b/config-ui/src/api/store/index.ts
similarity index 74%
copy from config-ui/src/routes/project/home/styled.ts
copy to config-ui/src/api/store/index.ts
index 3c73c29b7..ae479cb00 100644
--- a/config-ui/src/routes/project/home/styled.ts
+++ b/config-ui/src/api/store/index.ts
@@ -16,19 +16,8 @@
  *
  */
 
-import styled from 'styled-components';
+import { request } from '@/utils';
 
-export const DialogWrapper = styled.div``;
+export const get = (key: string) => request(`/store/${key}`);
 
-export const Label = styled.label`
-  font-size: 16px;
-  font-weight: 600;
-`;
-
-export const LabelInfo = styled.i`
-  color: #ff8b8b;
-`;
-
-export const LabelDescription = styled.p`
-  margin: 0;
-`;
+export const set = (key: string, value: any) => request(`/store/${key}`, { 
method: 'PUT', data: value });
diff --git a/config-ui/src/app/routrer.tsx b/config-ui/src/app/routrer.tsx
index e8d6ce94c..ac3113db7 100644
--- a/config-ui/src/app/routrer.tsx
+++ b/config-ui/src/app/routrer.tsx
@@ -20,6 +20,7 @@ import { createBrowserRouter, Navigate } from 
'react-router-dom';
 
 import {
   DBMigrate,
+  Onboard,
   Error,
   Layout,
   layoutLoader,
@@ -47,6 +48,10 @@ export const router = createBrowserRouter([
     path: `${PATH_PREFIX}/db-migrate`,
     element: <DBMigrate />,
   },
+  {
+    path: `${PATH_PREFIX}/onboard`,
+    element: <Onboard />,
+  },
   {
     path: `${PATH_PREFIX}`,
     element: <Layout />,
@@ -55,15 +60,7 @@ export const router = createBrowserRouter([
     children: [
       {
         index: true,
-        element: <Navigate to="connections" />,
-      },
-      {
-        path: 'connections',
-        element: <Connections />,
-      },
-      {
-        path: 'connections/:plugin/:id',
-        element: <Connection />,
+        element: <Navigate to="projects" />,
       },
       {
         path: 'projects',
@@ -77,6 +74,14 @@ export const router = createBrowserRouter([
         path: 'projects/:pname/:unique',
         element: <BlueprintConnectionDetailPage />,
       },
+      {
+        path: 'connections',
+        element: <Connections />,
+      },
+      {
+        path: 'connections/:plugin/:id',
+        element: <Connection />,
+      },
       {
         path: 'advanced',
         children: [
diff --git 
a/config-ui/src/plugins/components/connection-form/fields/password.tsx 
b/config-ui/src/plugins/components/connection-form/fields/password.tsx
index de6e7f190..6c8312911 100644
--- a/config-ui/src/plugins/components/connection-form/fields/password.tsx
+++ b/config-ui/src/plugins/components/connection-form/fields/password.tsx
@@ -26,7 +26,6 @@ interface Props {
   label?: string;
   subLabel?: string;
   placeholder?: string;
-  name: string;
   initialValue: string;
   value: string;
   error: string;
diff --git a/config-ui/src/plugins/components/connection-form/fields/token.tsx 
b/config-ui/src/plugins/components/connection-form/fields/token.tsx
index 1882a45f3..1788ed395 100644
--- a/config-ui/src/plugins/components/connection-form/fields/token.tsx
+++ b/config-ui/src/plugins/components/connection-form/fields/token.tsx
@@ -42,7 +42,6 @@ interface Props {
   type: 'create' | 'update';
   label?: string;
   subLabel?: string;
-  name: string;
   initialValue: string;
   value: string;
   error: string;
diff --git 
a/config-ui/src/plugins/components/connection-form/fields/username.tsx 
b/config-ui/src/plugins/components/connection-form/fields/username.tsx
index e69b43ad8..9c294eadb 100644
--- a/config-ui/src/plugins/components/connection-form/fields/username.tsx
+++ b/config-ui/src/plugins/components/connection-form/fields/username.tsx
@@ -15,23 +15,6 @@
  * limitations under the License.
  *
  */
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
 
 import { useEffect } from 'react';
 import { Input } from 'antd';
@@ -42,7 +25,6 @@ interface Props {
   label?: string;
   subLabel?: string;
   placeholder?: string;
-  name: string;
   initialValue: string;
   value: string;
   error: string;
diff --git a/config-ui/src/plugins/components/connection-select/index.tsx 
b/config-ui/src/plugins/components/connection-select/index.tsx
new file mode 100644
index 000000000..71c7dab37
--- /dev/null
+++ b/config-ui/src/plugins/components/connection-select/index.tsx
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { theme, Select, SelectProps } from 'antd';
+import styled from 'styled-components';
+
+import { getPluginConfig } from '@/plugins';
+
+const Option = styled.div`
+  display: flex;
+  align-items: center;
+
+  .icon {
+    display: inline-block;
+    width: 24px;
+    height: 24px;
+
+    & > svg {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .name {
+    margin-left: 8px;
+    max-width: 90%;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    overflow: hidden;
+  }
+`;
+
+interface Props extends Omit<SelectProps, 'optionRender'> {}
+
+export const ConnectionSelect = ({ ...props }: Props) => {
+  const {
+    token: { colorPrimary },
+  } = theme.useToken();
+
+  return (
+    <Select
+      style={{ width: 384 }}
+      placeholder="Select..."
+      optionRender={(option) => {
+        const plugin = getPluginConfig(option.data.plugin);
+        return (
+          <Option>
+            <span className="icon">{plugin.icon({ color: colorPrimary 
})}</span>
+            <span className="name">{option.label}</span>
+          </Option>
+        );
+      }}
+      {...props}
+    />
+  );
+};
diff --git 
a/config-ui/src/plugins/components/data-scope-remote/data-scope-remote.tsx 
b/config-ui/src/plugins/components/data-scope-remote/data-scope-remote.tsx
index 9e80307f8..6bf150916 100644
--- a/config-ui/src/plugins/components/data-scope-remote/data-scope-remote.tsx
+++ b/config-ui/src/plugins/components/data-scope-remote/data-scope-remote.tsx
@@ -16,7 +16,7 @@
  *
  */
 
-import { useState, useMemo } from 'react';
+import { useState, useEffect, useMemo } from 'react';
 import { Flex, Button } from 'antd';
 
 import API from '@/api';
@@ -27,17 +27,35 @@ import { SearchLocal } from './search-local';
 import { SearchRemote } from './search-remote';
 
 interface Props {
+  mode?: 'single' | 'multiple';
   plugin: string;
   connectionId: ID;
+  selectedScope?: any[];
   disabledScope?: any[];
-  onCancel: () => void;
-  onSubmit: (origin: any) => void;
+  onChangeSelectedScope?: (scope: any[]) => void;
+  footer?: React.ReactNode;
+  onCancel?: () => void;
+  onSubmit?: (origin: any) => void;
 }
 
-export const DataScopeRemote = ({ plugin, connectionId, disabledScope, 
onCancel, onSubmit }: Props) => {
+export const DataScopeRemote = ({
+  mode = 'multiple',
+  plugin,
+  connectionId,
+  disabledScope,
+  onChangeSelectedScope,
+  footer,
+  onCancel,
+  onSubmit,
+  ...props
+}: Props) => {
   const [selectedScope, setSelectedScope] = useState<any[]>([]);
   const [operating, setOperating] = useState(false);
 
+  useEffect(() => {
+    setSelectedScope(props.selectedScope ?? []);
+  }, [props.selectedScope]);
+
   const config = useMemo(() => getPluginConfig(plugin).dataScope, [plugin]);
 
   const handleSubmit = async () => {
@@ -50,7 +68,7 @@ export const DataScopeRemote = ({ plugin, connectionId, 
disabledScope, onCancel,
     );
 
     if (success) {
-      onSubmit(res);
+      onSubmit?.(res);
     }
   };
 
@@ -61,35 +79,41 @@ export const DataScopeRemote = ({ plugin, connectionId, 
disabledScope, onCancel,
           connectionId,
           disabledItems: disabledScope?.map((it) => ({ id: it.id })),
           selectedItems: selectedScope,
-          onChangeSelectedItems: setSelectedScope,
+          onChangeSelectedItems: onChangeSelectedScope ?? setSelectedScope,
         })
       ) : config.localSearch ? (
         <SearchLocal
+          mode={mode}
           plugin={plugin}
           connectionId={connectionId}
           config={config}
           disabledScope={disabledScope ?? []}
           selectedScope={selectedScope}
-          onChange={setSelectedScope}
+          onChange={onChangeSelectedScope ?? setSelectedScope}
         />
       ) : (
         <SearchRemote
+          mode={mode}
           plugin={plugin}
           connectionId={connectionId}
           config={config}
           disabledScope={disabledScope ?? []}
           selectedScope={selectedScope}
-          onChange={setSelectedScope}
+          onChange={onChangeSelectedScope ?? setSelectedScope}
         />
       )}
-      <Flex style={{ marginTop: 16 }} justify="flex-end" gap="small">
-        <Button disabled={operating} onClick={onCancel}>
-          Cancel
-        </Button>
-        <Button type="primary" loading={operating} 
disabled={!selectedScope.length} onClick={handleSubmit}>
-          Save
-        </Button>
-      </Flex>
+      {footer !== undefined ? (
+        footer
+      ) : (
+        <Flex style={{ marginTop: 16 }} justify="flex-end" gap="small">
+          <Button disabled={operating} onClick={onCancel}>
+            Cancel
+          </Button>
+          <Button type="primary" loading={operating} 
disabled={!selectedScope.length} onClick={handleSubmit}>
+            Save
+          </Button>
+        </Flex>
+      )}
     </Flex>
   );
 };
diff --git 
a/config-ui/src/plugins/components/data-scope-remote/search-local.tsx 
b/config-ui/src/plugins/components/data-scope-remote/search-local.tsx
index 244b72de2..70660c71f 100644
--- a/config-ui/src/plugins/components/data-scope-remote/search-local.tsx
+++ b/config-ui/src/plugins/components/data-scope-remote/search-local.tsx
@@ -31,6 +31,7 @@ import * as T from './types';
 import * as S from './styled';
 
 interface Props {
+  mode: 'single' | 'multiple';
   plugin: string;
   connectionId: ID;
   config: IPluginConfig['dataScope'];
@@ -41,7 +42,7 @@ interface Props {
 
 let canceling = false;
 
-export const SearchLocal = ({ plugin, connectionId, config, disabledScope, 
selectedScope, onChange }: Props) => {
+export const SearchLocal = ({ mode, plugin, connectionId, config, 
disabledScope, selectedScope, onChange }: Props) => {
   const [miller, setMiller] = useState<{
     items: McsItem<T.ResItem>[];
     loadedIds: ID[];
@@ -235,6 +236,7 @@ export const SearchLocal = ({ plugin, connectionId, config, 
disabledScope, selec
           <Input prefix={<SearchOutlined />} value={query} onChange={(e) => 
setQuery(e.target.value)} />
         )}
         <MillerColumnsSelect
+          mode={mode}
           items={scopes}
           columnCount={search ? 1 : config.millerColumn?.columnCount ?? 1}
           columnHeight={300}
diff --git 
a/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx 
b/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx
index f49a10244..4679b8564 100644
--- a/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx
+++ b/config-ui/src/plugins/components/data-scope-remote/search-remote.tsx
@@ -32,6 +32,7 @@ import * as T from './types';
 import * as S from './styled';
 
 interface Props {
+  mode: 'single' | 'multiple';
   plugin: string;
   connectionId: ID;
   config: IPluginConfig['dataScope'];
@@ -40,7 +41,7 @@ interface Props {
   onChange: (selectedScope: any[]) => void;
 }
 
-export const SearchRemote = ({ plugin, connectionId, config, disabledScope, 
selectedScope, onChange }: Props) => {
+export const SearchRemote = ({ mode, plugin, connectionId, config, 
disabledScope, selectedScope, onChange }: Props) => {
   const [miller, setMiller] = useState<{
     items: McsItem<T.ResItem>[];
     loadedIds: ID[];
@@ -180,6 +181,7 @@ export const SearchRemote = ({ plugin, connectionId, 
config, disabledScope, sele
         />
         {!searchDebounce ? (
           <MillerColumnsSelect
+            mode={mode}
             items={miller.items}
             columnCount={config.millerColumn?.columnCount ?? 1}
             columnHeight={300}
@@ -202,6 +204,7 @@ export const SearchRemote = ({ plugin, connectionId, 
config, disabledScope, sele
           />
         ) : (
           <MillerColumnsSelect
+            mode={mode}
             items={search.currentItems}
             columnCount={1}
             columnHeight={300}
diff --git a/config-ui/src/plugins/components/index.ts 
b/config-ui/src/plugins/components/index.ts
index 82d08d3be..2f4049cab 100644
--- a/config-ui/src/plugins/components/index.ts
+++ b/config-ui/src/plugins/components/index.ts
@@ -18,6 +18,7 @@
 
 export * from './connection-form';
 export * from './connection-list';
+export * from './connection-select';
 export * from './connection-status';
 export * from './data-scope-remote';
 export * from './data-scope-select';
diff --git a/config-ui/src/plugins/register/github/connection-fields/token.tsx 
b/config-ui/src/plugins/register/github/connection-fields/token.tsx
index b6f835349..f4a20d05a 100644
--- a/config-ui/src/plugins/register/github/connection-fields/token.tsx
+++ b/config-ui/src/plugins/register/github/connection-fields/token.tsx
@@ -41,7 +41,7 @@ interface Props {
   initialValue: string;
   value: string;
   error: string;
-  setValue: (value?: string) => void;
+  setValue: (value: string) => void;
   setError: (error?: string) => void;
 }
 
diff --git a/config-ui/src/routes/index.ts b/config-ui/src/routes/index.ts
index b640be499..025a0792f 100644
--- a/config-ui/src/routes/index.ts
+++ b/config-ui/src/routes/index.ts
@@ -23,5 +23,6 @@ export * from './db-migrate';
 export * from './error';
 export * from './layout';
 export * from './not-found';
+export * from './onboard';
 export * from './pipeline';
 export * from './project';
diff --git a/config-ui/src/routes/layout/config.tsx 
b/config-ui/src/routes/layout/config.tsx
index 0e0fd050f..48cf81595 100644
--- a/config-ui/src/routes/layout/config.tsx
+++ b/config-ui/src/routes/layout/config.tsx
@@ -40,16 +40,16 @@ type MenuItem = {
 };
 
 export const menuItems: MenuItem[] = [
-  {
-    key: `${PATH_PREFIX}/connections`,
-    label: 'Connections',
-    icon: <AppstoreOutlined />,
-  },
   {
     key: `${PATH_PREFIX}/projects`,
     label: 'Projects',
     icon: <ProjectOutlined />,
   },
+  {
+    key: `${PATH_PREFIX}/connections`,
+    label: 'Connections',
+    icon: <AppstoreOutlined />,
+  },
   {
     key: `${PATH_PREFIX}/advanced`,
     label: 'Advanced',
diff --git a/config-ui/src/routes/layout/layout.tsx 
b/config-ui/src/routes/layout/layout.tsx
index e33a74576..51f5c630c 100644
--- a/config-ui/src/routes/layout/layout.tsx
+++ b/config-ui/src/routes/layout/layout.tsx
@@ -37,7 +37,6 @@ import {
 import { useAppDispatch, useAppSelector } from '@/hooks';
 import { operator } from '@/utils';
 
-import { layoutLoader } from './loader';
 import { menuItems, menuItemsMatch, headerItems } from './config';
 import * as S from './styled';
 import './tips-transition.css';
@@ -49,7 +48,7 @@ export const Layout = () => {
   const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
   const [operating, setOperating] = useState(false);
 
-  const { version, plugins } = useLoaderData() as Awaited<ReturnType<typeof 
layoutLoader>>;
+  const { version, plugins } = useLoaderData() as { version: string; plugins: 
string[] };
 
   const navigate = useNavigate();
   const { pathname } = useLocation();
diff --git a/config-ui/src/routes/layout/loader.ts 
b/config-ui/src/routes/layout/loader.ts
index 2bba5a86a..b52021b41 100644
--- a/config-ui/src/routes/layout/loader.ts
+++ b/config-ui/src/routes/layout/loader.ts
@@ -16,6 +16,7 @@
  *
  */
 
+import { redirect } from 'react-router-dom';
 import { intersection } from 'lodash';
 
 import API from '@/api';
@@ -26,6 +27,12 @@ type Props = {
 };
 
 export const layoutLoader = async ({ request }: Props) => {
+  const onboard = await API.store.get('onboard');
+
+  if (!onboard) {
+    return redirect('/onboard');
+  }
+
   let fePlugins = getRegisterPlugins();
   const bePlugins = await API.plugin.list();
 
diff --git a/config-ui/src/routes/onboard/card.tsx 
b/config-ui/src/routes/onboard/card.tsx
new file mode 100644
index 000000000..0dc9639c2
--- /dev/null
+++ b/config-ui/src/routes/onboard/card.tsx
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useNavigate } from 'react-router-dom';
+import { Card, Flex, Progress, Button } from 'antd';
+
+import API from '@/api';
+import { useRefreshData } from '@/hooks';
+
+interface Props {
+  style?: React.CSSProperties;
+}
+
+export const OnboardCard = ({ style }: Props) => {
+  const navigate = useNavigate();
+
+  const { ready, data } = useRefreshData(() => API.store.get('onboard'), []);
+
+  if (!ready || !data || data.done) {
+    return null;
+  }
+
+  return (
+    <Card style={style}>
+      <Flex align="center" justify="space-between">
+        <Flex align="center">
+          <Progress type="circle" size={30} format={() => `${data.step}/4`} 
percent={(data.step / 4) * 100} />
+          <div style={{ marginLeft: 16 }}>
+            <h4>Onboard Session</h4>
+            <h5>You are not far from connecting to your first tool. Continue 
to finish it..</h5>
+          </div>
+        </Flex>
+        <Button type="primary" onClick={() => navigate('/onboard')}>
+          Continue
+        </Button>
+      </Flex>
+    </Card>
+  );
+};
diff --git a/config-ui/src/routes/project/home/styled.ts 
b/config-ui/src/routes/onboard/context.tsx
similarity index 57%
copy from config-ui/src/routes/project/home/styled.ts
copy to config-ui/src/routes/onboard/context.tsx
index 3c73c29b7..7f472d94f 100644
--- a/config-ui/src/routes/project/home/styled.ts
+++ b/config-ui/src/routes/onboard/context.tsx
@@ -16,19 +16,29 @@
  *
  */
 
-import styled from 'styled-components';
+import { createContext } from 'react';
 
-export const DialogWrapper = styled.div``;
+const initialValue: {
+  step: number;
+  records: Array<{
+    plugin: string;
+    connectionId: ID;
+  }>;
+  done: boolean;
+  projectName?: string;
+  plugin?: string;
+  setStep: (value: number) => void;
+  setRecords: (value: Array<{ plugin: string; connectionId: ID }>) => void;
+  setProjectName: (value: string) => void;
+  setPlugin: (value: string) => void;
+} = {
+  step: 0,
+  records: [],
+  done: false,
+  setStep: () => {},
+  setRecords: () => {},
+  setProjectName: () => {},
+  setPlugin: () => {},
+};
 
-export const Label = styled.label`
-  font-size: 16px;
-  font-weight: 600;
-`;
-
-export const LabelInfo = styled.i`
-  color: #ff8b8b;
-`;
-
-export const LabelDescription = styled.p`
-  margin: 0;
-`;
+export const Context = createContext(initialValue);
diff --git a/config-ui/src/routes/onboard/index.tsx 
b/config-ui/src/routes/onboard/index.tsx
new file mode 100644
index 000000000..d2b4d1e6e
--- /dev/null
+++ b/config-ui/src/routes/onboard/index.tsx
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useEffect } from 'react';
+import { CloseOutlined } from '@ant-design/icons';
+import { theme, Layout } from 'antd';
+
+import API from '@/api';
+import { PageLoading } from '@/components';
+import { useRefreshData } from '@/hooks';
+
+import { Context } from './context';
+import { Step0 } from './step-0';
+import { Step1 } from './step-1';
+import { Step2 } from './step-2';
+import { Step3 } from './step-3';
+import { Step4 } from './step-4';
+import * as S from './styled';
+
+const steps = [
+  {
+    step: 1,
+    title: 'Create Project',
+  },
+  {
+    step: 2,
+    title: 'Configure Connection',
+  },
+  {
+    step: 3,
+    title: 'Add data scope',
+  },
+];
+
+export const Onboard = () => {
+  const [step, setStep] = useState(0);
+  const [records, setRecords] = useState<Array<{ plugin: string; connectionId: 
ID }>>([]);
+  const [projectName, setProjectName] = useState<string>();
+  const [plugin, setPlugin] = useState<string>();
+
+  const {
+    token: { colorPrimary },
+  } = theme.useToken();
+
+  const { ready, data } = useRefreshData(() => API.store.get('onboard'));
+
+  useEffect(() => {
+    if (ready && data) {
+      setStep(data.step);
+      setRecords(data.records);
+      setProjectName(data.projectName);
+      setPlugin(data.plugin);
+    }
+  }, [ready, data]);
+
+  if (!ready) {
+    return <PageLoading />;
+  }
+
+  return (
+    <Context.Provider
+      value={{
+        step,
+        records,
+        done: false,
+        projectName,
+        plugin,
+        setStep,
+        setRecords,
+        setProjectName: setProjectName,
+        setPlugin: setPlugin,
+      }}
+    >
+      <Layout style={{ height: '100vh' }}>
+        <S.Inner>
+          {step === 0 ? (
+            <Step0 />
+          ) : (
+            <>
+              <S.Header>
+                <h1>Connect to your first repository</h1>
+                <CloseOutlined style={{ fontSize: 18, color: '#70727F', 
cursor: 'pointer' }} />
+              </S.Header>
+              <S.Content>
+                {[1, 2, 3].includes(step) && (
+                  <S.Step>
+                    {steps.map((it) => (
+                      <S.StepItem key={it.step} $actived={it.step === step} 
$activedColor={colorPrimary}>
+                        <span>{it.step}</span>
+                        <span>{it.title}</span>
+                      </S.StepItem>
+                    ))}
+                  </S.Step>
+                )}
+                {step === 1 && <Step1 />}
+                {step === 2 && <Step2 />}
+                {step === 3 && <Step3 />}
+                {step === 4 && <Step4 />}
+              </S.Content>
+            </>
+          )}
+        </S.Inner>
+      </Layout>
+    </Context.Provider>
+  );
+};
diff --git a/config-ui/src/routes/onboard/step-0.tsx 
b/config-ui/src/routes/onboard/step-0.tsx
new file mode 100644
index 000000000..fb35d5078
--- /dev/null
+++ b/config-ui/src/routes/onboard/step-0.tsx
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useContext } from 'react';
+import { Button } from 'antd';
+import styled from 'styled-components';
+
+import API from '@/api';
+import { operator } from '@/utils';
+
+import { Context } from './context';
+
+const Wrapper = styled.div`
+  margin-top: 100px;
+  text-align: center;
+
+  .welcome {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 30px 0;
+    font-size: 40px;
+
+    span.line {
+      display: inline-block;
+      width: 242px;
+      height: 1px;
+      background-color: #dbdcdf;
+    }
+
+    span.content {
+      margin: 0 24px;
+    }
+  }
+
+  .title {
+    margin: 15px 0;
+    padding: 16px 0;
+    font-size: 60px;
+    font-weight: 600;
+  }
+
+  .subTitle {
+    font-size: 16px;
+  }
+
+  .action {
+    margin: 0 auto;
+    width: 268px;
+    margin-top: 64px;
+  }
+`;
+
+export const Step0 = () => {
+  const [operating, setOperating] = useState(false);
+
+  const { step, records, done, projectName, plugin, setStep } = 
useContext(Context);
+
+  const handleSubmit = async () => {
+    const [success] = await operator(
+      async () => API.store.set('onboard', { step: 1, records, done, 
projectName, plugin }),
+      {
+        setOperating,
+        hideToast: true,
+      },
+    );
+
+    if (success) {
+      setStep(step + 1);
+    }
+  };
+
+  return (
+    <Wrapper>
+      <div className="welcome">
+        <span className="line" />
+        <span className="content">Welcome</span>
+        <span className="line" />
+      </div>
+      <div className="title">Connect to your first repository</div>
+      <div className="subTitle">
+        Integrate your first Git tool and observe engineering metrics with 
just a few clicks.
+      </div>
+      <div className="action">
+        <Button block type="primary" loading={operating} 
onClick={handleSubmit}>
+          Start
+        </Button>
+      </div>
+    </Wrapper>
+  );
+};
diff --git a/config-ui/src/routes/onboard/step-1.tsx 
b/config-ui/src/routes/onboard/step-1.tsx
new file mode 100644
index 000000000..73850c488
--- /dev/null
+++ b/config-ui/src/routes/onboard/step-1.tsx
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useEffect, useContext } from 'react';
+import { Input, Flex, Button, message } from 'antd';
+import Markdown from 'react-markdown';
+import rehypeRaw from 'rehype-raw';
+
+import API from '@/api';
+import { Block } from '@/components';
+import { ConnectionSelect } from '@/plugins';
+import { validName } from '@/routes/project';
+import { operator } from '@/utils';
+
+import { Context } from './context';
+import * as S from './styled';
+
+export const Step1 = () => {
+  const [QA, setQA] = useState('');
+  const [operating, setOperating] = useState(false);
+
+  const { step, records, done, projectName, plugin, setStep, setProjectName, 
setPlugin } = useContext(Context);
+
+  useEffect(() => {
+    fetch(`/onboard/step-1/${plugin ? plugin : 'default'}.md`)
+      .then((res) => res.text())
+      .then((text) => setQA(text));
+  }, [plugin]);
+
+  const handleSubmit = async () => {
+    if (!projectName || !validName(projectName)) {
+      message.error('Please enter alphanumeric or underscore');
+      return;
+    }
+
+    if (!plugin) {
+      message.error('Please select a plugin');
+      return;
+    }
+
+    const [success] = await operator(() => API.store.set('onboard', { step: 2, 
records, done, projectName, plugin }), {
+      setOperating,
+      hideToast: true,
+    });
+
+    if (success) {
+      setStep(step + 1);
+    }
+  };
+
+  return (
+    <>
+      <S.StepContent>
+        <div className="content">
+          <Block
+            title="Project Name"
+            description="Give your project a unique name with letters, 
numbers, -, _ or /"
+            required
+          >
+            <Input
+              style={{ width: 386 }}
+              placeholder="Your Project Name"
+              value={projectName}
+              onChange={(e) => setProjectName(e.target.value)}
+            />
+          </Block>
+          <Block title="Data Connections" description="You can only choose one 
data connection" required>
+            <ConnectionSelect
+              placeholder="Select a Data Connection"
+              options={[
+                {
+                  plugin: 'github',
+                  value: 'github',
+                  label: 'GitHub',
+                },
+                {
+                  plugin: 'gitlab',
+                  value: 'gitlab',
+                  label: 'GitLab',
+                },
+                {
+                  plugin: 'bitbucket',
+                  value: 'bitbucket',
+                  label: 'Bitbucket',
+                },
+                {
+                  plugin: 'azuredevops',
+                  value: 'azuredevops',
+                  label: 'Azure DevOps',
+                },
+              ]}
+              value={plugin}
+              onChange={setPlugin}
+            />
+          </Block>
+        </div>
+        <Markdown className="qa" rehypePlugins={[rehypeRaw]}>
+          {QA}
+        </Markdown>
+      </S.StepContent>
+      <Flex style={{ marginTop: 64 }} justify="space-between">
+        <Button ghost type="primary" loading={operating} onClick={() => 
setStep(step - 1)}>
+          Previous Step
+        </Button>
+        <Button type="primary" loading={operating} disabled={!projectName || 
!plugin} onClick={handleSubmit}>
+          Next Step
+        </Button>
+      </Flex>
+    </>
+  );
+};
diff --git a/config-ui/src/routes/onboard/step-2.tsx 
b/config-ui/src/routes/onboard/step-2.tsx
new file mode 100644
index 000000000..be4552463
--- /dev/null
+++ b/config-ui/src/routes/onboard/step-2.tsx
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useContext, useEffect, useMemo } from 'react';
+import { Flex, Button } from 'antd';
+import Markdown from 'react-markdown';
+import rehypeRaw from 'rehype-raw';
+
+import API from '@/api';
+import { getPluginConfig } from '@/plugins';
+import { ConnectionToken } from 
'@/plugins/components/connection-form/fields/token';
+import { ConnectionUsername } from 
'@/plugins/components/connection-form/fields/username';
+import { ConnectionPassword } from 
'@/plugins/components/connection-form/fields/password';
+import { operator } from '@/utils';
+
+import { Context } from './context';
+import * as S from './styled';
+
+const paramsMap: Record<string, any> = {
+  github: {
+    authMethod: 'AccessToken',
+    endpoint: 'https://api.github.com/',
+  },
+  gitlab: {
+    endpoint: 'https://gitlab.com/api/v4/',
+  },
+  bitbucket: {
+    endpoint: 'https://api.bitbucket.org/2.0/',
+  },
+  azuredevops: {},
+};
+
+export const Step2 = () => {
+  const [QA, setQA] = useState('');
+  const [operating, setOperating] = useState(false);
+  const [testing, setTesting] = useState(false);
+  const [testStaus, setTestStatus] = useState(false);
+  const [payload, setPayload] = useState<any>({});
+
+  const { step, records, done, projectName, plugin, setStep, setRecords } = 
useContext(Context);
+
+  const config = useMemo(() => getPluginConfig(plugin as string), [plugin]);
+
+  useEffect(() => {
+    fetch(`/onboard/step-2/${plugin}.md`)
+      .then((res) => res.text())
+      .then((text) => setQA(text));
+  }, [plugin]);
+
+  const handleTest = async () => {
+    if (!plugin) {
+      return;
+    }
+
+    const [success] = await operator(
+      async () =>
+        await API.connection.testOld(plugin, {
+          ...paramsMap[plugin],
+          ...payload,
+        }),
+      {
+        setOperating: setTesting,
+      },
+    );
+
+    if (success) {
+      setTestStatus(true);
+    }
+  };
+
+  const handleSubmit = async () => {
+    if (!plugin) {
+      return;
+    }
+
+    const [success] = await operator(
+      async () => {
+        const connection = await API.connection.create(plugin, {
+          name: `${plugin}-${Date.now()}`,
+          ...paramsMap[plugin],
+          ...payload,
+        });
+
+        const newRecords = [...records, { plugin, connectionId: connection.id 
}];
+
+        setRecords(newRecords);
+
+        await API.store.set('onboard', {
+          step: 3,
+          records: newRecords,
+          done,
+          projectName,
+          plugin,
+        });
+      },
+      {
+        setOperating,
+        hideToast: true,
+      },
+    );
+
+    if (success) {
+      setStep(step + 1);
+    }
+  };
+
+  if (!plugin) {
+    return null;
+  }
+
+  return (
+    <>
+      <S.StepContent>
+        {['github', 'gitlab', 'azuredevops'].includes(plugin) && (
+          <div className="content">
+            <ConnectionToken
+              type="create"
+              label="Personal Access Token"
+              subLabel={`Create a personal access token in ${config.name}`}
+              initialValue=""
+              value={payload.token}
+              setValue={(token) => setPayload({ ...payload, token })}
+              error=""
+              setError={() => {}}
+            />
+            <Button style={{ marginTop: 16 }} disabled={!payload.token} 
loading={testing} onClick={handleTest}>
+              Connect
+            </Button>
+          </div>
+        )}
+        {['bitbucket'].includes(plugin) && (
+          <div className="content">
+            <ConnectionUsername
+              initialValue=""
+              value={payload.username}
+              setValue={(username) => setPayload({ ...payload, username })}
+              error=""
+              setError={() => {}}
+            />
+            <ConnectionPassword
+              type="create"
+              label="App Password"
+              initialValue=""
+              value={payload.password}
+              setValue={(password) => setPayload({ ...payload, password })}
+              error=""
+              setError={() => {}}
+            />
+            <Button disabled={!payload.username || !payload.password} 
loading={testing} onClick={handleTest}>
+              Connect
+            </Button>
+          </div>
+        )}
+        <Markdown className="qa" rehypePlugins={[rehypeRaw]}>
+          {QA}
+        </Markdown>
+      </S.StepContent>
+      <Flex style={{ marginTop: 36 }} justify="space-between">
+        <Button ghost type="primary" loading={operating} onClick={() => 
setStep(step - 1)}>
+          Previous Step
+        </Button>
+        <Button type="primary" loading={operating} disabled={!testStaus} 
onClick={handleSubmit}>
+          Next Step
+        </Button>
+      </Flex>
+    </>
+  );
+};
diff --git a/config-ui/src/routes/onboard/step-3.tsx 
b/config-ui/src/routes/onboard/step-3.tsx
new file mode 100644
index 000000000..c66e3f9ab
--- /dev/null
+++ b/config-ui/src/routes/onboard/step-3.tsx
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { useState, useContext, useEffect, useMemo } from 'react';
+import { Flex, Button } from 'antd';
+import dayjs from 'dayjs';
+import Markdown from 'react-markdown';
+import rehypeRaw from 'rehype-raw';
+
+import API from '@/api';
+import { cronPresets } from '@/config';
+import { IBPMode } from '@/types';
+import { DataScopeRemote, getPluginScopeId } from '@/plugins';
+import { operator, formatTime } from '@/utils';
+
+import { Context } from './context';
+import * as S from './styled';
+
+export const Step3 = () => {
+  const [QA, setQA] = useState('');
+  const [operating, setOperating] = useState(false);
+  const [scopes, setScopes] = useState<any[]>([]);
+
+  const { step, records, done, projectName, plugin, setStep } = 
useContext(Context);
+
+  useEffect(() => {
+    fetch(`/onboard/step-3/${plugin}.md`)
+      .then((res) => res.text())
+      .then((text) => setQA(text));
+  }, [plugin]);
+
+  const presets = useMemo(() => cronPresets.map((preset) => preset.config), 
[]);
+  const connectionId = useMemo(() => {
+    const record = records.find((it) => it.plugin === plugin);
+    return record?.connectionId ?? null;
+  }, [plugin, records]);
+
+  const handleSubmit = async () => {
+    if (!projectName || !plugin || !connectionId) {
+      return;
+    }
+
+    const [success] = await operator(
+      async () => {
+        // 1. create a new project
+        await API.project.create({
+          name: projectName,
+          description: '',
+          metrics: [
+            {
+              pluginName: 'dora',
+              pluginOption: '',
+              enable: false,
+            },
+          ],
+        });
+
+        // 2. add a data scope to the connection
+        await API.scope.batch(plugin, connectionId, { data: scopes.map((it) => 
it.data) });
+
+        // 3. create a new blueprint
+        const blueprint = await API.blueprint.create({
+          name: `${projectName}-Blueprint`,
+          projectName,
+          mode: IBPMode.NORMAL,
+          enable: true,
+          cronConfig: presets[0],
+          isManual: false,
+          skipOnFail: true,
+          timeAfter: formatTime(dayjs().subtract(6, 
'month').startOf('day').toDate(), 'YYYY-MM-DD[T]HH:mm:ssZ'),
+          connections: [
+            {
+              pluginName: plugin,
+              connectionId,
+              scopes: scopes.map((it) => ({
+                scopeId: getPluginScopeId(plugin, it.data),
+              })),
+            },
+          ],
+        });
+
+        // 4. trigger this blueprint
+        await API.blueprint.trigger(blueprint.id, { skipCollectors: false, 
fullSync: false });
+
+        const newRecords = records.map((it) =>
+          it.plugin !== plugin
+            ? it
+            : {
+                ...it,
+                scopeId: getPluginScopeId(plugin, scopes[0].data),
+                scopeName: scopes[0]?.fullName ?? scopes[0].name,
+              },
+        );
+
+        // 5. update store
+        await API.store.set('onboard', {
+          step: 4,
+          records: newRecords,
+          done,
+          projectName,
+          plugin,
+        });
+      },
+      {
+        setOperating,
+        hideToast: true,
+      },
+    );
+
+    if (success) {
+      setStep(step + 1);
+    }
+  };
+
+  if (!plugin || !connectionId) {
+    return null;
+  }
+
+  return (
+    <>
+      <S.StepContent>
+        <div className="content">
+          <DataScopeRemote
+            mode="single"
+            plugin={plugin}
+            connectionId={connectionId}
+            selectedScope={scopes}
+            onChangeSelectedScope={setScopes}
+            footer={null}
+          />
+        </div>
+        <Markdown className="qa" rehypePlugins={[rehypeRaw]}>
+          {QA}
+        </Markdown>
+      </S.StepContent>
+      <Flex style={{ marginTop: 36 }} justify="space-between">
+        <Button ghost type="primary" loading={operating} onClick={() => 
setStep(step - 1)}>
+          Previous Step
+        </Button>
+        <Button type="primary" loading={operating} onClick={handleSubmit}>
+          Next Step
+        </Button>
+      </Flex>
+    </>
+  );
+};
diff --git a/config-ui/src/routes/project/home/styled.ts 
b/config-ui/src/routes/onboard/step-4.tsx
similarity index 54%
rename from config-ui/src/routes/project/home/styled.ts
rename to config-ui/src/routes/onboard/step-4.tsx
index 3c73c29b7..e2d51cfed 100644
--- a/config-ui/src/routes/project/home/styled.ts
+++ b/config-ui/src/routes/onboard/step-4.tsx
@@ -16,19 +16,38 @@
  *
  */
 
+import { SmileFilled } from '@ant-design/icons';
+import { theme } from 'antd';
 import styled from 'styled-components';
 
-export const DialogWrapper = styled.div``;
+import * as S from './styled';
 
-export const Label = styled.label`
-  font-size: 16px;
-  font-weight: 600;
-`;
+const Top = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: 100px;
+  margin-bottom: 24px;
+  height: 70px;
 
-export const LabelInfo = styled.i`
-  color: #ff8b8b;
+  span.text {
+    margin-left: 8px;
+    font-size: 20px;
+  }
 `;
 
-export const LabelDescription = styled.p`
-  margin: 0;
-`;
+export const Step4 = () => {
+  const {
+    token: { green5 },
+  } = theme.useToken();
+
+  return (
+    <>
+      <Top>
+        <SmileFilled style={{ fontSize: 36, color: green5 }} />
+        <span className="text">Congratulations!You have successfully connected 
to your first repository!</span>
+      </Top>
+      <S.StepContent style={{ padding: 24 }}></S.StepContent>
+    </>
+  );
+};
diff --git a/config-ui/src/routes/onboard/styled.ts 
b/config-ui/src/routes/onboard/styled.ts
new file mode 100644
index 000000000..976060653
--- /dev/null
+++ b/config-ui/src/routes/onboard/styled.ts
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import styled from 'styled-components';
+
+export const Wrapper = styled.div`
+  width: 100%;
+  height: 100vh;
+  background-color: #f9f9fa;
+`;
+
+export const Inner = styled.div`
+  margin: 0 auto;
+  padding: 36px 0;
+  width: 1200px;
+`;
+
+export const Header = styled.div`
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+`;
+
+export const Content = styled.div`
+  margin: 0 auto;
+  width: 860px;
+`;
+
+export const Step = styled.ul`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: 100px;
+  margin-bottom: 50px;
+`;
+
+export const StepItem = styled.li<{ $actived: boolean; $activedColor: string 
}>`
+  display: flex;
+  align-items: center;
+  position: relative;
+
+  span:first-child {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 8px;
+    width: 32px;
+    height: 32px;
+    color: rgba(0, 0, 0, 0.25);
+    border: 1px solid rgba(0, 0, 0, 0.25);
+    border-radius: 50%;
+
+    ${({ $actived, $activedColor }) => ($actived ? `color: #fff; 
background-color: ${$activedColor};` : '')}
+  }
+
+  span:last-child {
+    ${({ $actived }) =>
+      $actived
+        ? `
+    font-size: 24px;
+    font-weight: 600;`
+        : ''}
+  }
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: 18px;
+    left: -150px;
+    width: 100px;
+    height: 1px;
+    background-color: rgba(0, 0, 0, 0.25);
+  }
+
+  &:first-child::before {
+    display: none;
+  }
+`;
+
+export const StepContent = styled.div`
+  display: flex;
+  height: 450px;
+  background-color: #fff;
+  box-shadow: 0px 2.4px 4.8px -0.8px rgba(0, 0, 0, 0.1), 0px 1.6px 8px 0px 
rgba(0, 0, 0, 0.07);
+
+  .content {
+    flex: 0 0 540px;
+    padding: 24px;
+  }
+
+  .qa {
+    flex: auto;
+    padding: 24px;
+    font-size: 14px;
+    border-left: 1px solid #f0f0f0;
+    overflow-y: auto;
+
+    img {
+      width: 100%;
+    }
+
+    h5 {
+      margin-top: 16px;
+      margin-bottom: 16px;
+    }
+
+    ul {
+      padding-left: 1em;
+      list-style: disc;
+    }
+
+    ol {
+      padding-left: 1.5em;
+    }
+
+    li {
+      line-height: 20px;
+    }
+
+    p {
+      color: #6c6c6c;
+    }
+
+    code {
+      padding: 2px;
+      font-size: 12px;
+      font-family: Menlo;
+      line-height: 20px;
+      border-radius: 3px;
+      border: #f0f0f0;
+      background: #f5f5f5;
+    }
+  }
+`;
diff --git a/config-ui/src/routes/project/home/index.tsx 
b/config-ui/src/routes/project/home/index.tsx
index 69dc22e56..27474a002 100644
--- a/config-ui/src/routes/project/home/index.tsx
+++ b/config-ui/src/routes/project/home/index.tsx
@@ -28,14 +28,13 @@ import { getCron, cronPresets, PATHS } from '@/config';
 import { ConnectionName } from '@/features';
 import { useRefreshData } from '@/hooks';
 import { DOC_URL } from '@/release';
+import { OnboardCard } from '@/routes/onboard/card';
 import { formatTime, operator } from '@/utils';
 import { PipelineStatus } from '@/routes/pipeline';
 import { IBlueprint, IBPMode } from '@/types';
 
 import { validName } from '../utils';
 
-import * as S from './styled';
-
 export const ProjectHomePage = () => {
   const [version, setVersion] = useState(1);
   const [page, setPage] = useState(1);
@@ -119,6 +118,7 @@ export const ProjectHomePage = () => {
 
   return (
     <PageHeader breadcrumbs={[{ name: 'Projects', path: PATHS.PROJECTS() }]}>
+      <OnboardCard style={{ marginBottom: 32 }} />
       <Flex style={{ marginBottom: 16 }} justify="flex-end">
         <Button type="primary" icon={<PlusOutlined />} 
onClick={handleShowDialog}>
           New Project
@@ -218,36 +218,33 @@ export const ProjectHomePage = () => {
         onOk={handleCreate}
         onCancel={handleHideDialog}
       >
-        <S.DialogWrapper>
-          <Block
-            title="Project Name"
-            description="Give your project a unique name with letters, 
numbers, -, _ or /"
-            required
-          >
-            <Input
-              style={{ width: 386 }}
-              placeholder="Your Project Name"
-              value={name}
-              maxLength={100}
-              onChange={(e) => setName(e.target.value)}
-            />
-          </Block>
-          <Block
-            title="Project Settings"
-            description={
-              <>
-                <ExternalLink link={DOC_URL.DORA}>DORA metrics</ExternalLink>
-                <span style={{ marginLeft: 4 }}>
-                  are four widely-adopted metrics for measuring software 
delivery performance.
-                </span>
-              </>
-            }
-          >
-            <Checkbox checked={enableDora} onChange={(e) => 
setEnableDora(e.target.checked)}>
-              Enable DORA Metrics
-            </Checkbox>
-          </Block>
-        </S.DialogWrapper>
+        <Block
+          title="Project Name"
+          description="Give your project a unique name with letters, numbers, 
-, _ or /"
+          required
+        >
+          <Input
+            style={{ width: 386 }}
+            placeholder="Your Project Name"
+            value={name}
+            onChange={(e) => setName(e.target.value)}
+          />
+        </Block>
+        <Block
+          title="Project Settings"
+          description={
+            <>
+              <ExternalLink link={DOC_URL.DORA}>DORA metrics</ExternalLink>
+              <span style={{ marginLeft: 4 }}>
+                are four widely-adopted metrics for measuring software 
delivery performance.
+              </span>
+            </>
+          }
+        >
+          <Checkbox checked={enableDora} onChange={(e) => 
setEnableDora(e.target.checked)}>
+            Enable DORA Metrics
+          </Checkbox>
+        </Block>
       </Modal>
     </PageHeader>
   );
diff --git a/config-ui/vite.config.ts b/config-ui/vite.config.ts
index cf3899f0a..bbcb76b4d 100644
--- a/config-ui/vite.config.ts
+++ b/config-ui/vite.config.ts
@@ -23,7 +23,7 @@ import svgr from 'vite-plugin-svgr';
 
 // https://vitejs.dev/config/
 export default defineConfig({
-  plugins: [svgr(), react()],
+  plugins: [react(), svgr()],
 
   envPrefix: 'DEVLAKE_',
 
diff --git a/config-ui/yarn.lock b/config-ui/yarn.lock
index 8174fd80d..27acc8d19 100644
--- a/config-ui/yarn.lock
+++ b/config-ui/yarn.lock
@@ -2511,7 +2511,25 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/estree@npm:1.0.5, @types/estree@npm:^1.0.0":
+"@types/debug@npm:^4.0.0":
+  version: 4.1.12
+  resolution: "@types/debug@npm:4.1.12"
+  dependencies:
+    "@types/ms": "*"
+  checksum: 
47876a852de8240bfdaf7481357af2b88cb660d30c72e73789abf00c499d6bc7cd5e52f41c915d1b9cd8ec9fef5b05688d7b7aef17f7f272c2d04679508d1053
+  languageName: node
+  linkType: hard
+
+"@types/estree-jsx@npm:^1.0.0":
+  version: 1.0.5
+  resolution: "@types/estree-jsx@npm:1.0.5"
+  dependencies:
+    "@types/estree": "*"
+  checksum: 
a028ab0cd7b2950168a05c6a86026eb3a36a54a4adfae57f13911d7b49dffe573d9c2b28421b2d029b49b3d02fcd686611be2622dc3dad6d9791166c083f6008
+  languageName: node
+  linkType: hard
+
+"@types/estree@npm:*, @types/estree@npm:1.0.5, @types/estree@npm:^1.0.0":
   version: 1.0.5
   resolution: "@types/estree@npm:1.0.5"
   checksum: 
dd8b5bed28e6213b7acd0fb665a84e693554d850b0df423ac8076cc3ad5823a6bc26b0251d080bdc545af83179ede51dd3f6fa78cad2c46ed1f29624ddf3e41a
@@ -2525,6 +2543,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/hast@npm:^3.0.0":
+  version: 3.0.4
+  resolution: "@types/hast@npm:3.0.4"
+  dependencies:
+    "@types/unist": "*"
+  checksum: 
7a973e8d16fcdf3936090fa2280f408fb2b6a4f13b42edeb5fbd614efe042b82eac68e298e556d50f6b4ad585a3a93c353e9c826feccdc77af59de8dd400d044
+  languageName: node
+  linkType: hard
+
 "@types/history@npm:^4.7.11":
   version: 4.7.11
   resolution: "@types/history@npm:4.7.11"
@@ -2563,6 +2590,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/mdast@npm:^4.0.0":
+  version: 4.0.3
+  resolution: "@types/mdast@npm:4.0.3"
+  dependencies:
+    "@types/unist": "*"
+  checksum: 
345c5a22fccf05f35239ea6313ee4aaf6ebed5927c03ac79744abccb69b9ba5e692f9b771e36a012b79e17429082cada30f579e9c43b8a54e0ffb365431498b6
+  languageName: node
+  linkType: hard
+
+"@types/ms@npm:*":
+  version: 0.7.34
+  resolution: "@types/ms@npm:0.7.34"
+  checksum: 
f38d36e7b6edecd9badc9cf50474159e9da5fa6965a75186cceaf883278611b9df6669dc3a3cc122b7938d317b68a9e3d573d316fcb35d1be47ec9e468c6bd8a
+  languageName: node
+  linkType: hard
+
 "@types/node@npm:^18.19.20":
   version: 18.19.20
   resolution: "@types/node@npm:18.19.20"
@@ -2677,6 +2720,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/unist@npm:*, @types/unist@npm:^3.0.0":
+  version: 3.0.2
+  resolution: "@types/unist@npm:3.0.2"
+  checksum: 
3d04d0be69316e5f14599a0d993a208606c12818cf631fd399243d1dc7a9bd8a3917d6066baa6abc290814afbd744621484756803c80cba892c39cd4b4a85616
+  languageName: node
+  linkType: hard
+
+"@types/unist@npm:^2.0.0":
+  version: 2.0.10
+  resolution: "@types/unist@npm:2.0.10"
+  checksum: 
e2924e18dedf45f68a5c6ccd6015cd62f1643b1b43baac1854efa21ae9e70505db94290434a23da1137d9e31eb58e54ca175982005698ac37300a1c889f6c4aa
+  languageName: node
+  linkType: hard
+
 "@types/use-sync-external-store@npm:^0.0.3":
   version: 0.0.3
   resolution: "@types/use-sync-external-store@npm:0.0.3"
@@ -2816,7 +2873,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@ungap/structured-clone@npm:^1.2.0":
+"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0":
   version: 1.2.0
   resolution: "@ungap/structured-clone@npm:1.2.0"
   checksum: 
4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524
@@ -3309,6 +3366,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"bail@npm:^2.0.0":
+  version: 2.0.2
+  resolution: "bail@npm:2.0.2"
+  checksum: 
aab4e8ccdc8d762bf3fdfce8e706601695620c0c2eda256dd85088dc0be3cfd7ff126f6e99c2bee1f24f5d418414aacf09d7f9702f16d6963df2fa488cda8824
+  languageName: node
+  linkType: hard
+
 "balanced-match@npm:^1.0.0":
   version: 1.0.2
   resolution: "balanced-match@npm:1.0.2"
@@ -3419,6 +3483,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"ccount@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "ccount@npm:2.0.1"
+  checksum: 
48193dada54c9e260e0acf57fc16171a225305548f9ad20d5471e0f7a8c026aedd8747091dccb0d900cde7df4e4ddbd235df0d8de4a64c71b12f0d3303eeafd4
+  languageName: node
+  linkType: hard
+
 "chalk@npm:5.3.0":
   version: 5.3.0
   resolution: "chalk@npm:5.3.0"
@@ -3447,6 +3518,34 @@ __metadata:
   languageName: node
   linkType: hard
 
+"character-entities-html4@npm:^2.0.0":
+  version: 2.1.0
+  resolution: "character-entities-html4@npm:2.1.0"
+  checksum: 
7034aa7c7fa90309667f6dd50499c8a760c3d3a6fb159adb4e0bada0107d194551cdbad0714302f62d06ce4ed68565c8c2e15fdef2e8f8764eb63fa92b34b11d
+  languageName: node
+  linkType: hard
+
+"character-entities-legacy@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "character-entities-legacy@npm:3.0.0"
+  checksum: 
7582af055cb488b626d364b7d7a4e46b06abd526fb63c0e4eb35bcb9c9799cc4f76b39f34fdccef2d1174ac95e53e9ab355aae83227c1a2505877893fce77731
+  languageName: node
+  linkType: hard
+
+"character-entities@npm:^2.0.0":
+  version: 2.0.2
+  resolution: "character-entities@npm:2.0.2"
+  checksum: 
cf1643814023697f725e47328fcec17923b8f1799102a8a79c1514e894815651794a2bffd84bb1b3a4b124b050154e4529ed6e81f7c8068a734aecf07a6d3def
+  languageName: node
+  linkType: hard
+
+"character-reference-invalid@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "character-reference-invalid@npm:2.0.1"
+  checksum: 
98d3b1a52ae510b7329e6ee7f6210df14f1e318c5415975d4c9e7ee0ef4c07875d47c6e74230c64551f12f556b4a8ccc24d9f3691a2aa197019e72a95e9297ee
+  languageName: node
+  linkType: hard
+
 "chownr@npm:^2.0.0":
   version: 2.0.0
   resolution: "chownr@npm:2.0.0"
@@ -3535,6 +3634,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"comma-separated-tokens@npm:^2.0.0":
+  version: 2.0.3
+  resolution: "comma-separated-tokens@npm:2.0.3"
+  checksum: 
e3bf9e0332a5c45f49b90e79bcdb4a7a85f28d6a6f0876a94f1bb9b2bfbdbbb9292aac50e1e742d8c0db1e62a0229a106f57917e2d067fca951d81737651700d
+  languageName: node
+  linkType: hard
+
 "commander@npm:11.0.0":
   version: 11.0.0
   resolution: "commander@npm:11.0.0"
@@ -3588,16 +3694,18 @@ __metadata:
     husky: ^8.0.0
     lint-staged: ^13.1.0
     lodash: ^4.17.21
-    miller-columns-select: 1.3.2
+    miller-columns-select: 1.4.0
     prettier: ^2.7.1
     react: ^18.2.0
     react-copy-to-clipboard: ^5.1.0
     react-dom: ^18.2.0
     react-is: ^18.2.0
+    react-markdown: ^9.0.1
     react-redux: ^9.1.0
     react-router-dom: ^6.22.2
     react-transition-group: ^4.4.5
     redux: ^5.0.1
+    rehype-raw: ^7.0.0
     styled-components: ^6.1.8
     typescript: ^5.1.6
     vite: ^5.1.4
@@ -3742,7 +3850,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.0, debug@npm:^4.1.1, 
debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4":
+"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, 
debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4":
   version: 4.3.4
   resolution: "debug@npm:4.3.4"
   dependencies:
@@ -3763,6 +3871,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"decode-named-character-reference@npm:^1.0.0":
+  version: 1.0.2
+  resolution: "decode-named-character-reference@npm:1.0.2"
+  dependencies:
+    character-entities: ^2.0.0
+  checksum: 
f4c71d3b93105f20076052f9cb1523a22a9c796b8296cd35eef1ca54239c78d182c136a848b83ff8da2071e3ae2b1d300bf29d00650a6d6e675438cc31b11d78
+  languageName: node
+  linkType: hard
+
 "decode-uri-component@npm:^0.2.0":
   version: 0.2.2
   resolution: "decode-uri-component@npm:0.2.2"
@@ -3806,13 +3923,22 @@ __metadata:
   languageName: node
   linkType: hard
 
-"dequal@npm:^2.0.3":
+"dequal@npm:^2.0.0, dequal@npm:^2.0.3":
   version: 2.0.3
   resolution: "dequal@npm:2.0.3"
   checksum: 
8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90
   languageName: node
   linkType: hard
 
+"devlop@npm:^1.0.0, devlop@npm:^1.1.0":
+  version: 1.1.0
+  resolution: "devlop@npm:1.1.0"
+  dependencies:
+    dequal: ^2.0.0
+  checksum: 
d2ff650bac0bb6ef08c48f3ba98640bb5fec5cce81e9957eb620408d1bab1204d382a45b785c6b3314dc867bb0684936b84c6867820da6db97cbb5d3c15dd185
+  languageName: node
+  linkType: hard
+
 "dir-glob@npm:^3.0.1":
   version: 3.0.1
   resolution: "dir-glob@npm:3.0.1"
@@ -4491,6 +4617,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"estree-util-is-identifier-name@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "estree-util-is-identifier-name@npm:3.0.0"
+  checksum: 
ea3909f0188ea164af0aadeca87c087e3e5da78d76da5ae9c7954ff1340ea3e4679c4653bbf4299ffb70caa9b322218cc1128db2541f3d2976eb9704f9857787
+  languageName: node
+  linkType: hard
+
 "estree-walker@npm:^2.0.2":
   version: 2.0.2
   resolution: "estree-walker@npm:2.0.2"
@@ -4536,6 +4669,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"extend@npm:^3.0.0":
+  version: 3.0.2
+  resolution: "extend@npm:3.0.2"
+  checksum: 
a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515
+  languageName: node
+  linkType: hard
+
 "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3":
   version: 3.1.3
   resolution: "fast-deep-equal@npm:3.1.3"
@@ -4965,6 +5105,112 @@ __metadata:
   languageName: node
   linkType: hard
 
+"hast-util-from-parse5@npm:^8.0.0":
+  version: 8.0.1
+  resolution: "hast-util-from-parse5@npm:8.0.1"
+  dependencies:
+    "@types/hast": ^3.0.0
+    "@types/unist": ^3.0.0
+    devlop: ^1.0.0
+    hastscript: ^8.0.0
+    property-information: ^6.0.0
+    vfile: ^6.0.0
+    vfile-location: ^5.0.0
+    web-namespaces: ^2.0.0
+  checksum: 
fdd1ab8b03af13778ecb94ef9a58b1e3528410cdfceb3d6bb7600508967d0d836b451bc7bc3baf66efb7c730d3d395eea4bb1b30352b0162823d9f0de976774b
+  languageName: node
+  linkType: hard
+
+"hast-util-parse-selector@npm:^4.0.0":
+  version: 4.0.0
+  resolution: "hast-util-parse-selector@npm:4.0.0"
+  dependencies:
+    "@types/hast": ^3.0.0
+  checksum: 
76087670d3b0b50b23a6cb70bca53a6176d6608307ccdbb3ed18b650b82e7c3513bfc40348f1389dc0c5ae872b9a768851f4335f44654abd7deafd6974c52402
+  languageName: node
+  linkType: hard
+
+"hast-util-raw@npm:^9.0.0":
+  version: 9.0.2
+  resolution: "hast-util-raw@npm:9.0.2"
+  dependencies:
+    "@types/hast": ^3.0.0
+    "@types/unist": ^3.0.0
+    "@ungap/structured-clone": ^1.0.0
+    hast-util-from-parse5: ^8.0.0
+    hast-util-to-parse5: ^8.0.0
+    html-void-elements: ^3.0.0
+    mdast-util-to-hast: ^13.0.0
+    parse5: ^7.0.0
+    unist-util-position: ^5.0.0
+    unist-util-visit: ^5.0.0
+    vfile: ^6.0.0
+    web-namespaces: ^2.0.0
+    zwitch: ^2.0.0
+  checksum: 
27fd7c723b3b1e06481cd85ca20b447d58d340c53abd2bd61f4a502982109d16aa17b3d71db2ef7c9d24bd627e306ad81cbcaf98c146a3641ba150db731e644c
+  languageName: node
+  linkType: hard
+
+"hast-util-to-jsx-runtime@npm:^2.0.0":
+  version: 2.3.0
+  resolution: "hast-util-to-jsx-runtime@npm:2.3.0"
+  dependencies:
+    "@types/estree": ^1.0.0
+    "@types/hast": ^3.0.0
+    "@types/unist": ^3.0.0
+    comma-separated-tokens: ^2.0.0
+    devlop: ^1.0.0
+    estree-util-is-identifier-name: ^3.0.0
+    hast-util-whitespace: ^3.0.0
+    mdast-util-mdx-expression: ^2.0.0
+    mdast-util-mdx-jsx: ^3.0.0
+    mdast-util-mdxjs-esm: ^2.0.0
+    property-information: ^6.0.0
+    space-separated-tokens: ^2.0.0
+    style-to-object: ^1.0.0
+    unist-util-position: ^5.0.0
+    vfile-message: ^4.0.0
+  checksum: 
599a97c6ec61c1430776813d7fb42e6f96032bf4a04dfcbb8eceef3bc8d1845ecf242387a4426b9d3f52320dbbfa26450643b81124b3d6a0b9bbb0fff4d0ba83
+  languageName: node
+  linkType: hard
+
+"hast-util-to-parse5@npm:^8.0.0":
+  version: 8.0.0
+  resolution: "hast-util-to-parse5@npm:8.0.0"
+  dependencies:
+    "@types/hast": ^3.0.0
+    comma-separated-tokens: ^2.0.0
+    devlop: ^1.0.0
+    property-information: ^6.0.0
+    space-separated-tokens: ^2.0.0
+    web-namespaces: ^2.0.0
+    zwitch: ^2.0.0
+  checksum: 
137469209cb2b32b57387928878dc85310fbd5afa4807a8da69529199bb1d19044bfc95b50c3dc68d4fb2b6cb8bf99b899285597ab6ab318f50422eefd5599dd
+  languageName: node
+  linkType: hard
+
+"hast-util-whitespace@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "hast-util-whitespace@npm:3.0.0"
+  dependencies:
+    "@types/hast": ^3.0.0
+  checksum: 
41d93ccce218ba935dc3c12acdf586193c35069489c8c8f50c2aa824c00dec94a3c78b03d1db40fa75381942a189161922e4b7bca700b3a2cc779634c351a1e4
+  languageName: node
+  linkType: hard
+
+"hastscript@npm:^8.0.0":
+  version: 8.0.0
+  resolution: "hastscript@npm:8.0.0"
+  dependencies:
+    "@types/hast": ^3.0.0
+    comma-separated-tokens: ^2.0.0
+    hast-util-parse-selector: ^4.0.0
+    property-information: ^6.0.0
+    space-separated-tokens: ^2.0.0
+  checksum: 
ae3c20223e7b847320c0f98b6fb3c763ebe1bf3913c5805fbc176cf84553a9db1117ca34cf842a5235890b4b9ae0e94501bfdc9a9b870a5dbf5fc52426db1097
+  languageName: node
+  linkType: hard
+
 "hoist-non-react-statics@npm:^3.0.0, hoist-non-react-statics@npm:^3.3.0":
   version: 3.3.2
   resolution: "hoist-non-react-statics@npm:3.3.2"
@@ -4974,6 +5220,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"html-url-attributes@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "html-url-attributes@npm:3.0.0"
+  checksum: 
9f499d33e6ddff6c2d2766fd73d2f22f3c370b4e485a92b0b2938303665b306dc7f36b2724c9466764e8f702351c01f342f5ec933be41a31c1fa40b72087b91d
+  languageName: node
+  linkType: hard
+
+"html-void-elements@npm:^3.0.0":
+  version: 3.0.0
+  resolution: "html-void-elements@npm:3.0.0"
+  checksum: 
59be397525465a7489028afa064c55763d9cccd1d7d9f630cca47137317f0e897a9ca26cef7e745e7cff1abc44260cfa407742b243a54261dfacd42230e94fce
+  languageName: node
+  linkType: hard
+
 "http-cache-semantics@npm:^4.1.1":
   version: 4.1.1
   resolution: "http-cache-semantics@npm:4.1.1"
@@ -5081,6 +5341,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"inline-style-parser@npm:0.2.2":
+  version: 0.2.2
+  resolution: "inline-style-parser@npm:0.2.2"
+  checksum: 
698893d6542d4e7c0377936a1c7daec34a197765bd77c5599384756a95ce8804e6b79347b783aa591d5e9c6f3d33dae74c6d4cad3a94647eb05f3a785e927a3f
+  languageName: node
+  linkType: hard
+
 "internal-slot@npm:^1.0.5, internal-slot@npm:^1.0.7":
   version: 1.0.7
   resolution: "internal-slot@npm:1.0.7"
@@ -5109,6 +5376,23 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-alphabetical@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "is-alphabetical@npm:2.0.1"
+  checksum: 
56207db8d9de0850f0cd30f4966bf731eb82cedfe496cbc2e97e7c3bacaf66fc54a972d2d08c0d93bb679cb84976a05d24c5ad63de56fabbfc60aadae312edaa
+  languageName: node
+  linkType: hard
+
+"is-alphanumerical@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "is-alphanumerical@npm:2.0.1"
+  dependencies:
+    is-alphabetical: ^2.0.0
+    is-decimal: ^2.0.0
+  checksum: 
87acc068008d4c9c4e9f5bd5e251041d42e7a50995c77b1499cf6ed248f971aadeddb11f239cabf09f7975ee58cac7a48ffc170b7890076d8d227b24a68663c9
+  languageName: node
+  linkType: hard
+
 "is-array-buffer@npm:^3.0.4":
   version: 3.0.4
   resolution: "is-array-buffer@npm:3.0.4"
@@ -5179,6 +5463,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-decimal@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "is-decimal@npm:2.0.1"
+  checksum: 
97132de7acdce77caa7b797632970a2ecd649a88e715db0e4dbc00ab0708b5e7574ba5903962c860cd4894a14fd12b100c0c4ac8aed445cf6f55c6cf747a4158
+  languageName: node
+  linkType: hard
+
 "is-extglob@npm:^2.1.1":
   version: 2.1.1
   resolution: "is-extglob@npm:2.1.1"
@@ -5227,6 +5518,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-hexadecimal@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "is-hexadecimal@npm:2.0.1"
+  checksum: 
66a2ea85994c622858f063f23eda506db29d92b52580709eb6f4c19550552d4dcf3fb81952e52f7cf972097237959e00adc7bb8c9400cd12886e15bf06145321
+  languageName: node
+  linkType: hard
+
 "is-lambda@npm:^1.0.1":
   version: 1.0.1
   resolution: "is-lambda@npm:1.0.1"
@@ -5271,6 +5569,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"is-plain-obj@npm:^4.0.0":
+  version: 4.1.0
+  resolution: "is-plain-obj@npm:4.1.0"
+  checksum: 
6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce
+  languageName: node
+  linkType: hard
+
 "is-regex@npm:^1.1.4":
   version: 1.1.4
   resolution: "is-regex@npm:1.1.4"
@@ -5661,6 +5966,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"longest-streak@npm:^3.0.0":
+  version: 3.1.0
+  resolution: "longest-streak@npm:3.1.0"
+  checksum: 
d7f952ed004cbdb5c8bcfc4f7f5c3d65449e6c5a9e9be4505a656e3df5a57ee125f284286b4bf8ecea0c21a7b3bf2b8f9001ad506c319b9815ad6a63a47d0fd0
+  languageName: node
+  linkType: hard
+
 "loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
   version: 1.4.0
   resolution: "loose-envify@npm:1.4.0"
@@ -5732,6 +6044,127 @@ __metadata:
   languageName: node
   linkType: hard
 
+"mdast-util-from-markdown@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "mdast-util-from-markdown@npm:2.0.0"
+  dependencies:
+    "@types/mdast": ^4.0.0
+    "@types/unist": ^3.0.0
+    decode-named-character-reference: ^1.0.0
+    devlop: ^1.0.0
+    mdast-util-to-string: ^4.0.0
+    micromark: ^4.0.0
+    micromark-util-decode-numeric-character-reference: ^2.0.0
+    micromark-util-decode-string: ^2.0.0
+    micromark-util-normalize-identifier: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+    unist-util-stringify-position: ^4.0.0
+  checksum: 
4e8d8a46b4b588486c41b80c39da333a91593bc8d60cd7421c6cd3c22003b8e5a62478292fb7bc97b9255b6301a2250cca32340ef43c309156e215453c5b92be
+  languageName: node
+  linkType: hard
+
+"mdast-util-mdx-expression@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "mdast-util-mdx-expression@npm:2.0.0"
+  dependencies:
+    "@types/estree-jsx": ^1.0.0
+    "@types/hast": ^3.0.0
+    "@types/mdast": ^4.0.0
+    devlop: ^1.0.0
+    mdast-util-from-markdown: ^2.0.0
+    mdast-util-to-markdown: ^2.0.0
+  checksum: 
4e1183000e183e07a7264e192889b4fd57372806103031c71b9318967f85fd50a5dd0f92ef14f42c331e77410808f5de3341d7bc8ad4ee91b7fa8f0a30043a8a
+  languageName: node
+  linkType: hard
+
+"mdast-util-mdx-jsx@npm:^3.0.0":
+  version: 3.1.2
+  resolution: "mdast-util-mdx-jsx@npm:3.1.2"
+  dependencies:
+    "@types/estree-jsx": ^1.0.0
+    "@types/hast": ^3.0.0
+    "@types/mdast": ^4.0.0
+    "@types/unist": ^3.0.0
+    ccount: ^2.0.0
+    devlop: ^1.1.0
+    mdast-util-from-markdown: ^2.0.0
+    mdast-util-to-markdown: ^2.0.0
+    parse-entities: ^4.0.0
+    stringify-entities: ^4.0.0
+    unist-util-remove-position: ^5.0.0
+    unist-util-stringify-position: ^4.0.0
+    vfile-message: ^4.0.0
+  checksum: 
33cb8a657702d5bb8d3f658d158f448c45147664cdb2475501a1c467e3a167d75842546296a06f758f07cce4d2a6ba1add405dbdb6caa145a6980c9782e411e2
+  languageName: node
+  linkType: hard
+
+"mdast-util-mdxjs-esm@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "mdast-util-mdxjs-esm@npm:2.0.1"
+  dependencies:
+    "@types/estree-jsx": ^1.0.0
+    "@types/hast": ^3.0.0
+    "@types/mdast": ^4.0.0
+    devlop: ^1.0.0
+    mdast-util-from-markdown: ^2.0.0
+    mdast-util-to-markdown: ^2.0.0
+  checksum: 
1f9dad04d31d59005332e9157ea9510dc1d03092aadbc607a10475c7eec1c158b475aa0601a3a4f74e13097ca735deb8c2d9d37928ddef25d3029fd7c9e14dc3
+  languageName: node
+  linkType: hard
+
+"mdast-util-phrasing@npm:^4.0.0":
+  version: 4.1.0
+  resolution: "mdast-util-phrasing@npm:4.1.0"
+  dependencies:
+    "@types/mdast": ^4.0.0
+    unist-util-is: ^6.0.0
+  checksum: 
3a97533e8ad104a422f8bebb34b3dde4f17167b8ed3a721cf9263c7416bd3447d2364e6d012a594aada40cac9e949db28a060bb71a982231693609034ed5324e
+  languageName: node
+  linkType: hard
+
+"mdast-util-to-hast@npm:^13.0.0":
+  version: 13.1.0
+  resolution: "mdast-util-to-hast@npm:13.1.0"
+  dependencies:
+    "@types/hast": ^3.0.0
+    "@types/mdast": ^4.0.0
+    "@ungap/structured-clone": ^1.0.0
+    devlop: ^1.0.0
+    micromark-util-sanitize-uri: ^2.0.0
+    trim-lines: ^3.0.0
+    unist-util-position: ^5.0.0
+    unist-util-visit: ^5.0.0
+    vfile: ^6.0.0
+  checksum: 
640bc897286af8fe760cd477fb04bbf544a5a897cdc2220ce36fe2f892f067b483334610387aeb969511bd78a2d841a54851079cd676ac513d6a5ff75852514e
+  languageName: node
+  linkType: hard
+
+"mdast-util-to-markdown@npm:^2.0.0":
+  version: 2.1.0
+  resolution: "mdast-util-to-markdown@npm:2.1.0"
+  dependencies:
+    "@types/mdast": ^4.0.0
+    "@types/unist": ^3.0.0
+    longest-streak: ^3.0.0
+    mdast-util-phrasing: ^4.0.0
+    mdast-util-to-string: ^4.0.0
+    micromark-util-decode-string: ^2.0.0
+    unist-util-visit: ^5.0.0
+    zwitch: ^2.0.0
+  checksum: 
3a2cf3957e23b34e2e092e6e76ae72ee0b8745955bd811baba6814cf3a3d916c3fd52264b4b58f3bb3d512a428f84a1e998b6fc7e28434e388a9ae8fb6a9c173
+  languageName: node
+  linkType: hard
+
+"mdast-util-to-string@npm:^4.0.0":
+  version: 4.0.0
+  resolution: "mdast-util-to-string@npm:4.0.0"
+  dependencies:
+    "@types/mdast": ^4.0.0
+  checksum: 
35489fb5710d58cbc2d6c8b6547df161a3f81e0f28f320dfb3548a9393555daf07c310c0c497708e67ed4dfea4a06e5655799e7d631ca91420c288b4525d6c29
+  languageName: node
+  linkType: hard
+
 "merge-stream@npm:^2.0.0":
   version: 2.0.0
   resolution: "merge-stream@npm:2.0.0"
@@ -5746,6 +6179,242 @@ __metadata:
   languageName: node
   linkType: hard
 
+"micromark-core-commonmark@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-core-commonmark@npm:2.0.0"
+  dependencies:
+    decode-named-character-reference: ^1.0.0
+    devlop: ^1.0.0
+    micromark-factory-destination: ^2.0.0
+    micromark-factory-label: ^2.0.0
+    micromark-factory-space: ^2.0.0
+    micromark-factory-title: ^2.0.0
+    micromark-factory-whitespace: ^2.0.0
+    micromark-util-character: ^2.0.0
+    micromark-util-chunked: ^2.0.0
+    micromark-util-classify-character: ^2.0.0
+    micromark-util-html-tag-name: ^2.0.0
+    micromark-util-normalize-identifier: ^2.0.0
+    micromark-util-resolve-all: ^2.0.0
+    micromark-util-subtokenize: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
9c12fb580cf4ce71f60872043bd2794efe129f44d7b2b73afa155bbc0a66b7bc35655ba8cef438a6bd068441837ed3b6dc6ad7e5a18f815462c1750793e03a42
+  languageName: node
+  linkType: hard
+
+"micromark-factory-destination@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-factory-destination@npm:2.0.0"
+  dependencies:
+    micromark-util-character: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
d36e65ed1c072ff4148b016783148ba7c68a078991154625723e24bda3945160268fb91079fb28618e1613c2b6e70390a8ddc544c45410288aa27b413593071a
+  languageName: node
+  linkType: hard
+
+"micromark-factory-label@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-factory-label@npm:2.0.0"
+  dependencies:
+    devlop: ^1.0.0
+    micromark-util-character: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
c021dbd0ed367610d35f2bae21209bc804d1a6d1286ffce458fd6a717f4d7fe581a7cba7d5c2d7a63757c44eb927c80d6a571d6ea7969fae1b48ab6461d109c4
+  languageName: node
+  linkType: hard
+
+"micromark-factory-space@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-factory-space@npm:2.0.0"
+  dependencies:
+    micromark-util-character: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
4ffdcdc2f759887bbb356500cb460b3915ecddcb5d85c3618d7df68ad05d13ed02b1153ee1845677b7d8126df8f388288b84fcf0d943bd9c92bcc71cd7222e37
+  languageName: node
+  linkType: hard
+
+"micromark-factory-title@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-factory-title@npm:2.0.0"
+  dependencies:
+    micromark-factory-space: ^2.0.0
+    micromark-util-character: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
39e1ac23af3554e6e652e56065579bc7faf21ade7b8704b29c175871b4152b7109b790bb3cae0f7e088381139c6bac9553b8400772c3d322e4fa635f813a3578
+  languageName: node
+  linkType: hard
+
+"micromark-factory-whitespace@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-factory-whitespace@npm:2.0.0"
+  dependencies:
+    micromark-factory-space: ^2.0.0
+    micromark-util-character: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
9587c2546d1a58b4d5472b42adf05463f6212d0449455285662d63cd8eaed89c6b159ac82713fcee5f9dd88628c24307d9533cccd8971a2f3f4d48702f8f850a
+  languageName: node
+  linkType: hard
+
+"micromark-util-character@npm:^2.0.0":
+  version: 2.1.0
+  resolution: "micromark-util-character@npm:2.1.0"
+  dependencies:
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
36ee910f84077cf16626fa618cfe46ac25956b3242e3166b8e8e98c5a8c524af7e5bf3d70822264b1fd2d297a36104a7eb7e3462c19c28353eaca7b0d8717594
+  languageName: node
+  linkType: hard
+
+"micromark-util-chunked@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-chunked@npm:2.0.0"
+  dependencies:
+    micromark-util-symbol: ^2.0.0
+  checksum: 
324f95cccdae061332a8241936eaba6ef0782a1e355bac5c607ad2564fd3744929be7dc81651315a2921535747a33243e6a5606bcb64b7a56d49b6d74ea1a3d4
+  languageName: node
+  linkType: hard
+
+"micromark-util-classify-character@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-classify-character@npm:2.0.0"
+  dependencies:
+    micromark-util-character: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
086e52904deffebb793fb1c08c94aabb8901f76958142dfc3a6282890ebaa983b285e69bd602b9d507f1b758ed38e75a994d2ad9fbbefa7de2584f67a16af405
+  languageName: node
+  linkType: hard
+
+"micromark-util-combine-extensions@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-combine-extensions@npm:2.0.0"
+  dependencies:
+    micromark-util-chunked: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
107c47700343f365b4ed81551e18bc3458b573c500e56ac052b2490bd548adc475216e41d2271633a8867fac66fc22ba3e0a2d74a31ed79b9870ca947eb4e3ba
+  languageName: node
+  linkType: hard
+
+"micromark-util-decode-numeric-character-reference@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "micromark-util-decode-numeric-character-reference@npm:2.0.1"
+  dependencies:
+    micromark-util-symbol: ^2.0.0
+  checksum: 
9512507722efd2033a9f08715eeef787fbfe27e23edf55db21423d46d82ab46f76c89b4f960be3f5e50a2d388d89658afc0647989cf256d051e9ea01277a1adb
+  languageName: node
+  linkType: hard
+
+"micromark-util-decode-string@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-decode-string@npm:2.0.0"
+  dependencies:
+    decode-named-character-reference: ^1.0.0
+    micromark-util-character: ^2.0.0
+    micromark-util-decode-numeric-character-reference: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+  checksum: 
a75daf32a4a6b549e9f19b4d833ebfeb09a32a9a1f9ce50f35dec6b6a3e4f9f121f49024ba7f9c91c55ebe792f7c7a332fc9604795181b6a612637df0df5b959
+  languageName: node
+  linkType: hard
+
+"micromark-util-encode@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-encode@npm:2.0.0"
+  checksum: 
853a3f33fce72aaf4ffa60b7f2b6fcfca40b270b3466e1b96561b02185d2bd8c01dd7948bc31a24ac014f4cc854e545ca9a8e9cf7ea46262f9d24c9e88551c66
+  languageName: node
+  linkType: hard
+
+"micromark-util-html-tag-name@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-html-tag-name@npm:2.0.0"
+  checksum: 
d786d4486f93eb0ac5b628779809ca97c5dc60f3c9fc03eb565809831db181cf8cb7f05f9ac76852f3eb35461af0f89fa407b46f3a03f4f97a96754d8dc540d8
+  languageName: node
+  linkType: hard
+
+"micromark-util-normalize-identifier@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-normalize-identifier@npm:2.0.0"
+  dependencies:
+    micromark-util-symbol: ^2.0.0
+  checksum: 
b36da2d3fd102053dadd953ce5c558328df12a63a8ac0e5aad13d4dda8e43b6a5d4a661baafe0a1cd8a260bead4b4a8e6e0e74193dd651e8484225bd4f4e68aa
+  languageName: node
+  linkType: hard
+
+"micromark-util-resolve-all@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-resolve-all@npm:2.0.0"
+  dependencies:
+    micromark-util-types: ^2.0.0
+  checksum: 
31fe703b85572cb3f598ebe32750e59516925c7ff1f66cfe6afaebe0771a395a9eaa770787f2523d3c46082ea80e6c14f83643303740b3d650af7c96ebd30ccc
+  languageName: node
+  linkType: hard
+
+"micromark-util-sanitize-uri@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-sanitize-uri@npm:2.0.0"
+  dependencies:
+    micromark-util-character: ^2.0.0
+    micromark-util-encode: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+  checksum: 
ea4c28bbffcf2430e9aff2d18554296789a8b0a1f54ac24020d1dde76624a7f93e8f2a83e88cd5a846b6d2c4287b71b1142d1b89fa7f1b0363a9b33711a141fe
+  languageName: node
+  linkType: hard
+
+"micromark-util-subtokenize@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-subtokenize@npm:2.0.0"
+  dependencies:
+    devlop: ^1.0.0
+    micromark-util-chunked: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
77d9c7d59c05a20468d49ce2a3640e9cb268c083ccad02322f26c84e1094c25b44f4b8139ef0a247ca11a4fef7620c5bf82fbffd98acdb2989e79cbe7bd8f1db
+  languageName: node
+  linkType: hard
+
+"micromark-util-symbol@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-symbol@npm:2.0.0"
+  checksum: 
fa4a05bff575d9fbf0ad96a1013003e3bb6087ed6b34b609a141b6c0d2137b57df594aca409a95f4c5fda199f227b56a7d8b1f82cea0768df161d8a3a3660764
+  languageName: node
+  linkType: hard
+
+"micromark-util-types@npm:^2.0.0":
+  version: 2.0.0
+  resolution: "micromark-util-types@npm:2.0.0"
+  checksum: 
819fef3ab5770c37893d2a60381fb2694396c8d22803b6e103c830c3a1bc1490363c2b0470bb2acaaddad776dfbc2fc1fcfde39cb63c4f54d95121611672e3d0
+  languageName: node
+  linkType: hard
+
+"micromark@npm:^4.0.0":
+  version: 4.0.0
+  resolution: "micromark@npm:4.0.0"
+  dependencies:
+    "@types/debug": ^4.0.0
+    debug: ^4.0.0
+    decode-named-character-reference: ^1.0.0
+    devlop: ^1.0.0
+    micromark-core-commonmark: ^2.0.0
+    micromark-factory-space: ^2.0.0
+    micromark-util-character: ^2.0.0
+    micromark-util-chunked: ^2.0.0
+    micromark-util-combine-extensions: ^2.0.0
+    micromark-util-decode-numeric-character-reference: ^2.0.0
+    micromark-util-encode: ^2.0.0
+    micromark-util-normalize-identifier: ^2.0.0
+    micromark-util-resolve-all: ^2.0.0
+    micromark-util-sanitize-uri: ^2.0.0
+    micromark-util-subtokenize: ^2.0.0
+    micromark-util-symbol: ^2.0.0
+    micromark-util-types: ^2.0.0
+  checksum: 
b84ab5ab1a0b28c063c52e9c2c9d7d44b954507235c10c9492d66e0b38f7de24bf298f914a1fbdf109f2a57a88cf0412de217c84cfac5fd60e3e42a74dbac085
+  languageName: node
+  linkType: hard
+
 "micromatch@npm:4.0.5, micromatch@npm:^4.0.4":
   version: 4.0.5
   resolution: "micromatch@npm:4.0.5"
@@ -5756,9 +6425,9 @@ __metadata:
   languageName: node
   linkType: hard
 
-"miller-columns-select@npm:1.3.2":
-  version: 1.3.2
-  resolution: "miller-columns-select@npm:1.3.2"
+"miller-columns-select@npm:1.4.0":
+  version: 1.4.0
+  resolution: "miller-columns-select@npm:1.4.0"
   dependencies:
     classnames: ^2.3.2
     react-infinite-scroll-component: ^6.1.0
@@ -5766,7 +6435,7 @@ __metadata:
   peerDependencies:
     react: ^18.2.0
     react-dom: ^18.2.0
-  checksum: 
57a3e2ae0ac28738f83d94a3ca8691639222d2cd62a3ac286ee97ce7e7161f8417d8254ccaa4be4ee8b4e26cc5688444ba8851820f6a3e852a42a6f946e47de0
+  checksum: 
f8f4c32c546bd5cbaf73dad608b6527c7608417d5b54245fe83ff1004e78c7ed0e8a53a09f913b9cc1b6e05a7c31f9dbdc3cf943b309c9a5c401337fa17c2832
   languageName: node
   linkType: hard
 
@@ -6192,6 +6861,22 @@ __metadata:
   languageName: node
   linkType: hard
 
+"parse-entities@npm:^4.0.0":
+  version: 4.0.1
+  resolution: "parse-entities@npm:4.0.1"
+  dependencies:
+    "@types/unist": ^2.0.0
+    character-entities: ^2.0.0
+    character-entities-legacy: ^3.0.0
+    character-reference-invalid: ^2.0.0
+    decode-named-character-reference: ^1.0.0
+    is-alphanumerical: ^2.0.0
+    is-decimal: ^2.0.0
+    is-hexadecimal: ^2.0.0
+  checksum: 
32a6ff5b9acb9d2c4d71537308521fd265e685b9215691df73feedd9edfe041bb6da9f89bd0c35c4a2bc7d58e3e76e399bb6078c2fd7d2a343ff1dd46edbf1bd
+  languageName: node
+  linkType: hard
+
 "parse-json@npm:^5.0.0, parse-json@npm:^5.2.0":
   version: 5.2.0
   resolution: "parse-json@npm:5.2.0"
@@ -6204,6 +6889,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"parse5@npm:^7.0.0":
+  version: 7.1.2
+  resolution: "parse5@npm:7.1.2"
+  dependencies:
+    entities: ^4.4.0
+  checksum: 
59465dd05eb4c5ec87b76173d1c596e152a10e290b7abcda1aecf0f33be49646ea74840c69af975d7887543ea45564801736356c568d6b5e71792fd0f4055713
+  languageName: node
+  linkType: hard
+
 "path-exists@npm:^4.0.0":
   version: 4.0.0
   resolution: "path-exists@npm:4.0.0"
@@ -6375,6 +7069,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"property-information@npm:^6.0.0":
+  version: 6.4.1
+  resolution: "property-information@npm:6.4.1"
+  checksum: 
d9eece5f14b6fea9e6a1fa65fba88554956a58825eb9a5c8327bffee06bcc265117eaeae901871e8e8a5caec8d5e05ce39ab6872d5cef3b49a6f07815b6ef285
+  languageName: node
+  linkType: hard
+
 "proxy-from-env@npm:^1.1.0":
   version: 1.1.0
   resolution: "proxy-from-env@npm:1.1.0"
@@ -7000,6 +7701,27 @@ __metadata:
   languageName: node
   linkType: hard
 
+"react-markdown@npm:^9.0.1":
+  version: 9.0.1
+  resolution: "react-markdown@npm:9.0.1"
+  dependencies:
+    "@types/hast": ^3.0.0
+    devlop: ^1.0.0
+    hast-util-to-jsx-runtime: ^2.0.0
+    html-url-attributes: ^3.0.0
+    mdast-util-to-hast: ^13.0.0
+    remark-parse: ^11.0.0
+    remark-rehype: ^11.0.0
+    unified: ^11.0.0
+    unist-util-visit: ^5.0.0
+    vfile: ^6.0.0
+  peerDependencies:
+    "@types/react": ">=18"
+    react: ">=18"
+  checksum: 
ca1daa650d48b84a5a9771683cdb3f3d2d418247ce0faf73ede3207c65f2a21cdebb9df37afda67f6fc8f0f0a7b9ce00eb239781954a4d6c7ad88ea4df068add
+  languageName: node
+  linkType: hard
+
 "react-redux@npm:^9.1.0":
   version: 9.1.0
   resolution: "react-redux@npm:9.1.0"
@@ -7177,6 +7899,42 @@ __metadata:
   languageName: node
   linkType: hard
 
+"rehype-raw@npm:^7.0.0":
+  version: 7.0.0
+  resolution: "rehype-raw@npm:7.0.0"
+  dependencies:
+    "@types/hast": ^3.0.0
+    hast-util-raw: ^9.0.0
+    vfile: ^6.0.0
+  checksum: 
f9e28dcbf4c6c7d91a97c10a840310f18ef3268aa45abb3e0428b6b191ff3c4fa8f753b910d768588a2dac5c7da7e557b4ddc3f1b6cd252e8d20cb62d60c65ed
+  languageName: node
+  linkType: hard
+
+"remark-parse@npm:^11.0.0":
+  version: 11.0.0
+  resolution: "remark-parse@npm:11.0.0"
+  dependencies:
+    "@types/mdast": ^4.0.0
+    mdast-util-from-markdown: ^2.0.0
+    micromark-util-types: ^2.0.0
+    unified: ^11.0.0
+  checksum: 
d83d245290fa84bb04fb3e78111f09c74f7417e7c012a64dd8dc04fccc3699036d828fbd8eeec8944f774b6c30cc1d925c98f8c46495ebcee7c595496342ab7f
+  languageName: node
+  linkType: hard
+
+"remark-rehype@npm:^11.0.0":
+  version: 11.1.0
+  resolution: "remark-rehype@npm:11.1.0"
+  dependencies:
+    "@types/hast": ^3.0.0
+    "@types/mdast": ^4.0.0
+    mdast-util-to-hast: ^13.0.0
+    unified: ^11.0.0
+    vfile: ^6.0.0
+  checksum: 
f0c731f0ab92a122e7f9c9bcbd10d6a31fdb99f0ea3595d232ddd9f9d11a308c4ec0aff4d56e1d0d256042dfad7df23b9941e50b5038da29786959a5926814e1
+  languageName: node
+  linkType: hard
+
 "reselect@npm:^5.0.1":
   version: 5.1.0
   resolution: "reselect@npm:5.1.0"
@@ -7567,6 +8325,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"space-separated-tokens@npm:^2.0.0":
+  version: 2.0.2
+  resolution: "space-separated-tokens@npm:2.0.2"
+  checksum: 
202e97d7ca1ba0758a0aa4fe226ff98142073bcceeff2da3aad037968878552c3bbce3b3231970025375bbba5aee00c5b8206eda408da837ab2dc9c0f26be990
+  languageName: node
+  linkType: hard
+
 "split-on-first@npm:^1.0.0":
   version: 1.1.0
   resolution: "split-on-first@npm:1.1.0"
@@ -7690,6 +8455,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"stringify-entities@npm:^4.0.0":
+  version: 4.0.3
+  resolution: "stringify-entities@npm:4.0.3"
+  dependencies:
+    character-entities-html4: ^2.0.0
+    character-entities-legacy: ^3.0.0
+  checksum: 
59e8f523b403bf7d415690e72ae52982decd6ea5426bd8b3f5c66225ddde73e766c0c0d91627df082d0794e30b19dd907ffb5864cef3602e4098d6777d7ca3c2
+  languageName: node
+  linkType: hard
+
 "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, 
strip-ansi@npm:^6.0.1":
   version: 6.0.1
   resolution: "strip-ansi@npm:6.0.1"
@@ -7729,6 +8504,15 @@ __metadata:
   languageName: node
   linkType: hard
 
+"style-to-object@npm:^1.0.0":
+  version: 1.0.5
+  resolution: "style-to-object@npm:1.0.5"
+  dependencies:
+    inline-style-parser: 0.2.2
+  checksum: 
6201063204b6a94645f81b189452b2ca3e63d61867ec48523f4d52609c81e96176739fa12020d97fbbf023efb57a6f7ec3a15fb3a7fb7eb3ffea0b52b9dd6b8c
+  languageName: node
+  linkType: hard
+
 "styled-components@npm:^5.3.6":
   version: 5.3.11
   resolution: "styled-components@npm:5.3.11"
@@ -7875,6 +8659,20 @@ __metadata:
   languageName: node
   linkType: hard
 
+"trim-lines@npm:^3.0.0":
+  version: 3.0.1
+  resolution: "trim-lines@npm:3.0.1"
+  checksum: 
e241da104682a0e0d807222cc1496b92e716af4db7a002f4aeff33ae6a0024fef93165d49eab11aa07c71e1347c42d46563f91dfaa4d3fb945aa535cdead53ed
+  languageName: node
+  linkType: hard
+
+"trough@npm:^2.0.0":
+  version: 2.2.0
+  resolution: "trough@npm:2.2.0"
+  checksum: 
6097df63169aca1f9b08c263b1b501a9b878387f46e161dde93f6d0bba7febba93c95f876a293c5ea370f6cb03bcb687b2488c8955c3cfb66c2c0161ea8c00f6
+  languageName: node
+  linkType: hard
+
 "tsconfig-paths@npm:^3.15.0":
   version: 3.15.0
   resolution: "tsconfig-paths@npm:3.15.0"
@@ -8064,6 +8862,21 @@ __metadata:
   languageName: node
   linkType: hard
 
+"unified@npm:^11.0.0":
+  version: 11.0.4
+  resolution: "unified@npm:11.0.4"
+  dependencies:
+    "@types/unist": ^3.0.0
+    bail: ^2.0.0
+    devlop: ^1.0.0
+    extend: ^3.0.0
+    is-plain-obj: ^4.0.0
+    trough: ^2.0.0
+    vfile: ^6.0.0
+  checksum: 
cfb023913480ac2bd5e787ffb8c27782c43e6be4a55f8f1c288233fce46a7ebe7718ccc5adb80bf8d56b7ef85f5fc32239c7bfccda006f9f2382e0cc2e2a77e4
+  languageName: node
+  linkType: hard
+
 "unique-filename@npm:^3.0.0":
   version: 3.0.0
   resolution: "unique-filename@npm:3.0.0"
@@ -8082,6 +8895,64 @@ __metadata:
   languageName: node
   linkType: hard
 
+"unist-util-is@npm:^6.0.0":
+  version: 6.0.0
+  resolution: "unist-util-is@npm:6.0.0"
+  dependencies:
+    "@types/unist": ^3.0.0
+  checksum: 
f630a925126594af9993b091cf807b86811371e465b5049a6283e08537d3e6ba0f7e248e1e7dab52cfe33f9002606acef093441137181b327f6fe504884b20e2
+  languageName: node
+  linkType: hard
+
+"unist-util-position@npm:^5.0.0":
+  version: 5.0.0
+  resolution: "unist-util-position@npm:5.0.0"
+  dependencies:
+    "@types/unist": ^3.0.0
+  checksum: 
f89b27989b19f07878de9579cd8db2aa0194c8360db69e2c99bd2124a480d79c08f04b73a64daf01a8fb3af7cba65ff4b45a0b978ca243226084ad5f5d441dde
+  languageName: node
+  linkType: hard
+
+"unist-util-remove-position@npm:^5.0.0":
+  version: 5.0.0
+  resolution: "unist-util-remove-position@npm:5.0.0"
+  dependencies:
+    "@types/unist": ^3.0.0
+    unist-util-visit: ^5.0.0
+  checksum: 
8aabdb9d0e3e744141bc123d8f87b90835d521209ad3c6c4619d403b324537152f0b8f20dda839b40c3aa0abfbf1828b3635a7a8bb159c3ed469e743023510ee
+  languageName: node
+  linkType: hard
+
+"unist-util-stringify-position@npm:^4.0.0":
+  version: 4.0.0
+  resolution: "unist-util-stringify-position@npm:4.0.0"
+  dependencies:
+    "@types/unist": ^3.0.0
+  checksum: 
e2e7aee4b92ddb64d314b4ac89eef7a46e4c829cbd3ee4aee516d100772b490eb6b4974f653ba0717a0071ca6ea0770bf22b0a2ea62c65fcba1d071285e96324
+  languageName: node
+  linkType: hard
+
+"unist-util-visit-parents@npm:^6.0.0":
+  version: 6.0.1
+  resolution: "unist-util-visit-parents@npm:6.0.1"
+  dependencies:
+    "@types/unist": ^3.0.0
+    unist-util-is: ^6.0.0
+  checksum: 
08927647c579f63b91aafcbec9966dc4a7d0af1e5e26fc69f4e3e6a01215084835a2321b06f3cbe7bf7914a852830fc1439f0fc3d7153d8804ac3ef851ddfa20
+  languageName: node
+  linkType: hard
+
+"unist-util-visit@npm:^5.0.0":
+  version: 5.0.0
+  resolution: "unist-util-visit@npm:5.0.0"
+  dependencies:
+    "@types/unist": ^3.0.0
+    unist-util-is: ^6.0.0
+    unist-util-visit-parents: ^6.0.0
+  checksum: 
9ec42e618e7e5d0202f3c191cd30791b51641285732767ee2e6bcd035931032e3c1b29093f4d7fd0c79175bbc1f26f24f26ee49770d32be76f8730a652a857e6
+  languageName: node
+  linkType: hard
+
 "update-browserslist-db@npm:^1.0.13":
   version: 1.0.13
   resolution: "update-browserslist-db@npm:1.0.13"
@@ -8114,6 +8985,37 @@ __metadata:
   languageName: node
   linkType: hard
 
+"vfile-location@npm:^5.0.0":
+  version: 5.0.2
+  resolution: "vfile-location@npm:5.0.2"
+  dependencies:
+    "@types/unist": ^3.0.0
+    vfile: ^6.0.0
+  checksum: 
b61c048cedad3555b4f007f390412c6503f58a6a130b58badf4ee340c87e0d7421e9c86bbc1494c57dedfccadb60f5176cc60ba3098209d99fb3a3d8804e4c38
+  languageName: node
+  linkType: hard
+
+"vfile-message@npm:^4.0.0":
+  version: 4.0.2
+  resolution: "vfile-message@npm:4.0.2"
+  dependencies:
+    "@types/unist": ^3.0.0
+    unist-util-stringify-position: ^4.0.0
+  checksum: 
964e7e119f4c0e0270fc269119c41c96da20afa01acb7c9809a88365c8e0c64aa692fafbd952669382b978002ecd7ad31ef4446d85e8a22cdb62f6df20186c2d
+  languageName: node
+  linkType: hard
+
+"vfile@npm:^6.0.0":
+  version: 6.0.1
+  resolution: "vfile@npm:6.0.1"
+  dependencies:
+    "@types/unist": ^3.0.0
+    unist-util-stringify-position: ^4.0.0
+    vfile-message: ^4.0.0
+  checksum: 
05ccee73aeb00402bc8a5d0708af299e9f4a33f5132805449099295085e3ca3b0d018328bad9ff44cf2e6f4cd364f1d558d3fb9b394243a25b2739207edcb0ed
+  languageName: node
+  linkType: hard
+
 "vite-plugin-svgr@npm:^4.2.0":
   version: 4.2.0
   resolution: "vite-plugin-svgr@npm:4.2.0"
@@ -8167,6 +9069,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"web-namespaces@npm:^2.0.0":
+  version: 2.0.1
+  resolution: "web-namespaces@npm:2.0.1"
+  checksum: 
b6d9f02f1a43d0ef0848a812d89c83801d5bbad57d8bb61f02eb6d7eb794c3736f6cc2e1191664bb26136594c8218ac609f4069722c6f56d9fc2d808fa9271c6
+  languageName: node
+  linkType: hard
+
 "which-boxed-primitive@npm:^1.0.2":
   version: 1.0.2
   resolution: "which-boxed-primitive@npm:1.0.2"
@@ -8325,3 +9234,10 @@ __metadata:
   checksum: 
3f33850913a237fb7e59b0b5c71d2cc6bdb1d6b2b01fc24c53ec68b35ad2b95cd49f735532117136495cb57f0ec58f0a7e1f1c55fad685555c7114155170f1ea
   languageName: node
   linkType: hard
+
+"zwitch@npm:^2.0.0":
+  version: 2.0.4
+  resolution: "zwitch@npm:2.0.4"
+  checksum: 
f22ec5fc2d5f02c423c93d35cdfa83573a3a3bd98c66b927c368ea4d0e7252a500df2a90a6b45522be536a96a73404393c958e945fdba95e6832c200791702b6
+  languageName: node
+  linkType: hard

Reply via email to