Initial commit

This commit is contained in:
2026-07-13 09:25:40 +07:00
parent c08ff54676
commit bccfb156d7
1938 changed files with 641646 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.SignalR.Client;
namespace RobotNet10.Components.Clients;
public abstract class HubClient
{
public event Func<HubConnectionState, Task>? ConnectionStateChanged;
public bool IsConnected => Connection.State == HubConnectionState.Connected;
protected HubConnection Connection { get; }
private readonly ManualResetEvent connectedWaitHandler = new(false);
protected HubClient(Uri url)
{
Connection = new HubConnectionBuilder()
.WithUrl(url)
.WithAutomaticReconnect(new HubClientRepeatRetryPolicy(TimeSpan.FromSeconds(3)))
.Build();
Connection.Closed += Connection_Closed;
Connection.Reconnected += Connection_Reconnected;
}
private Task Connection_Closed(Exception? arg)
{
return ConnectionStateChanged?.Invoke(Connection.State) ?? Task.CompletedTask;
}
private Task Connection_Reconnected(string? arg)
{
return ConnectionStateChanged?.Invoke(Connection.State) ?? Task.CompletedTask;
}
public virtual async Task StartAsync()
{
if (Connection.State == HubConnectionState.Disconnected)
{
await Connection.StartAsync();
ConnectionStateChanged?.Invoke(Connection.State);
connectedWaitHandler.Set();
}
}
public void WaitForConnected() => connectedWaitHandler.WaitOne();
public virtual async Task StopAsync()
{
if (Connection.State != HubConnectionState.Disconnected)
{
await Connection.StopAsync();
ConnectionStateChanged?.Invoke(Connection.State);
}
}
public class HubClientRepeatRetryPolicy(TimeSpan repeatSpan) : IRetryPolicy
{
private readonly TimeSpan RepeatTimeSpan = repeatSpan;
public TimeSpan? NextRetryDelay(RetryContext retryContext) => RepeatTimeSpan;
}
}

View File

@@ -0,0 +1,26 @@
<div class="@WidthClass @HeightClass position-relative overflow-hidden">
<div class="w-100 h-100 position-absolute top-0 start-0 @OverflowX @OverflowY @Class">
@ChildContent
</div>
</div>
@code{
[Parameter]
public RenderFragment? ChildContent { get; set; }
[Parameter]
public string WidthClass { get; set; } = "w-100";
[Parameter]
public string HeightClass { get; set; } = "h-100";
[Parameter]
public string OverflowX { get; set; } = "overflow-x-hidden";
[Parameter]
public string OverflowY { get; set; } = "overflow-y-hidden";
[Parameter]
public string Class { get; set; } = string.Empty;
}

View File

@@ -0,0 +1,25 @@
@using Microsoft.AspNetCore.Components.Web
<button class="icon-button @DisabledClass" @onclick="HandleClick" title="@Title" disabled="@Disabled">
<span class="mdi @IconClass"></span>
</button>
@code {
[Parameter] public string Icon { get; set; } = string.Empty;
[Parameter] public string? Title { get; set; }
[Parameter] public EventCallback<MouseEventArgs> OnClick { get; set; }
[Parameter] public bool Disabled { get; set; }
private string IconClass => $"mdi-{Icon} mdi-18px"; /* Giảm icon size để phù hợp với button nhỏ hơn */
private string DisabledClass => Disabled ? "disabled" : string.Empty;
private async Task HandleClick(MouseEventArgs e)
{
if (Disabled || !OnClick.HasDelegate)
{
return;
}
await OnClick.InvokeAsync(e);
}
}

View File

@@ -0,0 +1,43 @@
.icon-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 20px; /* Giảm từ 24px xuống 20px */
height: 20px; /* Giảm từ 24px xuống 20px */
padding: 0;
margin: 0 1px; /* Giảm margin từ 2px xuống 1px */
border: none;
background-color: transparent;
color: #858585;
cursor: pointer;
transition: color 0.15s ease, background-color 0.15s ease;
border-radius: 2px;
flex-shrink: 0; /* Không cho phép shrink */
}
.icon-button:hover {
background-color: #2a2d2e;
color: #cccccc;
}
.icon-button:active {
background-color: #3e3e42;
color: #ffffff;
}
.icon-button:focus {
outline: none;
}
.icon-button.disabled,
.icon-button:disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
.icon-button span {
display: inline-block;
line-height: 1;
}

View File

