From ce2fd2f8ad9bc127b7d2354227f7764d7fb01ab1 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Mon, 31 Jul 2023 16:52:20 +0800
Subject: [PATCH 01/31] update .net 8

---
 global.json                                   |  5 -----
 src/Blogifier.Admin/Blogifier.Admin.csproj    | 10 ++++-----
 src/Blogifier.Shared/Blogifier.Shared.csproj  |  4 ++--
 .../Blogifier.Themes.Standard.csproj          |  2 +-
 src/Blogifier/Blogifier.csproj                | 22 +++++++++----------
 tests/Blogifier.Tests/Blogifier.Tests.csproj  |  2 +-
 6 files changed, 20 insertions(+), 25 deletions(-)
 delete mode 100644 global.json

diff --git a/global.json b/global.json
deleted file mode 100644
index e383752a1..000000000
--- a/global.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "sdk": {
-    "version": "7.0.304"
-  }
-}
diff --git a/src/Blogifier.Admin/Blogifier.Admin.csproj b/src/Blogifier.Admin/Blogifier.Admin.csproj
index e89e12a05..df9def5ed 100644
--- a/src/Blogifier.Admin/Blogifier.Admin.csproj
+++ b/src/Blogifier.Admin/Blogifier.Admin.csproj
@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
   <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
     <!-- <BlazorEnableCompression>false</BlazorEnableCompression> -->
     <!--<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
@@ -15,10 +15,10 @@
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
     <PackageReference Include="Blazored.Typeahead" Version="4.7.0" />
     <PackageReference Include="ChartJs.Blazor.Fork" Version="2.0.2" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.9" />
-    <PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.9" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.0-preview.6.23329.11" />
     <PackageReference Include="Sotsera.Blazor.Toaster" Version="3.0.0" />
   </ItemGroup>
 
diff --git a/src/Blogifier.Shared/Blogifier.Shared.csproj b/src/Blogifier.Shared/Blogifier.Shared.csproj
index bc42f863d..91540bdeb 100644
--- a/src/Blogifier.Shared/Blogifier.Shared.csproj
+++ b/src/Blogifier.Shared/Blogifier.Shared.csproj
@@ -1,12 +1,12 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.9" />
+    <PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.0-preview.6.23329.11" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/Blogifier.Themes.Standard/Blogifier.Themes.Standard.csproj b/src/Blogifier.Themes.Standard/Blogifier.Themes.Standard.csproj
index 70a13b88c..fbfc7d793 100644
--- a/src/Blogifier.Themes.Standard/Blogifier.Themes.Standard.csproj
+++ b/src/Blogifier.Themes.Standard/Blogifier.Themes.Standard.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Razor">
 
   <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
     <ImplicitUsings>enable</ImplicitUsings>
     <AddRazorSupportForMvc>true</AddRazorSupportForMvc>
diff --git a/src/Blogifier/Blogifier.csproj b/src/Blogifier/Blogifier.csproj
index 7db59c99a..59d573268 100644
--- a/src/Blogifier/Blogifier.csproj
+++ b/src/Blogifier/Blogifier.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <Nullable>enable</Nullable>
     <SelfContained>false</SelfContained>
     <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
@@ -14,25 +14,25 @@
   <ItemGroup>
     <PackageReference Include="AutoMapper" Version="12.0.1" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.9" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.9">
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0-preview.6.23329.4">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.9" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.9" />
-    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.9" />
-    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.6.23329.4" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-preview.6.23329.4" />
+    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0-preview.4" />
     <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
     <PackageReference Include="Minio" Version="5.0.0" />
     <PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
     <PackageReference Include="ReverseMarkdown" Version="3.25.0" />
     <PackageReference Include="Markdig" Version="0.31.0" />
     <PackageReference Include="NETCore.MailKit" Version="2.1.0" />
-    <PackageReference Include="System.ServiceModel.Syndication" Version="7.0.0" />
+    <PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0-preview.6.23329.7" />
     <PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
   </ItemGroup>
 
diff --git a/tests/Blogifier.Tests/Blogifier.Tests.csproj b/tests/Blogifier.Tests/Blogifier.Tests.csproj
index 1a87258bf..90ab84e4d 100644
--- a/tests/Blogifier.Tests/Blogifier.Tests.csproj
+++ b/tests/Blogifier.Tests/Blogifier.Tests.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
     <PropertyGroup>
-        <TargetFramework>net7.0</TargetFramework>
+        <TargetFramework>net8.0</TargetFramework>
     </PropertyGroup>
 
     <ItemGroup>

From d775bb65fd2bc38eb936b6e508946711d6323a73 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Mon, 31 Jul 2023 17:58:33 +0800
Subject: [PATCH 02/31] docker build

---
 .dockerignore | 8 ++++----
 Dockerfile    | 4 ++--
 docker.sh     | 4 ++--
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/.dockerignore b/.dockerignore
index 3bdd11108..0e89580c3 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,8 +1,8 @@
 .git
-**/bin
-**/obj
-**/node_modules
+**/bin/*
+**/obj/*
+**/node_modules/*
 Dockerfile
 docker-compose.yml
-deploy/
+deploy/*
 **/package-lock.json
diff --git a/Dockerfile b/Dockerfile
index 3e237e2bb..8871a8749 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine as sdk
+FROM mcr.microsoft.com/dotnet/sdk:8.0-preview-alpine as sdk
 # TOTO zh-CH
 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 RUN apk add --no-cache npm
@@ -7,7 +7,7 @@ COPY ./ /opt/blogifier
 WORKDIR /opt/blogifier
 RUN ["dotnet","publish", "-c", "Release","/p:RuntimeIdentifier=linux-musl-x64", "./src/Blogifier/Blogifier.csproj","-o","dist" ]
 
-FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine as run
+FROM mcr.microsoft.com/dotnet/aspnet:8.0.0-preview.6-alpine3.18 as run
 # TOTO zh-CH
 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 RUN apk add --no-cache icu-libs
diff --git a/docker.sh b/docker.sh
index 17697cc0a..16328b1bf 100644
--- a/docker.sh
+++ b/docker.sh
@@ -1,3 +1,3 @@
 # docker
-docker build -t dorthl/blogifier:latest .
-docker push dorthl/blogifier:latest
+docker build -t dorthl/blogifier:preview .
+docker push dorthl/blogifier:preview

From 2fbf13538569c1003b5e6b36d73c3be2df43189b Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 1 Aug 2023 09:24:15 +0800
Subject: [PATCH 03/31] remove nuget

---
 src/Blogifier.Admin/Blogifier.Admin.csproj | 2 --
 src/Blogifier.Admin/_Imports.razor         | 1 -
 src/Blogifier.Admin/wwwroot/index.html     | 3 ---
 3 files changed, 6 deletions(-)

diff --git a/src/Blogifier.Admin/Blogifier.Admin.csproj b/src/Blogifier.Admin/Blogifier.Admin.csproj
index df9def5ed..d6ae69bda 100644
--- a/src/Blogifier.Admin/Blogifier.Admin.csproj
+++ b/src/Blogifier.Admin/Blogifier.Admin.csproj
@@ -13,8 +13,6 @@
   <ItemGroup>
     <PackageReference Include="AutoMapper" Version="12.0.1" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
-    <PackageReference Include="Blazored.Typeahead" Version="4.7.0" />
-    <PackageReference Include="ChartJs.Blazor.Fork" Version="2.0.2" />
     <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0-preview.6.23329.11" />
     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0-preview.6.23329.11" />
     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0-preview.6.23329.11" />
diff --git a/src/Blogifier.Admin/_Imports.razor b/src/Blogifier.Admin/_Imports.razor
index d57b9c6aa..2a062649d 100644
--- a/src/Blogifier.Admin/_Imports.razor
+++ b/src/Blogifier.Admin/_Imports.razor
@@ -14,7 +14,6 @@
 
 @using AutoMapper;
 
-@using Blazored.Typeahead
 @using Sotsera.Blazor.Toaster
 
 @using Blogifier;
diff --git a/src/Blogifier.Admin/wwwroot/index.html b/src/Blogifier.Admin/wwwroot/index.html
index 03d58d1a6..628017365 100644
--- a/src/Blogifier.Admin/wwwroot/index.html
+++ b/src/Blogifier.Admin/wwwroot/index.html
@@ -10,7 +10,6 @@
       TODO: for rtl:
       <link href="admin/css/blogifier.rtl.css" rel="stylesheet" />
   -->
-  <link href="_content/Blazored.Typeahead/blazored-typeahead.css" rel="stylesheet" />
   <link rel="apple-touch-icon" sizes="180x180" href="amdin/favicons/apple-touch-icon.png?v=2">
   <link rel="icon" type="image/png" sizes="32x32" href="admin/favicons/favicon-32x32.png?v=2">
   <link rel="icon" type="image/png" sizes="16x16" href="admin/favicons/favicon-16x16.png?v=2">
@@ -55,8 +54,6 @@
   </form>
 
   <script src="_framework/blazor.webassembly.js"></script>
-  <script src="_content/ChartJs.Blazor.Fork/ChartJsBlazorInterop.js"></script>
-  <script src="_content/Blazored.Typeahead/blazored-typeahead.js"></script>
   <script src="admin/js/blogifier.js"></script>
 </body>
 </html>

From 8d5facc5e057d6d89dd5c68ecbe7a6967cee168c Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 1 Aug 2023 09:30:02 +0800
Subject: [PATCH 04/31] ChartJs

---
 src/Blogifier.Admin/Pages/HomeView.razor | 108 +++++++++++------------
 1 file changed, 54 insertions(+), 54 deletions(-)

diff --git a/src/Blogifier.Admin/Pages/HomeView.razor b/src/Blogifier.Admin/Pages/HomeView.razor
index 6eb030280..6abf69271 100644
--- a/src/Blogifier.Admin/Pages/HomeView.razor
+++ b/src/Blogifier.Admin/Pages/HomeView.razor
@@ -1,8 +1,8 @@
-@using ChartJs.Blazor
+@* @using ChartJs.Blazor
 @using ChartJs.Blazor.Common
 @using ChartJs.Blazor.Common.Enums
 @using ChartJs.Blazor.Util
-@using ChartJs.Blazor.BarChart
+@using ChartJs.Blazor.BarChart *@
 @using System.Drawing
 
 @page "/admin"
