mirror of
https://github.com/Maassoft/ColorControl.git
synced 2026-10-21 16:12:18 -05:00
fix issues with new UI, add color profiles, add sdr brightness for AMD
This commit is contained in:
1 parent
4d2ca8a552
commit
f42a46e53f
95 files changed
+12129
-10005
No files matched your search
@@ -1,5 +1,5 @@
|
||||
using ColorControl.Shared.Contracts;
|
||||
using ColorControl.Shared.Services;
|
||||
using ColorControl.UI.Services;
|
||||
|
||||
namespace ColorControl.UI;
|
||||
|
||||
@@ -15,6 +15,9 @@ public static class Blazor
|
||||
|
||||
builder.Services.AddSingleton<AppState>(new AppState { SelectedTheme = (config?.UseDarkMode ?? true) ? "dark" : "light" });
|
||||
builder.Services.AddTransient<RpcUiClientService>();
|
||||
builder.Services.AddTransient<JSHelper>();
|
||||
builder.Services.AddHttpContextAccessor();
|
||||
builder.Services.AddSingleton<NotificationService>();
|
||||
builder.WebHost.ConfigureKestrel(o => o.ListenAnyIP(config?.UiPort > 0 ? config.UiPort : 5000));
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
@@ -13,9 +13,13 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Shared\Shared.csproj" />
|
||||
<ProjectReference Include="..\MHC2Gen\MHC2Gen.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Components\Pages\ColorProfile\ColorProfilePage.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
<Content Update="Components\Pages\Generic\FieldDefs.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
@@ -25,6 +29,9 @@
|
||||
<Content Update="Components\Pages\Game\GameSummary.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
<Content Update="Components\Pages\Generic\NotificationsComponent.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
<Content Update="Components\Pages\Generic\TriggerInput.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
@@ -52,6 +59,15 @@
|
||||
<Content Update="Components\Pages\Samsung\SamsungServiceMenu.razor">
|
||||
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
|
||||
</Content>
|
||||
<Content Update="wwwroot\app.css">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\favicon.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="wwwroot\helper.js">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,8 +1,15 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
|
||||
@inject AppState AppState
|
||||
@inject RpcUiClientService ClientService;
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject NavigationManager _navigationManager
|
||||
@inject NotificationService _notificationService
|
||||
@inject ProtectedSessionStorage _sessionStorage
|
||||
|
||||
@implements IDisposable
|
||||
@attribute [StreamRendering]
|
||||
|
||||
@@ -15,28 +22,33 @@
|
||||
<base href="/" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="ColorControl.UI.styles.css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes />
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<NotificationsComponent></NotificationsComponent>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@code {
|
||||
private Config? Config;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var config = await ClientService.CallAsync<Config>("OptionsService", "GetConfig");
|
||||
Config ??= await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
|
||||
|
||||
var newTheme = config.UseDarkMode ? "dark" : "light";
|
||||
var newTheme = Config?.UseDarkMode == true ? "dark" : "light";
|
||||
|
||||
if (newTheme != AppState.SelectedTheme)
|
||||
{
|
||||
AppState.SelectedTheme = config.UseDarkMode ? "dark" : "light";
|
||||
AppState.SelectedTheme = Config?.UseDarkMode == true ? "dark" : "light";
|
||||
_navigationManager.Refresh();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Services
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -28,28 +29,34 @@
|
||||
@foreach (var module in modules.Where(m => m.IsActive))
|
||||
{
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3" href="@module.Link">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> @module.DisplayName
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="@module.Link">
|
||||
<span class="bi bi-@module.GetIconClass()" aria-hidden="true"></span> @module.DisplayName
|
||||
</NavLink>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3" href="options">
|
||||
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Options
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="options">
|
||||
<span class="bi bi-gear-fill" aria-hidden="true"></span> Options
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3" href="logging">
|
||||
<span class="bi bi-list-logging-nav-menu" aria-hidden="true"></span> Logging
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="ColorProfile">
|
||||
<span class="bi bi-palette-fill" aria-hidden="true"></span> Color Profiles
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3" href="info">
|
||||
<span class="bi bi-list-logging-nav-menu" aria-hidden="true"></span> Info
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="logging">
|
||||
<span class="bi bi-list-columns-reverse" aria-hidden="true"></span> Logging
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link px-3 align-items-baseline" href="info">
|
||||
<span class="bi bi-info-circle-fill" aria-hidden="true"></span> Info
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
@inject AppState AppState
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Services
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.IsDisplayPreset == true ? $"Settings for display {Preset?.displayName}" : Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
@@ -316,7 +316,7 @@
|
||||
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
|
||||
selectedRefreshRate = Preset?.DisplayConfig.RefreshRate?.ToString();
|
||||
|
||||
if (Preset?.IsDisplayPreset != true)
|
||||
if (Preset?.IsDisplayPreset == false)
|
||||
{
|
||||
displayPresets = await _rpcClientService.CallAsync<List<AmdPreset>>("AmdService", "GetDisplayPresets");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="settingsModal" tabindex="-1" @onfocus="OnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">AMD settings</h5>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@@ -92,6 +92,10 @@ else
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
@@ -106,11 +110,21 @@ else
|
||||
}
|
||||
</h5>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-1">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz @(preset.applyColorData ? preset.colorDepth : "")</p>
|
||||
<small class="text-muted">@preset.shortcut</small>
|
||||
@@ -124,7 +138,6 @@ else
|
||||
@code {
|
||||
private List<AmdPreset>? Presets;
|
||||
private List<AmdPreset>? DisplayPresets;
|
||||
private List<VirtualResolution>? Resolutions;
|
||||
|
||||
private AmdPreset? CurrentPreset;
|
||||
|
||||
@@ -134,7 +147,6 @@ else
|
||||
{
|
||||
await RefreshPresets();
|
||||
DisplayPresets = await _rpcClientService.CallAsync<List<AmdPreset>>("AmdService", "GetDisplayPresets");
|
||||
Resolutions = await _rpcClientService.CallAsync<List<VirtualResolution>>("AmdService", "GetAvailableResolutionsV2", DisplayPresets?.FirstOrDefault());
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstTime)
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.AMD
|
||||
@using ColorControl.Shared.Contracts.DisplayInfo
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Services
|
||||
@using MHC2Gen
|
||||
@using Microsoft.JSInterop;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject JSHelper jsHelper;
|
||||
|
||||
<div class="modal modal-lg" id="colorProfileModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(ColorProfile?.Name == null ? $"Settings for new color profile" : $"Settings of color profile {ColorProfile.Name}")</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => IsVisible = false"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
@if (ColorProfile?.HasExtraInfo == false)
|
||||
{
|
||||
<div class="alert alert-warning" role="alert">
|
||||
This is not a profile created by ColorControl. Some fields are set to default.
|
||||
</div>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(_rpcClientService.LastErrorMessage))
|
||||
{
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Error: @_rpcClientService.LastErrorMessage
|
||||
</div>
|
||||
}
|
||||
<form class="row g-1 needs-validation @WasValidated" id="profileForm" novalidate>
|
||||
@if (ColorProfile == null || DisplayInfo == null || CurrentColorInfo == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="display">Display</label>
|
||||
<input class="form-control" type="text" id="display" value="@DisplayInfo" disabled />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<input class="form-control" type="text" id="name" @bind="ColorProfile.Name" @bind:after="AfterNameChanged" required />
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="description">Description</label>
|
||||
<input class="form-control" type="text" id="description" @bind="ColorProfile.Description" />
|
||||
</div>
|
||||
<div class="accordion accordion-flush" id="presetAccordion">
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#displaySettings">
|
||||
Color gamut
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="displaySettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-2">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<label class="form-label" for="primariesSource"><small>Show Display Primaries and Luminance values of</small></label>
|
||||
<select class="form-select" id="primariesSource" @bind="DisplayPrimariesSource" @bind:after="DisplayPrimariesSourceChanged">
|
||||
@foreach (var colorInfo in DisplayColors)
|
||||
{
|
||||
<option value="@colorInfo.DisplayPrimariesSource">@(colorInfo.CustomName ?? colorInfo.DisplayPrimariesSource.ToString())</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col align-content-end">
|
||||
<button class="btn btn-primary" type="button" disabled="@(DisplayPrimariesSource == DisplayPrimariesSource.ColorProfile)" @onclick="OverwritePrimariesOnClick">Overwrite color profile values</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-danger" for="redX">Red X-coordinate</label>
|
||||
<input class="form-control" id="redX" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Red.X" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-danger" for="redY">Red Y-coordinate</label>
|
||||
<input class="form-control" id="redY" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Red.Y" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-success" for="greenX">Green X-coordinate</label>
|
||||
<input class="form-control" id="greenX" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Green.X" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-success" for="greenY">Green Y-coordinate</label>
|
||||
<input class="form-control" id="greenY" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Green.Y" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-primary" for="blueX">Blue X-coordinate</label>
|
||||
<input class="form-control" id="blueX" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Blue.X" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label text-primary" for="blueY">Blue Y-coordinate</label>
|
||||
<input class="form-control" id="blueY" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.Blue.Y" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="whiteX">White X-coordinate</label>
|
||||
<input class="form-control" id="whiteX" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.White.X" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="whiteY">White Y-coordinate</label>
|
||||
<input class="form-control" id="whiteY" type="number" min="0" max="1" step="0.000000000000001" @bind="CurrentColorInfo.RgbPrimaries.White.Y" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="targetGamut">Target gamut</label>
|
||||
<select class="form-select" id="targetGamut" @bind="ColorProfile.ColorGamut">
|
||||
@foreach (var gamut in Enum.GetValues<ColorGamut>())
|
||||
{
|
||||
<option value="@gamut">@gamut.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#luminanceSettings">
|
||||
Luminance settings
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="luminanceSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="minLuminance">Minimum luminance (nits)</label>
|
||||
<input class="form-control" id="minLuminance" type="number" min="0" max="10" @bind="CurrentColorInfo.BlackLuminance" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxLuminance">Max full frame luminance (nits)</label>
|
||||
<input class="form-control" id="maxLuminance" type="number" min="0" max="10000" @bind="CurrentColorInfo.WhiteLuminance" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="minCLL">Minimum brightness (MinCLL) (nits)</label>
|
||||
<input class="form-control" id="minCLL" type="number" min="0" max="10" @bind="CurrentColorInfo.MinCLL" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxCLL">Peak brightness (MaxCLL) (nits)</label>
|
||||
<input class="form-control" id="maxCLL" type="number" min="0" max="10000" @bind="CurrentColorInfo.MaxCLL" disabled="@PrimariesDisabled" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
<button class="accordion-button collapsed py-2" type="button" data-bs-toggle="collapse" data-bs-target="#hdrSettings">
|
||||
Advanced EOTF settings
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="hdrSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="sdrTransfer">SDR Transfer Function</label>
|
||||
<select class="form-select" id="sdrTransfer" @bind="ColorProfile.SDRTransferFunction">
|
||||
@foreach (var function in Enum.GetValues<SDRTransferFunction>())
|
||||
{
|
||||
<option value="@function">@function.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="gamma">Gamma</label>
|
||||
<input class="form-control" id="gamma" type="number" min="0.1" max="10" step="0.01" @bind="ColorProfile.Gamma" disabled="@(ColorProfile.SDRTransferFunction != SDRTransferFunction.PurePower)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (ColorProfile.IsHDRProfile)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="minSDR">Minimum SDR brightness (nits)</label>
|
||||
<input class="form-control" id="minSDR" type="number" min="0" max="10" step="0.001" @bind="ColorProfile.SDRMinBrightness" disabled="@(ColorProfile.SDRTransferFunction != SDRTransferFunction.PurePower)" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="maxSDR">Maximum SDR brightness (nits)</label>
|
||||
<input class="form-control" id="maxSDR" type="number" min="0" max="10000" @bind="ColorProfile.SDRMaxBrightness" disabled="@(ColorProfile.SDRTransferFunction != SDRTransferFunction.PurePower)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="brightnessBoost">Brightness boost (%)</label>
|
||||
<input class="form-control" id="brightnessBoost" type="number" min="-100" max="100" @bind="ColorProfile.SDRBrightnessBoost" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="shadowDetailBoost">Shadow detail boost (%)</label>
|
||||
<input class="form-control" id="shadowDetailBoost" type="number" min="0" max="100" @bind="ColorProfile.ShadowDetailBoost" disabled="@(ColorProfile.SDRTransferFunction == SDRTransferFunction.Piecewise)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary" @onclick="InstallOnClick" disabled="@(ColorProfile == null)">Install and set as default</button>
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" @onclick="() => IsVisible = false">Close</button>
|
||||
<button type="submit" class="btn btn-primary" @onclick="ApplyClick" disabled="@(ColorProfile == null)">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
private ColorProfileDto? ColorProfile { get; set; }
|
||||
private DisplayColorInfo? CurrentColorInfo { get; set; }
|
||||
private DisplayColorInfo? ColorProfileColorInfo { get; set; }
|
||||
private List<DisplayColorInfo> DisplayColors { get; set; } = new();
|
||||
private bool PrimariesDisabled = false;
|
||||
|
||||
[Parameter]
|
||||
public string? ColorProfileName { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public bool UseAsTemplate { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public DisplayInfoDto? DisplayInfo { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public Func<ColorProfileDto, Task>? AfterApply { get; set; }
|
||||
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
private DisplayPrimariesSource DisplayPrimariesSource;
|
||||
private bool IsInstalled;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (ColorProfileName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ColorProfileName == "")
|
||||
{
|
||||
ColorProfile = ColorProfileDto.CreateDefault(DisplayInfo?.IsHdrSupportedAndEnabled ?? false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorProfile = await _rpcClientService.CallAsync<ColorProfileDto>("ColorProfileService", "GetColorProfile", ColorProfileName);
|
||||
}
|
||||
|
||||
if (ColorProfile != null)
|
||||
{
|
||||
ColorProfileColorInfo = new DisplayColorInfo
|
||||
{
|
||||
DisplayPrimariesSource = DisplayPrimariesSource.ColorProfile,
|
||||
BlackLuminance = ColorProfile.BlackLuminance,
|
||||
WhiteLuminance = ColorProfile.WhiteLuminance,
|
||||
MinCLL = ColorProfile.MinCLL,
|
||||
MaxCLL = ColorProfile.MaxCLL,
|
||||
RgbPrimaries = new RgbPrimariesDto(ColorProfile.DevicePrimaries)
|
||||
};
|
||||
|
||||
CurrentColorInfo = ColorProfileColorInfo;
|
||||
|
||||
DisplayColors.Clear();
|
||||
DisplayColors.Add(CurrentColorInfo);
|
||||
|
||||
if (DisplayInfo != null)
|
||||
{
|
||||
DisplayColors.AddRange(DisplayInfo.DisplayColors);
|
||||
}
|
||||
|
||||
if (UseAsTemplate)
|
||||
{
|
||||
if (ColorProfile.Name != null)
|
||||
{
|
||||
ColorProfile.Name = Path.GetFileNameWithoutExtension(ColorProfile.Name) + " (clone).icm";
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorProfile.Name = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ApplyClick(MouseEventArgs e)
|
||||
{
|
||||
if (ColorProfile == null || CurrentColorInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await IsValid()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ColorProfile.UpdatePrimariesAndLuminance(CurrentColorInfo);
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("ColorProfileService", "UpdateColorProfile", ColorProfile, DisplayInfo?.DeviceName);
|
||||
|
||||
if (result)
|
||||
{
|
||||
await jsHelper.CloseModal("colorProfileModal");
|
||||
IsVisible = false;
|
||||
if (AfterApply != null)
|
||||
{
|
||||
await AfterApply.Invoke(ColorProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InstallOnClick()
|
||||
{
|
||||
if (ColorProfile == null || DisplayInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await IsValid()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ColorProfile.UpdatePrimariesAndLuminance(CurrentColorInfo);
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("ColorProfileService", "InstallAndActivateColorProfile", ColorProfile, DisplayInfo.DeviceName);
|
||||
|
||||
IsInstalled = true;
|
||||
}
|
||||
|
||||
private void ModalOnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
WasValidated = "";
|
||||
IsVisible = true;
|
||||
DisplayPrimariesSource = DisplayPrimariesSource.ColorProfile;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> IsValid()
|
||||
{
|
||||
var valid = await jsHelper.IsFormValid("profileForm");
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
WasValidated = "was-validated";
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
private void DisplayPrimariesSourceChanged()
|
||||
{
|
||||
if (DisplayInfo == null || ColorProfile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentColorInfo = DisplayColors.First(dc => dc.DisplayPrimariesSource == DisplayPrimariesSource);
|
||||
PrimariesDisabled = CurrentColorInfo.DisplayPrimariesSource != DisplayPrimariesSource.ColorProfile;
|
||||
}
|
||||
|
||||
private void OverwritePrimariesOnClick(MouseEventArgs e)
|
||||
{
|
||||
if (ColorProfileColorInfo == null || CurrentColorInfo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ColorProfileColorInfo.BlackLuminance = CurrentColorInfo.BlackLuminance;
|
||||
ColorProfileColorInfo.WhiteLuminance = CurrentColorInfo.WhiteLuminance;
|
||||
ColorProfileColorInfo.MinCLL = CurrentColorInfo.MinCLL;
|
||||
ColorProfileColorInfo.MaxCLL = CurrentColorInfo.MaxCLL;
|
||||
ColorProfileColorInfo.RgbPrimaries = new RgbPrimariesDto(CurrentColorInfo.RgbPrimaries);
|
||||
}
|
||||
|
||||
private void AfterNameChanged()
|
||||
{
|
||||
if (ColorProfile?.Name == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ColorProfile.Name.EndsWith(".icm", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
ColorProfile.Name += ".icm";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
@page "/ColorProfile"
|
||||
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.DisplayInfo
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@using ColorControl.UI.Services
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject JSHelper jsHelper;
|
||||
|
||||
<PageTitle>Color profiles</PageTitle>
|
||||
|
||||
<ColorProfilePage ColorProfileName="@SelectedColorProfileName" UseAsTemplate="UseAsTemplate" DisplayInfo="SelectedDisplay" AfterApply="AfterColorProfileSaved"></ColorProfilePage>
|
||||
|
||||
<ConfirmModal Title="Delete color profile" Message="Are you sure you want to delete the color profile?" OnConfirm="ColorProfileOnDeleteConfirmed" />
|
||||
<ConfirmModal ModalId="disassociateConfirmModal" Title="Disassociate color profile" Message="Are you sure you want to disassociate the color profile from the selected display?" OnConfirm="ColorProfileOnDisassociateConfirmed" />
|
||||
<ConfirmModal ModalId="associateConfirmModal" Title="Associate color profile" Message="Are you sure you want to associate the color profile with the selected display?" OnConfirm="ColorProfileOnAssociateConfirmed" />
|
||||
|
||||
<h2>Displays</h2>
|
||||
|
||||
@if (Displays == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="row mb-3">
|
||||
@if (!Displays.Any())
|
||||
{
|
||||
<span>No displays found</span>
|
||||
}
|
||||
@foreach (var display in Displays)
|
||||
{
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="device">Selected display</label>
|
||||
<select class="form-select" id="device" @bind="SelectedDisplayId" @bind:after="SelectedDisplayChanged">
|
||||
@foreach (var device in Displays)
|
||||
{
|
||||
<option value="@display.DisplayId">@display.DisplayName @@ @display.AdapterName</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
@* <div class="col-lg-6 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header d-flex align-items-baseline">
|
||||
<span>
|
||||
@display.DisplayName @@ @display.AdapterName
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<small class="text-body">@display.DevicePath</small>
|
||||
<small class="text-muted">EDID? @display.DisplayColors.Any(dc => dc.DisplayPrimariesSource == MHC2Gen.DisplayPrimariesSource.EDID)</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
*@ }
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (SelectedDisplay == null)
|
||||
{
|
||||
<div>Loading...</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="mb-2 d-flex align-items-center">
|
||||
<h2>Display Associated Color profiles (@(SelectedDisplay.IsHdrSupportedAndEnabled ? "HDR" : "SDR"))</h2>
|
||||
<div class="d-flex ms-auto">
|
||||
<button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => AddColorProfileOnClick()">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!SelectedDisplay.ColorProfiles.Any())
|
||||
{
|
||||
<span>No color profiles found that are associated with the display</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="list-group">
|
||||
@foreach (var colorProfile in SelectedDisplay.ColorProfiles)
|
||||
{
|
||||
<a class="list-group-item list-group-item-action" @onclick="() => SetColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<h5 class="mb-1">
|
||||
@colorProfile
|
||||
@if (colorProfile == SelectedDisplay?.DefaultColorProfile)
|
||||
{
|
||||
<span class="ms-2 badge text-bg-warning" title="Active color profile">Active</span>
|
||||
}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#disassociateConfirmModal" @onclick="() => SelectedColorProfileName = colorProfile" @onclick:preventDefault @onclick:stopPropagation>Disassociate</button>
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => DeleteColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => CloneColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => EditColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#disassociateConfirmModal" @onclick="() => SelectedColorProfileName = colorProfile">Disassociate</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => DeleteColorProfileOnClick(colorProfile)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => CloneColorProfileOnClick(colorProfile)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => EditColorProfileOnClick(colorProfile)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="mb-2 d-flex align-items-center">
|
||||
<h2>Other Color profiles</h2>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (ColorProfileNames == null)
|
||||
{
|
||||
<span>No profiles found</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="list-group">
|
||||
@foreach (var colorProfile in ColorProfileNames)
|
||||
{
|
||||
<span class="list-group-item list-group-item-action" @onclick="() => EditColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<h5 class="mb-1">
|
||||
@colorProfile
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-warning btn-sm" data-bs-toggle="modal" data-bs-target="#associateConfirmModal" @onclick="() => SelectedColorProfileName = colorProfile" @onclick:preventDefault @onclick:stopPropagation>Associate</button>
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => DeleteColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => CloneColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => EditColorProfileOnClick(colorProfile)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#associateConfirmModal" @onclick="() => SelectedColorProfileName = colorProfile">Associate</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => DeleteColorProfileOnClick(colorProfile)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => CloneColorProfileOnClick(colorProfile)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#colorProfileModal" @onclick="() => EditColorProfileOnClick(colorProfile)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
private string? SelectedDisplayId;
|
||||
private DisplayInfoDto? SelectedDisplay;
|
||||
private List<DisplayInfoDto>? Displays;
|
||||
private string? SelectedColorProfileName;
|
||||
private bool UseAsTemplate;
|
||||
private List<string>? ColorProfileNames;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await RefreshDisplays();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstTime)
|
||||
{
|
||||
}
|
||||
|
||||
private async Task AddColorProfileOnClick()
|
||||
{
|
||||
SelectedColorProfileName = "";
|
||||
}
|
||||
|
||||
private async Task EditColorProfileOnClick(string colorProfile)
|
||||
{
|
||||
UseAsTemplate = false;
|
||||
SelectedColorProfileName = colorProfile;
|
||||
await jsHelper.OpenModal("colorProfileModal");
|
||||
}
|
||||
|
||||
private async Task SetColorProfileOnClick(string colorProfile)
|
||||
{
|
||||
if (SelectedDisplay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = await _rpcClientService.CallAsync<bool>("ColorProfileService", "SetDisplayDefaultColorProfile", SelectedDisplay.DeviceName, colorProfile);
|
||||
if (result)
|
||||
{
|
||||
SelectedDisplay.DefaultColorProfile = colorProfile;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DeleteColorProfileOnClick(string colorProfile)
|
||||
{
|
||||
SelectedColorProfileName = colorProfile;
|
||||
}
|
||||
|
||||
private async Task CloneColorProfileOnClick(string colorProfile)
|
||||
{
|
||||
UseAsTemplate = true;
|
||||
SelectedColorProfileName = colorProfile;
|
||||
}
|
||||
|
||||
private async Task ColorProfileOnDeleteConfirmed()
|
||||
{
|
||||
if (SelectedColorProfileName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _rpcClientService.CallAsync<bool>("ColorProfileService", "DeleteColorProfile", SelectedColorProfileName);
|
||||
|
||||
SelectedColorProfileName = null;
|
||||
|
||||
await RefreshDisplays();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task ColorProfileOnDisassociateConfirmed()
|
||||
{
|
||||
if (SelectedColorProfileName == null || SelectedDisplay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _rpcClientService.CallAsync<bool>("ColorProfileService", "RemoveDisplayColorProfile", SelectedDisplay.DeviceName, SelectedColorProfileName);
|
||||
|
||||
SelectedColorProfileName = null;
|
||||
|
||||
await RefreshDisplays();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task ColorProfileOnAssociateConfirmed()
|
||||
{
|
||||
if (SelectedColorProfileName == null || SelectedDisplay == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await _rpcClientService.CallAsync<bool>("ColorProfileService", "AddDisplayColorProfile", SelectedDisplay.DeviceName, SelectedColorProfileName, false);
|
||||
|
||||
SelectedColorProfileName = null;
|
||||
|
||||
await RefreshDisplays();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private void SelectedDisplayChanged()
|
||||
{
|
||||
SelectedDisplay = Displays?.FirstOrDefault(d => d.DisplayId == SelectedDisplayId);
|
||||
}
|
||||
|
||||
private async Task AfterColorProfileSaved(ColorProfileDto colorProfile)
|
||||
{
|
||||
await RefreshDisplays();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task RefreshDisplays()
|
||||
{
|
||||
Displays = await _rpcClientService.CallAsync<List<DisplayInfoDto>>("ColorProfileService", "GetDisplayInfos");
|
||||
|
||||
ColorProfileNames = await _rpcClientService.CallAsync<List<string>>("ColorProfileService", "GetAllColorProfileNames");
|
||||
|
||||
if (Displays?.Any() == true)
|
||||
{
|
||||
SelectedDisplay = Displays.FirstOrDefault(d => d.DisplayId == SelectedDisplayId) ?? Displays.First();
|
||||
SelectedDisplayId = SelectedDisplay.DisplayId;
|
||||
|
||||
ColorProfileNames = ColorProfileNames?.Except(SelectedDisplay.ColorProfiles).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Game
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@@ -13,7 +13,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Game
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="settingsModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">NVIDIA settings</h5>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@using ColorControl.Shared.Contracts;
|
||||
@using ColorControl.Shared.Contracts.Game;
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Generics
|
||||
@@ -52,6 +52,10 @@ else
|
||||
{
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
@@ -66,11 +70,21 @@ else
|
||||
}
|
||||
</h5>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<FieldsModal ModalId="confirmModal" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick"></FieldsModal>
|
||||
<FieldsModal ModalId="@ModalId" Fields="Fields" Title="@Title" OnConfirm="ConfirmClick"></FieldsModal>
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
@@ -16,6 +16,9 @@
|
||||
[Parameter]
|
||||
public Func<Task>? OnConfirm { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public string ModalId { get; set; } = "confirmModal";
|
||||
|
||||
public List<FieldDefinition>? Fields { get; set; }
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
@@ -7,7 +7,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="@ModalId" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@Title</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
<div class="row mx-0 mb-2">
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Services
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject JSHelper jsHelper;
|
||||
@inject NotificationService notificationService;
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
@if (Notifications?.Any() == true)
|
||||
{
|
||||
<div aria-live="polite" aria-atomic="true" class="position-relative">
|
||||
<div class="toast-container bottom-0 end-0 p-3">
|
||||
|
||||
@foreach (var notification in Notifications)
|
||||
{
|
||||
<div class="toast" id="toast_@notification.Id" role="alert" aria-live="assertive" aria-atomic="true" @onclick="() => NotificationOnClick(notification)">
|
||||
<div class="toast-header" style="background-color: var(--bs-@notification.Level-bg-subtle)">
|
||||
<span class="w-100 d-flex">
|
||||
<div class="alert alert-@notification.Level mb-0 py-1">
|
||||
<strong class="me-auto">@notification.Category</strong>
|
||||
</div>
|
||||
</span>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
@if (notification.InternalUrl != null)
|
||||
{
|
||||
<ul class="list-group-flush p-0 mb-0">
|
||||
<li class="list-group-item">@notification.Message</li>
|
||||
</ul>
|
||||
}
|
||||
else
|
||||
{
|
||||
@notification.Message
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@code {
|
||||
private List<NotificationDto> Notifications { get; } = [];
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
notificationService.OnNotification += AddNotification;
|
||||
}
|
||||
}
|
||||
|
||||
protected async Task AddNotification(NotificationDto notification)
|
||||
{
|
||||
RemoveOldNotifications();
|
||||
|
||||
var minShownAt = DateTime.UtcNow.AddSeconds(-5);
|
||||
|
||||
// Suppress duplicate noficiations
|
||||
if (Notifications.Any(n => n.Message == notification.Message && n.Level == notification.Level && n.ShownAt >= minShownAt))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Notifications.Add(notification);
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await jsHelper.ShowToast($"toast_{notification.Id}");
|
||||
notification.ShownAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
private async Task NotificationOnClick(NotificationDto notification)
|
||||
{
|
||||
if (notification.InternalUrl != null)
|
||||
{
|
||||
_navigationManager.NavigateTo(notification.InternalUrl);
|
||||
}
|
||||
|
||||
Notifications.Remove(notification);
|
||||
}
|
||||
|
||||
private void RemoveOldNotifications()
|
||||
{
|
||||
var minDateTime = DateTime.UtcNow.AddMinutes(-1);
|
||||
|
||||
Notifications.RemoveAll(n => n.ShownAt.HasValue && n.ShownAt <= minDateTime);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@typeparam TPreset where TPreset : PresetBase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.EventDispatcher
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
@page "/"
|
||||
|
||||
@using ColorControl.Shared.Common
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Generics
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject NotificationService _notificationService
|
||||
@inject ProtectedSessionStorage _sessionStorage
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
||||
@@ -24,10 +29,10 @@ else
|
||||
<div class="row">
|
||||
@foreach (var module in Modules.Where(m => m.IsActive))
|
||||
{
|
||||
<div class="col">
|
||||
<div class="card h-100 mb-3">
|
||||
<div class="col-lg-4 col-xl mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
@module.DisplayName
|
||||
<span class="me-2 bi bi-@module.GetIconClass()" aria-hidden="true"></span>@module.DisplayName
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<a href="@module.Link" class="btn btn-primary stretched-link mb-2">Open module</a>
|
||||
@@ -47,6 +52,8 @@ else
|
||||
@code {
|
||||
private Config? Config;
|
||||
private List<ModuleDto>? Modules;
|
||||
private InfoDto? Info;
|
||||
private bool IsUpdateAvailable;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -57,4 +64,28 @@ else
|
||||
Modules.ForEach(m => m.Link = m.DisplayName.Split(' ')[0]);
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender || Info != null || Config?.CheckForUpdates != true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var checkedForUpdates = await _sessionStorage.TryGet<bool>("checkedForUpdates");
|
||||
|
||||
if (!checkedForUpdates)
|
||||
{
|
||||
Info = await _rpcClientService.CallAsync<InfoDto>("OptionsService", "GetInfo");
|
||||
|
||||
await _sessionStorage.SetAsync("checkedForUpdates", true);
|
||||
|
||||
if (!IsUpdateAvailable && Info?.UpdateInfoDto?.UpdateAvailable == true)
|
||||
{
|
||||
IsUpdateAvailable = true;
|
||||
var message = $"Update available: version {Info.UpdateInfoDto.NewVersionNumber}";
|
||||
_notificationService.SendNotification(new NotificationDto(message) { InternalUrl = "/info", ScheduledAt = DateTime.UtcNow.AddSeconds(5) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
@page "/info"
|
||||
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject IHttpContextAccessor _httpContextAccessor
|
||||
|
||||
<PageTitle>Logging</PageTitle>
|
||||
|
||||
@@ -20,7 +21,41 @@
|
||||
else
|
||||
{
|
||||
<h3 class="mb-2">@info.ApplicationTitle - @info.LegalCopyright</h3>
|
||||
|
||||
<div class="d-flex align-items-baseline">
|
||||
@if (IsUpdateInstalled)
|
||||
{
|
||||
if (IsEmbeddedBrowser)
|
||||
{
|
||||
<button type="button" class="btn btn-primary me-2" @onclick="RestartOnClick">Restart</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-primary me-2" @onclick="ReloadOnClick">Reload</button>
|
||||
}
|
||||
<span>New version @info.UpdateInfoDto.NewVersionNumber is installed. Click the button to restart the application.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<button type="button" class="btn btn-primary me-2" disabled="@(!info.UpdateInfoDto.UpdateAvailable || IsInstallingUpdate)" @onclick="InstallUpdateOnClick">
|
||||
@if (IsInstallingUpdate)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span>
|
||||
}
|
||||
<span role="status">Install update</span>
|
||||
</button>
|
||||
@if (info.UpdateInfoDto.UpdateAvailable)
|
||||
{
|
||||
<span>New version @info.UpdateInfoDto.NewVersionNumber available. Click the button to install the update.</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>You have the latest version</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
<h5>This app contains the following 3rd party plugins:</h5>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">lgtv.net by gr4b4z</li>
|
||||
@@ -41,10 +76,46 @@ else
|
||||
|
||||
@code {
|
||||
private InfoDto? info;
|
||||
private bool IsInstallingUpdate;
|
||||
private bool IsUpdateInstalled;
|
||||
private bool IsEmbeddedBrowser;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
info = await _rpcClientService.CallAsync<InfoDto>("OptionsService", "GetInfo");
|
||||
|
||||
}
|
||||
|
||||
private async Task InstallUpdateOnClick(MouseEventArgs e)
|
||||
{
|
||||
IsInstallingUpdate = true;
|
||||
try
|
||||
{
|
||||
var result = await _rpcClientService.CallAsync<bool>("OptionsService", "InstallUpdate");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsUpdateInstalled = true;
|
||||
IsEmbeddedBrowser = _httpContextAccessor.HttpContext?.Request.Headers["user-agent"].ToString().Contains("ColorControlEmbedded") == true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsInstallingUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RestartOnClick(MouseEventArgs e)
|
||||
{
|
||||
var result = await _rpcClientService.CallAsync<bool>("OptionsService", "RestartAfterUpdate");
|
||||
}
|
||||
|
||||
private async Task ReloadOnClick(MouseEventArgs e)
|
||||
{
|
||||
var result = await _rpcClientService.CallAsync<bool>("OptionsService", "RestartAfterUpdate");
|
||||
|
||||
_navigationManager.Refresh();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="deviceModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Device?.Name == null ? $"Settings for new device" : $"Settings of device {Device.Name}")</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@@ -13,7 +13,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.LG
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@@ -155,6 +155,10 @@ else
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
@@ -168,11 +172,21 @@ else
|
||||
}
|
||||
<div><small class="text-muted">@preset.shortcut</small></div>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
@@ -215,7 +229,7 @@ else
|
||||
Devices = await _rpcClientService.CallAsync<List<LgDeviceDto>>("LgService", "GetDevices");
|
||||
InvokableActions = await _rpcClientService.CallAsync<List<InvokableActionDto<LgPreset>>>("LgService", "GetInvokableActions");
|
||||
|
||||
ActionGroups = InvokableActions.GroupBy(a => a.Category).Select(g => new ActionGroup(g.Key, g.ToList())).ToList();
|
||||
ActionGroups = InvokableActions?.GroupBy(a => a.Category).Select(g => new ActionGroup(g.Key, g.ToList())).ToList() ?? new List<ActionGroup>();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstTime)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@page "/logging"
|
||||
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
|
||||
@rendermode InteractiveServer
|
||||
|
||||
@@ -154,10 +154,13 @@ else
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
config = await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
|
||||
config ??= await _rpcClientService.CallAsync<Config>("OptionsService", "GetConfig");
|
||||
logLevel = config.LogLevel;
|
||||
|
||||
await GetLog();
|
||||
if (allLines == null)
|
||||
{
|
||||
await GetLog();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetLog()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal modal-lg" id="driverSettingsModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" @onfocus="OnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset != null ? Preset.IsDisplayPreset ? $"Driver settings for display {Preset.displayName}" : $"Driver settings for {Preset.IdOrName}" : "Driver settings")</h5>
|
||||
@@ -38,6 +38,12 @@
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" @bind="OnlyFoundPrograms" id="onlyFoundPrograms" @bind:after="OnlyFoundProgramsOnChange">
|
||||
<label class="form-check-label" for="onlyFoundPrograms">
|
||||
Only show found programs
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="accordion accordion-flush" id="driverSettingsAccordion">
|
||||
@@ -137,6 +143,8 @@
|
||||
|
||||
private bool IsVisible { get; set; }
|
||||
|
||||
private bool OnlyFoundPrograms = true;
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
if (_preset != Preset)
|
||||
@@ -232,12 +240,17 @@
|
||||
await LoadDriverSettings();
|
||||
}
|
||||
|
||||
private async Task OnlyFoundProgramsOnChange()
|
||||
{
|
||||
ProfileNames = await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
}
|
||||
|
||||
private async Task OnFocus(FocusEventArgs e)
|
||||
{
|
||||
if (!IsVisible)
|
||||
{
|
||||
IsVisible = true;
|
||||
ProfileNames ??= await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames");
|
||||
ProfileNames ??= await _rpcClientService.CallAsync<List<string>>("NvService", "GetDriverProfileNames", OnlyFoundPrograms);
|
||||
await LoadDriverSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common;
|
||||
@@ -12,7 +12,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.IsDisplayPreset == true ? $"Settings for display {Preset?.displayName}" : Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
@@ -448,6 +448,7 @@
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="extendedColorimetry">Extended colorimetry</label>
|
||||
<select class="form-select" id="extendedColorimetry" @bind="Preset.HdmiInfoFrameSettings.ExtendedColorimetry" @oninput="HdmiSettingsOnInput">
|
||||
<option value="">Not selected</option>
|
||||
@foreach (var extendedColorimetry in Enum.GetValues<InfoFrameVideoExtendedColorimetry>())
|
||||
{
|
||||
<option value="@extendedColorimetry.ToString()">@extendedColorimetry.GetDescription()</option>
|
||||
@@ -458,6 +459,48 @@
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (ColorProfileNames?.Any() == true)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<span class="d-flex align-items-baseline">
|
||||
@if (!Preset.IsDisplayPreset)
|
||||
{
|
||||
<input class="form-check-input me-1" style="font-size: 1rem" title="Apply these settings" type="checkbox" @bind="Preset.applyOther" id="applyOther">
|
||||
}
|
||||
<button class="accordion-button collapsed py-2 @(Preset.applyOther ? "text-warning fw-bold" : "")" type="button" data-bs-toggle="collapse" data-bs-target="#otherSettings">
|
||||
Other settings
|
||||
</button>
|
||||
</span>
|
||||
</h2>
|
||||
<div id="otherSettings" class="accordion-collapse collapse" data-bs-parent="#presetAccordion">
|
||||
<div class="accordion-body">
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="colorProfile">Color profile</label>
|
||||
<select class="form-select" id="colorProfile" @bind="Preset.ColorProfileSettings.ProfileName">
|
||||
<option value="">Not selected</option>
|
||||
@foreach (var colorProfile in ColorProfileNames)
|
||||
{
|
||||
<option value="@colorProfile">@colorProfile</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label" for="nvScaling">Scaling</label>
|
||||
<select class="form-select" id="nvScaling" @bind="Preset.scaling">
|
||||
<option value="">Not selected</option>
|
||||
@foreach (var scaling in Enum.GetValues<Scaling>())
|
||||
{
|
||||
<option value="@scaling">@scaling.GetDescription()</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
@@ -489,6 +532,7 @@
|
||||
private List<VirtualResolution>? resolutions;
|
||||
private List<Rational>? refreshRates;
|
||||
private List<NvPreset>? displayPresets;
|
||||
private List<string>? ColorProfileNames;
|
||||
|
||||
private string WasValidated = "";
|
||||
private bool IsVisible;
|
||||
@@ -511,10 +555,17 @@
|
||||
selectedVirtualResolution = Preset?.DisplayConfig.Resolution?.GetVirtualString();
|
||||
selectedRefreshRate = Preset?.DisplayConfig.RefreshRate?.ToString();
|
||||
|
||||
if (Preset?.IsDisplayPreset != true)
|
||||
if (Preset?.IsDisplayPreset == false)
|
||||
{
|
||||
displayPresets = await _rpcClientService.CallAsync<List<NvPreset>>("NvService", "GetDisplayPresets");
|
||||
}
|
||||
|
||||
var deviceName = Preset?.IsDisplayPreset == true ? Preset.DisplayId : Preset?.DisplayId ?? displayPresets?.FirstOrDefault()?.DisplayId;
|
||||
|
||||
if (Preset != null)
|
||||
{
|
||||
ColorProfileNames = await _rpcClientService.CallAsync<List<string>>("ColorProfileService", "GetDisplayColorProfiles", deviceName);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnAfterRender(bool firstRender)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
|
||||
<div class="modal" id="settingsModal" tabindex="-1" @onfocus="OnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">NVIDIA settings</h5>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Generics
|
||||
@@ -74,6 +74,21 @@ else
|
||||
<div class="card-body">
|
||||
<small class="text-body">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz</small>
|
||||
<small class="text-muted">@preset.colorData</small>
|
||||
@if (preset.DitherState != NvDitherState.Auto)
|
||||
{
|
||||
<br />
|
||||
<small class="text-body">Dithering: @preset.DitherState.GetDescription() @preset.NvDitherBits.GetDescription() @preset.NvDitherMode.GetDescription()</small>
|
||||
}
|
||||
@if (preset.HDREnabled)
|
||||
{
|
||||
<br />
|
||||
<small class="text-body">HDR enabled @(preset.HdrSettings.OutputMode.HasValue ? $"({preset.HdrSettings.DisplayOutputMode.GetDescription()})" : ""), SDR brightness: @preset.SDRBrightness%</small>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(preset.ColorProfileSettings.ProfileName))
|
||||
{
|
||||
<br />
|
||||
<small class="text-body">Color profile: @preset.ColorProfileSettings.ProfileName</small>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -103,10 +118,14 @@ else
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
<a class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<span class="list-group-item list-group-item-action" @onclick="() => PresetOnClick(preset)" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div>
|
||||
<h5 class="mb-1">
|
||||
@@ -117,34 +136,68 @@ else
|
||||
}
|
||||
</h5>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mb-1">
|
||||
@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz
|
||||
@(preset.applyColorData ? " " + preset.colorData : "")
|
||||
@if (preset.ApplyColorEnhancements)
|
||||
{
|
||||
<span>, @preset.ColorEnhancementSettings</span>
|
||||
}
|
||||
@if (preset.applyHDR)
|
||||
{
|
||||
<span>, HDR: @preset.GetHdrDescription()</span>
|
||||
}
|
||||
@if (preset.applyDriverSettings)
|
||||
{
|
||||
<span>, @preset.driverSettings.Count changed driver settings</span>
|
||||
}
|
||||
@if (preset.ApplyNovideoSettings)
|
||||
{
|
||||
<span>, @preset.NovideoSettings</span>
|
||||
}
|
||||
</p>
|
||||
<small>
|
||||
<ul class="list-group list-group-horizontal-md mb-1">
|
||||
@if (preset.DisplayConfig.ApplyResolution || preset.DisplayConfig.ApplyRefreshRate)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.DisplayConfig.Resolution.ToString() @@ @preset.DisplayConfig.RefreshRate.ToString()Hz</li>
|
||||
}
|
||||
@if (preset.applyColorData)
|
||||
{
|
||||
<li class="list-group-item p-1">Color settings: @preset.colorData</li>
|
||||
}
|
||||
@if (preset.applyDithering)
|
||||
{
|
||||
<li class="list-group-item p-1">Dithering: @preset.GetDitheringDescription()</li>
|
||||
}
|
||||
@if (preset.ApplyColorEnhancements)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.ColorEnhancementSettings</li>
|
||||
}
|
||||
@if (preset.applyHDR)
|
||||
{
|
||||
<li class="list-group-item p-1">HDR: @preset.GetHdrDescription()</li>
|
||||
}
|
||||
@if (preset.applyDriverSettings)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.driverSettings.Count changed driver settings</li>
|
||||
}
|
||||
@if (preset.ApplyNovideoSettings)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.NovideoSettings</li>
|
||||
}
|
||||
@if (preset.applyHdmiSettings)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.GetHdmiSettingsDescription()</li>
|
||||
}
|
||||
@if (preset.applyOverclocking)
|
||||
{
|
||||
<li class="list-group-item p-1">@preset.GetOverclockingSettingsDescription()</li>
|
||||
}
|
||||
@if (preset.applyOther && !string.IsNullOrEmpty(preset.ColorProfileSettings.ProfileName))
|
||||
{
|
||||
<li class="list-group-item p-1">Color profile: @preset.ColorProfileSettings.ProfileName</li>
|
||||
}
|
||||
</ul>
|
||||
</small>
|
||||
<small class="text-muted">@preset.shortcut</small>
|
||||
</a>
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@page "/options"
|
||||
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
@inject RpcUiClientService _rpcClientService
|
||||
@inject AppState AppState
|
||||
@inject NavigationManager _navigationManager;
|
||||
@inject NavigationManager _navigationManager
|
||||
@inject NotificationService _notificationService
|
||||
|
||||
<PageTitle>Options</PageTitle>
|
||||
|
||||
@@ -250,6 +251,11 @@ else
|
||||
{
|
||||
_navigationManager.NavigateTo("/options", true);
|
||||
}
|
||||
else if (module.IsActive)
|
||||
{
|
||||
_notificationService.SendNotification(new NotificationDto("Cannot activate the module: check the log for more info", Constants.Danger) { InternalUrl = "/logging" });
|
||||
module.IsActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PollingIntervalOnInput()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="deviceModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Device?.Name == null ? $"Add new device" : $"Settings of device {Device.Name}")</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services
|
||||
@using ColorControl.UI.Services
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using NvAPIWrapper.Native.Display
|
||||
@using ColorControl.Shared.Common
|
||||
@@ -13,7 +13,7 @@
|
||||
@inject NavigationManager _navigationManager;
|
||||
|
||||
<div class="modal" id="presetModal" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false" @onfocus="ModalOnFocus">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-dialog modal-dialog-scrollable modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">@(Preset?.id == 0 ? $"Settings for new preset" : $"Settings of preset {Preset?.IdOrName}")</h5>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.Shared.Common;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@using ColorControl.Shared.Contracts
|
||||
@using ColorControl.Shared.Contracts.NVIDIA
|
||||
@using ColorControl.Shared.Contracts.Samsung
|
||||
@using ColorControl.Shared.Services;
|
||||
@using ColorControl.UI.Services;
|
||||
@using ColorControl.UI.Components.Pages.Generic
|
||||
@using ColorControl.UI.Generics
|
||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||
@@ -153,6 +153,10 @@ else
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
@if (!Presets.Any())
|
||||
{
|
||||
<span>No presets found</span>
|
||||
}
|
||||
<div class="list-group">
|
||||
@foreach (var preset in Presets)
|
||||
{
|
||||
@@ -166,11 +170,21 @@ else
|
||||
}
|
||||
<div><small class="text-muted">@preset.shortcut</small></div>
|
||||
</div>
|
||||
<span>
|
||||
<span class="d-none d-lg-block">
|
||||
<button class="btn btn-danger btn-sm" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Delete</button>
|
||||
<button class="btn btn-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Clone</button>
|
||||
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)" @onclick:preventDefault @onclick:stopPropagation>Edit</button>
|
||||
</span>
|
||||
<div class="d-lg-none" @onclick:preventDefault @onclick:stopPropagation>
|
||||
<button type="button" class="btn btn-outline-secondary" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="bi bi-three-dots-vertical"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#confirmModal" @onclick="() => PresetOnDeleteClick(preset)">Delete</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnCloneClick(preset)">Clone</a></li>
|
||||
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#presetModal" @onclick="() => PresetOnEditClick(preset)">Edit</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
public static class Constants
|
||||
{
|
||||
public const string Primary = "primary";
|
||||
public const string Secondary = "secondary";
|
||||
public const string Success = "success";
|
||||
public const string Danger = "danger";
|
||||
public const string Warning = "warning";
|
||||
public const string Info = "info";
|
||||
public const string Light = "light";
|
||||
public const string Dark = "dark";
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace ColorControl.UI.Generics;
|
||||
|
||||
public static class GeneralExtensions
|
||||
{
|
||||
public static async ValueTask<TValue?> TryGet<TValue>(this ProtectedBrowserStorage storage, string key, TValue? def = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await storage.GetAsync<TValue>(key);
|
||||
var value = result.Success ? result.Value : def;
|
||||
|
||||
return value;
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{
|
||||
await storage.DeleteAsync(key);
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,25 @@
|
||||
using ColorControl.Shared.Contracts;
|
||||
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace ColorControl.UI.Generics;
|
||||
|
||||
public static class PresetExtensions
|
||||
{
|
||||
public static List<T>? OrderPresetsBy<T>(this List<T> presets, PresetOrder order) where T : PresetBase
|
||||
{
|
||||
if (presets == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public static List<T>? OrderPresetsBy<T>(this List<T> presets, PresetOrder order) where T : PresetBase
|
||||
{
|
||||
if (presets == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (order == PresetOrder.ByName)
|
||||
{
|
||||
presets = presets.OrderBy(p => p.name).ToList();
|
||||
}
|
||||
else if (order == PresetOrder.ByLastUsed)
|
||||
{
|
||||
presets = presets.OrderByDescending(p => p.LastUsed).ToList();
|
||||
}
|
||||
if (order == PresetOrder.ByName)
|
||||
{
|
||||
presets = presets.OrderBy(p => p.name).ToList();
|
||||
}
|
||||
else if (order == PresetOrder.ByLastUsed)
|
||||
{
|
||||
presets = presets.OrderByDescending(p => p.LastUsed).ToList();
|
||||
}
|
||||
|
||||
return presets;
|
||||
}
|
||||
|
||||
public static async ValueTask<TValue?> TryGet<TValue>(this ProtectedBrowserStorage storage, string key, TValue? def = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await storage.GetAsync<TValue>(key);
|
||||
var value = result.Success ? result.Value : def;
|
||||
|
||||
return value;
|
||||
}
|
||||
catch (CryptographicException)
|
||||
{
|
||||
await storage.DeleteAsync(key);
|
||||
return def;
|
||||
}
|
||||
}
|
||||
return presets;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using NStandard;
|
||||
|
||||
namespace ColorControl.UI;
|
||||
|
||||
public class NotificationDto
|
||||
{
|
||||
public long Id { get; }
|
||||
public string Message { get; }
|
||||
public string Level { get; } = Constants.Info;
|
||||
public DateTime? ScheduledAt { get; set; }
|
||||
public string? InternalUrl { get; set; }
|
||||
public DateTime? ShownAt { get; set; }
|
||||
|
||||
public NotificationDto(string message, string level = Constants.Info)
|
||||
{
|
||||
Message = message;
|
||||
Level = level;
|
||||
Id = Random.Shared.NextInt64();
|
||||
}
|
||||
|
||||
public string Category => Level switch
|
||||
{
|
||||
Constants.Danger => "Error",
|
||||
_ => Level[0].ToUpper() + Level.Substring(1)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace ColorControl.UI.Services;
|
||||
|
||||
public class JSHelper(IJSRuntime jsRuntime)
|
||||
{
|
||||
private IJSObjectReference? jsModule;
|
||||
|
||||
public async Task<bool> IsFormValid(string id)
|
||||
{
|
||||
return await InvokeAsync<bool>("FormValid", id);
|
||||
}
|
||||
|
||||
public async Task OpenModal(string id)
|
||||
{
|
||||
await InvokeVoidAsync("OpenModal", id);
|
||||
}
|
||||
|
||||
public async Task CloseModal(string id)
|
||||
{
|
||||
await InvokeVoidAsync("CloseModal", id);
|
||||
}
|
||||
|
||||
public async Task ShowToast(string id)
|
||||
{
|
||||
await InvokeVoidAsync("ShowToast", id);
|
||||
}
|
||||
|
||||
private async ValueTask<T> InvokeAsync<T>(string identifier, params object?[]? names) where T : struct
|
||||
{
|
||||
jsModule = await LoadModule();
|
||||
|
||||
return await jsModule.InvokeAsync<T>(identifier, names);
|
||||
}
|
||||
|
||||
private async ValueTask InvokeVoidAsync(string identifier, params object?[]? names)
|
||||
{
|
||||
jsModule = await LoadModule();
|
||||
|
||||
await jsModule.InvokeVoidAsync(identifier, names);
|
||||
}
|
||||
|
||||
private async Task<IJSObjectReference> LoadModule()
|
||||
{
|
||||
return jsModule ??= await jsRuntime.InvokeAsync<IJSObjectReference>("import", "./helper.js");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace ColorControl.UI.Services;
|
||||
|
||||
public class NotificationService
|
||||
{
|
||||
public event Func<NotificationDto, Task>? OnNotification;
|
||||
|
||||
private List<NotificationDto> _scheduledNotifications = new();
|
||||
|
||||
public void SendNotification(NotificationDto notification)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
if (notification.ScheduledAt == null || notification.ScheduledAt <= now)
|
||||
{
|
||||
OnNotification?.Invoke(notification);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(notification.ScheduledAt.Value - now);
|
||||
|
||||
OnNotification?.Invoke(notification);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using ColorControl.Shared.Common;
|
||||
using ColorControl.Shared.Contracts;
|
||||
using Newtonsoft.Json;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ColorControl.UI.Services;
|
||||
|
||||
public class RpcUiClientOptions
|
||||
{
|
||||
public int Timeout { get; set; } = PipeUtils.DefaultTimeout;
|
||||
}
|
||||
|
||||
public class RpcUiClientService(NotificationService notificationService)
|
||||
{
|
||||
public string? LastErrorMessage { get; private set; }
|
||||
|
||||
public async Task<T> CallAsync<T>(string serviceName, string method, params object[] arguments)
|
||||
{
|
||||
var rpcMessage = new SvcRpcMessage
|
||||
{
|
||||
ServiceName = serviceName,
|
||||
MethodName = method,
|
||||
Arguments = arguments
|
||||
};
|
||||
|
||||
var timeout = Debugger.IsAttached ? 60000 : PipeUtils.DefaultTimeout;
|
||||
|
||||
return await SendRpcMessageAsync<T>(rpcMessage, timeout, pipeName: PipeUtils.MainPipe);
|
||||
}
|
||||
|
||||
public async Task<T> CallWithOptionsAsync<T>(string serviceName, string method, RpcUiClientOptions options, params object[] arguments)
|
||||
{
|
||||
var rpcMessage = new SvcRpcMessage
|
||||
{
|
||||
ServiceName = serviceName,
|
||||
MethodName = method,
|
||||
Arguments = arguments
|
||||
};
|
||||
|
||||
return await SendRpcMessageAsync<T>(rpcMessage, options.Timeout, pipeName: PipeUtils.MainPipe);
|
||||
}
|
||||
|
||||
public T Call<T>(string serviceName, string method, params object[] arguments)
|
||||
{
|
||||
var rpcMessage = new SvcRpcMessage
|
||||
{
|
||||
ServiceName = serviceName,
|
||||
MethodName = method,
|
||||
Arguments = arguments
|
||||
};
|
||||
|
||||
var timeout = Debugger.IsAttached ? 60000 : 2000;
|
||||
|
||||
return PipeUtils.SendRpcMessage<T>(rpcMessage, timeout, pipeName: PipeUtils.MainPipe);
|
||||
}
|
||||
|
||||
private async Task<T> SendRpcMessageAsync<T>(SvcRpcMessage message, int timeout = PipeUtils.DefaultTimeout, string pipeName = PipeUtils.ServicePipe)
|
||||
{
|
||||
var messageJson = JsonConvert.SerializeObject(message);
|
||||
|
||||
var resultJson = await PipeUtils.SendMessageAsync(messageJson, timeout, pipeName);
|
||||
|
||||
if (resultJson == null)
|
||||
{
|
||||
notificationService.SendNotification(new NotificationDto("Unknown communication error", Constants.Danger));
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
var resultMessage = JsonConvert.DeserializeObject<SvcResultMessage>(resultJson);
|
||||
|
||||
if (resultMessage?.Data == null)
|
||||
{
|
||||
LastErrorMessage = resultMessage?.ErrorMessage ?? "Unknown communication error";
|
||||
|
||||
notificationService.SendNotification(new NotificationDto(LastErrorMessage, Constants.Danger));
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
LastErrorMessage = null;
|
||||
|
||||
var result = JsonConvert.DeserializeObject<T>(resultMessage.Data);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -42,9 +42,9 @@ h1:focus {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.blazor-error-boundary::after {
|
||||
content: "An error has occurred."
|
||||
}
|
||||
.blazor-error-boundary::after {
|
||||
content: "An error has occurred."
|
||||
}
|
||||
|
||||
.darker-border-checkbox.form-check-input {
|
||||
border-color: #929292;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
@charset "UTF-8";/*!
|
||||
* Bootstrap v5.1.0 (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)
|
||||
*/:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-rgb:33,37,41;--bs-font-sans-serif: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";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){leLine truncated
|
||||
/*# sourceMappingURL=bootstrap.min.css.map */
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"sources":["../../scss/bootstrap.scss","../../scss/_root.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/vendor/_rfs.scss","../../scss/mixins/_border-radius.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_containers.scss","../../scss/mixins/_container.scss","../../scss/mixins/_breakpoints.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/_tables.scss","../../scss/mixins/_table-variants.scss","../../scss/forms/_labels.scss","../../scss/forms/_form-text.scss","../../scss/forms/_form-control.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_gradients.scss","../../scss/forms/_form-select.scss","../../scss/forms/_form-check.scss","../../scss/forms/_form-range.scss","../../scss/forms/_floating-labels.scss","../../scss/forms/_input-group.scss","../../scss/mixins/_forms.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/_button-group.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_accordion.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_toasts.scss","../../scss/_modal.scss","../../scss/mixins/_backdrop.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/mixins/_clearfix.scss","../../scss/_spinners.scss","../../scss/_offcanvas.scss","../../scss/_placeholders.scss","../../scss/helpers/_colored-links.scss","../../scss/helpers/_ratio.scss","../../scss/helpers/_position.scss","../../scss/helpers/_stacks.scss","../../scss/helpers/_visually-hidden.scss","../../scss/mixins/_visually-hidden.scss","../../scss/helpers/_stretched-link.scss","../../scss/helpers/_text-truncation.scss","../../scss/mixins/_text-truncate.scss","../../scss/helpers/_vr.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"iBAAA;;;;;ACAA,MAQI,UAAA,QAAA,YAAA,QAAA,YAAA,QAAA,UAAA,QAAA,SAAA,QAAA,YAAA,QAAA,YAAA,QAAA,WAAA,QAAA,UAAA,QAAA,UAAA,QAAA,WAAA,KAAA,UAAA,QAAA,eAAA,QAIA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAAA,cAAA,QAIA,aAAA,QAAA,eAAA,QAAA,aAAA,QAAA,UAAA,QAAA,aAAA,QAAA,YAAA,QAAA,WAAA,QAAA,UAAA,QAIA,iBAAA,EAAA,CAAA,GAAA,CAAA,IAAA,mBAAA,GAAA,CAAA,GAAA,CAAA,IAAA,iBAAA,EAAA,CAAA,GAAA,CAAA,GAAA,cAAA,EAAA,CAAA,GAAA,CAAA,IAAA,iBAAA,GAAA,CAAA,GAAA,CAAA,EAAA,gBAAA,GAAA,CAAA,EAAA,CAAA,GAAA,eAAA,GAAA,CAAA,GAAA,CAAA,IAAA,cAAA,EAAA,CAAA,EAAA,CAAA,GAGF,eAAA,GAAA,CAAA,GAAA,CAAA,IACA,eAAA,CAAA,CAAA,CAAA,CAAA,EACA,cAAA,EAAA,CAAA,EAAA,CAAA,GAMA,qBAAA,SAAA,CAAA,aAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,WAAA,CAAA,iBAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBACA,oBAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UACA,cAAA,2EAQA,sBAAA,0BACA,oBAAA,KACA,sBAAA,IACA,sBAAA,IACA,gBAAA,QAIA,aAAA,KClCF,EC+CA,QADA,SD3CE,WAAA,WAeE,8CANJ,MAOM,gBAAA,QAcN,KACE,OAAA,EACA,YAAA,2BEmPI,UAAA,yBFjPJ,YAAA,2BACA,YAAA,2BACA,MAAA,qBACA,WAAA,0BACA,iBAAA,kBACA,yBAAA,KACA,4BAAA,YAUF,GACE,OAAA,KAAA,EACA,MAAA,QACA,iBAAA,aACA,OAAA,EACA,QAAA,IAGF,eACE,OAAA,IAUF,IAAA,IAAA,IAAA,IAAA,IAAA,IAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAGA,YAAA,IACA,YAAA,IAIF,IAAA,GEwMQ,UAAA,uBAlKJ,0BFtCJ,IAAA,GE+MQ,UAAA,QF1MR,IAAA,GEmMQ,UAAA,sBAlKJ,0BFjCJ,IAAA,GE0MQ,UAAA,MFrMR,IAAA,GE8LQ,UAAA,oBAlKJ,0BF5BJ,IAAA,GEqMQ,UAAA,SFhMR,IAAA,GEyLQ,UAAA,sBAlKJ,0BFvBJ,IAAA,GEgMQ,UAAA,QF3LR,IAAA,GEgLM,UAAA,QF3KN,IAAA,GE2KM,UAAA,KFhKN,EACE,WAAA,EACA,cAAA,KCmBF,6BDRA,YAEE,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,iCAAA,KAAA,yBAAA,KAMF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QAMF,GCIA,GDFE,aAAA,KCQF,GDLA,GCIA,GDDE,WAAA,EACA,cAAA,KAGF,MCKA,MACA,MAFA,MDAE,cAAA,EAGF,GACE,YAAA,IAKF,GACE,cAAA,MACA,YAAA,EAMF,WACE,OAAA,EAAA,EAAA,KAQF,ECNA,ODQE,YAAA,OAQF,OAAA,ME4EM,UAAA,OFrEN,MAAA,KACE,QAAA,KACA,iBAAA,QASF,ICpBA,IDsBE,SAAA,SEwDI,UAAA,MFtDJ,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAKN,EACE,MAAA,QACA,gBAAA,UAEA,QACE,MAAA,QAWF,2BAAA,iCAEE,MAAA,QACA,gBAAA,KCxBJ,KACA,ID8BA,IC7BA,KDiCE,YAAA,yBEcI,UAAA,IFZJ,UAAA,IACA,aAAA,cAOF,IACE,QAAA,MACA,WAAA,EACA,cAAA,KACA,SAAA,KEAI,UAAA,OFKJ,SELI,UAAA,QFOF,MAAA,QACA,WAAA,OAIJ,KEZM,UAAA,OFcJ,MAAA,QACA,UAAA,WAGA,OACE,MAAA,QAIJ,IACE,QAAA,MAAA,MExBI,UAAA,OF0BJ,MAAA,KACA,iBAAA,QG7SE,cAAA,MHgTF,QACE,QAAA,EE/BE,UAAA,IFiCF,YAAA,IASJ,OACE,OAAA,EAAA,EAAA,KAMF,ICjDA,IDmDE,eAAA,OAQF,MACE,aAAA,OACA,gBAAA,SAGF,QACE,YAAA,MACA,eAAA,MACA,MAAA,QACA,WAAA,KAOF,GAEE,WAAA,QACA,WAAA,qBCxDF,MAGA,GAFA,MAGA,GDuDA,MCzDA,GD+DE,aAAA,QACA,aAAA,MACA,aAAA,EAQF,MACE,QAAA,aAMF,OAEE,cAAA,EAQF,iCACE,QAAA,ECtEF,OD2EA,MCzEA,SADA,OAEA,SD6EE,OAAA,EACA,YAAA,QE9HI,UAAA,QFgIJLine truncated
|
||||
@@ -0,0 +1,17 @@
|
||||
export function FormValid(formId) {
|
||||
let form = document.getElementById(formId);
|
||||
return form.checkValidity();
|
||||
}
|
||||
|
||||
export function CloseModal(modalId) {
|
||||
bootstrap.Modal.getOrCreateInstance('#' + modalId)?.hide();
|
||||
}
|
||||
|
||||
export function OpenModal(modalId) {
|
||||
bootstrap.Modal.getOrCreateInstance('#' + modalId)?.show();
|
||||
}
|
||||
|
||||
export function ShowToast(toastId) {
|
||||
const toastBootstrap = bootstrap.Toast.getOrCreateInstance('#' + toastId);
|
||||
toastBootstrap?.show();
|
||||
}
|
||||
Reference in new issue
Block a user