@@ -0,0 +1,54 @@
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@inherits LayoutComponentBase
@inject IJSRuntime JS
@inject OptionLayout Options
<div class="w-100 h-100 d-flex flex-column overflow-hidden">
<div class="d-flex flex-row justify-content-between position-relative" style="height: 50px; background-color: #233871">
<div class="h-100 d-flex align-items-center" onclick="robotnet.components.toggleSidebar()">
<img class="ms-1" src="_content/RobotNet10.Components/images/logoLight.svg" alt="PhenikaaX" style="height: 35px;" />
</div>
<div class="w-100 h-100 position-absolute pe-none">
<div class="h-100 mx-auto pe-none" style="width: fit-content;">
<div class="h-100 d-flex align-items-center">
<span class="text-white" style="font-size: 36px;">
@Options.AppName
</span>
</div>
</div>
</div>
<AuthorizeView>
<Authorized>
<div class="d-flex flex-row align-items-center">
<div>
<span class="mdi mdi-account mdi-36px text-white "></span>
</div>
<MudText Class="text-white name" Typo="Typo.subtitle1">@context.User.Identity?.Name</MudText>
<MudSpacer />
<form action="Account/Logout" method="post">
<AntiforgeryToken />
<input type="hidden" name="returnUrl" value="" />
<MudIconButton Class="text-white" ButtonType="@ButtonType.Submit" Icon="@Icons.Material.Filled.Logout" />
</form>
</div>
</Authorized>
</AuthorizeView>
</div>
<div class="w-100 flex-grow-1 d-flex flex-row">
<NavMenu />
<main>
<div>
@Body
</div>
</main>
</div>
</div>
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>

View File

@@ -0,0 +1,34 @@
main {
flex-grow: 1;
height: 100%;
position: relative;
}
main > div {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
#blazor-error-ui {
color-scheme: light only;
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
box-sizing: border-box;
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@@ -0,0 +1,46 @@
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.Extensions.Options
@using Microsoft.JSInterop
@using RobotNet10.Components
@inject IJSRuntime JS
@inject OptionLayout Options
<div class="sidebar">
<DivContainer HeightClass="flex-grow-1" OverflowY="overflow-y-auto" Class="d-flex flex-column">
<AuthorizeView>
<Authorized>
@foreach (var nav in Options.NavModels)
{
<div class="nav-item px-3">
<NavLink class="nav-link" href="@nav.Path" Match="@nav.Match">
<div class="d-flex align-items-center">
<div class="nav-icon">
<span class="mdi @nav.Icon mdi-36px" aria-hidden="true"></span>
</div>
<span class="nav-label">@nav.Label</span>
</div>
</NavLink>
</div>
}
</Authorized>
</AuthorizeView>
</DivContainer>
<div class="copyright d-flex align-items-center justify-content-end">
<span class="text-white" style="font-size: 10px; text-align: end; margin-right: 5px;">
© Copyright 2025 Phenikaa X <br />v@(Options.Version) | All Rights Reserved
</span>
</div>
</div>
@code {
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JS.InvokeVoidAsync("robotnet.components.loadToggleSidebarState");
}
}
}

View File