@@ -13,7 +13,7 @@
 
 @code {
 
-  protected BarConfig _config = default!;
+  // protected BarConfig _config = default!;
   protected List<OptionItem> _dateOptions = default!;
   protected List<PostVisit> _visits = default!;
   protected bool _hideGraph = false;
@@ -22,17 +22,17 @@
 
   protected override async Task OnInitializedAsync()
   {
-    _config = new BarConfig
-    {
-      Options = new BarOptions
-      {
-        Responsive = true,
-        Legend = new Legend
-        {
-          Position = Position.Top
-        }
-      }
-    };
+    // _config = new BarConfig
+    // {
+    //   Options = new BarOptions
+    //   {
+    //     Responsive = true,
+    //     Legend = new Legend
+    //     {
+    //       Position = Position.Top
+    //     }
+    //   }
+    // };
     _dateOptions = new List<OptionItem>
     {
       new OptionItem { Id = 1, Title = _localizer["today"] },
@@ -41,58 +41,58 @@
       new OptionItem { Id = 4, Title = _localizer["30-days"] },
       new OptionItem { Id = 5, Title = _localizer["90-days"] },
     };
-    Load();
+    // Load();
     _analytics = await _http.GetFromJsonAsync<AnalyticsDto>("api/analytics");
   }
 
-  protected void Load()
-  {
-    var dataset = new BarDataset<int>()
-      {
-        Label = "Latest Post Views",
-        BackgroundColor = ColorUtil.FromDrawingColor(Color.FromArgb(98, 42, 255)),
-        BorderWidth = 0
-      };
+  // protected void Load()
+  // {
+  //   var dataset = new BarDataset<int>()
+  //     {
+  //       Label = "Latest Post Views",
+  //       BackgroundColor = ColorUtil.FromDrawingColor(Color.FromArgb(98, 42, 255)),
+  //       BorderWidth = 0
+  //     };
 
-    if (_analytics == null || _analytics.LatestPostViews == null)
-    {
-      LoadData(dataset, TestData());
-    }
-    else
-    {
-      _hideList = _analytics.DisplayType == AnalyticsListType.Graph;
-      _hideGraph = _analytics.DisplayType == AnalyticsListType.List;
+  //   if (_analytics == null || _analytics.LatestPostViews == null)
+  //   {
+  //     LoadData(dataset, TestData());
+  //   }
+  //   else
+  //   {
+  //     _hideList = _analytics.DisplayType == AnalyticsListType.Graph;
+  //     _hideGraph = _analytics.DisplayType == AnalyticsListType.List;
 
-      LoadData(dataset, _analytics.LatestPostViews);
-    }
+  //     LoadData(dataset, _analytics.LatestPostViews);
+  //   }
 
-    if (_config.Data.Datasets.Count > 0)
-    {
-      _config.Data.Datasets.Clear();
-    }
+  //   if (_config.Data.Datasets.Count > 0)
+  //   {
+  //     _config.Data.Datasets.Clear();
+  //   }
 
-    _config.Data.Datasets.Add(dataset);
-  }
+  //   _config.Data.Datasets.Add(dataset);
+  // }
 
 
 
-  protected void LoadData(IDataset<int> dataset, BarChartModel model)
-  {
-    _visits = new List<PostVisit>();
-    var labels = model.Labels.ToList();
-    var values = model.Data.ToList();
+  // protected void LoadData(IDataset<int> dataset, BarChartModel model)
+  // {
+  //   _visits = new List<PostVisit>();
+  //   var labels = model.Labels.ToList();
+  //   var values = model.Data.ToList();
 
-    _config.Data.Labels.Clear();
+  //   _config.Data.Labels.Clear();
 
-    for (int i = 0; i < labels.Count; i++)
-    {
-      _config.Data.Labels.Add(labels[i]);
-      dataset.Add(values[i]);
+  //   for (int i = 0; i < labels.Count; i++)
+  //   {
+  //     _config.Data.Labels.Add(labels[i]);
+  //     dataset.Add(values[i]);
 
-      _visits.Add(new PostVisit { Name = labels[i], Value = values[i] });
-    }
-    _visits = _visits.OrderByDescending(v => v.Value).ToList();
-  }
+  //     _visits.Add(new PostVisit { Name = labels[i], Value = values[i] });
+  //   }
+  //   _visits = _visits.OrderByDescending(v => v.Value).ToList();
+  // }
 
   protected async Task ToggleAnalyticsView(bool isGraph)
   {
@@ -272,7 +272,7 @@
 
         <!-- Chart must show visits per day not by the title of the post. -->
         <div class="dash-pan-body" hidden="@_hideGraph">
-          <Chart Config="_config"></Chart>
+          @* <Chart Config="_config"></Chart> *@
         </div>
 
       </div>

From 944331336d03f291ad5cf26a2f0f19cff6fbb53c Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 1 Aug 2023 09:35:18 +0800
Subject: [PATCH 05/31] ChartJs

---
 src/Blogifier.Admin/Blogifier.Admin.csproj |   1 +
 src/Blogifier.Admin/Pages/HomeView.razor   | 126 ++++++++++-----------
 src/Blogifier.Admin/wwwroot/index.html     |   1 +
 3 files changed, 65 insertions(+), 63 deletions(-)

diff --git a/src/Blogifier.Admin/Blogifier.Admin.csproj b/src/Blogifier.Admin/Blogifier.Admin.csproj
index d6ae69bda..d47fbeffe 100644
--- a/src/Blogifier.Admin/Blogifier.Admin.csproj
+++ b/src/Blogifier.Admin/Blogifier.Admin.csproj
@@ -13,6 +13,7 @@
   <ItemGroup>
     <PackageReference Include="AutoMapper" Version="12.0.1" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
+    <PackageReference Include="ChartJs.Blazor.Fork" Version="2.0.2" />
     <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0-preview.6.23329.11" />
     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0-preview.6.23329.11" />
     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0-preview.6.23329.11" />
diff --git a/src/Blogifier.Admin/Pages/HomeView.razor b/src/Blogifier.Admin/Pages/HomeView.razor
index 6abf69271..11025577f 100644
--- a/src/Blogifier.Admin/Pages/HomeView.razor
+++ b/src/Blogifier.Admin/Pages/HomeView.razor
@@ -1,8 +1,8 @@
-@* @using ChartJs.Blazor
+@using ChartJs.Blazor
 @using ChartJs.Blazor.Common
 @using ChartJs.Blazor.Common.Enums
 @using ChartJs.Blazor.Util
-@using ChartJs.Blazor.BarChart *@
+@using ChartJs.Blazor.BarChart
 @using System.Drawing
 
 @page "/admin"
@@ -13,7 +13,7 @@
 
 @code {
 
-  // protected BarConfig _config = default!;
+  protected BarConfig _config = default!;
   protected List<OptionItem> _dateOptions = default!;
   protected List<PostVisit> _visits = default!;
   protected bool _hideGraph = false;
@@ -22,17 +22,17 @@
 
   protected override async Task OnInitializedAsync()
   {
-    // _config = new BarConfig
-    // {
-    //   Options = new BarOptions
-    //   {
-    //     Responsive = true,
-    //     Legend = new Legend
-    //     {
-    //       Position = Position.Top
-    //     }
-    //   }
-    // };
+    _config = new BarConfig
+    {
+      Options = new BarOptions
+      {
+        Responsive = true,
+        Legend = new Legend
+        {
+          Position = Position.Top
+        }
+      }
+    };
     _dateOptions = new List<OptionItem>
     {
       new OptionItem { Id = 1, Title = _localizer["today"] },
@@ -41,58 +41,58 @@
       new OptionItem { Id = 4, Title = _localizer["30-days"] },
       new OptionItem { Id = 5, Title = _localizer["90-days"] },
     };
-    // Load();
+    Load();
     _analytics = await _http.GetFromJsonAsync<AnalyticsDto>("api/analytics");
   }
 
-  // protected void Load()
-  // {
-  //   var dataset = new BarDataset<int>()
-  //     {
-  //       Label = "Latest Post Views",
-  //       BackgroundColor = ColorUtil.FromDrawingColor(Color.FromArgb(98, 42, 255)),
-  //       BorderWidth = 0
-  //     };
-
-  //   if (_analytics == null || _analytics.LatestPostViews == null)
-  //   {
-  //     LoadData(dataset, TestData());
-  //   }
-  //   else
-  //   {
-  //     _hideList = _analytics.DisplayType == AnalyticsListType.Graph;
-  //     _hideGraph = _analytics.DisplayType == AnalyticsListType.List;
-
-  //     LoadData(dataset, _analytics.LatestPostViews);
-  //   }
-
-  //   if (_config.Data.Datasets.Count > 0)
-  //   {
-  //     _config.Data.Datasets.Clear();
-  //   }
-
-  //   _config.Data.Datasets.Add(dataset);
-  // }
-
-
-
-  // protected void LoadData(IDataset<int> dataset, BarChartModel model)
-  // {
-  //   _visits = new List<PostVisit>();
-  //   var labels = model.Labels.ToList();
-  //   var values = model.Data.ToList();
-
-  //   _config.Data.Labels.Clear();
-
-  //   for (int i = 0; i < labels.Count; i++)
-  //   {
-  //     _config.Data.Labels.Add(labels[i]);
-  //     dataset.Add(values[i]);
-
-  //     _visits.Add(new PostVisit { Name = labels[i], Value = values[i] });
-  //   }
-  //   _visits = _visits.OrderByDescending(v => v.Value).ToList();
-  // }
+  protected void Load()
+  {
+    var dataset = new BarDataset<int>()
+      {
+        Label = "Latest Post Views",
+        BackgroundColor = ColorUtil.FromDrawingColor(Color.FromArgb(98, 42, 255)),
+        BorderWidth = 0
+      };
+
+    if (_analytics == null || _analytics.LatestPostViews == null)
+    {
+      LoadData(dataset, TestData());
+    }
+    else
+    {
+      _hideList = _analytics.DisplayType == AnalyticsListType.Graph;
+      _hideGraph = _analytics.DisplayType == AnalyticsListType.List;
+
+      LoadData(dataset, _analytics.LatestPostViews);
+    }
+
+    if (_config.Data.Datasets.Count > 0)
+    {
+      _config.Data.Datasets.Clear();
+    }
+
+    _config.Data.Datasets.Add(dataset);
+  }
+
+
+
+  protected void LoadData(IDataset<int> dataset, BarChartModel model)
+  {
+    _visits = new List<PostVisit>();
+    var labels = model.Labels.ToList();
+    var values = model.Data.ToList();
+
+    _config.Data.Labels.Clear();
+
+    for (int i = 0; i < labels.Count; i++)
+    {
+      _config.Data.Labels.Add(labels[i]);
+      dataset.Add(values[i]);
+
+      _visits.Add(new PostVisit { Name = labels[i], Value = values[i] });
+    }
+    _visits = _visits.OrderByDescending(v => v.Value).ToList();
+  }
 
   protected async Task ToggleAnalyticsView(bool isGraph)
   {
diff --git a/src/Blogifier.Admin/wwwroot/index.html b/src/Blogifier.Admin/wwwroot/index.html
index 628017365..84f14a0b0 100644
--- a/src/Blogifier.Admin/wwwroot/index.html
+++ b/src/Blogifier.Admin/wwwroot/index.html
@@ -54,6 +54,7 @@
   </form>
 
   <script src="_framework/blazor.webassembly.js"></script>
+  <script src="_content/Blazored.Typeahead/blazored-typeahead.js"></script>
   <script src="admin/js/blogifier.js"></script>
 </body>
 </html>

From 7b9f8bfcbd03a08496a05e9943e839d71546675a Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 1 Aug 2023 11:52:16 +0800
Subject: [PATCH 06/31] update test

---
 tests/Blogifier.Tests/Blogifier.Tests.csproj | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/Blogifier.Tests/Blogifier.Tests.csproj b/tests/Blogifier.Tests/Blogifier.Tests.csproj
index 90ab84e4d..310ae5c25 100644
--- a/tests/Blogifier.Tests/Blogifier.Tests.csproj
+++ b/tests/Blogifier.Tests/Blogifier.Tests.csproj
@@ -5,10 +5,10 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0-preview-23364-03" />
         <PackageReference Include="Moq" Version="4.18.4" />
-        <PackageReference Include="xunit" Version="2.5.0" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
+        <PackageReference Include="xunit" Version="2.5.1-pre.12" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.1-pre.4">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>

From b795231aa39536f0b312d53216a4f47ea0f67d40 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 1 Aug 2023 13:18:19 +0800
Subject: [PATCH 07/31] fix

---
 src/Blogifier.Admin/Blogifier.Admin.csproj | 3 +--
 src/Blogifier/Blogifier.csproj             | 4 ----
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/Blogifier.Admin/Blogifier.Admin.csproj b/src/Blogifier.Admin/Blogifier.Admin.csproj
index 6c829bb9e..ab514b1a7 100644
--- a/src/Blogifier.Admin/Blogifier.Admin.csproj
+++ b/src/Blogifier.Admin/Blogifier.Admin.csproj
@@ -13,8 +13,7 @@
   <ItemGroup>
     <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0-preview.6.23329.11" />
     <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0-preview.6.23329.11" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0-preview.6.23329.11" />
-    <PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0-preview.6.23329.11" PrivateAssets="all" />
     <PackageReference Include="Sotsera.Blazor.Toaster" Version="3.0.0" />
   </ItemGroup>
 
diff --git a/src/Blogifier/Blogifier.csproj b/src/Blogifier/Blogifier.csproj
index 59d573268..168af66ee 100644
--- a/src/Blogifier/Blogifier.csproj
+++ b/src/Blogifier/Blogifier.csproj
@@ -7,10 +7,6 @@
     <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
   </PropertyGroup>
 
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <BlazorWebAssemblyOmitDebugProxyOutput>true</BlazorWebAssemblyOmitDebugProxyOutput>
-  </PropertyGroup>
-
   <ItemGroup>
     <PackageReference Include="AutoMapper" Version="12.0.1" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />

From 5917597f5327b28c381617a86514cc2d8165e681 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 1 Aug 2023 14:08:38 +0800
Subject: [PATCH 08/31] Webcil false

---
 src/Blogifier.Admin/Blogifier.Admin.csproj | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/Blogifier.Admin/Blogifier.Admin.csproj b/src/Blogifier.Admin/Blogifier.Admin.csproj
index ab514b1a7..12af8eadb 100644
--- a/src/Blogifier.Admin/Blogifier.Admin.csproj
+++ b/src/Blogifier.Admin/Blogifier.Admin.csproj
@@ -8,6 +8,9 @@
     <ClientAssetsDirectory>assets\</ClientAssetsDirectory>
     <ClientAssetsRestoreCommand>npm i</ClientAssetsRestoreCommand>
     <ClientAssetsBuildCommand>npm run build:$(Configuration)</ClientAssetsBuildCommand>
+
+    <!-- https://github.com/dotnet/runtime/issues/89234 -->
+    <WasmEnableWebcil>false</WasmEnableWebcil>
   </PropertyGroup>
 
   <ItemGroup>

From d3ca5632692fa8e197904d6e58131f3ec869fc50 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 1 Aug 2023 14:17:27 +0800
Subject: [PATCH 09/31] dockerignore

---
 .dockerignore | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/.dockerignore b/.dockerignore
index 0e89580c3..30c012657 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,8 +1,8 @@
-.git
-**/bin/*
-**/obj/*
-**/node_modules/*
+.git*
+**/bin*
+**/obj*
+**/node_modules*
 Dockerfile
 docker-compose.yml
-deploy/*
+deploy*
 **/package-lock.json

From 13deb93ec644cac46659e08b9f50c1a167c1c679 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 1 Aug 2023 15:09:15 +0800
Subject: [PATCH 10/31] BlazorWebAssemblyOmitDebugProxyOutput

---
 src/Blogifier/Blogifier.csproj | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/Blogifier/Blogifier.csproj b/src/Blogifier/Blogifier.csproj
index 168af66ee..59d573268 100644
--- a/src/Blogifier/Blogifier.csproj
+++ b/src/Blogifier/Blogifier.csproj
@@ -7,6 +7,10 @@
     <IsTransformWebConfigDisabled>true</IsTransformWebConfigDisabled>
   </PropertyGroup>
 
+  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+    <BlazorWebAssemblyOmitDebugProxyOutput>true</BlazorWebAssemblyOmitDebugProxyOutput>
+  </PropertyGroup>
+
   <ItemGroup>
     <PackageReference Include="AutoMapper" Version="12.0.1" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />

From 36b7c4bb22b8c0d37ae9987e852cec9808ac20af Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Fri, 4 Aug 2023 16:26:04 +0800
Subject: [PATCH 11/31] config mirror

---
 Dockerfile | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Dockerfile b/Dockerfile
index 8871a8749..b47639836 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,6 +2,9 @@ FROM mcr.microsoft.com/dotnet/sdk:8.0-preview-alpine as sdk
 # TOTO zh-CH
 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 RUN apk add --no-cache npm
+# VOLUME ["/root/.nuget","/root/.local/share/NuGet","/root/.npm"]
+# TOTO zh-CH
+CMD [ "npm config set registry http://mirrors.cloud.tencent.com/npm" ]
 # Copy everything else and build
 COPY ./ /opt/blogifier
 WORKDIR /opt/blogifier

From 047be82b09431f982b3b0629b19c19ccf7ea1054 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Fri, 4 Aug 2023 16:52:27 +0800
Subject: [PATCH 12/31] github workflows docker-image

---
 .github/workflows/docker-image.yml | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 .github/workflows/docker-image.yml

diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
new file mode 100644
index 000000000..dc973905a
--- /dev/null
+++ b/.github/workflows/docker-image.yml
@@ -0,0 +1,28 @@
+name: build release docker
+
+on:
+  pull_request:
+    branches: [ "preview" ]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      -
+        name: Set up QEMU
+        uses: docker/setup-qemu-action@v2
+      -
+        name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v2
+      -
+        name: Login to Docker Hub
+        uses: docker/login-action@v2
+        with:
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKERHUB_TOKEN }}
+      -
+        name: Build and push
+        uses: docker/build-push-action@v4
+        with:
+          push: true
+          tags: dorthl/blogifier:preview

From 66f99025750103c02d999314995590315094add3 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Fri, 4 Aug 2023 16:53:45 +0800
Subject: [PATCH 13/31] on up

---
 .github/workflows/docker-image.yml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
index dc973905a..8853844c5 100644
--- a/.github/workflows/docker-image.yml
+++ b/.github/workflows/docker-image.yml
@@ -1,8 +1,11 @@
 name: build release docker
 
 on:
+  push:
+    branches:
+      - 'preview'
   pull_request:
-    branches: [ "preview" ]
+      - 'preview'
 
 jobs:
   build:

From 261b43695790665179b1df2f19fbd3696689e157 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Fri, 4 Aug 2023 16:54:47 +0800
Subject: [PATCH 14/31] fix on err

---
 .github/workflows/docker-image.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
index 8853844c5..baa40f1c7 100644
--- a/.github/workflows/docker-image.yml
+++ b/.github/workflows/docker-image.yml
@@ -5,6 +5,7 @@ on:
     branches:
       - 'preview'
   pull_request:
+    branches:
       - 'preview'
 
 jobs:

From 607a9fd6fc9e1311f6a69e26d9fb40935a56baa4 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Fri, 4 Aug 2023 16:56:24 +0800
Subject: [PATCH 15/31] rm zh-CH mirror

---
 Dockerfile | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index b47639836..b8ca9d710 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,10 @@
 FROM mcr.microsoft.com/dotnet/sdk:8.0-preview-alpine as sdk
 # TOTO zh-CH
-RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
+# RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 RUN apk add --no-cache npm
 # VOLUME ["/root/.nuget","/root/.local/share/NuGet","/root/.npm"]
 # TOTO zh-CH
-CMD [ "npm config set registry http://mirrors.cloud.tencent.com/npm" ]
+# CMD [ "npm config set registry http://mirrors.cloud.tencent.com/npm" ]
 # Copy everything else and build
 COPY ./ /opt/blogifier
 WORKDIR /opt/blogifier
@@ -12,7 +12,7 @@ RUN ["dotnet","publish", "-c", "Release","/p:RuntimeIdentifier=linux-musl-x64",
 
 FROM mcr.microsoft.com/dotnet/aspnet:8.0.0-preview.6-alpine3.18 as run
 # TOTO zh-CH
-RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
+# RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 RUN apk add --no-cache icu-libs
 ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=false
 COPY --from=sdk /opt/blogifier/dist /opt/blogifier/

From 12ddea78ee793fac5e77c7aa11bf3457f06464de Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Mon, 7 Aug 2023 18:38:25 +0800
Subject: [PATCH 16/31] post cover def null

---
 .../Components/NavMenuComponent.razor         |  2 +-
 .../Components/PostEditorComponent.razor      | 21 +++++++------------
 .../Pages/Blogs/EditorView.razor              |  1 -
 .../BlogifierSharedConstant.cs                |  2 +-
 .../Helper/{UserHelper.cs => PageHelper.cs}   |  8 ++++++-
 .../Views/Themes/standard/page.cshtml         |  2 +-
 .../Views/Themes/standard/post.cshtml         |  4 ++--
 .../Views/Themes/standard/post/author.cshtml  |  2 +-
 .../Themes/standard/post/featured.cshtml      |  4 ++--
 .../Views/Themes/standard/post/related.cshtml |  4 ++--
 .../Themes/standard/post/view-grid.cshtml     |  4 ++--
 .../Themes/standard/post/view-list.cshtml     |  4 ++--
 .../Views/Themes/standard/profile.cshtml      |  2 +-
 src/Blogifier/Posts/ImportManager.cs          |  3 ++-
 src/Blogifier/Posts/ImportRssProvider.cs      |  1 -
 15 files changed, 32 insertions(+), 32 deletions(-)
 rename src/Blogifier.Shared/Helper/{UserHelper.cs => PageHelper.cs} (50%)

diff --git a/src/Blogifier.Admin/Components/NavMenuComponent.razor b/src/Blogifier.Admin/Components/NavMenuComponent.razor
index dad13a69d..1f1065497 100644
--- a/src/Blogifier.Admin/Components/NavMenuComponent.razor
+++ b/src/Blogifier.Admin/Components/NavMenuComponent.razor
@@ -88,7 +88,7 @@
       {
         <li class="menu-item dropdown" title="@_claims.NickName" data-bs-toggle="tooltip">
           <NavLink class="menu-link" href="/admin/profile/" role="button" id="profileDropdownMenu" data-bs-toggle="dropdown" aria-expanded="false">
-            <img class="rounded-circle profilePicture" width="32" height="32" src="@UserHelper.CheckGetAvatarUrl(_claims.Avatar)" alt="@_claims.NickName">
+            <img class="rounded-circle profilePicture" width="32" height="32" src="@PageHelper.CheckGetAvatarUrl(_claims.Avatar)" alt="@_claims.NickName">
           </NavLink>
           <div class="user-nav dropdown-menu dropdown-menu-end" aria-labelledby="profileDropdownMenu">
             <div class="user-nav-info">
diff --git a/src/Blogifier.Admin/Components/PostEditorComponent.razor b/src/Blogifier.Admin/Components/PostEditorComponent.razor
index 98b7bdcf8..434c9a3be 100644
--- a/src/Blogifier.Admin/Components/PostEditorComponent.razor
+++ b/src/Blogifier.Admin/Components/PostEditorComponent.razor
@@ -5,7 +5,7 @@
 
 <div class="bfeditor">
   <div class="bfeditor-header">
-    <img class="bfeditor-cover" src="@Post.Cover" alt="@_localizer["cover"]" id="postCover">
+    <img class="bfeditor-cover" src="@PageHelper.CheckGetCoverrUrl(Post.Cover)" alt="@_localizer["cover"]">
     <div class="bfeditor-actions">
       <div class="container d-flex">
         @if (string.IsNullOrEmpty(Post.Slug))
@@ -64,7 +64,7 @@
                 <input type="hidden" class="txt-upload" @bind="Post.Cover" name="cover" id="cover" readonly />
               </li>
               <li>
-                <button class="dropdown-item" type="button" @onclick="() => ResetCoverAsync()">@_localizer["reset"]</button>
+                <button class="dropdown-item" type="button" @onclick="() => RemoveCoverAsync()">@_localizer["reset"]</button>
               </li>
             </ul>
           </div>
@@ -100,9 +100,9 @@
       return;
     }
     Post.Content = content;
-    Post.Cover = await _jsruntime.InvokeAsync<string>("commonJsFunctions.getSrcValue", "postCover");
-    Post.Cover = Post.Cover.Replace(_navigation.BaseUri, "");
-    if (string.IsNullOrEmpty(Post.Cover)) Post.Cover = BlogifierSharedConstant.DefaultCover;
+    //Post.Cover = await _jsruntime.InvokeAsync<string>("commonJsFunctions.getSrcValue", "postCover");
+    //Post.Cover = Post.Cover.Replace(_navigation.BaseUri, "");
+    //if (string.IsNullOrEmpty(Post.Cover)) Post.Cover = BlogifierSharedConstant.DefaultCover;
     if (string.IsNullOrEmpty(Post.Description)) Post.Description = Post.Title;
     Post.State = postState;
     await OnSaveCallback.InvokeAsync(Post);
@@ -131,16 +131,11 @@
     }
   }
 
-  protected async Task ResetCoverAsync()
-  {
-    Post.Cover = BlogifierSharedConstant.DefaultCover;
-    await SaveAsync();
-  }
-
-  protected async Task RemoveCoverAsync()
+  protected  Task RemoveCoverAsync()
   {
     Post.Cover = null;
-    await SaveAsync();
+    StateHasChanged();
+    return Task.CompletedTask;
   }
 
 }
diff --git a/src/Blogifier.Admin/Pages/Blogs/EditorView.razor b/src/Blogifier.Admin/Pages/Blogs/EditorView.razor
index b51c328c4..e808c15f2 100644
--- a/src/Blogifier.Admin/Pages/Blogs/EditorView.razor
+++ b/src/Blogifier.Admin/Pages/Blogs/EditorView.razor
@@ -22,7 +22,6 @@
       Description = string.Empty,
       Content = string.Empty,
       PostType = PostType.Post,
-      Cover = BlogifierSharedConstant.DefaultCover,
       Categories = new List<CategoryDto>(),
     };
 
diff --git a/src/Blogifier.Shared/BlogifierSharedConstant.cs b/src/Blogifier.Shared/BlogifierSharedConstant.cs
index ef8d7a568..e2d10ac82 100644
--- a/src/Blogifier.Shared/BlogifierSharedConstant.cs
+++ b/src/Blogifier.Shared/BlogifierSharedConstant.cs
@@ -8,7 +8,7 @@ public static class BlogifierSharedConstant
   public const string PolicyAdminName = "Administrator";
   public static readonly string PolicyAdminValue = $"{((int)UserType.Administrator)}";
   public static readonly string DefaultAvatar = "/img/avatar.jpg";
-  public static readonly string DefaultCover = "img/cover.jpg";
+  public static readonly string DefaultCover = "/img/cover.jpg";
   public static readonly string DefaultLogo = "img/logo-sm.png";
   public static readonly JsonSerializerOptions DefaultJsonSerializerOptions = new(JsonSerializerDefaults.Web);
 }
diff --git a/src/Blogifier.Shared/Helper/UserHelper.cs b/src/Blogifier.Shared/Helper/PageHelper.cs
similarity index 50%
rename from src/Blogifier.Shared/Helper/UserHelper.cs
rename to src/Blogifier.Shared/Helper/PageHelper.cs
index 5a83fc028..18902f061 100644
--- a/src/Blogifier.Shared/Helper/UserHelper.cs
+++ b/src/Blogifier.Shared/Helper/PageHelper.cs
@@ -1,10 +1,16 @@
 namespace Blogifier.Shared;
 
-public static class UserHelper
+public static class PageHelper
 {
   public static string CheckGetAvatarUrl(string? avatar)
   {
     if (!string.IsNullOrEmpty(avatar)) return avatar;
     return BlogifierSharedConstant.DefaultAvatar;
   }
+
+  public static string CheckGetCoverrUrl(string? avatar)
+  {
+    if (!string.IsNullOrEmpty(avatar)) return avatar;
+    return BlogifierSharedConstant.DefaultCover;
+  }
 }
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml
index 81f970c42..00204ca29 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml
@@ -10,7 +10,7 @@
 <main class="post-detail">
   <article class="post">
     <figure class="post-cover">
-      <img class="post-cover-img" src="~/@Model.PostSlug.Post.Cover" alt="@Model.PostSlug.Post.Title">
+      <img class="post-cover-img" src="@PageHelper.CheckGetCoverrUrl(Model.PostSlug.Post.Cover)" alt="@Model.PostSlug.Post.Title">
     </figure>
     <header class="post-header post-container">
       <h1 class="post-title">@Model.PostSlug.Post.Title</h1>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml
index 4d14f2c84..adcc25ba9 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml
@@ -8,13 +8,13 @@
 <main class="post-detail">
   <article class="post">
     <figure class="post-cover">
-      <img class="post-cover-img" src="~/@Model.PostSlug.Post.Cover" alt="@Model.PostSlug.Post.Title" aria-hidden="true">
+      <img class="post-cover-img" src="@PageHelper.CheckGetCoverrUrl(Model.PostSlug.Post.Cover)" alt="@Model.PostSlug.Post.Title" aria-hidden="true">
     </figure>
     <header class="post-header post-container">
       <h1 class="post-title">@Model.PostSlug.Post.Title</h1>
       <div class="post-meta d-flex align-items-center">
         <div class="post-meta-item post-meta-author">
-          <img class="post-meta-author-img d-none d-md-block" width="32" height="32" src="@UserHelper.CheckGetAvatarUrl(Model.PostSlug.Post.User.Avatar)"
+          <img class="post-meta-author-img d-none d-md-block" width="32" height="32" src="@PageHelper.CheckGetAvatarUrl(Model.PostSlug.Post.User.Avatar)"
                alt="@Model.PostSlug.Post.User.NickName" aria-hidden="true" />
           <div class="post-meta-author-details">
             <span class="post-meta-label">@_localizer["author"]</span>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/author.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/author.cshtml
index 88f4e4fb3..c9fa54f55 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/author.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/author.cshtml
@@ -2,7 +2,7 @@
 
 <section class="post-author d-md-flex align-items-md-center text-center text-md-start">
   <figure class="post-author-cover mb-3 mb-md-0">
-    <img class="post-author-img" width="96" height="96" src="@UserHelper.CheckGetAvatarUrl(Model.PostSlug.Post.User.Avatar)" alt="@Model.PostSlug.Post.User.NickName" />
+    <img class="post-author-img" width="96" height="96" src="@PageHelper.CheckGetAvatarUrl(Model.PostSlug.Post.User.Avatar)" alt="@Model.PostSlug.Post.User.NickName" />
   </figure>
   <div class="post-author-details">
     <h5 class="post-author-name">@Model.PostSlug.Post.User.NickName</h5>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/featured.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/featured.cshtml
index 0b00e4273..f42a85161 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/featured.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/featured.cshtml
@@ -21,7 +21,7 @@
               <section class="col-lg-7">
                 <figure class="featured-cover" aria-hidden="true">
                   <a href="~/post/@item.Slug" class="featured-cover-link" tabindex="-1">
-                    <img class="featured-cover-img" alt="POST_TITLE" src="~/@item.Cover">
+                    <img class="featured-cover-img" alt="POST_TITLE" src="@PageHelper.CheckGetCoverrUrl(item.Cover)">
                   </a>
                   <figcaption class="visually-hidden">@item.Title</figcaption>
                 </figure>
@@ -32,7 +32,7 @@
                 </h2>
                 <div class="featured-meta d-none d-md-flex">
                   <div class="featured-author">
-                    <img class="featured-author-img" src="@UserHelper.CheckGetAvatarUrl(item.User.Avatar)" width="16" height="16" alt="@item.User.NickName">
+                    <img class="featured-author-img" src="@PageHelper.CheckGetAvatarUrl(item.User.Avatar)" width="16" height="16" alt="@item.User.NickName">
                     <span class="featured-author-name">@item.User.NickName</span>
                   </div>
                   <div class="featured-date">
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/related.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/related.cshtml
index 69b0ea7f0..affb8fddc 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/related.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/related.cshtml
@@ -32,7 +32,7 @@
         <div class="col-12 col-md mb-4 mb-md-0">
           <article class="post-grid d-flex flex-column">
             <figure class="post-grid-cover" aria-hidden="true">
-              <img class="post-grid-img" src="~/@post.Cover" alt="@post.Title">
+              <img class="post-grid-img" src="@PageHelper.CheckGetCoverrUrl(post.Cover)" alt="@post.Title">
             </figure>
             @if (post.Categories != null)
             {
@@ -51,7 +51,7 @@
              </p>
             <div class="post-grid-meta d-flex">
               <div class="post-grid-author">
-                <img class="post-grid-author-img" src="@UserHelper.CheckGetAvatarUrl(post.User.Avatar)" width="16" height="16" alt="@post.User.NickName" aria-hidden="true">
+                <img class="post-grid-author-img" src="@PageHelper.CheckGetAvatarUrl(post.User.Avatar)" width="16" height="16" alt="@post.User.NickName" aria-hidden="true">
                 <span class="post-grid-author-name">@post.User.NickName</span>
               </div>
               <div class="post-grid-date">
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-grid.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-grid.cshtml
index 1a4c03326..c17ad76c7 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-grid.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-grid.cshtml
@@ -10,7 +10,7 @@
       <div class="col">
         <article class="post-grid d-flex flex-column">
           <figure class="post-grid-cover">
-            <img class="post-grid-img" src="~/@post.Cover" alt="@post.Title">
+            <img class="post-grid-img" src="@PageHelper.CheckGetCoverrUrl(post.Cover)" alt="@post.Title">
           </figure>
           @if (post.Categories != null)
           {
@@ -27,7 +27,7 @@
           <p class="post-grid-desc">@Html.Raw(post.Description)</p>
           <div class="post-grid-meta d-flex">
             <div class="post-grid-author">
-              <img class="post-grid-author-img" src="@UserHelper.CheckGetAvatarUrl(post.User.Avatar)" width="16" height="16" alt="@post.User.NickName">
+              <img class="post-grid-author-img" src="@PageHelper.CheckGetAvatarUrl(post.User.Avatar)" width="16" height="16" alt="@post.User.NickName">
               <span class="post-grid-author-name">@post.User.NickName</span>
             </div>
             <div class="post-grid-date">
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-list.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-list.cshtml
index 73e2e716a..ed9f89ee0 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-list.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post/view-list.cshtml
@@ -7,7 +7,7 @@
   {
     <article class="post-list d-flex">
       <figure class="post-list-cover">
-        <img class="post-list-img" src="~/@post.Cover" alt="@post.Title">
+        <img class="post-list-img" src="@PageHelper.CheckGetCoverrUrl(post.Cover)" alt="@post.Title">
       </figure>
       <section class="post-list-details">
         <h2 class="post-list-title">
@@ -15,7 +15,7 @@
         </h2>
         <div class="post-list-meta d-flex">
           <div class="post-list-meta-item post-list-author">
-            <img class="post-list-author-img" src="@UserHelper.CheckGetAvatarUrl(post.User.Avatar)" width="16" height="16"
+            <img class="post-list-author-img" src="@PageHelper.CheckGetAvatarUrl(post.User.Avatar)" width="16" height="16"
                  alt="@post.User.NickName">
             <span class="post-list-author-name">@post.User.NickName</span>
           </div>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml
index 418f99b23..222484eaf 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml
@@ -21,7 +21,7 @@
     <div class="form-item">
       <label class="form-label mb-1">@_localizer["profile-picture"]</label>
       <div class="d-flex">
-        <img src="@UserHelper.CheckGetAvatarUrl(Model.Avatar)" width="39" height="39" class="profilePicture rounded me-3" alt="@Model.NickName" />
+        <img src="@PageHelper.CheckGetAvatarUrl(Model.Avatar)" width="39" height="39" class="profilePicture rounded me-3" alt="@Model.NickName" />
         <button class="btn btn-link" onclick="return fileManager.uploadClick('@UploadType.Avatar');" type="button" title="@_localizer["upload"]" data-bs-toggle="tooltip">
           <svg width="18" height="18" class="bi bi-arrow-up-circle">
             <use xlink:href="/_content/@ThemesStandardConstant.AssemblyName/img/icon-sprites.svg#bi-arrow-up-circle"></use>
diff --git a/src/Blogifier/Posts/ImportManager.cs b/src/Blogifier/Posts/ImportManager.cs
index 34333766e..4286b2892 100644
--- a/src/Blogifier/Posts/ImportManager.cs
+++ b/src/Blogifier/Posts/ImportManager.cs
@@ -2,6 +2,7 @@
 using Blogifier.Identity;
 using Blogifier.Shared;
 using Blogifier.Storages;
+using Microsoft.IdentityModel.Tokens;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -47,7 +48,7 @@ public async Task<IEnumerable<PostEditorDto>> WriteAsync(ImportDto request, int
 
       var publishedAt = post.PublishedAt!.Value.ToUniversalTime();
       var baseAddress = new Uri(post.Slug!);
-      if (post.Cover != null && !post.Cover.Equals(BlogifierSharedConstant.DefaultCover, StringComparison.OrdinalIgnoreCase))
+      if (!string.IsNullOrEmpty(post.Cover))
         await _storageManager.UploadAsync(publishedAt, user.Id, baseAddress, post.Cover);
 
       var uploadeContent = await _storageManager.UploadsFoHtmlAsync(publishedAt, user.Id, baseAddress, post.Content);
diff --git a/src/Blogifier/Posts/ImportRssProvider.cs b/src/Blogifier/Posts/ImportRssProvider.cs
index 3e9288368..9d2a13538 100644
--- a/src/Blogifier/Posts/ImportRssProvider.cs
+++ b/src/Blogifier/Posts/ImportRssProvider.cs
@@ -29,7 +29,6 @@ public ImportDto Analysis(string feedUrl)
         Title = item.Title.Text,
         Description = GetDescription(item.Summary.Text),
         Content = content,
-        Cover = BlogifierSharedConstant.DefaultCover,
         PublishedAt = item.PublishDate.DateTime,
         PostType = PostType.Post,
       };

From f6a293384f9d0d229dcf6e0708835c4c86940f2b Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 8 Aug 2023 17:58:15 +0800
Subject: [PATCH 17/31] rm EditorComponent

---
 .../Components/EditorComponent.razor          | 73 -------------------
 .../Components/PageTitleComponent.razor       |  3 +-
 .../Components/PostEditorComponent.razor      | 71 ++++++++++++++++--
 .../Pages/Blogs/EditorView.razor              | 18 +----
 .../Resources/Resource.zh-CN.resx             |  3 +
 5 files changed, 69 insertions(+), 99 deletions(-)
 delete mode 100644 src/Blogifier.Admin/Components/EditorComponent.razor

diff --git a/src/Blogifier.Admin/Components/EditorComponent.razor b/src/Blogifier.Admin/Components/EditorComponent.razor
deleted file mode 100644
index e9881bf67..000000000
--- a/src/Blogifier.Admin/Components/EditorComponent.razor
+++ /dev/null
@@ -1,73 +0,0 @@
-@using System.Text.RegularExpressions;
-@using System.Text;
-
-@implements IAsyncDisposable
-
-@inject IStringLocalizer<Resource> _localizer
-@inject IJSRuntime _jsRuntime
-@inject HttpClient _httpClient
-
-<div class="easymde-wrapper">
-  <textarea @ref="_textareaReference" tabindex="2" class="visually-hidden" placeholder="@_localizer["type-here"]"></textarea>
-</div>
-<InputFile @ref="_inputFileReference" OnChange="@LoadImageFiles" style="display:none;" accept="image/*" />
-
-@code {
-
-  [Parameter] public string Toolbar { get; set; } = default!;
-
-  private ValueTask<IJSObjectReference> _taskModule;
-  private ElementReference? _textareaReference;
-  private InputFile? _inputFileReference;
-
-  protected override async Task OnAfterRenderAsync(bool firstRender)
-  {
-    if (firstRender)
-    {
-      _taskModule = _jsRuntime.InvokeAsync<IJSObjectReference>("import", "./admin/js/editor.js");
-      var module = await _taskModule;
-      var element = _inputFileReference?.Element;
-      await module.InvokeVoidAsync("loadEditor", Toolbar, _textareaReference, element);
-    }
-  }
-
-  protected async Task LoadImageFiles(InputFileChangeEventArgs args)
-  {
-    var module = await _taskModule;
-    var element = _inputFileReference?.Element;
-    await module.InvokeVoidAsync("writeFrontFile", element);
-  }
-
-  public async ValueTask SetValueAsync(string value)
-  {
-    var module = await _taskModule;
-    await module.InvokeVoidAsync("setEditorValue", value);
-  }
-
-  public async ValueTask<string?> GetValueAsync()
-  {
-    var module = await _taskModule;
-    var content = await module.InvokeAsync<string>("getEditorValue");
-    var imgsMatches = StringHelper.MatchesMarkdownImgBlob(content);
-
-    if (imgsMatches.Count > 0)
-    {
-      var contentStringBuilder = new StringBuilder(content);
-      foreach (Match match in imgsMatches)
-      {
-        var imageUrl = match.Groups[1].Value;
-        var imageBytes = await _httpClient.GetByteArrayAsync(imageUrl);
-        var base64String = Convert.ToBase64String(imageBytes);
-        contentStringBuilder.Replace(imageUrl, "data:image/png;base64," + base64String);
-      }
-      content = contentStringBuilder.ToString();
-    }
-    return content;
-  }
-
-  async ValueTask IAsyncDisposable.DisposeAsync()
-  {
-    var module = await _taskModule;
-    await module.DisposeAsync();
-  }
-}
diff --git a/src/Blogifier.Admin/Components/PageTitleComponent.razor b/src/Blogifier.Admin/Components/PageTitleComponent.razor
index fba394574..369f9e00a 100644
--- a/src/Blogifier.Admin/Components/PageTitleComponent.razor
+++ b/src/Blogifier.Admin/Components/PageTitleComponent.razor
@@ -5,7 +5,6 @@
 
   protected override async Task OnInitializedAsync()
   {
-    await SetTitleAsync();
+    await _jsRuntime.InvokeVoidAsync("commonJsFunctions.setTitle", Title);
   }
-  private async Task SetTitleAsync() => await _jsRuntime.InvokeVoidAsync("commonJsFunctions.setTitle", Title);
 }
diff --git a/src/Blogifier.Admin/Components/PostEditorComponent.razor b/src/Blogifier.Admin/Components/PostEditorComponent.razor
index 434c9a3be..7f991f172 100644
--- a/src/Blogifier.Admin/Components/PostEditorComponent.razor
+++ b/src/Blogifier.Admin/Components/PostEditorComponent.razor
@@ -1,7 +1,13 @@
+@using System.Text.RegularExpressions;
+@using System.Text;
+
+@implements IAsyncDisposable
+
 @inject IStringLocalizer<Resource> _localizer
-@inject IJSRuntime _jsruntime
+@inject IJSRuntime _jsRuntime
 @inject NavigationManager _navigation
 @inject IToaster _toaster
+@inject HttpClient _httpClient
 
 <div class="bfeditor">
   <div class="bfeditor-header">
@@ -73,7 +79,10 @@
       </div>
     </div>
   </div>
-  <EditorComponent @ref="_editorComponent" Toolbar="fullToolbar" />
+  <div class="easymde-wrapper">
+    <textarea @ref="_textareaReference" tabindex="2" class="visually-hidden" placeholder="@_localizer["type-here"]"></textarea>
+    <InputFile @ref="_inputFileReference" OnChange="@LoadImageFiles" style="display:none;" accept="image/*" />
+  </div>
 </div>
 
 @code {
@@ -82,18 +91,60 @@
   [Parameter] public EventCallback<PostEditorDto> OnSaveCallback { get; set; }
   [Parameter] public EventCallback<int> OnRemoveCallback { get; set; }
 
-  private EditorComponent _editorComponent = default!;
+  private ValueTask<IJSObjectReference> _taskModule;
+  private ElementReference? _textareaReference;
+  private InputFile? _inputFileReference;
+
+  protected override async Task OnAfterRenderAsync(bool firstRender)
+  {
+    if (firstRender)
+    {
+      _taskModule = _jsRuntime.InvokeAsync<IJSObjectReference>("import", "./admin/js/editor.js");
+      var module = await _taskModule;
+      var element = _inputFileReference?.Element;
+      await module.InvokeVoidAsync("loadEditor", "fullToolbar", _textareaReference, element);
+    }
+  }
 
   public async Task SetPostInfoAsync(PostEditorDto post)
   {
     var headTitle = _localizer["edit"] + " - " + post.Title;
-    await _jsruntime.InvokeVoidAsync("commonJsFunctions.setTitle", headTitle);
-    await _editorComponent.SetValueAsync(post.Content);
+    await _jsRuntime.InvokeVoidAsync("commonJsFunctions.setTitle", headTitle);
+    var module = await _taskModule;
+    await module.InvokeVoidAsync("setEditorValue", post.Content);
+  }
+
+  protected async Task LoadImageFiles(InputFileChangeEventArgs args)
+  {
+    var module = await _taskModule;
+    var element = _inputFileReference?.Element;
+    await module.InvokeVoidAsync("writeFrontFile", element);
+  }
+
+  async ValueTask<string?> GetValueAsync()
+  {
+    var module = await _taskModule;
+    var content = await module.InvokeAsync<string>("getEditorValue");
+    var imgsMatches = StringHelper.MatchesMarkdownImgBlob(content);
+
+    if (imgsMatches.Count > 0)
+    {
+      var contentStringBuilder = new StringBuilder(content);
+      foreach (Match match in imgsMatches)
+      {
+        var imageUrl = match.Groups[1].Value;
+        var imageBytes = await _httpClient.GetByteArrayAsync(imageUrl);
+        var base64String = Convert.ToBase64String(imageBytes);
+        contentStringBuilder.Replace(imageUrl, "data:image/png;base64," + base64String);
+      }
+      content = contentStringBuilder.ToString();
+    }
+    return content;
   }
 
   protected async Task SaveCoreAsync(PostState postState)
   {
-    var content = await _editorComponent.GetValueAsync();
+    var content = await GetValueAsync();
     if (string.IsNullOrEmpty(Post.Title) || string.IsNullOrEmpty(content))
     {
       _toaster.Error(_localizer["title-content-required"]);
@@ -125,7 +176,7 @@
 
   protected async Task RemoveAsync(int id)
   {
-    if (await _jsruntime.InvokeAsync<bool>("confirm", _localizer["confirm-delete"]))
+    if (await _jsRuntime.InvokeAsync<bool>("confirm", _localizer["confirm-delete"]))
     {
       await OnRemoveCallback.InvokeAsync(id);
     }
@@ -138,4 +189,10 @@
     return Task.CompletedTask;
   }
 
+  async ValueTask IAsyncDisposable.DisposeAsync()
+  {
+    var module = await _taskModule;
+    await module.DisposeAsync();
+  }
+
 }
diff --git a/src/Blogifier.Admin/Pages/Blogs/EditorView.razor b/src/Blogifier.Admin/Pages/Blogs/EditorView.razor
index e808c15f2..e9b628a40 100644
--- a/src/Blogifier.Admin/Pages/Blogs/EditorView.razor
+++ b/src/Blogifier.Admin/Pages/Blogs/EditorView.razor
@@ -58,22 +58,6 @@
     var result = await _httpClient.DeleteAsync($"api/post/{id}");
     if (result.IsSuccessStatusCode) _toaster.Success(_localizer["completed"]);
     else _toaster.Error(_localizer["generic-error"]);
-    _navigation.NavigateTo($"admin");
+    _navigation.NavigateTo("admin");
   }
-
-  //protected async Task OnFileUploadAsync(FrontFilePreviewDto file)
-  //{
-  //  using var fileContent = new StreamContent(file.BrowserFile.OpenReadStream());
-  //  fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.BrowserFile.ContentType);
-  //  using var content = new MultipartFormDataContent();
-  //  content.Add(content: fileContent, name: "\"file\"", fileName: file.BrowserFile.Name);
-  //  var response = await _httpClient.PostAsync("api/storage/upload", content);
-  //  if (response.IsSuccessStatusCode)
-  //  {
-  //    var stream = await response.Content.ReadAsStreamAsync();
-  //    var storage = JsonSerializer.Deserialize<StorageDto>(stream, BlogifierSharedConstant.DefaultJsonSerializerOptions)!;
-  //    await _postEditorComponent.ReplaceSelectionAsync(file, storage);
-  //  }
-  //}
-
 }
diff --git a/src/Blogifier.Shared/Resources/Resource.zh-CN.resx b/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
index f83ff6081..12731d74f 100644
--- a/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
+++ b/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
@@ -504,4 +504,7 @@
   <data name="view-all" xml:space="preserve">
     <value>查看所有</value>
   </data>
+  <data name="actions" xml:space="preserve">
+    <value>操作</value>
+  </data>
 </root>
\ No newline at end of file

From 62ef7f2242635d5b9d9bf39a2b7f1bd49c6c2783 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Tue, 8 Aug 2023 18:33:17 +0800
Subject: [PATCH 18/31] editor js interop

---
 .../Components/PostEditorComponent.razor      | 49 +++++++++---------
 .../Interop/CommonJsInterop.cs                | 25 ++++++++++
 .../Interop/EditorJsInterop.cs                | 50 +++++++++++++++++++
 src/Blogifier.Admin/Program.cs                |  2 +
 src/Blogifier.Admin/_Imports.razor            |  1 +
 src/Blogifier.Admin/assets/js/blogifier.js    |  4 +-
 6 files changed, 104 insertions(+), 27 deletions(-)
 create mode 100644 src/Blogifier.Admin/Interop/CommonJsInterop.cs
 create mode 100644 src/Blogifier.Admin/Interop/EditorJsInterop.cs

diff --git a/src/Blogifier.Admin/Components/PostEditorComponent.razor b/src/Blogifier.Admin/Components/PostEditorComponent.razor
index 7f991f172..38fa73988 100644
--- a/src/Blogifier.Admin/Components/PostEditorComponent.razor
+++ b/src/Blogifier.Admin/Components/PostEditorComponent.razor
@@ -1,13 +1,12 @@
 @using System.Text.RegularExpressions;
 @using System.Text;
 
-@implements IAsyncDisposable
-
 @inject IStringLocalizer<Resource> _localizer
 @inject IJSRuntime _jsRuntime
 @inject NavigationManager _navigation
 @inject IToaster _toaster
 @inject HttpClient _httpClient
+@inject EditorJsInterop _editorJsInterop
 
 <div class="bfeditor">
   <div class="bfeditor-header">
@@ -66,8 +65,8 @@
             </a>
             <ul class="dropdown-menu" aria-labelledby="coverDropdown">
               <li>
-                <button class="dropdown-item" onclick="return fileManager.uploadClick('@UploadType.PostCover', @Post.Id);" type="button">@_localizer["change"]</button>
-                <input type="hidden" class="txt-upload" @bind="Post.Cover" name="cover" id="cover" readonly />
+                <button class="dropdown-item" type="button" @onclick="() => ChangeCoverAsync()">@_localizer["change"]</button>
+                <InputFile @ref="_inputCovereference" OnChange="@LoadCovereFile" style="display:none;" accept="image/*" />
               </li>
               <li>
                 <button class="dropdown-item" type="button" @onclick="() => RemoveCoverAsync()">@_localizer["reset"]</button>
@@ -91,18 +90,16 @@
   [Parameter] public EventCallback<PostEditorDto> OnSaveCallback { get; set; }
   [Parameter] public EventCallback<int> OnRemoveCallback { get; set; }
 
-  private ValueTask<IJSObjectReference> _taskModule;
   private ElementReference? _textareaReference;
   private InputFile? _inputFileReference;
+  private InputFile? _inputCovereference;
 
   protected override async Task OnAfterRenderAsync(bool firstRender)
   {
     if (firstRender)
     {
-      _taskModule = _jsRuntime.InvokeAsync<IJSObjectReference>("import", "./admin/js/editor.js");
-      var module = await _taskModule;
       var element = _inputFileReference?.Element;
-      await module.InvokeVoidAsync("loadEditor", "fullToolbar", _textareaReference, element);
+      await _editorJsInterop.LoadEditorAsync(_textareaReference, element);
     }
   }
 
@@ -110,21 +107,12 @@
   {
     var headTitle = _localizer["edit"] + " - " + post.Title;
     await _jsRuntime.InvokeVoidAsync("commonJsFunctions.setTitle", headTitle);
-    var module = await _taskModule;
-    await module.InvokeVoidAsync("setEditorValue", post.Content);
-  }
-
-  protected async Task LoadImageFiles(InputFileChangeEventArgs args)
-  {
-    var module = await _taskModule;
-    var element = _inputFileReference?.Element;
-    await module.InvokeVoidAsync("writeFrontFile", element);
+    await _editorJsInterop.SetEditorValueAsync(post.Content);
   }
 
   async ValueTask<string?> GetValueAsync()
   {
-    var module = await _taskModule;
-    var content = await module.InvokeAsync<string>("getEditorValue");
+    var content = await _editorJsInterop.GetEditorValueAsync();
     var imgsMatches = StringHelper.MatchesMarkdownImgBlob(content);
 
     if (imgsMatches.Count > 0)
@@ -142,6 +130,12 @@
     return content;
   }
 
+  protected async Task LoadImageFiles(InputFileChangeEventArgs args)
+  {
+    var element = _inputFileReference?.Element;
+    await _editorJsInterop.WriteFrontFileAsync(element);
+  }
+
   protected async Task SaveCoreAsync(PostState postState)
   {
     var content = await GetValueAsync();
@@ -182,17 +176,20 @@
     }
   }
 
-  protected  Task RemoveCoverAsync()
+  protected async Task ChangeCoverAsync()
   {
-    Post.Cover = null;
-    StateHasChanged();
-    return Task.CompletedTask;
+    await _jsRuntime.InvokeVoidAsync("commonJsFunctions.triggerClick", _inputCovereference?.Element);
   }
 
-  async ValueTask IAsyncDisposable.DisposeAsync()
+  protected async Task LoadCovereFile(InputFileChangeEventArgs args)
   {
-    var module = await _taskModule;
-    await module.DisposeAsync();
+
   }
 
+  protected Task RemoveCoverAsync()
+  {
+    Post.Cover = null;
+    StateHasChanged();
+    return Task.CompletedTask;
+  }
 }