@@ -0,0 +1,76 @@
.sidebar {
background-image: linear-gradient(180deg, #e06e2e 0%, #3a0647 70%);
height: 100%;
width: 250px;
display: flex;
flex-direction: column;
transition: width 0.3s;
}
.sidebar.collapsed {
width: 74px;
}
.sidebar.collapsed .copyright {
display: none !important;
}
/*.sidebar .user {
display: flex;
flex-direction: row;
align-items: center;
}
.sidebar.collapsed .user > div {
display: none !important;
}*/
.sidebar.collapsed .nav-label {
display: none;
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
overflow: hidden;
}
.nav-item .nav-label {
font-size: 18px;
text-wrap: nowrap;
}
.nav-item .nav-icon {
height: 48px;
width: 48px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep a {
color: #d7d7d7;
border-radius: 4px;
height: 48px;
display: flex;
align-items: center;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}

View File

@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Components.Routing;
namespace RobotNet10.Components;
public record NavModel(string Icon, string Path, string Label, NavLinkMatch Match);
public class OptionLayout()
{
public string AppName { get; set; } = "RobotNet";
public string Version { get; set; } = "";
public NavModel[] NavModels { get; set; } = [];
}

View File

@@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Components.Web;
namespace RobotNet10.Components;
public static class RenderMode
{
public readonly static InteractiveWebAssemblyRenderMode InteractiveWebAssemblyNoPrerender = new(prerender: false);
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="10.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="10.0.3" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.3" />
<PackageReference Include="MudBlazor" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\RobotNet10.ScriptEngine.Shared\RobotNet10.ScriptEngine.Shared.csproj" />
<ProjectReference Include="..\..\Shared\RobotNet10.Shared\RobotNet10.Shared.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,5 @@
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Microsoft.JSInterop
@using MudBlazor
@using RobotNet10.Components

View File

@@ -0,0 +1,15 @@
{
"version": "3.0",
"defaultProvider": "cdnjs",
"libraries": [
{
"library": "bootstrap@5.3.8",
"destination": "wwwroot/lib/bootstrap/"
},
{
"provider": "jsdelivr",
"library": "@mdi/font@7.4.47",
"destination": "wwwroot/lib/mdi/font/"
}
]
}

View File

@@ -0,0 +1,81 @@
/* cyrillic-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmZiArmlw.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmQiArmlw.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmYiArmlw.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmXiArmlw.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* math */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVnoiArmlw.woff2) format('woff2');
unicode-range: U+0302-0303, U+0305, U+0307-0308, U+0310, U+0312, U+0315, U+031A, U+0326-0327, U+032C, U+032F-0330, U+0332-0333, U+0338, U+033A, U+0346, U+034D, U+0391-03A1, U+03A3-03A9, U+03B1-03C9, U+03D1, U+03D5-03D6, U+03F0-03F1, U+03F4-03F5, U+2016-2017, U+2034-2038, U+203C, U+2040, U+2043, U+2047, U+2050, U+2057, U+205F, U+2070-2071, U+2074-208E, U+2090-209C, U+20D0-20DC, U+20E1, U+20E5-20EF, U+2100-2112, U+2114-2115, U+2117-2121, U+2123-214F, U+2190, U+2192, U+2194-21AE, U+21B0-21E5, U+21F1-21F2, U+21F4-2211, U+2213-2214, U+2216-22FF, U+2308-230B, U+2310, U+2319, U+231C-2321, U+2336-237A, U+237C, U+2395, U+239B-23B7, U+23D0, U+23DC-23E1, U+2474-2475, U+25AF, U+25B3, U+25B7, U+25BD, U+25C1, U+25CA, U+25CC, U+25FB, U+266D-266F, U+27C0-27FF, U+2900-2AFF, U+2B0E-2B11, U+2B30-2B4C, U+2BFE, U+3030, U+FF5B, U+FF5D, U+1D400-1D7FF, U+1EE00-1EEFF;
}
/* symbols */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVn6iArmlw.woff2) format('woff2');
unicode-range: U+0001-000C, U+000E-001F, U+007F-009F, U+20DD-20E0, U+20E2-20E4, U+2150-218F, U+2190, U+2192, U+2194-2199, U+21AF, U+21E6-21F0, U+21F3, U+2218-2219, U+2299, U+22C4-22C6, U+2300-243F, U+2440-244A, U+2460-24FF, U+25A0-27BF, U+2800-28FF, U+2921-2922, U+2981, U+29BF, U+29EB, U+2B00-2BFF, U+4DC0-4DFF, U+FFF9-FFFB, U+10140-1018E, U+10190-1019C, U+101A0, U+101D0-101FD, U+102E0-102FB, U+10E60-10E7E, U+1D2C0-1D2D3, U+1D2E0-1D37F, U+1F000-1F0FF, U+1F100-1F1AD, U+1F1E6-1F1FF, U+1F30D-1F30F, U+1F315, U+1F31C, U+1F31E, U+1F320-1F32C, U+1F336, U+1F378, U+1F37D, U+1F382, U+1F393-1F39F, U+1F3A7-1F3A8, U+1F3AC-1F3AF, U+1F3C2, U+1F3C4-1F3C6, U+1F3CA-1F3CE, U+1F3D4-1F3E0, U+1F3ED, U+1F3F1-1F3F3, U+1F3F5-1F3F7, U+1F408, U+1F415, U+1F41F, U+1F426, U+1F43F, U+1F441-1F442, U+1F444, U+1F446-1F449, U+1F44C-1F44E, U+1F453, U+1F46A, U+1F47D, U+1F4A3, U+1F4B0, U+1F4B3, U+1F4B9, U+1F4BB, U+1F4BF, U+1F4C8-1F4CB, U+1F4D6, U+1F4DA, U+1F4DF, U+1F4E3-1F4E6, U+1F4EA-1F4ED, U+1F4F7, U+1F4F9-1F4FB, U+1F4FD-1F4FE, U+1F503, U+1F507-1F50B, U+1F50D, U+1F512-1F513, U+1F53E-1F54A, U+1F54F-1F5FA, U+1F610, U+1F650-1F67F, U+1F687, U+1F68D, U+1F691, U+1F694, U+1F698, U+1F6AD, U+1F6B2, U+1F6B9-1F6BA, U+1F6BC, U+1F6C6-1F6CF, U+1F6D3-1F6D7, U+1F6E0-1F6EA, U+1F6F0-1F6F3, U+1F6F7-1F6FC, U+1F700-1F7FF, U+1F800-1F80B, U+1F810-1F847, U+1F850-1F859, U+1F860-1F887, U+1F890-1F8AD, U+1F8B0-1F8BB, U+1F8C0-1F8C1, U+1F900-1F90B, U+1F93B, U+1F946, U+1F984, U+1F996, U+1F9E9, U+1FA00-1FA6F, U+1FA70-1FA7C, U+1FA80-1FA89, U+1FA8F-1FAC6, U+1FACE-1FADC, U+1FADF-1FAE9, U+1FAF0-1FAF8, U+1FB00-1FBFF;
}
/* vietnamese */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmbiArmlw.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmaiArmlw.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-stretch: 100%;
src: url(KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAo.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

View File

@@ -0,0 +1,5 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="M31.81,1.79,21.29,14.32l-2.48-3a2.6,2.6,0,0,1,0-3.34l4.45-5.31a2.64,2.64,0,0,1,2-.93Z" fill="#e06e2e"/>
<path d="M.19,1.8,10.71,14.34l2.48-2.95a2.6,2.6,0,0,0,0-3.34L8.75,2.74a2.66,2.66,0,0,0-2-.94Z" fill="#e06e2e"/>
<path d="M32,30.21H25.36a2.61,2.61,0,0,1-2-.92L16.5,21.1a.65.65,0,0,0-1,0L8.63,29.29a2.58,2.58,0,0,1-2,.92H0L12.07,15.83,14,13.57a2.67,2.67,0,0,1,4.08,0l1.89,2.26Z" fill="#233871"/>
</svg>

After

Width:  |  Height:  |  Size: 514 B

View File

@@ -0,0 +1,10 @@
<svg
id="Layer_1"
data-name="Layer 1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
>
<path d="M31.81,1.79,21.29,14.32l-2.48-3a2.6,2.6,0,0,1,0-3.34l4.45-5.31a2.64,2.64,0,0,1,2-.93Z" fill="#e06e2e"/>
<path d="M.19,1.8,10.71,14.34l2.48-2.95a2.6,2.6,0,0,0,0-3.34L8.75,2.74a2.66,2.66,0,0,0-2-.94Z" fill="#e06e2e"/>
<path d="M32,30.21H25.36a2.61,2.61,0,0,1-2-.92L16.5,21.1a.65.65,0,0,0-1,0L8.63,29.29a2.58,2.58,0,0,1-2,.92H0L12.07,15.83,14,13.57a2.67,2.67,0,0,1,4.08,0l1.89,2.26Z" fill="#233871"/>
</svg>

After

Width:  |  Height:  |  Size: 532 B

View File

@@ -0,0 +1,48 @@
<svg
id="Layer_1"
data-name="Layer 1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 370.84 59.64"
>
<path
d="M66.68,0,44.62,26.28,39.43,20.1a5.45,5.45,0,0,1,0-7L48.75,2A5.53,5.53,0,0,1,53,0Z"
fill="#e06e2e"
/>
<path
d="M.4,0,22.46,26.31l5.19-6.18a5.45,5.45,0,0,0,0-7L18.33,2a5.53,5.53,0,0,0-4.22-2Z"
fill="#e06e2e"
/>
<path
d="M67.08,59.59H53.15A5.44,5.44,0,0,1,49,57.65L34.58,40.49a1.36,1.36,0,0,0-2.09,0L18.09,57.65a5.46,5.46,0,0,1-4.17,1.94H0L25.31,29.43l4-4.72a5.57,5.57,0,0,1,8.54,0l4,4.72Z"
fill="#ffffff"
/>
<path
d="M98.34,24.3A12.87,12.87,0,0,1,104,42.84a12.29,12.29,0,0,1-5.61,4.56A21.47,21.47,0,0,1,89.74,49H81.17v7.93a2.72,2.72,0,0,1-2.72,2.73H74.13V22.72H89.74a21.64,21.64,0,0,1,8.6,1.58m-1.93,17a6.52,6.52,0,0,0,2.39-5.43,6.54,6.54,0,0,0-2.39-5.43q-2.39-1.91-7-1.9H81.17V43.18h8.25q4.6,0,7-1.9"
fill="#ffffff"
/>
<path
d="M145.86,25.44V56.91a2.72,2.72,0,0,1-2.72,2.73h-4.33V43.81H119.18V59.64h-4.32a2.73,2.73,0,0,1-2.73-2.73V25.44a2.72,2.72,0,0,1,2.73-2.72h4.32V38h19.63V22.72h4.33a2.72,2.72,0,0,1,2.72,2.72"
fill="#ffffff"
/>
<path
d="M183,56.58v3h-24.8a3.6,3.6,0,0,1-3.6-3.6V26.36a3.6,3.6,0,0,1,3.6-3.6h24.05v3a2.73,2.73,0,0,1-2.73,2.73H161.62v9.57h18.29V43.7H161.62V53.86h18.65A2.72,2.72,0,0,1,183,56.58"
fill="#ffffff"
/>
<path
d="M222.73,22.76V59.59h-4.52a2.71,2.71,0,0,1-2.09-1l-20.06-24V59.59h-7V22.76h4.51a2.72,2.72,0,0,1,2.09,1l20.07,24V22.76Z"
fill="#ffffff"
/>
<rect x="231.85" y="22.76" width="7.03" height="36.83" fill="#ffffff" />
<path
d="M261,44.18l-6,6v9.42h-7V22.76h7V41.65L273,23.57a2.71,2.71,0,0,1,1.93-.81h6.77L265.75,39.23l16.88,20.36h-7a2.72,2.72,0,0,1-2.06-.94Z"
fill="#ffffff"
/>
<path
d="M315.59,51.07H296.65l-3,6.89a2.74,2.74,0,0,1-2.5,1.63h-5.47l16.08-34.74A3.6,3.6,0,0,1,305,22.76h2.32a3.61,3.61,0,0,1,3.27,2.08l16.13,34.75h-5.59A2.72,2.72,0,0,1,318.66,58Zm-2.33-5.37-7.14-16.1L299,45.7Z"
fill="#ffffff"
/>
<path
d="M359.69,51.07H340.76l-3,6.89a2.71,2.71,0,0,1-2.49,1.63h-5.47l16.07-34.74a3.62,3.62,0,0,1,3.27-2.09h2.33a3.59,3.59,0,0,1,3.26,2.08l16.13,34.75h-5.59A2.7,2.7,0,0,1,362.77,58Zm-2.32-5.37-7.14-16.1-7.09,16.1Z"
fill="#ffffff"
/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,36 @@
window.robotnet = {
localStorageHelper: {
getItem: function (key) {
return localStorage.getItem(key);
},
setItem: function (key, value) {
localStorage.setItem(key, value);
},
removeItem: function (key) {
localStorage.removeItem(key);
},
clear: function () {
localStorage.clear();
}
},
components: {
loadToggleSidebarState: function () {
let toggle = localStorage.getItem("navMenuCollapsed");
if (toggle === "collapsed") {
let sidebar = document.querySelector(".sidebar");
sidebar.classList.add("collapsed");
}
},
toggleSidebar: function () {
let sidebar = document.querySelector(".sidebar");
sidebar.classList.toggle("collapsed");
let toggle = localStorage.getItem("navMenuCollapsed");
if (toggle === "collapsed") {
localStorage.setItem("navMenuCollapsed", "");
}
else {
localStorage.setItem("navMenuCollapsed", "collapsed");
}
},
},
};

View File

@@ -0,0 +1,15 @@
@import url("lib/bootstrap/css/bootstrap.min.css");
@import url("lib/mdi/font/css/materialdesignicons.min.css");
@import url("fonts/fonts.googleapis.com.css");
@import url("/_content/MudBlazor/MudBlazor.min.css");
@import url("/_content/BlazorMonaco/lib/monaco-editor/min/vs/editor/editor.main.css");
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
width: 100vw;
height: 100vh;
}
.mud-popover-provider > div {
position: fixed;
}