diff --git a/src/Blogifier.Admin/Interop/CommonJsInterop.cs b/src/Blogifier.Admin/Interop/CommonJsInterop.cs
new file mode 100644
index 000000000..277ffa31e
--- /dev/null
+++ b/src/Blogifier.Admin/Interop/CommonJsInterop.cs
@@ -0,0 +1,25 @@
+using Microsoft.JSInterop;
+using System;
+using System.Threading.Tasks;
+
+namespace Blogifier.Admin.Interop;
+
+public class CommonJsInterop : IAsyncDisposable
+{
+  private readonly Lazy<Task<IJSObjectReference>> moduleTask;
+
+  public CommonJsInterop(IJSRuntime jsRuntime)
+  {
+    moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
+        "import", "./_content/RazorClassLibrary1/exampleJsInterop.js").AsTask());
+  }
+
+  public async ValueTask DisposeAsync()
+  {
+    if (moduleTask.IsValueCreated)
+    {
+      var module = await moduleTask.Value;
+      await module.DisposeAsync();
+    }
+  }
+}
diff --git a/src/Blogifier.Admin/Interop/EditorJsInterop.cs b/src/Blogifier.Admin/Interop/EditorJsInterop.cs
new file mode 100644
index 000000000..2af19b8c7
--- /dev/null
+++ b/src/Blogifier.Admin/Interop/EditorJsInterop.cs
@@ -0,0 +1,50 @@
+using Microsoft.AspNetCore.Components;
+using Microsoft.JSInterop;
+using System;
+using System.Threading.Tasks;
+
+namespace Blogifier.Admin.Interop;
+
+public class EditorJsInterop : IAsyncDisposable
+{
+  private readonly Lazy<Task<IJSObjectReference>> moduleTask;
+
+  public EditorJsInterop(IJSRuntime jsRuntime)
+  {
+    moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>("import", "./admin/js/editor.js").AsTask());
+  }
+
+  public async ValueTask LoadEditorAsync(ElementReference? textarea, ElementReference? imageUpload, string toolbar = "fullToolbar")
+  {
+    var module = await moduleTask.Value;
+    await module.InvokeVoidAsync("loadEditor", toolbar, textarea, imageUpload);
+  }
+
+  public async ValueTask SetEditorValueAsync(string content)
+  {
+    var module = await moduleTask.Value;
+    await module.InvokeVoidAsync("setEditorValue", content);
+  }
+
+  public async ValueTask<string> GetEditorValueAsync()
+  {
+    var module = await moduleTask.Value;
+    var content = await module.InvokeAsync<string>("getEditorValue");
+    return content;
+  }
+
+  public async ValueTask WriteFrontFileAsync(ElementReference? imageUpload)
+  {
+    var module = await moduleTask.Value;
+    await module.InvokeVoidAsync("writeFrontFile", imageUpload);
+  }
+
+  public async ValueTask DisposeAsync()
+  {
+    if (moduleTask.IsValueCreated)
+    {
+      var module = await moduleTask.Value;
+      await module.DisposeAsync();
+    }
+  }
+}
diff --git a/src/Blogifier.Admin/Program.cs b/src/Blogifier.Admin/Program.cs
index 1f2d30da2..ef200e824 100644
--- a/src/Blogifier.Admin/Program.cs
+++ b/src/Blogifier.Admin/Program.cs
@@ -1,5 +1,6 @@
 using Blogifier;
 using Blogifier.Admin;
+using Blogifier.Admin.Interop;
 using Blogifier.Admin.Services;
 using Blogifier.Identity;
 using Microsoft.AspNetCore.Components.Authorization;
@@ -30,4 +31,5 @@
   config.NewestOnTop = false;
 });
 builder.Services.AddScoped<ToasterService>();
+builder.Services.AddScoped<EditorJsInterop>();
 await builder.Build().RunAsync();
diff --git a/src/Blogifier.Admin/_Imports.razor b/src/Blogifier.Admin/_Imports.razor
index f7552e5f3..8c1f38278 100644
--- a/src/Blogifier.Admin/_Imports.razor
+++ b/src/Blogifier.Admin/_Imports.razor
@@ -24,3 +24,4 @@
 @using Blogifier.Helper;
 @using Blogifier.Identity
 @using Blogifier.Models
+@using Blogifier.Admin.Interop
diff --git a/src/Blogifier.Admin/assets/js/blogifier.js b/src/Blogifier.Admin/assets/js/blogifier.js
index 9f1685ef1..2a8769d21 100644
--- a/src/Blogifier.Admin/assets/js/blogifier.js
+++ b/src/Blogifier.Admin/assets/js/blogifier.js
@@ -5,6 +5,9 @@ import { Tooltip } from 'bootstrap'
 import "chart.js"
 
 window.commonJsFunctions = {
+  triggerClick: function (element) {
+    element.click();
+  },
   setTitle: function (title) {
     document.title = title + " - Blogifier";
   },
@@ -67,7 +70,6 @@ window.commonJsFunctions = {
     var clock = document.getElementById('clock');
     var clockDay = document.getElementById('clock-day');
     var clockMonth = document.getElementById('clock-month');
-
     function time() {
       var date = new Date();
       var hours = date.getHours();

From 91f3ad4f412fb75a28ecea2420243e18b4512ad8 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 00:36:08 +0800
Subject: [PATCH 19/31] common js

---
 .../Interop/CommonJsInterop.cs                |  3 +-
 src/Blogifier.Admin/assets/gulpfile.mjs       | 46 +++++++++++
 src/Blogifier.Admin/assets/js/blogifier.js    |  2 +-
 src/Blogifier.Admin/assets/js/common.js       | 78 +++++++++++++++++++
 4 files changed, 126 insertions(+), 3 deletions(-)
 create mode 100644 src/Blogifier.Admin/assets/js/common.js

diff --git a/src/Blogifier.Admin/Interop/CommonJsInterop.cs b/src/Blogifier.Admin/Interop/CommonJsInterop.cs
index 277ffa31e..98dbbcdc3 100644
--- a/src/Blogifier.Admin/Interop/CommonJsInterop.cs
+++ b/src/Blogifier.Admin/Interop/CommonJsInterop.cs
@@ -10,8 +10,7 @@ public class CommonJsInterop : IAsyncDisposable
 
   public CommonJsInterop(IJSRuntime jsRuntime)
   {
-    moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
-        "import", "./_content/RazorClassLibrary1/exampleJsInterop.js").AsTask());
+    moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>("import", "./admin/js/common.js").AsTask());
   }
 
   public async ValueTask DisposeAsync()
diff --git a/src/Blogifier.Admin/assets/gulpfile.mjs b/src/Blogifier.Admin/assets/gulpfile.mjs
index 47ba6db96..205cbe932 100644
--- a/src/Blogifier.Admin/assets/gulpfile.mjs
+++ b/src/Blogifier.Admin/assets/gulpfile.mjs
@@ -134,6 +134,50 @@ const editorJs = () => {
   return stream.pipe(dest('dist/admin/js'));
 }
 
+const commonJs = () => {
+  let outputOptions = {
+    sourcemap: true,
+    format: 'es'
+  }
+
+  if (mode !== 'Debug') {
+    outputOptions.sourcemap = false;
+    outputOptions.minifyInternalExports = true;
+    outputOptions.plugins = [terser()];
+  }
+
+  let stream = rollupStream({
+    input: 'js/common.js',
+    output: outputOptions,
+    plugins: [
+      babel({
+        exclude: 'node_modules/**',
+        presets: ['@babel/preset-env'],
+        babelHelpers: 'bundled',
+      }),
+      nodeResolve({
+        browser: true,
+        preferBuiltins: false,
+      }),
+      commonjs({
+        include: ['node_modules/**'],
+        exclude: [],
+        sourceMap: mode === 'Debug',
+      }),
+    ],
+  })
+
+  stream = stream.pipe(source('common.js'));
+  if (mode !== 'Debug') {
+    // JS Minify
+    stream = stream.pipe(buffer())
+    stream = stream.pipe(plumber())
+    stream = stream.pipe(uglify())
+  }
+  return stream.pipe(dest('dist/admin/js'));
+}
+
+
 // sass
 const scss = () => {
   let stream = src("./scss/**/*.scss")
@@ -170,6 +214,7 @@ export default series(
     scss,
     blogifierJs,
     editorJs,
+    commonJs,
     watcher
   )
 );
@@ -179,6 +224,7 @@ const build = series(
   scss,
   blogifierJs,
   editorJs,
+  commonJs,
 );
 
 export { debug, release, build };
diff --git a/src/Blogifier.Admin/assets/js/blogifier.js b/src/Blogifier.Admin/assets/js/blogifier.js
index 2a8769d21..94dbddb17 100644
--- a/src/Blogifier.Admin/assets/js/blogifier.js
+++ b/src/Blogifier.Admin/assets/js/blogifier.js
@@ -42,7 +42,6 @@ window.commonJsFunctions = {
     }, 500);
   },
   writeCookie: function (name, value, days) {
-
     var expires;
     if (days) {
       var date = new Date();
@@ -88,6 +87,7 @@ window.commonJsFunctions = {
     time();
     setInterval(time, 60 * 1000);
   }
+
 };
 
 window.DataService = function () {
diff --git a/src/Blogifier.Admin/assets/js/common.js b/src/Blogifier.Admin/assets/js/common.js
new file mode 100644
index 000000000..1ea03f0a4
--- /dev/null
+++ b/src/Blogifier.Admin/assets/js/common.js
@@ -0,0 +1,78 @@
+
+export function triggerClick(element) {
+  element.click();
+}
+
+export function setTitle(title) {
+  document.title = title + " - Blogifier";
+}
+
+export function setPageTitle() {
+
+}
+
+export function getFieldValue() {
+  if (field.type === 'checkbox') {
+    return document.getElementById(field.id).checked;
+  }
+  else {
+    return document.getElementById(field.id).value;
+  }
+}
+
+export function getTxtValue(txt) {
+  return document.getElementById(txt).value;
+}
+
+export function focusElement(id) {
+  setTimeout(function () {
+    const element = document.getElementById(id);
+    element.focus();
+  }, 500);
+}
+
+export function writeCookie() {
+  var expires;
+  if (days) {
+    var date = new Date();
+    date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
+    expires = "; expires=" + date.toGMTString();
+  }
+  else {
+    expires = "";
+  }
+  document.cookie = name + "=" + value + expires + "; path=/";
+}
+
+export function setTooltip(){
+  setTimeout(function () {
+    let options = { "trigger": "hover", fallbackPlacements: ['bottom'] };
+    let tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
+    let tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
+      return new Tooltip(tooltipTriggerEl, options)
+    });
+  }, 1000);
+}
+
+export function startClock() {
+  var clock = document.getElementById('clock');
+  var clockDay = document.getElementById('clock-day');
+  var clockMonth = document.getElementById('clock-month');
+  function time() {
+    var date = new Date();
+    var hours = date.getHours();
+    var minutes = date.getMinutes();
+    hours = hours % 12;
+    hours = hours ? hours : 12;
+    minutes = minutes < 10 ? '0' + minutes : minutes;
+    clock.textContent = hours + ':' + minutes;
+
+    var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+    var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
+
+    clockDay.textContent = days[date.getDay()];
+    clockMonth.textContent = months[date.getMonth()] + ' ' + date.getDate();
+  }
+  time();
+  setInterval(time, 60 * 1000);
+}

From 82702ae5b9fac5a7a776fb925d81a2e8a695525e Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 11:01:42 +0800
Subject: [PATCH 20/31] CommonJs

---
 .../Components/NavMenuComponent.razor         |  3 +-
 .../Components/PageTitleComponent.razor       |  3 +-
 .../Components/PostEditorComponent.razor      |  5 +-
 .../Interop/CommonJsInterop.cs                | 18 ++++
 src/Blogifier.Admin/Program.cs                |  1 +
 src/Blogifier.Admin/assets/js/blogifier.js    | 88 -------------------
 src/Blogifier.Admin/assets/js/common.js       |  1 +
 7 files changed, 27 insertions(+), 92 deletions(-)

diff --git a/src/Blogifier.Admin/Components/NavMenuComponent.razor b/src/Blogifier.Admin/Components/NavMenuComponent.razor
index 1f1065497..6a7b98856 100644
--- a/src/Blogifier.Admin/Components/NavMenuComponent.razor
+++ b/src/Blogifier.Admin/Components/NavMenuComponent.razor
@@ -3,6 +3,7 @@
 @inject AuthenticationStateProvider _stateProvider
 @inject IJSRuntime _jsRuntime
 @inject IStringLocalizer<Resource> _localizer
+@inject CommonJsInterop _commonJsInterop
 
 @code {
 
@@ -20,7 +21,7 @@
   {
     if (firstRender)
     {
-      await _jsRuntime.InvokeAsync<string>("commonJsFunctions.setTooltip", "");
+      await _commonJsInterop.SetTooltipAsync();
     }
   }
 }
diff --git a/src/Blogifier.Admin/Components/PageTitleComponent.razor b/src/Blogifier.Admin/Components/PageTitleComponent.razor
index 369f9e00a..4ddde8a07 100644
--- a/src/Blogifier.Admin/Components/PageTitleComponent.razor
+++ b/src/Blogifier.Admin/Components/PageTitleComponent.razor
@@ -1,10 +1,11 @@
 @inject IJSRuntime _jsRuntime;
+@inject CommonJsInterop _commonJsInterop
 
 @code {
   [Parameter] public string Title { get; set; } = default!;
 
   protected override async Task OnInitializedAsync()
   {
-    await _jsRuntime.InvokeVoidAsync("commonJsFunctions.setTitle", Title);
+    await _commonJsInterop.SetTitleAsync(Title);
   }
 }
diff --git a/src/Blogifier.Admin/Components/PostEditorComponent.razor b/src/Blogifier.Admin/Components/PostEditorComponent.razor
index 38fa73988..1cec425d9 100644
--- a/src/Blogifier.Admin/Components/PostEditorComponent.razor
+++ b/src/Blogifier.Admin/Components/PostEditorComponent.razor
@@ -7,6 +7,7 @@
 @inject IToaster _toaster
 @inject HttpClient _httpClient
 @inject EditorJsInterop _editorJsInterop
+@inject CommonJsInterop _commonJsInterop
 
 <div class="bfeditor">
   <div class="bfeditor-header">
@@ -106,7 +107,7 @@
   public async Task SetPostInfoAsync(PostEditorDto post)
   {
     var headTitle = _localizer["edit"] + " - " + post.Title;
-    await _jsRuntime.InvokeVoidAsync("commonJsFunctions.setTitle", headTitle);
+    await _commonJsInterop.SetTitleAsync(headTitle);
     await _editorJsInterop.SetEditorValueAsync(post.Content);
   }
 
@@ -178,7 +179,7 @@
 
   protected async Task ChangeCoverAsync()
   {
-    await _jsRuntime.InvokeVoidAsync("commonJsFunctions.triggerClick", _inputCovereference?.Element);
+    await _commonJsInterop.TriggerClickAsync();
   }
 
   protected async Task LoadCovereFile(InputFileChangeEventArgs args)
diff --git a/src/Blogifier.Admin/Interop/CommonJsInterop.cs b/src/Blogifier.Admin/Interop/CommonJsInterop.cs
index 98dbbcdc3..e99fd9ad9 100644
--- a/src/Blogifier.Admin/Interop/CommonJsInterop.cs
+++ b/src/Blogifier.Admin/Interop/CommonJsInterop.cs
@@ -13,6 +13,24 @@ public CommonJsInterop(IJSRuntime jsRuntime)
     moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>("import", "./admin/js/common.js").AsTask());
   }
 
+  public async ValueTask SetTooltipAsync()
+  {
+    var module = await moduleTask.Value;
+    await module.InvokeVoidAsync("setTooltip");
+  }
+
+  public async ValueTask SetTitleAsync(string content)
+  {
+    var module = await moduleTask.Value;
+    await module.InvokeVoidAsync("setTitle", content);
+  }
+
+  public async ValueTask TriggerClickAsync()
+  {
+    var module = await moduleTask.Value;
+    await module.InvokeVoidAsync("triggerClick");
+  }
+
   public async ValueTask DisposeAsync()
   {
     if (moduleTask.IsValueCreated)
diff --git a/src/Blogifier.Admin/Program.cs b/src/Blogifier.Admin/Program.cs
index ef200e824..9a91c4fcf 100644
--- a/src/Blogifier.Admin/Program.cs
+++ b/src/Blogifier.Admin/Program.cs
@@ -32,4 +32,5 @@
 });
 builder.Services.AddScoped<ToasterService>();
 builder.Services.AddScoped<EditorJsInterop>();
+builder.Services.AddScoped<CommonJsInterop>();
 await builder.Build().RunAsync();
diff --git a/src/Blogifier.Admin/assets/js/blogifier.js b/src/Blogifier.Admin/assets/js/blogifier.js
index 94dbddb17..c54f2d3c5 100644
--- a/src/Blogifier.Admin/assets/js/blogifier.js
+++ b/src/Blogifier.Admin/assets/js/blogifier.js
@@ -1,95 +1,7 @@
 
-import { Tooltip } from 'bootstrap'
-
 // Chart.js v2.9.4
 import "chart.js"
 
-window.commonJsFunctions = {
-  triggerClick: function (element) {
-    element.click();
-  },
-  setTitle: function (title) {
-    document.title = title + " - Blogifier";
-  },
-  setPageTitle: function () {
-
-  },
-  showPrompt: function (message) {
-    return prompt(message, 'Type anything here');
-  },
-  hideLoader: function (id) {
-    var el = document.getElementById(id);
-    el.style.display = 'none';
-  },
-  getFieldValue: function (field) {
-    if (field.type === 'checkbox') {
-      return document.getElementById(field.id).checked;
-    }
-    else {
-      return document.getElementById(field.id).value;
-    }
-  },
-  getTxtValue: function (txt) {
-    return document.getElementById(txt).value;
-  },
-  getSrcValue: function (src) {
-    return document.getElementById(src).src;
-  },
-  focusElement: function (id) {
-    setTimeout(function () {
-      const element = document.getElementById(id);
-      element.focus();
-    }, 500);
-  },
-  writeCookie: function (name, value, days) {
-    var expires;
-    if (days) {
-      var date = new Date();
-      date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
-      expires = "; expires=" + date.toGMTString();
-    }
-    else {
-      expires = "";
-    }
-    document.cookie = name + "=" + value + expires + "; path=/";
-  },
-  setTooltip: function (args) {
-    setTimeout(function () {
-      let options = { "trigger": "hover", fallbackPlacements: ['bottom'] };
-      let tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
-      let tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
-        return new Tooltip(tooltipTriggerEl, options)
-      });
-    }, 1000);
-  },
-  showModal: function (id) {
-    document.getElementById(id).showModal();
-  },
-  startClock: function () {
-    var clock = document.getElementById('clock');
-    var clockDay = document.getElementById('clock-day');
-    var clockMonth = document.getElementById('clock-month');
-    function time() {
-      var date = new Date();
-      var hours = date.getHours();
-      var minutes = date.getMinutes();
-      hours = hours % 12;
-      hours = hours ? hours : 12;
-      minutes = minutes < 10 ? '0' + minutes : minutes;
-      clock.textContent = hours + ':' + minutes;
-
-      var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
-      var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
-
-      clockDay.textContent = days[date.getDay()];
-      clockMonth.textContent = months[date.getMonth()] + ' ' + date.getDate();
-    }
-    time();
-    setInterval(time, 60 * 1000);
-  }
-
-};
-
 window.DataService = function () {
   let upload = function (url, obj, success, fail) {
     fetch(url, {
diff --git a/src/Blogifier.Admin/assets/js/common.js b/src/Blogifier.Admin/assets/js/common.js
index 1ea03f0a4..e376a3d6b 100644
--- a/src/Blogifier.Admin/assets/js/common.js
+++ b/src/Blogifier.Admin/assets/js/common.js
@@ -1,3 +1,4 @@
+import { Tooltip } from 'bootstrap';
 
 export function triggerClick(element) {
   element.click();

From 2a712be8280f1c353ac676e58ccf31c1a7bd96ee Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 11:10:18 +0800
Subject: [PATCH 21/31] admin assets

---
 src/Blogifier.Admin/assets/js/editor.js       |  2 +-
 .../wwwroot/admin/favicons/browserconfig.xml  | 12 +++----
 .../wwwroot/admin/favicons/site.webmanifest   | 34 +++++++++----------
 src/Blogifier.Admin/wwwroot/index.html        | 19 ++++-------
 4 files changed, 31 insertions(+), 36 deletions(-)

diff --git a/src/Blogifier.Admin/assets/js/editor.js b/src/Blogifier.Admin/assets/js/editor.js
index c71cb2eca..64f10d058 100644
--- a/src/Blogifier.Admin/assets/js/editor.js
+++ b/src/Blogifier.Admin/assets/js/editor.js
@@ -204,7 +204,7 @@ function editorToolbarTooltip() {
     buttons[i].setAttribute('data-bs-placement', 'bottom');
   }
   // TODO: remove this later:
-  commonJsFunctions.setTooltip();
+  //commonJsFunctions.setTooltip();
 }
 
 let easymde;
diff --git a/src/Blogifier.Admin/wwwroot/admin/favicons/browserconfig.xml b/src/Blogifier.Admin/wwwroot/admin/favicons/browserconfig.xml
index f4c19c27a..fa7e4a294 100644
--- a/src/Blogifier.Admin/wwwroot/admin/favicons/browserconfig.xml
+++ b/src/Blogifier.Admin/wwwroot/admin/favicons/browserconfig.xml
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <browserconfig>
-    <msapplication>
-        <tile>
-            <square150x150logo src="/mstile-150x150.png?v=3"/>
-            <TileColor>#ffffff</TileColor>
-        </tile>
-    </msapplication>
+  <msapplication>
+    <tile>
+      <square150x150logo src="/mstile-150x150.png?v=3"/>
+      <TileColor>#ffffff</TileColor>
+    </tile>
+  </msapplication>
 </browserconfig>
diff --git a/src/Blogifier.Admin/wwwroot/admin/favicons/site.webmanifest b/src/Blogifier.Admin/wwwroot/admin/favicons/site.webmanifest
index 0d4c82f8b..92f571f0c 100644
--- a/src/Blogifier.Admin/wwwroot/admin/favicons/site.webmanifest
+++ b/src/Blogifier.Admin/wwwroot/admin/favicons/site.webmanifest
@@ -1,19 +1,19 @@
 {
-    "name": "Blogifier",
-    "short_name": "Blogifier",
-    "icons": [
-        {
-            "src": "admin/favicons/android-chrome-192x192.png?v=3",
-            "sizes": "192x192",
-            "type": "image/png"
-        },
-        {
-            "src": "admin/favicons/android-chrome-512x512.png?v=3",
-            "sizes": "512x512",
-            "type": "image/png"
-        }
-    ],
-    "theme_color": "#ffffff",
-    "background_color": "#ffffff",
-    "display": "standalone"
+  "name": "Blogifier",
+  "short_name": "Blogifier",
+  "icons": [
+    {
+      "src": "admin/favicons/android-chrome-192x192.png?v=3",
+      "sizes": "192x192",
+      "type": "image/png"
+    },
+    {
+      "src": "admin/favicons/android-chrome-512x512.png?v=3",
+      "sizes": "512x512",
+      "type": "image/png"
+    }
+  ],
+  "theme_color": "#ffffff",
+  "background_color": "#ffffff",
+  "display": "standalone"
 }
diff --git a/src/Blogifier.Admin/wwwroot/index.html b/src/Blogifier.Admin/wwwroot/index.html
index 628017365..a6e34a242 100644
--- a/src/Blogifier.Admin/wwwroot/index.html
+++ b/src/Blogifier.Admin/wwwroot/index.html
@@ -10,16 +10,16 @@
       TODO: for rtl:
       <link href="admin/css/blogifier.rtl.css" rel="stylesheet" />
   -->
-  <link rel="apple-touch-icon" sizes="180x180" href="amdin/favicons/apple-touch-icon.png?v=2">
-  <link rel="icon" type="image/png" sizes="32x32" href="admin/favicons/favicon-32x32.png?v=2">
-  <link rel="icon" type="image/png" sizes="16x16" href="admin/favicons/favicon-16x16.png?v=2">
-  <link rel="manifest" href="admin/favicons/site.webmanifest?v=2">
-  <link rel="mask-icon" href="admin/favicons/safari-pinned-tab.svg?v=2" color="#622aff">
-  <link rel="shortcut icon" href="admin/favicons/favicon.ico?v=2">
+  <link rel="apple-touch-icon" sizes="180x180" href="amdin/favicons/apple-touch-icon.png">
+  <link rel="icon" type="image/png" sizes="32x32" href="admin/favicons/favicon-32x32.png">
+  <link rel="icon" type="image/png" sizes="16x16" href="admin/favicons/favicon-16x16.png">
+  <link rel="manifest" href="admin/favicons/site.webmanifest">
+  <link rel="mask-icon" href="admin/favicons/safari-pinned-tab.svg" color="#622aff">
+  <link rel="shortcut icon" href="admin/favicons/favicon.ico">
   <meta name="apple-mobile-web-app-title" content="Blogifier">
   <meta name="application-name" content="Blogifier">
   <meta name="msapplication-TileColor" content="#ffffff">
-  <meta name="msapplication-config" content="admin/favicons/browserconfig.xml?v=2" />
+  <meta name="msapplication-config" content="admin/favicons/browserconfig.xml" />
   <meta name="theme-color" content="#ffffff">
 </head>
 
@@ -48,11 +48,6 @@
     </div>
   </div>
 
-  <form class="d-none" method="post" name="frmUpload" id="frmUpload" action="upload" enctype="multipart/form-data">
-    <input type="file" name="file" id="frmUploadFile" onchange="fileManager.uploadSubmit(); return false;" />
-    <input type="submit" value="Upload" />
-  </form>
-
   <script src="_framework/blazor.webassembly.js"></script>
   <script src="admin/js/blogifier.js"></script>
 </body>

From 2eeefb6739710b60a7a9760c1911e190cccde30e Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 11:44:29 +0800
Subject: [PATCH 22/31] fix regex matche

---
 .../Components/PostEditorComponent.razor       |  8 +++++---
 src/Blogifier.Admin/Dtos/FrontBlobInfo.cs      |  7 +++++++
 src/Blogifier.Admin/Interop/CommonJsInterop.cs | 11 +++++++++--
 src/Blogifier.Admin/assets/js/common.js        |  9 ++++++++-
 src/Blogifier.Shared/Helper/StringHelper.cs    | 18 ++++--------------
 .../Resources/Resource.zh-CN.resx              |  6 ++++++
 src/Blogifier/Storages/StorageManager.cs       |  7 ++++---
 7 files changed, 43 insertions(+), 23 deletions(-)
 create mode 100644 src/Blogifier.Admin/Dtos/FrontBlobInfo.cs

diff --git a/src/Blogifier.Admin/Components/PostEditorComponent.razor b/src/Blogifier.Admin/Components/PostEditorComponent.razor
index 1cec425d9..db14d458a 100644
--- a/src/Blogifier.Admin/Components/PostEditorComponent.razor
+++ b/src/Blogifier.Admin/Components/PostEditorComponent.razor
@@ -114,7 +114,7 @@
   async ValueTask<string?> GetValueAsync()
   {
     var content = await _editorJsInterop.GetEditorValueAsync();
-    var imgsMatches = StringHelper.MatchesMarkdownImgBlob(content);
+    var imgsMatches = StringHelper.MarkdownImgBlobGeneratedRegex().Matches(content);
 
     if (imgsMatches.Count > 0)
     {
@@ -179,12 +179,14 @@
 
   protected async Task ChangeCoverAsync()
   {
-    await _commonJsInterop.TriggerClickAsync();
+    await _commonJsInterop.TriggerClickAsync(_inputCovereference?.Element);
   }
 
   protected async Task LoadCovereFile(InputFileChangeEventArgs args)
   {
-
+    var element = _inputCovereference?.Element;
+    var blobInfo = await _commonJsInterop.GetInputFileBlobInfoAsync(element);
+    Post.Cover = blobInfo.Url;
   }
 
   protected Task RemoveCoverAsync()
diff --git a/src/Blogifier.Admin/Dtos/FrontBlobInfo.cs b/src/Blogifier.Admin/Dtos/FrontBlobInfo.cs
new file mode 100644
index 000000000..dabd575f9
--- /dev/null
+++ b/src/Blogifier.Admin/Dtos/FrontBlobInfo.cs
@@ -0,0 +1,7 @@
+namespace Blogifier.Admin;
+
+public class FrontBlobInfo
+{
+  public string FileName { get; set; } = default!;
+  public string Url { get; set; } = default!;
+}
diff --git a/src/Blogifier.Admin/Interop/CommonJsInterop.cs b/src/Blogifier.Admin/Interop/CommonJsInterop.cs
index e99fd9ad9..76d2dccb6 100644
--- a/src/Blogifier.Admin/Interop/CommonJsInterop.cs
+++ b/src/Blogifier.Admin/Interop/CommonJsInterop.cs
@@ -1,3 +1,4 @@
+using Microsoft.AspNetCore.Components;
 using Microsoft.JSInterop;
 using System;
 using System.Threading.Tasks;
@@ -25,10 +26,16 @@ public async ValueTask SetTitleAsync(string content)
     await module.InvokeVoidAsync("setTitle", content);
   }
 
-  public async ValueTask TriggerClickAsync()
+  public async ValueTask TriggerClickAsync(ElementReference? element)
   {
     var module = await moduleTask.Value;
-    await module.InvokeVoidAsync("triggerClick");
+    await module.InvokeVoidAsync("triggerClick", element);
+  }
+
+  public async ValueTask<FrontBlobInfo> GetInputFileBlobInfoAsync(ElementReference? inputUpload)
+  {
+    var module = await moduleTask.Value;
+    return await module.InvokeAsync<FrontBlobInfo>("getInputFileBlobInfo", inputUpload);
   }
 
   public async ValueTask DisposeAsync()
diff --git a/src/Blogifier.Admin/assets/js/common.js b/src/Blogifier.Admin/assets/js/common.js
index e376a3d6b..7cdd02f3e 100644
--- a/src/Blogifier.Admin/assets/js/common.js
+++ b/src/Blogifier.Admin/assets/js/common.js
@@ -45,7 +45,7 @@ export function writeCookie() {
   document.cookie = name + "=" + value + expires + "; path=/";
 }
 
-export function setTooltip(){
+export function setTooltip() {
   setTimeout(function () {
     let options = { "trigger": "hover", fallbackPlacements: ['bottom'] };
     let tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
@@ -77,3 +77,10 @@ export function startClock() {
   time();
   setInterval(time, 60 * 1000);
 }
+
+export function getInputFileBlobInfo(inputElement) {
+  const file = inputElement.files[0];
+  const fileName = file.name;
+  const url = URL.createObjectURL(file);
+  return { fileName, url };
+}
diff --git a/src/Blogifier.Shared/Helper/StringHelper.cs b/src/Blogifier.Shared/Helper/StringHelper.cs
index 57d6bdf39..874bb17e8 100644
--- a/src/Blogifier.Shared/Helper/StringHelper.cs
+++ b/src/Blogifier.Shared/Helper/StringHelper.cs
@@ -4,35 +4,25 @@ namespace Blogifier.Helper;
 
 public static partial class StringHelper
 {
-
   [GeneratedRegex("<script[^>]*>[\\s\\S]*?</script>", RegexOptions.Compiled)]
   private static partial Regex HtmlScriptGeneratedRegex();
   public static string RemoveHtmlScriptTags(string input) => HtmlScriptGeneratedRegex().Replace(input, string.Empty);
 
-
   [GeneratedRegex("<img[^>]*>[\\s\\S]*?>", RegexOptions.Compiled)]
   private static partial Regex HtmlImgGeneratedRegex();
   public static string RemoveHtmlImgTags(string input) => HtmlImgGeneratedRegex().Replace(input, string.Empty);
 
-
   [GeneratedRegex("<img.+?src=[\"'](.+?)[\"'].+?>", RegexOptions.Compiled)]
-  private static partial Regex HtmlImgSrcGeneratedRegex();
-  public static Match MatchHtmlImgSrc(string input) => HtmlImgSrcGeneratedRegex().Match(input);
+  public static partial Regex HtmlImgSrcGeneratedRegex();
 
   [GeneratedRegex("<img[^>]*?src\\s*=\\s*[\"']?([^'\" >]+?)[ '\"][^>]*?>", RegexOptions.Compiled)]
-  private static partial Regex HtmlImgTagsGeneratedRegex();
-  public static MatchCollection MatchesHtmlImgTags(string input) => HtmlImgTagsGeneratedRegex().Matches(input);
-
+  public static partial Regex HtmlImgTagsGeneratedRegex();
 
   [GeneratedRegex("(?i)<a\\b[^>]*?>(?<text>.*?)</a>", RegexOptions.Compiled)]
-  private static partial Regex HtmlFileGeneratedRegex();
-  public static MatchCollection MatchesHtmlFile(string input) => HtmlFileGeneratedRegex().Matches(input);
-
+  public static partial Regex HtmlFileGeneratedRegex();
 
   [GeneratedRegex("!\\[[^\\]]*\\]\\((blob:[^)]+)\\)", RegexOptions.Compiled)]
-  private static partial Regex MarkdownImgBlobGeneratedRegex();
-  public static MatchCollection MatchesMarkdownImgBlob(string input) => MarkdownImgBlobGeneratedRegex().Matches(input);
-
+  public static partial Regex MarkdownImgBlobGeneratedRegex();
 
   [GeneratedRegex(@"!\[(?<filename>[^\]]+)\]\(data:image\/(?<type>.+);base64,(?<data>.+?)\)", RegexOptions.Compiled)]
   public static partial Regex MarkdownDataImageBase64BlobGeneratedRegex();
diff --git a/src/Blogifier.Shared/Resources/Resource.zh-CN.resx b/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
index 12731d74f..bf3c902be 100644
--- a/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
+++ b/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
@@ -507,4 +507,10 @@
   <data name="actions" xml:space="preserve">
     <value>操作</value>
   </data>
+  <data name="change" xml:space="preserve">
+    <value>修改</value>
+  </data>
+  <data name="reset" xml:space="preserve">
+    <value>重置</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/src/Blogifier/Storages/StorageManager.cs b/src/Blogifier/Storages/StorageManager.cs
index 6487bdc44..60af6d68e 100644
--- a/src/Blogifier/Storages/StorageManager.cs
+++ b/src/Blogifier/Storages/StorageManager.cs
@@ -108,14 +108,15 @@ public async Task<string> UploadsFoHtmlAsync(DateTime uploadAt, int userid, Uri
 
   public async Task<string> UploadImagesFoHtml(DateTime uploadAt, int userid, Uri baseAddress, string content)
   {
-    var matches = StringHelper.MatchesHtmlImgTags(content);
+    var matches = StringHelper.HtmlImgTagsGeneratedRegex().Matches(content);
     if (matches.Any())
     {
       var contentBuilder = new StringBuilder(content);
+      var htmlImgSrcRegex = StringHelper.HtmlImgSrcGeneratedRegex();
       foreach (Match match in matches.Cast<Match>())
       {
         var tag = match.Value;
-        var matchUrl = StringHelper.MatchHtmlImgSrc(tag);
+        var matchUrl = htmlImgSrcRegex.Match(tag);
         var urlString = matchUrl.Groups[1].Value;
         var storage = await UploadAsync(uploadAt, userid, baseAddress, urlString);
         var uploadTag = $"![{storage.Name}]({storage.Slug})";
@@ -128,7 +129,7 @@ public async Task<string> UploadImagesFoHtml(DateTime uploadAt, int userid, Uri
 
   public async Task<string> UploadFilesFoHtml(DateTime uploadAt, int userid, Uri baseAddress, string content)
   {
-    var matches = StringHelper.MatchesHtmlFile(content);
+    var matches = StringHelper.HtmlFileGeneratedRegex().Matches(content);
     if (matches.Any())
     {
       var contentBuilder = new StringBuilder(content);

From 4292759637368970636247b69cb0cdf919a97987 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 12:02:25 +0800
Subject: [PATCH 23/31] Cover Generated Data img

---
 .../Components/PostEditorComponent.razor       | 18 +++++++++++++++++-
 src/Blogifier.Admin/assets/gulpfile.mjs        |  1 -
 src/Blogifier.Admin/assets/js/common.js        |  1 +
 src/Blogifier.Shared/Helper/StringHelper.cs    |  3 +++
 src/Blogifier/Storages/StorageManager.cs       |  4 ----
 5 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/src/Blogifier.Admin/Components/PostEditorComponent.razor b/src/Blogifier.Admin/Components/PostEditorComponent.razor
index db14d458a..1ed41073f 100644
--- a/src/Blogifier.Admin/Components/PostEditorComponent.razor
+++ b/src/Blogifier.Admin/Components/PostEditorComponent.razor
@@ -146,12 +146,28 @@
       return;
     }
     Post.Content = content;
+    if (!string.IsNullOrEmpty(Post.Cover))
+    {
+      Console.WriteLine(Post.Cover);
+      var coverMatche = StringHelper.BlobUrlGeneratedRegex().Match(Post.Cover);
+      Console.WriteLine(coverMatche.Success);
+      if (coverMatche.Success)
+      {
+        var imageUrl = coverMatche.Value;
+        var imageBytes = await _httpClient.GetByteArrayAsync(imageUrl);
+        var base64String = Convert.ToBase64String(imageBytes);
+        var dataString = "data:image/png;base64," + base64String;
+        Post.Cover = dataString;
+        Console.WriteLine(dataString);
+      }
+    }
+
     //Post.Cover = await _jsruntime.InvokeAsync<string>("commonJsFunctions.getSrcValue", "postCover");
     //Post.Cover = Post.Cover.Replace(_navigation.BaseUri, "");
     //if (string.IsNullOrEmpty(Post.Cover)) Post.Cover = BlogifierSharedConstant.DefaultCover;
     if (string.IsNullOrEmpty(Post.Description)) Post.Description = Post.Title;
     Post.State = postState;
-    await OnSaveCallback.InvokeAsync(Post);
+    //await OnSaveCallback.InvokeAsync(Post);
   }
 
   protected async Task SaveAsync()
diff --git a/src/Blogifier.Admin/assets/gulpfile.mjs b/src/Blogifier.Admin/assets/gulpfile.mjs
index 205cbe932..6202f7955 100644
--- a/src/Blogifier.Admin/assets/gulpfile.mjs
+++ b/src/Blogifier.Admin/assets/gulpfile.mjs
@@ -209,7 +209,6 @@ const watcher = () => {
 };
 
 export default series(
-  clean,
   parallel(
     scss,
     blogifierJs,
diff --git a/src/Blogifier.Admin/assets/js/common.js b/src/Blogifier.Admin/assets/js/common.js
index 7cdd02f3e..765469dc3 100644
--- a/src/Blogifier.Admin/assets/js/common.js
+++ b/src/Blogifier.Admin/assets/js/common.js
@@ -82,5 +82,6 @@ export function getInputFileBlobInfo(inputElement) {
   const file = inputElement.files[0];
   const fileName = file.name;
   const url = URL.createObjectURL(file);
+  console.log(url);
   return { fileName, url };
 }
diff --git a/src/Blogifier.Shared/Helper/StringHelper.cs b/src/Blogifier.Shared/Helper/StringHelper.cs
index 874bb17e8..634c403e8 100644
--- a/src/Blogifier.Shared/Helper/StringHelper.cs
+++ b/src/Blogifier.Shared/Helper/StringHelper.cs
@@ -26,4 +26,7 @@ public static partial class StringHelper
 
   [GeneratedRegex(@"!\[(?<filename>[^\]]+)\]\(data:image\/(?<type>.+);base64,(?<data>.+?)\)", RegexOptions.Compiled)]
   public static partial Regex MarkdownDataImageBase64BlobGeneratedRegex();
+
+  [GeneratedRegex(@"blob:(https?://[^/]+/\S+)", RegexOptions.Compiled)]
+  public static partial Regex BlobUrlGeneratedRegex();
 }
diff --git a/src/Blogifier/Storages/StorageManager.cs b/src/Blogifier/Storages/StorageManager.cs
index 60af6d68e..8c87a85d1 100644
--- a/src/Blogifier/Storages/StorageManager.cs
+++ b/src/Blogifier/Storages/StorageManager.cs
@@ -5,11 +5,7 @@
 using Microsoft.AspNetCore.StaticFiles;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Microsoft.IdentityModel.Tokens;
 using System;
-using System.Collections.Generic;
-using System.IO;
 using System.Linq;
 using System.Net.Http;
 using System.Text;

From 25da050955a366d3d921e8a27094b2456c8c2160 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 13:06:23 +0800
Subject: [PATCH 24/31] up cover

---
 .../Components/PostEditorComponent.razor       | 10 ++--------
 src/Blogifier.Shared/Helper/StringHelper.cs    | 11 ++++++-----
 src/Blogifier/Interfaces/PostController.cs     | 10 ++++++++++
 src/Blogifier/Posts/PostProvider.cs            | 16 ++++++++++++----
 src/Blogifier/Storages/StorageManager.cs       | 18 ++++++++++++++++++
 5 files changed, 48 insertions(+), 17 deletions(-)

diff --git a/src/Blogifier.Admin/Components/PostEditorComponent.razor b/src/Blogifier.Admin/Components/PostEditorComponent.razor
index 1ed41073f..e6a44eec0 100644
--- a/src/Blogifier.Admin/Components/PostEditorComponent.razor
+++ b/src/Blogifier.Admin/Components/PostEditorComponent.razor
@@ -148,26 +148,20 @@
     Post.Content = content;
     if (!string.IsNullOrEmpty(Post.Cover))
     {
-      Console.WriteLine(Post.Cover);
       var coverMatche = StringHelper.BlobUrlGeneratedRegex().Match(Post.Cover);
-      Console.WriteLine(coverMatche.Success);
       if (coverMatche.Success)
       {
         var imageUrl = coverMatche.Value;
         var imageBytes = await _httpClient.GetByteArrayAsync(imageUrl);
         var base64String = Convert.ToBase64String(imageBytes);
         var dataString = "data:image/png;base64," + base64String;
-        Post.Cover = dataString;
         Console.WriteLine(dataString);
+        Post.Cover = dataString;
       }
     }
-
-    //Post.Cover = await _jsruntime.InvokeAsync<string>("commonJsFunctions.getSrcValue", "postCover");
-    //Post.Cover = Post.Cover.Replace(_navigation.BaseUri, "");
-    //if (string.IsNullOrEmpty(Post.Cover)) Post.Cover = BlogifierSharedConstant.DefaultCover;
     if (string.IsNullOrEmpty(Post.Description)) Post.Description = Post.Title;
     Post.State = postState;
-    //await OnSaveCallback.InvokeAsync(Post);
+    await OnSaveCallback.InvokeAsync(Post);
   }
 
   protected async Task SaveAsync()
diff --git a/src/Blogifier.Shared/Helper/StringHelper.cs b/src/Blogifier.Shared/Helper/StringHelper.cs
index 634c403e8..1f9c3c34c 100644
--- a/src/Blogifier.Shared/Helper/StringHelper.cs
+++ b/src/Blogifier.Shared/Helper/StringHelper.cs
@@ -5,12 +5,10 @@ namespace Blogifier.Helper;
 public static partial class StringHelper
 {
   [GeneratedRegex("<script[^>]*>[\\s\\S]*?</script>", RegexOptions.Compiled)]
-  private static partial Regex HtmlScriptGeneratedRegex();
-  public static string RemoveHtmlScriptTags(string input) => HtmlScriptGeneratedRegex().Replace(input, string.Empty);
+  public static partial Regex HtmlScriptGeneratedRegex();
 
   [GeneratedRegex("<img[^>]*>[\\s\\S]*?>", RegexOptions.Compiled)]
-  private static partial Regex HtmlImgGeneratedRegex();
-  public static string RemoveHtmlImgTags(string input) => HtmlImgGeneratedRegex().Replace(input, string.Empty);
+  public static partial Regex HtmlImgGeneratedRegex();
 
   [GeneratedRegex("<img.+?src=[\"'](.+?)[\"'].+?>", RegexOptions.Compiled)]
   public static partial Regex HtmlImgSrcGeneratedRegex();
@@ -24,9 +22,12 @@ public static partial class StringHelper
   [GeneratedRegex("!\\[[^\\]]*\\]\\((blob:[^)]+)\\)", RegexOptions.Compiled)]
   public static partial Regex MarkdownImgBlobGeneratedRegex();
 
-  [GeneratedRegex(@"!\[(?<filename>[^\]]+)\]\(data:image\/(?<type>.+);base64,(?<data>.+?)\)", RegexOptions.Compiled)]
+  [GeneratedRegex(@"!\[(?<filename>[^\]]+)\]\(data:image\/(?<type>.+);base64,(?<data>.*)\)", RegexOptions.Compiled)]
   public static partial Regex MarkdownDataImageBase64BlobGeneratedRegex();
 
   [GeneratedRegex(@"blob:(https?://[^/]+/\S+)", RegexOptions.Compiled)]
   public static partial Regex BlobUrlGeneratedRegex();
+
+  [GeneratedRegex(@"data:image\/(?<type>.+);base64,(?<data>.*)", RegexOptions.Compiled)]
+  public static partial Regex DataImageBase64GeneratedRegex();
 }
diff --git a/src/Blogifier/Interfaces/PostController.cs b/src/Blogifier/Interfaces/PostController.cs
index 9478b6da5..b70587f3b 100644
--- a/src/Blogifier/Interfaces/PostController.cs
+++ b/src/Blogifier/Interfaces/PostController.cs
@@ -46,6 +46,11 @@ public async Task<string> AddPostAsync([FromServices] StorageManager storageMana
   {
     var userId = User.FirstUserId();
     var uploadAt = DateTime.UtcNow;
+    if (!string.IsNullOrEmpty(post.Cover))
+    {
+      var coverUrl = await storageManager.UploadImagesBase64(uploadAt, userId, post.Cover);
+      post.Cover = coverUrl;
+    }
     var uploadContent = await storageManager.UploadImagesBase64FoHtml(uploadAt, userId, post.Content);
     post.Content = uploadContent;
     return await _postProvider.AddAsync(post, userId);
@@ -57,6 +62,11 @@ public async Task UpdateAsync([FromServices] StorageManager storageManager, [Fro
   {
     var userId = User.FirstUserId();
     var uploadAt = DateTime.UtcNow;
+    if (!string.IsNullOrEmpty(post.Cover))
+    {
+      var coverUrl = await storageManager.UploadImagesBase64(uploadAt, userId, post.Cover);
+      post.Cover = coverUrl;
+    }
     var uploadContent = await storageManager.UploadImagesBase64FoHtml(uploadAt, userId, post.Content);
     post.Content = uploadContent;
     await _postProvider.UpdateAsync(post, userId);
diff --git a/src/Blogifier/Posts/PostProvider.cs b/src/Blogifier/Posts/PostProvider.cs
index 586a2583a..47dc587c9 100644
--- a/src/Blogifier/Posts/PostProvider.cs
+++ b/src/Blogifier/Posts/PostProvider.cs
@@ -256,8 +256,12 @@ private async Task<Post> AddInternalAsync(PostEditorDto postInput, int userId)
   {
     var slug = await GetSlugFromTitle(postInput.Title);
     var postCategories = await CheckPostCategories(postInput.Categories);
-    var contentFiltr = StringHelper.RemoveHtmlImgTags(StringHelper.RemoveHtmlScriptTags(postInput.Content));
-    var descriptionFiltr = StringHelper.RemoveHtmlImgTags(StringHelper.RemoveHtmlScriptTags(postInput.Description));
+
+    var contentScriptFiltr = StringHelper.HtmlScriptGeneratedRegex().Replace(postInput.Content, string.Empty);
+    var descriptionScriptFiltr = StringHelper.HtmlScriptGeneratedRegex().Replace(postInput.Description, string.Empty);
+    var contentFiltr = StringHelper.HtmlImgGeneratedRegex().Replace(contentScriptFiltr, string.Empty);
+    var descriptionFiltr = StringHelper.HtmlImgGeneratedRegex().Replace(descriptionScriptFiltr, string.Empty);
+
     var publishedAt = GetPublishedAt(postInput.PublishedAt, postInput.State);
     var post = new Post
     {
@@ -316,8 +320,12 @@ public async Task UpdateAsync(PostEditorDto postInput, int userId)
 
     post.Slug = postInput.Slug!;
     post.Title = postInput.Title;
-    var contentFiltr = StringHelper.RemoveHtmlImgTags(StringHelper.RemoveHtmlScriptTags(postInput.Content));
-    var descriptionFiltr = StringHelper.RemoveHtmlImgTags(StringHelper.RemoveHtmlScriptTags(postInput.Description));
+
+    var contentScriptFiltr = StringHelper.HtmlScriptGeneratedRegex().Replace(postInput.Content, string.Empty);
+    var descriptionScriptFiltr = StringHelper.HtmlScriptGeneratedRegex().Replace(postInput.Description, string.Empty);
+    var contentFiltr = StringHelper.HtmlImgGeneratedRegex().Replace(contentScriptFiltr, string.Empty);
+    var descriptionFiltr = StringHelper.HtmlImgGeneratedRegex().Replace(descriptionScriptFiltr, string.Empty);
+
     post.Description = descriptionFiltr;
     post.Content = contentFiltr;
     post.Cover = postInput.Cover;
diff --git a/src/Blogifier/Storages/StorageManager.cs b/src/Blogifier/Storages/StorageManager.cs
index 8c87a85d1..4768ca6bd 100644
--- a/src/Blogifier/Storages/StorageManager.cs
+++ b/src/Blogifier/Storages/StorageManager.cs
@@ -171,6 +171,24 @@ public async Task<string> UploadImagesBase64FoHtml(DateTime uploadAt, int userid
     return content;
   }
 
+  public async Task<string> UploadImagesBase64(DateTime uploadAt, int userid, string dataOrUrl)
+  {
+    var match = StringHelper.DataImageBase64GeneratedRegex().Match(dataOrUrl);
+    if (match.Success)
+    {
+      var imageType = match.Groups["type"].Value;
+      var base64Data = match.Groups["data"].Value;
+      var imageDataBytes = Convert.FromBase64String(base64Data);
+      var fileName = Guid.NewGuid().ToString() + "." + imageType;
+      var path = $"{userid}/{uploadAt.Year}{uploadAt.Month}/{fileName}";
+      if (!_contentTypeProvider.TryGetContentType(fileName, out var contentType))
+        contentType = "text/html";
+      var storage = await _storageProvider.AddAsync(uploadAt, userid, path, fileName, imageDataBytes, contentType);
+      return storage.Slug;
+    }
+    return dataOrUrl;
+  }
+
   private bool InvalidFileName(string fileName)
   {
     var fileExtensions = _fileExtensions ?? BlogifierConstant.FileExtensions;

From 2540942d68f685cc30aa57dc036b596ce4526776 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 14:25:01 +0800
Subject: [PATCH 25/31] preview.7

---
 Dockerfile | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Dockerfile b/Dockerfile
index b8ca9d710..48bcce740 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/dotnet/sdk:8.0-preview-alpine as sdk
+FROM mcr.microsoft.com/dotnet/sdk:8.0.100-preview.7-alpine3.18 as sdk
 # TOTO zh-CH
 # RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 RUN apk add --no-cache npm
@@ -10,7 +10,7 @@ COPY ./ /opt/blogifier
 WORKDIR /opt/blogifier
 RUN ["dotnet","publish", "-c", "Release","/p:RuntimeIdentifier=linux-musl-x64", "./src/Blogifier/Blogifier.csproj","-o","dist" ]
 
-FROM mcr.microsoft.com/dotnet/aspnet:8.0.0-preview.6-alpine3.18 as run
+FROM mcr.microsoft.com/dotnet/aspnet:8.0.0-preview.7-alpine3.18 as run
 # TOTO zh-CH
 # RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 RUN apk add --no-cache icu-libs

From 27b4672c94b450853535bc63793e01001ba68e27 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 14:31:18 +0800
Subject: [PATCH 26/31] dotnet sdk 7.0.400

---
 global.json                                  |  2 +-
 src/Blogifier.Admin/Blogifier.Admin.csproj   |  8 ++++----
 src/Blogifier.Shared/Blogifier.Shared.csproj |  2 +-
 src/Blogifier/Blogifier.csproj               | 18 +++++++++---------
 tests/Blogifier.Tests/Blogifier.Tests.csproj |  4 ++--
 5 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/global.json b/global.json
index e383752a1..06ce1b485 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
 {
   "sdk": {
-    "version": "7.0.304"
+    "version": "7.0.400"
   }
 }
diff --git a/src/Blogifier.Admin/Blogifier.Admin.csproj b/src/Blogifier.Admin/Blogifier.Admin.csproj
index 1b365811c..549470a1e 100644
--- a/src/Blogifier.Admin/Blogifier.Admin.csproj
+++ b/src/Blogifier.Admin/Blogifier.Admin.csproj
@@ -11,10 +11,10 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.9" PrivateAssets="all" />
-    <PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.9" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.10" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.10" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.10" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.10" />
     <PackageReference Include="Sotsera.Blazor.Toaster" Version="3.0.0" />
   </ItemGroup>
 
diff --git a/src/Blogifier.Shared/Blogifier.Shared.csproj b/src/Blogifier.Shared/Blogifier.Shared.csproj
index bc42f863d..1a73688f7 100644
--- a/src/Blogifier.Shared/Blogifier.Shared.csproj
+++ b/src/Blogifier.Shared/Blogifier.Shared.csproj
@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.9" />
+    <PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.10" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/Blogifier/Blogifier.csproj b/src/Blogifier/Blogifier.csproj
index 7db59c99a..5e528df67 100644
--- a/src/Blogifier/Blogifier.csproj
+++ b/src/Blogifier/Blogifier.csproj
@@ -14,23 +14,23 @@
   <ItemGroup>
     <PackageReference Include="AutoMapper" Version="12.0.1" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.9" />
-    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.9" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.9">
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.10" />
+    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="7.0.10" />
+    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.10" />
+    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.10" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.9" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.9" />
-    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.9" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" />
+    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.10" />
     <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4" />
     <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
     <PackageReference Include="Minio" Version="5.0.0" />
     <PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
     <PackageReference Include="ReverseMarkdown" Version="3.25.0" />
-    <PackageReference Include="Markdig" Version="0.31.0" />
+    <PackageReference Include="Markdig" Version="0.32.0" />
     <PackageReference Include="NETCore.MailKit" Version="2.1.0" />
     <PackageReference Include="System.ServiceModel.Syndication" Version="7.0.0" />
     <PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
diff --git a/tests/Blogifier.Tests/Blogifier.Tests.csproj b/tests/Blogifier.Tests/Blogifier.Tests.csproj
index 1a87258bf..431efe15b 100644
--- a/tests/Blogifier.Tests/Blogifier.Tests.csproj
+++ b/tests/Blogifier.Tests/Blogifier.Tests.csproj
@@ -5,8 +5,8 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
-        <PackageReference Include="Moq" Version="4.18.4" />
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
+        <PackageReference Include="Moq" Version="4.20.1" />
         <PackageReference Include="xunit" Version="2.5.0" />
         <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

From 6007e677590879211cba663cc303908d1c91e543 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 14:44:23 +0800
Subject: [PATCH 27/31] sdk preview.7

---
 global.json                                  |  5 +++++
 src/Blogifier.Admin/Blogifier.Admin.csproj   |  6 +++---
 src/Blogifier.Shared/Blogifier.Shared.csproj |  2 +-
 src/Blogifier/Blogifier.csproj               | 18 +++++++++---------
 tests/Blogifier.Tests/Blogifier.Tests.csproj |  8 ++++----
 5 files changed, 22 insertions(+), 17 deletions(-)
 create mode 100644 global.json

diff --git a/global.json b/global.json
new file mode 100644
index 000000000..4ba67068e
--- /dev/null
+++ b/global.json
@@ -0,0 +1,5 @@
+{
+  "sdk": {
+    "version": "8.0.100-preview.7.23376.3"
+  }
+}
diff --git a/src/Blogifier.Admin/Blogifier.Admin.csproj b/src/Blogifier.Admin/Blogifier.Admin.csproj
index 625921847..e2496d344 100644
--- a/src/Blogifier.Admin/Blogifier.Admin.csproj
+++ b/src/Blogifier.Admin/Blogifier.Admin.csproj
@@ -14,9 +14,9 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0-preview.6.23329.11" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0-preview.6.23329.11" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0-preview.6.23329.11" PrivateAssets="all" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="8.0.0-preview.7.23375.9" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.0-preview.7.23375.9" />
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.0-preview.7.23375.9" PrivateAssets="all" />
     <PackageReference Include="Sotsera.Blazor.Toaster" Version="3.0.0" />
   </ItemGroup>
 
diff --git a/src/Blogifier.Shared/Blogifier.Shared.csproj b/src/Blogifier.Shared/Blogifier.Shared.csproj
index 91540bdeb..0ffd8284e 100644
--- a/src/Blogifier.Shared/Blogifier.Shared.csproj
+++ b/src/Blogifier.Shared/Blogifier.Shared.csproj
@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.0-preview.7.23375.9" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/Blogifier/Blogifier.csproj b/src/Blogifier/Blogifier.csproj
index b4164cd61..0f8460e96 100644
--- a/src/Blogifier/Blogifier.csproj
+++ b/src/Blogifier/Blogifier.csproj
@@ -14,17 +14,17 @@
   <ItemGroup>
     <PackageReference Include="AutoMapper" Version="12.0.1" />
     <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
-    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.0-preview.6.23329.11" />
-    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.0-preview.6.23329.11" />
-    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.0-preview.6.23329.11" />
-    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0-preview.6.23329.11" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0-preview.6.23329.4">
+    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="8.0.0-preview.7.23375.9" />
+    <PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="8.0.0-preview.7.23375.9" />
+    <PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.0-preview.7.23375.9" />
+    <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.0-preview.7.23375.9" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0-preview.7.23375.4">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.6.23329.4" />
-    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-preview.6.23329.4" />
-    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.0-preview.6.23329.11" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.0-preview.7.23375.4" />
+    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.0-preview.7.23375.4" />
+    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.0-preview.7.23375.9" />
     <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0-preview.4" />
     <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
     <PackageReference Include="Minio" Version="5.0.0" />
@@ -32,7 +32,7 @@
     <PackageReference Include="ReverseMarkdown" Version="3.25.0" />
     <PackageReference Include="Markdig" Version="0.32.0" />
     <PackageReference Include="NETCore.MailKit" Version="2.1.0" />
-    <PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0-preview.6.23329.7" />
+    <PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0-preview.7.23375.6" />
     <PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
   </ItemGroup>
 
diff --git a/tests/Blogifier.Tests/Blogifier.Tests.csproj b/tests/Blogifier.Tests/Blogifier.Tests.csproj
index 310ae5c25..df1d2b36b 100644
--- a/tests/Blogifier.Tests/Blogifier.Tests.csproj
+++ b/tests/Blogifier.Tests/Blogifier.Tests.csproj
@@ -5,10 +5,10 @@
     </PropertyGroup>
 
     <ItemGroup>
-        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0-preview-23364-03" />
-        <PackageReference Include="Moq" Version="4.18.4" />
-        <PackageReference Include="xunit" Version="2.5.1-pre.12" />
-        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.1-pre.4">
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0-preview-23371-04" />
+        <PackageReference Include="Moq" Version="4.20.1" />
+        <PackageReference Include="xunit" Version="2.5.1-pre.20" />
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.1-pre.10">
             <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
             <PrivateAssets>all</PrivateAssets>
         </PackageReference>

From dc7a607093eaf13ada3e650ef810849f8541bd87 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 15:09:56 +0800
Subject: [PATCH 28/31] aspnet-composite

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 48bcce740..dc6a203ff 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -10,7 +10,7 @@ COPY ./ /opt/blogifier
 WORKDIR /opt/blogifier
 RUN ["dotnet","publish", "-c", "Release","/p:RuntimeIdentifier=linux-musl-x64", "./src/Blogifier/Blogifier.csproj","-o","dist" ]
 
-FROM mcr.microsoft.com/dotnet/aspnet:8.0.0-preview.7-alpine3.18 as run
+FROM mcr.microsoft.com/dotnet/aspnet:8.0.0-preview.7-alpine3.18-composite as run
 # TOTO zh-CH
 # RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
 RUN apk add --no-cache icu-libs

From c66a888f6c3df832cfb8cc9f011ce2f65264f323 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 16:31:58 +0800
Subject: [PATCH 29/31] register user

---
 src/Blogifier.Shared/Resources/Resource.zh-CN.resx       | 9 +++++++++
 .../Views/Themes/standard/login.cshtml                   | 1 +
 .../Views/Themes/standard/register.cshtml                | 1 +
 src/Blogifier.Themes.Standard/assets/gulpfile.mjs        | 1 -
 src/Blogifier.Themes.Standard/assets/js/blogifier.js     | 2 +-
 5 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/Blogifier.Shared/Resources/Resource.zh-CN.resx b/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
index bf3c902be..0477d126a 100644
--- a/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
+++ b/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
@@ -513,4 +513,13 @@
   <data name="reset" xml:space="preserve">
     <value>重置</value>
   </data>
+  <data name="edit-profile" xml:space="preserve">
+    <value>编辑配置</value>
+  </data>
+  <data name="logout" xml:space="preserve">
+    <value>退出登陆</value>
+  </data>
+  <data name="register" xml:space="preserve">
+    <value>注册</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml
index 1c2e746db..e276eeb73 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml
@@ -25,4 +25,5 @@
     <span asp-validation-for="Password"></span>
   </div>
   <button type="submit" class="btn btn-blogifier btn-block btn-floating">@_localizer["login"]</button>
+  <a style="margin-top:8px" class="btn btn-default btn-block btn-floating" role="button" href="~/account/register">@_localizer["register"]</a>
 </form>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml
index eedc8a484..f10df5fc6 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml
@@ -44,4 +44,5 @@
     <span class="validation-message" asp-validation-for="PasswordConfirm"></span>
   </div>
   <button type="submit" class="btn btn-blogifier btn-block btn-floating">@_localizer["register"]</button>
+  <a style="margin-top:8px" class="btn btn-default btn-block btn-floating" role="button" href="~/account/login">@_localizer["cancel"]</a>
 </form>
diff --git a/src/Blogifier.Themes.Standard/assets/gulpfile.mjs b/src/Blogifier.Themes.Standard/assets/gulpfile.mjs
index 5dcc1d51e..57dce4911 100644
--- a/src/Blogifier.Themes.Standard/assets/gulpfile.mjs
+++ b/src/Blogifier.Themes.Standard/assets/gulpfile.mjs
@@ -134,7 +134,6 @@ const watcher = () => {
 };
 
 export default series(
-  clean,
   parallel(
     scss,
     rollupJs,
diff --git a/src/Blogifier.Themes.Standard/assets/js/blogifier.js b/src/Blogifier.Themes.Standard/assets/js/blogifier.js
index 7cbde4ed8..bdc88ffb8 100644
--- a/src/Blogifier.Themes.Standard/assets/js/blogifier.js
+++ b/src/Blogifier.Themes.Standard/assets/js/blogifier.js
@@ -5,10 +5,10 @@ hljs.initHighlightingOnLoad();
 const form = document.getElementById("newsletter");
 const form_email = document.getElementById("newsletter_email");
 const form_status = document.getElementById("newsletter_status");
-const newsletterSucessMsg = form_status.dataset.success, newsletterErrorMsg = form_status.dataset.error;
 
 // Success, Loading and Error functions
 function successNewsletter() {
+  const newsletterSucessMsg = form_status.dataset.success, newsletterErrorMsg = form_status.dataset.error;
   form_status.innerHTML = `<div class="newsletter-msg bg-success"><div class="m-auto"> ${newsletterSucessMsg} </div></div>`;
   setTimeout(resetNewsletter, 2000);
 }

From d0fb3095de113e2a00f6947d18e9d03ddfbe8f44 Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 17:56:27 +0800
Subject: [PATCH 30/31] fix themes assets

---
 src/Blogifier.Shared/Resources/Resource.resx  |   3 +
 .../Resources/Resource.zh-CN.resx             |   3 +
 .../Views/Themes/standard/404.cshtml          |  88 +---------
 .../Themes/standard/components/nav.cshtml     |   2 +-
 .../Views/Themes/standard/initialize.cshtml   |   2 +-
 .../Themes/standard/layouts/_account.cshtml   |   6 +-
 .../Themes/standard/layouts/_base.cshtml      |   5 +-
 .../Themes/standard/layouts/_main.cshtml      |   6 +-
 .../Themes/standard/layouts/_profile.cshtml   |   8 +-
 .../Views/Themes/standard/login.cshtml        |   2 +-
 .../Views/Themes/standard/page.cshtml         |   4 +
 .../Views/Themes/standard/password.cshtml     |   2 +-
 .../Views/Themes/standard/post.cshtml         |   5 +
 .../Views/Themes/standard/profile.cshtml      |  12 +-
 .../Views/Themes/standard/register.cshtml     |   2 +-
 .../assets/gulpfile.mjs                       | 155 +++++++++++++++++-
 .../assets/js/blogifier.js                    |  81 ---------
 .../assets/js/highlight.js                    |   2 +
 .../assets/js/main.js                         |  79 +++++++++
 .../assets/js/profile.js                      |  15 ++
 .../assets/scss/account.scss                  |   8 -
 .../assets/scss/base.scss                     |  84 ++++++++++
 22 files changed, 372 insertions(+), 202 deletions(-)
 create mode 100644 src/Blogifier.Themes.Standard/assets/js/highlight.js
 create mode 100644 src/Blogifier.Themes.Standard/assets/js/main.js
 create mode 100644 src/Blogifier.Themes.Standard/assets/js/profile.js
 create mode 100644 src/Blogifier.Themes.Standard/assets/scss/base.scss

diff --git a/src/Blogifier.Shared/Resources/Resource.resx b/src/Blogifier.Shared/Resources/Resource.resx
index a415c3dfb..536d0b30f 100644
--- a/src/Blogifier.Shared/Resources/Resource.resx
+++ b/src/Blogifier.Shared/Resources/Resource.resx
@@ -786,4 +786,7 @@
   <data name="view-all" xml:space="preserve">
     <value>View All</value>
   </data>
+  <data name="account-profile" xml:space="preserve">
+    <value>Account Profile</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/src/Blogifier.Shared/Resources/Resource.zh-CN.resx b/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
index 0477d126a..465691f7e 100644
--- a/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
+++ b/src/Blogifier.Shared/Resources/Resource.zh-CN.resx
@@ -522,4 +522,7 @@
   <data name="register" xml:space="preserve">
     <value>注册</value>
   </data>
+  <data name="account-profile" xml:space="preserve">
+    <value>用户信息</value>
+  </data>
 </root>
\ No newline at end of file
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/404.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/404.cshtml
index ac79f4d21..d48bff059 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/404.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/404.cshtml
@@ -3,93 +3,7 @@
   Layout = "layouts/_base.cshtml";
 }
 
-@section HeadMeta{
-  <style>
-    *,
-    *::before,
-    *::after {
-      box-sizing: border-box;
-    }
-
-    html {
-      height: 100%;
-    }
-
-    body {
-      margin: 0;
-      font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
-      font-size: 1rem;
-      font-weight: 400;
-      line-height: 1.5;
-      color: #000;
-      background-color: #f8f9fa;
-      display: flex;
-      height: 100%;
-    }
-
-    p {
-      margin: 0;
-    }
-
-    .container {
-      width: 32rem;
-      max-width: 100%;
-      padding: 2rem;
-      margin: auto;
-    }
-
-    .title {
-      margin: 0 0 1rem;
-      font-weight: 500;
-      font-size: 2rem;
-    }
-
-    .message {
-      margin-bottom: 2rem;
-      font-size: 1.25rem;
-      color: #727272;
-    }
-
-    .button {
-      padding: 0 1.5rem 0 .75rem;
-      border-radius: .3rem;
-      display: inline-block;
-      background-color: #009e66;
-      color: #fff;
-      text-decoration: none;
-      font-size: .875rem;
-      font-weight: 500;
-      line-height: 2.5rem;
-    }
-
-      .button:hover {
-        background-color: #009460;
-      }
-
-      .button .icon {
-        vertical-align: middle;
-      }
-
-    @@media screen and (min-width:480px) {
-      .title {
-        font-size: 2.5rem;
-        font-weight: 700;
-      }
-
-      .message {
-        font-size: 1.5rem;
-        font-weight: 300;
-      }
-
-        .message .br {
-          display: block;
-        }
-
-      .button {
-        line-height: 2.75rem;
-      }
-    }
-  </style>
+@section HeaderScript{
   <title>@_localizer["page-not-found"]</title>
   <meta name="description" content="@_localizer["page-not-found-message"]">
 }
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/components/nav.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/components/nav.cshtml
index e3d4f9860..15fedd335 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/components/nav.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/components/nav.cshtml
@@ -87,7 +87,7 @@
         <li><a class="dropdown-item" href="~/admin/newsletter/">@_localizer["newsletter"]</a></li>
         <li><a class="dropdown-item" href="~/admin/settings/">@_localizer["settings"]</a></li>
         <li><a class="dropdown-item" href="~/admin/settings/customize/">@_localizer["customize"]</a></li>
-        <li><a class="dropdown-item" href="~/account/profile?redirecturi=@Model.Main.PathUrl">@_localizer["edit-profile"]</a></li>
+        <li><a class="dropdown-item" href="~/account/profile?redirecturi=@Model.Main.PathUrl">@_localizer["account-profile"]</a></li>
         <li>
           <hr class="dropdown-divider">
         </li>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/initialize.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/initialize.cshtml
index 9b8b68d9a..3d22500b6 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/initialize.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/initialize.cshtml
@@ -4,7 +4,7 @@
   Layout = "layouts/_account.cshtml";
 }
 
-@section HeadMeta{
+@section HeaderScript{
   <title>@_localizer["initialize"]</title>
 }
 
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml
index 201dbb63d..f43490f1f 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml
@@ -6,9 +6,9 @@
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
-  @await RenderSectionAsync("HeadMeta",false)
   <!-- TODO: These variables comes from customfiedls -->
-  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/account.css" asp-append-version="true">
+  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/account.css">
+  @await RenderSectionAsync("HeaderScript", required: false)
 </head>
 
 <body>
@@ -27,7 +27,7 @@
       @RenderBody()
     </div>
   </main>
-  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/blogifier.js" asp-append-version="true"></script>
+  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/blogifier.js"></script>
 </body>
 
 </html>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_base.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_base.cshtml
index b9ca833d4..f6350fc9a 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_base.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_base.cshtml
@@ -4,7 +4,6 @@
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
-  @await RenderSectionAsync("HeadMeta",false)
   <!-- TODO: These variables comes from customfiedls -->
   <style>
     :root {
@@ -24,12 +23,12 @@
       --bf-radius: .5rem;
     }
   </style>
-  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/blogifier.css" asp-append-version="true">
+  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/base.css">
+  @await RenderSectionAsync("HeaderScript", required: false)
 </head>
 
 <body>
   @RenderBody()
-  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/blogifier.js" asp-append-version="true"></script>
 </body>
 
 </html>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_main.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_main.cshtml
index 61581df89..4607be766 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_main.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_main.cshtml
@@ -29,14 +29,16 @@
       --bf-radius: .5rem;
     }
   </style>
-  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/blogifier.css" asp-append-version="true">
+  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/blogifier.css">
   @Html.Raw(Model.Main.HeaderScript)
 </head>
 <body>
   <partial name="../components/header.cshtml" />
   @RenderBody()
   <partial name="../components/footer.cshtml" />
-  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/blogifier.js" asp-append-version="true"></script>
+  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/blogifier.js"></script>
+  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/main.js"></script>
+  @RenderSection("FooterScript", required: false)
   @Html.Raw(Model.Main.FooterScript)
 </body>
 
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_profile.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_profile.cshtml
index 27cbb22da..addc7dc57 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_profile.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_profile.cshtml
@@ -7,9 +7,8 @@
 <head>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
-  @await RenderSectionAsync("HeadMeta",false)
-  <!-- TODO: These variables comes from customfiedls -->
-  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/account.css" asp-append-version="true">
+  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/account.css">
+  @await RenderSectionAsync("HeaderScript",false)
 </head>
 
 <body>
@@ -53,7 +52,8 @@
       </div>
     </div>
   </main>
-  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/blogifier.js" asp-append-version="true"></script>
+  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/blogifier.js"></script>
+  @await RenderSectionAsync("FooterScript",false)
 </body>
 
 </html>
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml
index e276eeb73..33f3c6cff 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/login.cshtml
@@ -4,7 +4,7 @@
 }
 @inject IStringLocalizer<Resource> _localizer
 
-@section HeadMeta{
+@section HeaderScript{
   <title>@_localizer["login"]</title>
 }
 
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml
index 00204ca29..e50ff1a9a 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/page.cshtml
@@ -7,6 +7,10 @@
   Layout = "layouts/_main.cshtml";
 }
 
+@section FooterScript {
+  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/highlight.js"></script>
+}
+
 <main class="post-detail">
   <article class="post">
     <figure class="post-cover">
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/password.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/password.cshtml
index 2703d0e87..8b1333603 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/password.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/password.cshtml
@@ -6,7 +6,7 @@
 
 @inject IStringLocalizer<Resource> _localizer
 
-@section HeadMeta{
+@section HeaderScript{
   <title>@_localizer["edit-profile"]</title>
 }
 
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml
index adcc25ba9..f5b798103 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/post.cshtml
@@ -1,10 +1,15 @@
 @model PostModel
 
 @inject IStringLocalizer<Resource> _localizer
+
 @{
   Layout = "layouts/_main.cshtml"; ;
 }
 
+@section FooterScript {
+  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/highlight.js"></script>
+}
+
 <main class="post-detail">
   <article class="post">
     <figure class="post-cover">
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml
index 222484eaf..dd1b15afb 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/profile.cshtml
@@ -4,11 +4,15 @@
 }
 @inject IStringLocalizer<Resource> _localizer
 
-@section HeadMeta{
-  <title>@_localizer["edit-profile"]</title>
+@section HeaderScript{
+  <title>@_localizer["account-profile"]</title>
 }
 
-<h1 class="section-title">@_localizer["edit-profile"]</h1>
+@section FooterScript {
+  <script src="~/_content/@ThemesStandardConstant.AssemblyName/js/profile.js"></script>
+}
+
+<h1 class="section-title">@_localizer["account-profile"]</h1>
 <div class="section-content -half">
   @if (!string.IsNullOrEmpty(Model.Error))
   {
@@ -19,7 +23,7 @@
     <input type="hidden" asp-for="IsProfile" />
     <input type="hidden" asp-for="Avatar" />
     <div class="form-item">
-      <label class="form-label mb-1">@_localizer["profile-picture"]</label>
+      <label class="form-label mb-1">@_localizer["avatar"]</label>
       <div class="d-flex">
         <img src="@PageHelper.CheckGetAvatarUrl(Model.Avatar)" width="39" height="39" class="profilePicture rounded me-3" alt="@Model.NickName" />
         <button class="btn btn-link" onclick="return fileManager.uploadClick('@UploadType.Avatar');" type="button" title="@_localizer["upload"]" data-bs-toggle="tooltip">
diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml
index f10df5fc6..c452e327e 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/register.cshtml
@@ -4,7 +4,7 @@
   Layout = "layouts/_account.cshtml";
 }
 
-@section HeadMeta{
+@section HeaderScript{
   <title>@_localizer["register"]</title>
 }
 
diff --git a/src/Blogifier.Themes.Standard/assets/gulpfile.mjs b/src/Blogifier.Themes.Standard/assets/gulpfile.mjs
index 57dce4911..ce4bdea1e 100644
--- a/src/Blogifier.Themes.Standard/assets/gulpfile.mjs
+++ b/src/Blogifier.Themes.Standard/assets/gulpfile.mjs
@@ -45,8 +45,8 @@ const release = (done) => {
   done();
 };
 
-// Rollup.js
-const rollupJs = () => {
+// blogifier.js
+const blogifierJs = () => {
   let outputOptions = {
     sourcemap: true,
     format: 'iife'
@@ -89,6 +89,140 @@ const rollupJs = () => {
   return stream.pipe(dest('dist/js'));
 }
 
+// highlight.js
+const highlightJs = () => {
+  let outputOptions = {
+    sourcemap: true,
+    format: 'iife'
+  }
+
+  if (mode !== 'Debug') {
+    outputOptions.sourcemap = false;
+    outputOptions.minifyInternalExports = true;
+    outputOptions.plugins = [terser()];
+  }
+
+  let stream = rollupStream({
+    input: 'js/highlight.js',
+    output: outputOptions,
+    plugins: [
+      babel({
+        exclude: 'node_modules/**',
+        presets: ['@babel/preset-env'],
+        babelHelpers: 'bundled',
+      }),
+      nodeResolve({
+        browser: true,
+        preferBuiltins: false,
+      }),
+      commonjs({
+        include: ['node_modules/**'],
+        exclude: [],
+        sourceMap: mode === 'Debug',
+      }),
+    ],
+  })
+
+  stream = stream.pipe(source('highlight.js'));
+  if (mode !== 'Debug') {
+    // JS Minify
+    stream = stream.pipe(buffer())
+    stream = stream.pipe(plumber())
+    stream = stream.pipe(uglify())
+  }
+  return stream.pipe(dest('dist/js'));
+}
+
+
+// main.js
+const mainJs = () => {
+  let outputOptions = {
+    sourcemap: true,
+    format: 'iife'
+  }
+
+  if (mode !== 'Debug') {
+    outputOptions.sourcemap = false;
+    outputOptions.minifyInternalExports = true;
+    outputOptions.plugins = [terser()];
+  }
+
+  let stream = rollupStream({
+    input: 'js/main.js',
+    output: outputOptions,
+    plugins: [
+      babel({
+        exclude: 'node_modules/**',
+        presets: ['@babel/preset-env'],
+        babelHelpers: 'bundled',
+      }),
+      nodeResolve({
+        browser: true,
+        preferBuiltins: false,
+      }),
+      commonjs({
+        include: ['node_modules/**'],
+        exclude: [],
+        sourceMap: mode === 'Debug',
+      }),
+    ],
+  })
+
+  stream = stream.pipe(source('main.js'));
+  if (mode !== 'Debug') {
+    // JS Minify
+    stream = stream.pipe(buffer())
+    stream = stream.pipe(plumber())
+    stream = stream.pipe(uglify())
+  }
+  return stream.pipe(dest('dist/js'));
+}
+
+
+// profile.js
+const profileJs = () => {
+  let outputOptions = {
+    sourcemap: true,
+    format: 'iife'
+  }
+
+  if (mode !== 'Debug') {
+    outputOptions.sourcemap = false;
+    outputOptions.minifyInternalExports = true;
+    outputOptions.plugins = [terser()];
+  }
+
+  let stream = rollupStream({
+    input: 'js/profile.js',
+    output: outputOptions,
+    plugins: [
+      babel({
+        exclude: 'node_modules/**',
+        presets: ['@babel/preset-env'],
+        babelHelpers: 'bundled',
+      }),
+      nodeResolve({
+        browser: true,
+        preferBuiltins: false,
+      }),
+      commonjs({
+        include: ['node_modules/**'],
+        exclude: [],
+        sourceMap: mode === 'Debug',
+      }),
+    ],
+  })
+
+  stream = stream.pipe(source('profile.js'));
+  if (mode !== 'Debug') {
+    // JS Minify
+    stream = stream.pipe(buffer())
+    stream = stream.pipe(plumber())
+    stream = stream.pipe(uglify())
+  }
+  return stream.pipe(dest('dist/js'));
+}
+
 // sass
 const scss = () => {
   let stream = src("./scss/**/*.scss")
@@ -129,14 +263,22 @@ const svgSprite = () => {
 }
 
 const watcher = () => {
-  watch('./js/**/*.js', series(rollupJs));
+  watch('./js/**/*.js', series(
+    blogifierJs,
+    highlightJs,
+    mainJs,
+    profileJs,
+  ));
   watch('./scss/**/*.scss', series(scss));
 };
 
 export default series(
   parallel(
     scss,
-    rollupJs,
+    blogifierJs,
+    highlightJs,
+    mainJs,
+    profileJs,
     svgSprite,
     watcher
   )
@@ -145,7 +287,10 @@ export default series(
 const build = series(
   clean,
   scss,
-  rollupJs,
+  blogifierJs,
+  highlightJs,
+  mainJs,
+  profileJs,
   svgSprite,
 );
 
diff --git a/src/Blogifier.Themes.Standard/assets/js/blogifier.js b/src/Blogifier.Themes.Standard/assets/js/blogifier.js
index bdc88ffb8..aa9524295 100644
--- a/src/Blogifier.Themes.Standard/assets/js/blogifier.js
+++ b/src/Blogifier.Themes.Standard/assets/js/blogifier.js
@@ -1,87 +1,6 @@
 import "bootstrap";
-import hljs from 'highlight.js';
-hljs.initHighlightingOnLoad();
-// get the newsletter form elements
-const form = document.getElementById("newsletter");
-const form_email = document.getElementById("newsletter_email");
-const form_status = document.getElementById("newsletter_status");
 
-// Success, Loading and Error functions
-function successNewsletter() {
-  const newsletterSucessMsg = form_status.dataset.success, newsletterErrorMsg = form_status.dataset.error;
-  form_status.innerHTML = `<div class="newsletter-msg bg-success"><div class="m-auto"> ${newsletterSucessMsg} </div></div>`;
-  setTimeout(resetNewsletter, 2000);
-}
-function loadingNewsletter() {
-  form_status.innerHTML = '<div class="newsletter-msg"><div class="m-auto spinner-border" role="status"></div></div>'
-}
-function errorNewsletter(msg) {
-  form_status.innerHTML = `<div class="newsletter-msg bg-danger"><div class="m-auto">${newsletterErrorMsg} <br> ${msg}</div></div>`;
-}
-function resetNewsletter() {
-  form.reset();
-  form_status.innerHTML = "";
-}
 
-function subscribeNewsletter(url, data) {
-  var options = {
-    method: "POST",
-    headers: { "Content-Type": "application/json" },
-    body: JSON.stringify(data)
-  }
-  fetch(url, options)
-    .then((response) => {
-      if (response.status == 200) {
-        return response.json();
-      } else {
-        throw new Error('The Newsletter is not working!');
-      }
-    })
-    .then(() => {
-      successNewsletter();
-    })
-    .catch((err) => {
-      errorNewsletter(err);
-    });
-}
-
-form.addEventListener("submit", function (e) {
-  e.preventDefault();
-  loadingNewsletter();
-  var subscriber_data = {
-    Email: form_email.value,
-    Ip: "unknown",
-    Country: "unknown",
-    Region: "unknown"
-  };
-  fetch('https://ipapi.co/json/')
-    .then((response) => {
-      if (response.status == 200) {
-        return response.json();
-      } else {
-        throw new Error('Not sure where you are!');
-      }
-    })
-    .then((loc) => {
-      subscriber_data.Ip = loc.ip;
-      subscriber_data.Country = loc.country_name;
-      subscriber_data.Region = loc.region;
-      subscribeNewsletter(form.action, subscriber_data);
-    })
-    .catch((err) => {
-      subscribeNewsletter(form.action, subscriber_data);
-    });
-});
-
-
-// search modal auto focus
-var myModal = document.getElementById('searchModal');
-var myInput = document.getElementById('searchFormInput');
-if (myModal) {
-  myModal.addEventListener('shown.bs.modal', function () {
-    myInput.focus()
-  })
-}
 
 // copy input
 function copyInput(elm) {
diff --git a/src/Blogifier.Themes.Standard/assets/js/highlight.js b/src/Blogifier.Themes.Standard/assets/js/highlight.js
new file mode 100644
index 000000000..2a5ed565e
--- /dev/null
+++ b/src/Blogifier.Themes.Standard/assets/js/highlight.js
@@ -0,0 +1,2 @@
+import hljs from 'highlight.js';
+hljs.initHighlightingOnLoad();
diff --git a/src/Blogifier.Themes.Standard/assets/js/main.js b/src/Blogifier.Themes.Standard/assets/js/main.js
new file mode 100644
index 000000000..b602c41dc
--- /dev/null
+++ b/src/Blogifier.Themes.Standard/assets/js/main.js
@@ -0,0 +1,79 @@
+// get the newsletter form elements
+const form = document.getElementById("newsletter");
+const form_email = document.getElementById("newsletter_email");
+const form_status = document.getElementById("newsletter_status");
+
+// Success, Loading and Error functions
+function successNewsletter() {
+  const newsletterSucessMsg = form_status.dataset.success, newsletterErrorMsg = form_status.dataset.error;
+  form_status.innerHTML = `<div class="newsletter-msg bg-success"><div class="m-auto"> ${newsletterSucessMsg} </div></div>`;
+  setTimeout(resetNewsletter, 2000);
+}
+function loadingNewsletter() {
+  form_status.innerHTML = '<div class="newsletter-msg"><div class="m-auto spinner-border" role="status"></div></div>'
+}
+function errorNewsletter(msg) {
+  form_status.innerHTML = `<div class="newsletter-msg bg-danger"><div class="m-auto">${newsletterErrorMsg} <br> ${msg}</div></div>`;
+}
+function resetNewsletter() {
+  form.reset();
+  form_status.innerHTML = "";
+}
+
+function subscribeNewsletter(url, data) {
+  var options = {
+    method: "POST",
+    headers: { "Content-Type": "application/json" },
+    body: JSON.stringify(data)
+  }
+  fetch(url, options)
+    .then((response) => {
+      if (response.status == 200) {
+        return response.json();
+      } else {
+        throw new Error('The Newsletter is not working!');
+      }
+    })
+    .then(() => {
+      successNewsletter();
+    })
+    .catch((err) => {
+      errorNewsletter(err);
+    });
+}
+
+form.addEventListener("submit", function (e) {
+  e.preventDefault();
+  loadingNewsletter();
+  var subscriber_data = {
+    Email: form_email.value,
+    Ip: "unknown",
+    Country: "unknown",
+    Region: "unknown"
+  };
+  fetch('https://ipapi.co/json/')
+    .then((response) => {
+      if (response.status == 200) {
+        return response.json();
+      } else {
+        throw new Error('Not sure where you are!');
+      }
+    })
+    .then((loc) => {
+      subscriber_data.Ip = loc.ip;
+      subscriber_data.Country = loc.country_name;
+      subscriber_data.Region = loc.region;
+      subscribeNewsletter(form.action, subscriber_data);
+    })
+    .catch((err) => {
+      subscribeNewsletter(form.action, subscriber_data);
+    });
+});
+
+// search modal auto focus
+var myModal = document.getElementById('searchModal');
+if (myModal) {
+  myModal.addEventListener('shown.bs.modal', function () {
+    document.getElementById('searchFormInput').focus()
+  })
+}
diff --git a/src/Blogifier.Themes.Standard/assets/js/profile.js b/src/Blogifier.Themes.Standard/assets/js/profile.js
new file mode 100644
index 000000000..47eec7b47
--- /dev/null
+++ b/src/Blogifier.Themes.Standard/assets/js/profile.js
@@ -0,0 +1,15 @@
+
+// copy input
+function test(elm) {
+  var copyText = document.getElementById(elm);
+  var copyTextStore = copyText.dataset.link;
+  copyText.select();
+  copyText.setSelectionRange(0, 99999);
+  document.execCommand("copy");
+  copyText.value = "Copied!";
+  copyText.classList.add("copied");
+  setTimeout(function () {
+    copyText.value = copyTextStore;
+    copyText.classList.remove("copied");
+  }, 500);
+}
diff --git a/src/Blogifier.Themes.Standard/assets/scss/account.scss b/src/Blogifier.Themes.Standard/assets/scss/account.scss
index 40a2dc70b..b217a220e 100644
--- a/src/Blogifier.Themes.Standard/assets/scss/account.scss
+++ b/src/Blogifier.Themes.Standard/assets/scss/account.scss
@@ -143,11 +143,3 @@ body {
   display: flex;
   flex-direction: column;
 }
-
-// shared/MainLayout.razor
-.content {
-}
-
-// Components/NavMenuComponent.razor
-.menu {
-}
diff --git a/src/Blogifier.Themes.Standard/assets/scss/base.scss b/src/Blogifier.Themes.Standard/assets/scss/base.scss
new file mode 100644
index 000000000..7de07fe86
--- /dev/null
+++ b/src/Blogifier.Themes.Standard/assets/scss/base.scss
@@ -0,0 +1,84 @@
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+}
+
+html {
+  height: 100%;
+}
+
+body {
+  margin: 0;
+  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+  font-size: 1rem;
+  font-weight: 400;
+  line-height: 1.5;
+  color: #000;
+  background-color: #f8f9fa;
+  display: flex;
+  height: 100%;
+}
+
+p {
+  margin: 0;
+}
+
+.container {
+  width: 32rem;
+  max-width: 100%;
+  padding: 2rem;
+  margin: auto;
+}
+
+.title {
+  margin: 0 0 1rem;
+  font-weight: 500;
+  font-size: 2rem;
+}
+
+.message {
+  margin-bottom: 2rem;
+  font-size: 1.25rem;
+  color: #727272;
+}
+
+.button {
+  padding: 0 1.5rem 0 .75rem;
+  border-radius: .3rem;
+  display: inline-block;
+  background-color: #009e66;
+  color: #fff;
+  text-decoration: none;
+  font-size: .875rem;
+  font-weight: 500;
+  line-height: 2.5rem;
+}
+
+  .button:hover {
+    background-color: #009460;
+  }
+
+  .button .icon {
+    vertical-align: middle;
+  }
+
+@media screen and (min-width:480px) {
+  .title {
+    font-size: 2.5rem;
+    font-weight: 700;
+  }
+
+  .message {
+    font-size: 1.5rem;
+    font-weight: 300;
+  }
+
+    .message .br {
+      display: block;
+    }
+
+  .button {
+    line-height: 2.75rem;
+  }
+}

From e42f0284099cd39bf0caae746d6891f3774a24bc Mon Sep 17 00:00:00 2001
From: dorthl <x344527085@outlook.com>
Date: Wed, 9 Aug 2023 18:31:18 +0800
Subject: [PATCH 31/31] fix scss

---
 .../Themes/standard/layouts/_account.cshtml   |  1 +
 .../assets/scss/account.scss                  |  5 +-
 .../assets/scss/blogifier.scss                | 58 ++++++++++++++++++-
 .../assets/scss/helpers/_bootstrap.scss       | 53 -----------------
 4 files changed, 62 insertions(+), 55 deletions(-)
 delete mode 100644 src/Blogifier.Themes.Standard/assets/scss/helpers/_bootstrap.scss

diff --git a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml
index f43490f1f..32d238add 100644
--- a/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml
+++ b/src/Blogifier.Themes.Standard/Views/Themes/standard/layouts/_account.cshtml
@@ -7,6 +7,7 @@
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <!-- TODO: These variables comes from customfiedls -->
+  <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/blogifier.css">
   <link rel="stylesheet" href="~/_content/@ThemesStandardConstant.AssemblyName/css/account.css">
   @await RenderSectionAsync("HeaderScript", required: false)
 </head>
diff --git a/src/Blogifier.Themes.Standard/assets/scss/account.scss b/src/Blogifier.Themes.Standard/assets/scss/account.scss
index b217a220e..7b1f10a37 100644
--- a/src/Blogifier.Themes.Standard/assets/scss/account.scss
+++ b/src/Blogifier.Themes.Standard/assets/scss/account.scss
@@ -102,7 +102,10 @@ $form-floating-padding-y: .875rem;
 $form-floating-label-opacity: .65;
 $form-floating-label-transform: scale(.8) translateY(-.325rem) translateX(.15rem);
 
-@import "helpers/bootstrap";
+@import "../node_modules/bootstrap/scss/functions";
+@import "../node_modules/bootstrap/scss/variables";
+@import "../node_modules/bootstrap/scss/root";
+
 @import "layout/sidebar";
 @import "include/buttons";
 @import "include/forms";
diff --git a/src/Blogifier.Themes.Standard/assets/scss/blogifier.scss b/src/Blogifier.Themes.Standard/assets/scss/blogifier.scss
index f299d05da..af19e22c1 100644
--- a/src/Blogifier.Themes.Standard/assets/scss/blogifier.scss
+++ b/src/Blogifier.Themes.Standard/assets/scss/blogifier.scss
@@ -6,7 +6,63 @@
 
 // helpers
 @import "helpers/variables";
-@import "helpers/bootstrap";
+
+/*
+ * Bootstrap v5.0.0-beta3 (https://getbootstrap.com/)
+ * Copyright 2011-2021 The Bootstrap Authors
+ * Copyright 2011-2021 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+
+// scss-docs-start import-stack
+// Configuration
+@import "../node_modules/bootstrap/scss/functions";
+@import "../node_modules/bootstrap/scss/variables";
+@import "../node_modules/bootstrap/scss/mixins";
+@import "helpers/colors";
+@import "../node_modules/bootstrap/scss/utilities";
+@import "../node_modules/bootstrap/scss/root";
+
+// Layout & components
+
+@import "../node_modules/bootstrap/scss/reboot";
+@import "../node_modules/bootstrap/scss/type";
+@import "../node_modules/bootstrap/scss/images";
+@import "../node_modules/bootstrap/scss/containers";
+@import "../node_modules/bootstrap/scss/grid";
+@import "../node_modules/bootstrap/scss/tables";
+@import "../node_modules/bootstrap/scss/forms";
+@import "../node_modules/bootstrap/scss/buttons";
+@import "../node_modules/bootstrap/scss/transitions";
+@import "../node_modules/bootstrap/scss/dropdown";
+@import "../node_modules/bootstrap/scss/button-group";
+// @import "../node_modules/bootstrap/scss/nav";
+// @import "../node_modules/bootstrap/scss/navbar";
+// @import "../node_modules/bootstrap/scss/card";
+@import "../node_modules/bootstrap/scss/accordion";
+// @import "../node_modules/bootstrap/scss/breadcrumb";
+@import "../node_modules/bootstrap/scss/pagination";
+@import "../node_modules/bootstrap/scss/badge";
+@import "../node_modules/bootstrap/scss/alert";
+// @import "../node_modules/bootstrap/scss/progress";
+@import "../node_modules/bootstrap/scss/list-group";
+@import "../node_modules/bootstrap/scss/close";
+// @import "../node_modules/bootstrap/scss/toasts";
+@import "../node_modules/bootstrap/scss/modal";
+@import "../node_modules/bootstrap/scss/tooltip";
+@import "../node_modules/bootstrap/scss/popover";
+@import "../node_modules/bootstrap/scss/carousel";
+@import "../node_modules/bootstrap/scss/spinners";
+// @import "../node_modules/bootstrap/scss/offcanvas";
+
+// Helpers
+@import "../node_modules/bootstrap/scss/helpers";
+
+// Utilities
+@import "../node_modules/bootstrap/scss/utilities/api";
+// scss-docs-end import-stack
+
+
 @import "helpers/mixins";
 @import "helpers/reset";
 @import "helpers/base";
diff --git a/src/Blogifier.Themes.Standard/assets/scss/helpers/_bootstrap.scss b/src/Blogifier.Themes.Standard/assets/scss/helpers/_bootstrap.scss
deleted file mode 100644
index 759829d45..000000000
--- a/src/Blogifier.Themes.Standard/assets/scss/helpers/_bootstrap.scss
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Bootstrap v5.0.0-beta3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors
- * Copyright 2011-2021 Twitter, Inc.
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- */
-
-// scss-docs-start import-stack
-// Configuration
-@import "../../node_modules/bootstrap/scss/functions";
-@import "../../node_modules/bootstrap/scss/variables";
-@import "../../node_modules/bootstrap/scss/mixins";
-@import "colors";
-@import "../../node_modules/bootstrap/scss/utilities";
-
-// Layout & components
-@import "../../node_modules/bootstrap/scss/root";
-@import "../../node_modules/bootstrap/scss/reboot";
-@import "../../node_modules/bootstrap/scss/type";
-@import "../../node_modules/bootstrap/scss/images";
-@import "../../node_modules/bootstrap/scss/containers";
-@import "../../node_modules/bootstrap/scss/grid";
-@import "../../node_modules/bootstrap/scss/tables";
-@import "../../node_modules/bootstrap/scss/forms";
-@import "../../node_modules/bootstrap/scss/buttons";
-@import "../../node_modules/bootstrap/scss/transitions";
-@import "../../node_modules/bootstrap/scss/dropdown";
-@import "../../node_modules/bootstrap/scss/button-group";
-// @import "../../node_modules/bootstrap/scss/nav";
-// @import "../../node_modules/bootstrap/scss/navbar";
-// @import "../../node_modules/bootstrap/scss/card";
-@import "../../node_modules/bootstrap/scss/accordion";
-// @import "../../node_modules/bootstrap/scss/breadcrumb";
-@import "../../node_modules/bootstrap/scss/pagination";
-@import "../../node_modules/bootstrap/scss/badge";
-@import "../../node_modules/bootstrap/scss/alert";
-// @import "../../node_modules/bootstrap/scss/progress";
-@import "../../node_modules/bootstrap/scss/list-group";
-@import "../../node_modules/bootstrap/scss/close";
-// @import "../../node_modules/bootstrap/scss/toasts";
-@import "../../node_modules/bootstrap/scss/modal";
-@import "../../node_modules/bootstrap/scss/tooltip";
-@import "../../node_modules/bootstrap/scss/popover";
-@import "../../node_modules/bootstrap/scss/carousel";
-@import "../../node_modules/bootstrap/scss/spinners";
-// @import "../../node_modules/bootstrap/scss/offcanvas";
-
-// Helpers
-@import "../../node_modules/bootstrap/scss/helpers";
-
-// Utilities
-@import "../../node_modules/bootstrap/scss/utilities/api";
-// scss-docs-end import-stack