feat: Convert website from static HTML to .NET 9.0 Blazor Web App

Major architectural upgrade from static HTML site to modern Blazor Web App:

- Migrated to .NET 9.0 Blazor Web App framework
- Converted home page with 4 gateway cards (Help Desk, App Store, Cloud, SDK)
- Added new SDK card linking to comprehensive SDK documentation
- Converted SDK documentation page to Blazor component
- Updated template download links to nuget.silverlabs.uk repository
- Implemented multi-stage Docker build with .NET SDK 9.0
- Created Blazor-optimized nginx configuration
- Preserved all original styling and animations
- Added .gitignore for Blazor build artifacts

Technical changes:
- New BlazorApp/ project structure with Components architecture
- MainLayout simplified (no default navigation)
- CSS ported to wwwroot (styles.css + sdk-styles.css)
- Multi-stage Dockerfile: Build with dotnet SDK, serve with nginx
- GitLab CI/CD pipeline compatible (auto-detects new Dockerfile)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-20 19:11:53 +01:00
parent a7fd925187
commit 3aa191cd3d
75 changed files with 60705 additions and 676 deletions

34
.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
# .NET Build artifacts
BlazorApp/bin/
BlazorApp/obj/
BlazorApp/.vs/
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio cache/options directory
.vs/
# Rider
.idea/
# Backup folder
old-static-site/
# OS Files
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="@Assets["styles.css"]" />
<link rel="stylesheet" href="@Assets["sdk-styles.css"]" />
<ImportMap />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>
</html>

View File

@@ -0,0 +1,9 @@
@inherits LayoutComponentBase
@Body
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="." class="reload">Reload</a>
<span class="dismiss">🗙</span>
</div>

View File

@@ -0,0 +1,98 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row ::deep .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
text-decoration: none;
}
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
text-decoration: underline;
}
.top-row ::deep a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}
#blazor-error-ui {
color-scheme: light only;
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
box-sizing: border-box;
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}

View File

@@ -0,0 +1,30 @@
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">SilverLabs.Website</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="nav flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
</NavLink>
</div>
</nav>
</div>

View File

@@ -0,0 +1,105 @@
.navbar-toggler {
appearance: none;
cursor: pointer;
width: 3.5rem;
height: 2.5rem;
color: white;
position: absolute;
top: 0.5rem;
right: 1rem;
border: 1px solid rgba(255, 255, 255, 0.1);
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
}
.navbar-toggler:checked {
background-color: rgba(255, 255, 255, 0.5);
}
.top-row {
min-height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.bi {
display: inline-block;
position: relative;
width: 1.25rem;
height: 1.25rem;
margin-right: 0.75rem;
top: -1px;
background-size: cover;
}
.bi-house-door-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
}
.bi-plus-square-fill-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
}
.bi-list-nested-nav-menu {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep .nav-link {
color: #d7d7d7;
background: none;
border: none;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
width: 100%;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.37);
color: white;
}
.nav-item ::deep .nav-link:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
.nav-scrollable {
display: none;
}
.navbar-toggler:checked ~ .nav-scrollable {
display: block;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.nav-scrollable {
/* Never collapse the sidebar for wide screens */
display: block;
/* Allow sidebar to scroll for tall menus */
height: calc(100vh - 3.5rem);
overflow-y: auto;
}
}

View File

@@ -0,0 +1,19 @@
@page "/counter"
@rendermode InteractiveServer
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}

View File

@@ -0,0 +1,36 @@
@page "/Error"
@using System.Diagnostics
<PageTitle>Error</PageTitle>
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
@code{
[CascadingParameter]
private HttpContext? HttpContext { get; set; }
private string? RequestId { get; set; }
private bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
protected override void OnInitialized() =>
RequestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
}

View File

@@ -0,0 +1,66 @@
@page "/"
<PageTitle>SilverLabs - Innovation Gateway</PageTitle>
<div class="main-content visible">
<header class="header">
<img src="logo.png" alt="SilverLabs Logo" class="logo">
</header>
<main class="main">
<h1 class="title">Welcome to SilverLabs</h1>
<p class="subtitle">Your Innovation Gateway</p>
<div class="gateway-grid">
<a href="https://helpdesk.silverlabs.uk" class="gateway-card">
<div class="card-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
</div>
<h2 class="card-title">Help Desk</h2>
<p class="card-description">Support & Assistance</p>
</a>
<a href="https://appstore.silverlabs.uk" class="gateway-card">
<div class="card-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
</div>
<h2 class="card-title">App Store</h2>
<p class="card-description">Applications & Tools</p>
</a>
<a href="https://cloud.silverlabs.uk" class="gateway-card">
<div class="card-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"></path>
</svg>
</div>
<h2 class="card-title">Cloud</h2>
<p class="card-description">Storage & Collaboration</p>
</a>
<a href="/sdk" class="gateway-card">
<div class="card-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="16 18 22 12 16 6"></polyline>
<polyline points="8 6 2 12 8 18"></polyline>
</svg>
</div>
<h2 class="card-title">SDK</h2>
<p class="card-description">Developer Resources</p>
</a>
</div>
</main>
<footer class="footer">
<p>&copy; 2025 SilverLabs. All rights reserved.</p>
</footer>
</div>

View File

@@ -0,0 +1,253 @@
@page "/sdk"
<PageTitle>SilverSHELL SDK - SilverLabs</PageTitle>
<div class="main-content visible">
<header class="header">
<img src="../logo.png" alt="SilverLabs Logo" class="logo">
</header>
<div class="sdk-container">
<div class="sdk-header">
<h1>SilverSHELL SDK</h1>
<p style="font-size: 1.2rem; color: rgba(255, 255, 255, 0.7);">Build modular Blazor WebAssembly applications with ease</p>
</div>
<!-- Quick Start Section -->
<div class="sdk-section">
<h2>🚀 Quick Start</h2>
<p>Get started with SilverSHELL in just a few minutes. Follow these simple steps:</p>
<ol class="steps-list">
<li>
<strong>Download the templates</strong> (see downloads below)
</li>
<li>
<strong>Extract the templates</strong> to a directory of your choice
</li>
<li>
<strong>Install the templates</strong>
<div class="code-block"><code>dotnet new install ./path/to/SilverSHELL.Starter.Template
dotnet new install ./path/to/SilverSHELL.AppModule.Template</code></div>
</li>
<li>
<strong>Create your first project</strong>
<div class="code-block"><code>dotnet new silvershell-starter -n MyApp --pwa true --module-repository true
cd MyApp
dotnet run</code></div>
</li>
<li>
<strong>Access your application</strong> at <code>https://localhost:5001</code>
</li>
</ol>
</div>
<!-- Downloads Section -->
<div class="sdk-section">
<h2>📦 Downloads</h2>
<p>Download the SilverSHELL project templates to get started:</p>
<div class="download-grid">
<div class="download-card">
<h3>Starter Template</h3>
<p>Create a new SilverSHELL application with minimal configuration</p>
<p><strong>Size:</strong> 6.7 KB</p>
<p><strong>Includes:</strong> Blazor WebAssembly setup, module loading, PWA support</p>
<a href="https://nuget.silverlabs.uk/templates/silvershell-starter-template.tar.gz" class="download-btn" download>
Download Starter Template
</a>
</div>
<div class="download-card">
<h3>Module Template</h3>
<p>Create reusable SilverSHELL modules with best practices</p>
<p><strong>Size:</strong> 17 KB</p>
<p><strong>Includes:</strong> Module structure, configuration, CI/CD templates</p>
<a href="https://nuget.silverlabs.uk/templates/silvershell-module-template.tar.gz" class="download-btn" download>
Download Module Template
</a>
</div>
</div>
</div>
<!-- Creating Applications Section -->
<div class="sdk-section">
<h2>🏗️ Creating Applications</h2>
<h3>Using the Starter Template</h3>
<div class="code-block"><code># Create a new application
dotnet new silvershell-starter -n MyAwesomeApp
# With PWA support
dotnet new silvershell-starter -n MyAwesomeApp --pwa true
# With Module Repository integration
dotnet new silvershell-starter -n MyAwesomeApp --module-repository true</code></div>
<h3>Adding Modules</h3>
<p>SilverSHELL supports multiple ways to add modules to your application:</p>
<h3>Option 1: Configuration File</h3>
<p>Edit <code>wwwroot/appsettings.json</code>:</p>
<div class="code-block"><code>{
"AMS": {
"Deployment": {
"PreloadModules": [
"SilverLabs.SilverSHELL.Auth.Login",
"SilverSHELL.Modules.ModuleBrowser"
]
}
}
}</code></div>
<h3>Option 2: Module Browser UI</h3>
<ol>
<li>Navigate to <code>/modules/browse</code> in your application</li>
<li>Search for modules from <strong>library.silverlabs.uk</strong></li>
<li>Click "Install" on any module</li>
<li>Modules are downloaded and installed automatically</li>
</ol>
<h3>Option 3: Manual Installation</h3>
<div class="code-block"><code># Copy module DLLs to the modules directory
cp SomeModule.dll wwwroot/modules/
dotnet run
# Module is automatically discovered and loaded!</code></div>
</div>
<!-- Creating Modules Section -->
<div class="sdk-section">
<h2>🔧 Creating Modules</h2>
<h3>Using the Module Template</h3>
<div class="code-block"><code># Create a basic module
dotnet new silvershell-module -n MyModule
# Create a module with widgets
dotnet new silvershell-module -n MyModule --includeWidgets true
# Create a module with search provider
dotnet new silvershell-module -n MyModule --includeSearchProvider true
# Create a module with everything
dotnet new silvershell-module -n MyModule \
--includeWidgets true \
--includeSearchProvider true \
--includeTests true</code></div>
<h3>Module Structure</h3>
<p>The template creates an organized structure:</p>
<div class="code-block"><code>MyModule/
├── Configuration/
│ ├── ModuleMetadata.cs # Module identity and version
│ ├── EndpointConfiguration.cs # Navigation routes
│ └── WidgetConfiguration.cs # Dashboard widgets
├── Pages/
│ └── Index.razor # Razor pages
├── Components/
│ └── ... # Razor components
├── .gitlab-ci.yml # GitLab CI/CD
├── .github/workflows/
│ └── publish.yml # GitHub Actions
└── MyModuleMain.cs # Module entry point</code></div>
</div>
<!-- Publishing Modules Section -->
<div class="sdk-section">
<h2>🚀 Publishing Modules</h2>
<div class="info-box">
<strong>CI/CD Included!</strong> The module template includes ready-to-use CI/CD pipelines for both GitLab and GitHub.
</div>
<h3>Automated Publishing (Recommended)</h3>
<p>The templates include CI/CD pipelines for automatic publishing:</p>
<ol class="steps-list">
<li>
<strong>Configure CI/CD variables</strong>
<div class="code-block"><code># In GitLab: Settings > CI/CD > Variables
# In GitHub: Settings > Secrets > Actions
# Add variable:
MODULE_REPO_TOKEN: [your token from library.silverlabs.uk]</code></div>
</li>
<li>
<strong>Commit and push your code</strong>
<div class="code-block"><code>git add .
git commit -m "feat: Initial module implementation"
git push</code></div>
</li>
<li>
<strong>Create a release tag</strong>
<div class="code-block"><code>git tag v1.0.0
git push --tags</code></div>
</li>
<li>
<strong>Trigger publish</strong> from your CI/CD pipeline UI
</li>
</ol>
<h3>Manual Publishing</h3>
<div class="code-block"><code># Build and package
dotnet pack --configuration Release -o dist/
# Upload to repository
curl -X POST "https://library.silverlabs.uk/api/modules/publish" \
-F "id=MyModule" \
-F "name=My Awesome Module" \
-F "version=1.0.0" \
-F "description=A great module" \
-F "author=Your Name" \
-F "package=@@dist/MyModule.1.0.0.nupkg"</code></div>
</div>
<!-- Available Modules Section -->
<div class="sdk-section">
<h2>📚 Available Modules</h2>
<p>Browse and install modules from the SilverSHELL module repository:</p>
<div class="info-box">
<strong>Module Repository:</strong> <a href="https://library.silverlabs.uk" target="_blank" style="color: #a78bfa;">library.silverlabs.uk</a>
</div>
<h3>Featured Modules:</h3>
<ul>
<li><strong>Auth.Login</strong> - User authentication and login UI</li>
<li><strong>Auth.Registration</strong> - User registration system</li>
<li><strong>Auth.UserManagement</strong> - User administration</li>
<li><strong>Auth.MyAccount</strong> - User profile management</li>
<li><strong>ModuleBrowser</strong> - Browse and install modules from the UI</li>
</ul>
<h3>Explore All Modules</h3>
<div class="code-block"><code># List all available modules via API
curl https://library.silverlabs.uk/api/modules
# Search for specific modules
curl https://library.silverlabs.uk/api/modules/search?q=auth</code></div>
</div>
<!-- Resources Section -->
<div class="sdk-section">
<h2>📖 Resources</h2>
<ul>
<li><strong>Module Repository API:</strong> <a href="https://library.silverlabs.uk/api/modules" target="_blank" style="color: #a78bfa;">https://library.silverlabs.uk/api/modules</a></li>
<li><strong>Demo Application:</strong> <a href="https://demo.silverlabs.uk" target="_blank" style="color: #a78bfa;">https://demo.silverlabs.uk</a></li>
<li><strong>GitLab Repository:</strong> <a href="https://gitlab.silverlabs.uk/silverlabs/silvershell" target="_blank" style="color: #a78bfa;">GitLab</a></li>
</ul>
</div>
<!-- Support Section -->
<div class="sdk-section">
<h2>💬 Support</h2>
<p>Need help? We're here for you:</p>
<ul>
<li><strong>Help Desk:</strong> <a href="https://helpdesk.silverlabs.uk" target="_blank" style="color: #a78bfa;">helpdesk.silverlabs.uk</a></li>
<li><strong>Issues:</strong> <a href="https://gitlab.silverlabs.uk/silverlabs/silvershell/-/issues" target="_blank" style="color: #a78bfa;">GitLab Issues</a></li>
</ul>
</div>
<a href="/" class="back-link">← Back to SilverLabs Home</a>
</div>
</div>

View File

@@ -0,0 +1,64 @@
@page "/weather"
@attribute [StreamRendering]
<PageTitle>Weather</PageTitle>
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th aria-label="Temperature in Celsius">Temp. (C)</th>
<th aria-label="Temperature in Farenheit">Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate streaming rendering
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}

View File

@@ -0,0 +1,6 @@
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>

View File

@@ -0,0 +1,10 @@
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using static Microsoft.AspNetCore.Components.Web.RenderMode
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using SilverLabs.Website
@using SilverLabs.Website.Components

28
BlazorApp/Program.cs Normal file
View File

@@ -0,0 +1,28 @@
using SilverLabs.Website.Components;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();

View File

@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/launchsettings.json",
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "http://localhost:5072",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"applicationUrl": "https://localhost:7104;http://localhost:5072",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

60
BlazorApp/wwwroot/app.css Normal file
View File

@@ -0,0 +1,60 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
a, .btn-link {
color: #006bb7;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}
.content {
padding-top: 1.1rem;
}
h1:focus {
outline: none;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid #e50000;
}
.validation-message {
color: #e50000;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}
.darker-border-checkbox.form-check-input {
border-color: #929292;
}
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
color: var(--bs-secondary-color);
text-align: end;
}
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
text-align: start;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,597 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--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-black: #000;
--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-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, 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-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
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: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
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;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-left: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: left;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
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]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]: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: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: left;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
/* rtl:raw:
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
*/
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,594 @@
/*!
* Bootstrap Reboot v5.3.3 (https://getbootstrap.com/)
* Copyright 2011-2024 The Bootstrap Authors
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
:root,
[data-bs-theme=light] {
--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-black: #000;
--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-primary-text-emphasis: #052c65;
--bs-secondary-text-emphasis: #2b2f32;
--bs-success-text-emphasis: #0a3622;
--bs-info-text-emphasis: #055160;
--bs-warning-text-emphasis: #664d03;
--bs-danger-text-emphasis: #58151c;
--bs-light-text-emphasis: #495057;
--bs-dark-text-emphasis: #495057;
--bs-primary-bg-subtle: #cfe2ff;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-success-bg-subtle: #d1e7dd;
--bs-info-bg-subtle: #cff4fc;
--bs-warning-bg-subtle: #fff3cd;
--bs-danger-bg-subtle: #f8d7da;
--bs-light-bg-subtle: #fcfcfd;
--bs-dark-bg-subtle: #ced4da;
--bs-primary-border-subtle: #9ec5fe;
--bs-secondary-border-subtle: #c4c8cb;
--bs-success-border-subtle: #a3cfbb;
--bs-info-border-subtle: #9eeaf9;
--bs-warning-border-subtle: #ffe69c;
--bs-danger-border-subtle: #f1aeb5;
--bs-light-border-subtle: #e9ecef;
--bs-dark-border-subtle: #adb5bd;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, 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-color-rgb: 33, 37, 41;
--bs-body-bg: #fff;
--bs-body-bg-rgb: 255, 255, 255;
--bs-emphasis-color: #000;
--bs-emphasis-color-rgb: 0, 0, 0;
--bs-secondary-color: rgba(33, 37, 41, 0.75);
--bs-secondary-color-rgb: 33, 37, 41;
--bs-secondary-bg: #e9ecef;
--bs-secondary-bg-rgb: 233, 236, 239;
--bs-tertiary-color: rgba(33, 37, 41, 0.5);
--bs-tertiary-color-rgb: 33, 37, 41;
--bs-tertiary-bg: #f8f9fa;
--bs-tertiary-bg-rgb: 248, 249, 250;
--bs-heading-color: inherit;
--bs-link-color: #0d6efd;
--bs-link-color-rgb: 13, 110, 253;
--bs-link-decoration: underline;
--bs-link-hover-color: #0a58ca;
--bs-link-hover-color-rgb: 10, 88, 202;
--bs-code-color: #d63384;
--bs-highlight-color: #212529;
--bs-highlight-bg: #fff3cd;
--bs-border-width: 1px;
--bs-border-style: solid;
--bs-border-color: #dee2e6;
--bs-border-color-translucent: rgba(0, 0, 0, 0.175);
--bs-border-radius: 0.375rem;
--bs-border-radius-sm: 0.25rem;
--bs-border-radius-lg: 0.5rem;
--bs-border-radius-xl: 1rem;
--bs-border-radius-xxl: 2rem;
--bs-border-radius-2xl: var(--bs-border-radius-xxl);
--bs-border-radius-pill: 50rem;
--bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
--bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
--bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);
--bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075);
--bs-focus-ring-width: 0.25rem;
--bs-focus-ring-opacity: 0.25;
--bs-focus-ring-color: rgba(13, 110, 253, 0.25);
--bs-form-valid-color: #198754;
--bs-form-valid-border-color: #198754;
--bs-form-invalid-color: #dc3545;
--bs-form-invalid-border-color: #dc3545;
}
[data-bs-theme=dark] {
color-scheme: dark;
--bs-body-color: #dee2e6;
--bs-body-color-rgb: 222, 226, 230;
--bs-body-bg: #212529;
--bs-body-bg-rgb: 33, 37, 41;
--bs-emphasis-color: #fff;
--bs-emphasis-color-rgb: 255, 255, 255;
--bs-secondary-color: rgba(222, 226, 230, 0.75);
--bs-secondary-color-rgb: 222, 226, 230;
--bs-secondary-bg: #343a40;
--bs-secondary-bg-rgb: 52, 58, 64;
--bs-tertiary-color: rgba(222, 226, 230, 0.5);
--bs-tertiary-color-rgb: 222, 226, 230;
--bs-tertiary-bg: #2b3035;
--bs-tertiary-bg-rgb: 43, 48, 53;
--bs-primary-text-emphasis: #6ea8fe;
--bs-secondary-text-emphasis: #a7acb1;
--bs-success-text-emphasis: #75b798;
--bs-info-text-emphasis: #6edff6;
--bs-warning-text-emphasis: #ffda6a;
--bs-danger-text-emphasis: #ea868f;
--bs-light-text-emphasis: #f8f9fa;
--bs-dark-text-emphasis: #dee2e6;
--bs-primary-bg-subtle: #031633;
--bs-secondary-bg-subtle: #161719;
--bs-success-bg-subtle: #051b11;
--bs-info-bg-subtle: #032830;
--bs-warning-bg-subtle: #332701;
--bs-danger-bg-subtle: #2c0b0e;
--bs-light-bg-subtle: #343a40;
--bs-dark-bg-subtle: #1a1d20;
--bs-primary-border-subtle: #084298;
--bs-secondary-border-subtle: #41464b;
--bs-success-border-subtle: #0f5132;
--bs-info-border-subtle: #087990;
--bs-warning-border-subtle: #997404;
--bs-danger-border-subtle: #842029;
--bs-light-border-subtle: #495057;
--bs-dark-border-subtle: #343a40;
--bs-heading-color: inherit;
--bs-link-color: #6ea8fe;
--bs-link-hover-color: #8bb9fe;
--bs-link-color-rgb: 110, 168, 254;
--bs-link-hover-color-rgb: 139, 185, 254;
--bs-code-color: #e685b5;
--bs-highlight-color: #dee2e6;
--bs-highlight-bg: #664d03;
--bs-border-color: #495057;
--bs-border-color-translucent: rgba(255, 255, 255, 0.15);
--bs-form-valid-color: #75b798;
--bs-form-valid-border-color: #75b798;
--bs-form-invalid-color: #ea868f;
--bs-form-invalid-border-color: #ea868f;
}
*,
*::before,
*::after {
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: rgba(0, 0, 0, 0);
}
hr {
margin: 1rem 0;
color: inherit;
border: 0;
border-top: var(--bs-border-width) solid;
opacity: 0.25;
}
h6, h5, h4, h3, h2, h1 {
margin-top: 0;
margin-bottom: 0.5rem;
font-weight: 500;
line-height: 1.2;
color: var(--bs-heading-color);
}
h1 {
font-size: calc(1.375rem + 1.5vw);
}
@media (min-width: 1200px) {
h1 {
font-size: 2.5rem;
}
}
h2 {
font-size: calc(1.325rem + 0.9vw);
}
@media (min-width: 1200px) {
h2 {
font-size: 2rem;
}
}
h3 {
font-size: calc(1.3rem + 0.6vw);
}
@media (min-width: 1200px) {
h3 {
font-size: 1.75rem;
}
}
h4 {
font-size: calc(1.275rem + 0.3vw);
}
@media (min-width: 1200px) {
h4 {
font-size: 1.5rem;
}
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
p {
margin-top: 0;
margin-bottom: 1rem;
}
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-right: 2rem;
}
ol,
ul,
dl {
margin-top: 0;
margin-bottom: 1rem;
}
ol ol,
ul ul,
ol ul,
ul ol {
margin-bottom: 0;
}
dt {
font-weight: 700;
}
dd {
margin-bottom: 0.5rem;
margin-right: 0;
}
blockquote {
margin: 0 0 1rem;
}
b,
strong {
font-weight: bolder;
}
small {
font-size: 0.875em;
}
mark {
padding: 0.1875em;
color: var(--bs-highlight-color);
background-color: var(--bs-highlight-bg);
}
sub,
sup {
position: relative;
font-size: 0.75em;
line-height: 0;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
a {
color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1));
text-decoration: underline;
}
a:hover {
--bs-link-color-rgb: var(--bs-link-hover-color-rgb);
}
a:not([href]):not([class]), a:not([href]):not([class]):hover {
color: inherit;
text-decoration: none;
}
pre,
code,
kbd,
samp {
font-family: var(--bs-font-monospace);
font-size: 1em;
}
pre {
display: block;
margin-top: 0;
margin-bottom: 1rem;
overflow: auto;
font-size: 0.875em;
}
pre code {
font-size: inherit;
color: inherit;
word-break: normal;
}
code {
font-size: 0.875em;
color: var(--bs-code-color);
word-wrap: break-word;
}
a > code {
color: inherit;
}
kbd {
padding: 0.1875rem 0.375rem;
font-size: 0.875em;
color: var(--bs-body-bg);
background-color: var(--bs-body-color);
border-radius: 0.25rem;
}
kbd kbd {
padding: 0;
font-size: 1em;
}
figure {
margin: 0 0 1rem;
}
img,
svg {
vertical-align: middle;
}
table {
caption-side: bottom;
border-collapse: collapse;
}
caption {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: var(--bs-secondary-color);
text-align: right;
}
th {
text-align: inherit;
text-align: -webkit-match-parent;
}
thead,
tbody,
tfoot,
tr,
td,
th {
border-color: inherit;
border-style: solid;
border-width: 0;
}
label {
display: inline-block;
}
button {
border-radius: 0;
}
button:focus:not(:focus-visible) {
outline: 0;
}
input,
button,
select,
optgroup,
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]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator {
display: none !important;
}
button,
[type=button],
[type=reset],
[type=submit] {
-webkit-appearance: button;
}
button:not(:disabled),
[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]: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: right;
width: 100%;
padding: 0;
margin-bottom: 0.5rem;
font-size: calc(1.275rem + 0.3vw);
line-height: inherit;
}
@media (min-width: 1200px) {
legend {
font-size: 1.5rem;
}
}
legend + * {
clear: right;
}
::-webkit-datetime-edit-fields-wrapper,
::-webkit-datetime-edit-text,
::-webkit-datetime-edit-minute,
::-webkit-datetime-edit-hour-field,
::-webkit-datetime-edit-day-field,
::-webkit-datetime-edit-month-field,
::-webkit-datetime-edit-year-field {
padding: 0;
}
::-webkit-inner-spin-button {
height: auto;
}
[type=search] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type="tel"],
[type="url"],
[type="email"],
[type="number"] {
direction: ltr;
}
::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-color-swatch-wrapper {
padding: 0;
}
::-webkit-file-upload-button {
font: inherit;
-webkit-appearance: button;
}
::file-selector-button {
font: inherit;
-webkit-appearance: button;
}
output {
display: inline-block;
}
iframe {
border: 0;
}
summary {
display: list-item;
cursor: pointer;
}
progress {
vertical-align: baseline;
}
[hidden] {
display: none !important;
}
/*# sourceMappingURL=bootstrap-reboot.rtl.css.map */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -0,0 +1,170 @@
.sdk-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.sdk-header {
text-align: center;
margin-bottom: 3rem;
}
.sdk-header h1 {
font-size: 3rem;
margin-bottom: 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sdk-section {
background: rgba(255, 255, 255, 0.05);
border-radius: 1rem;
padding: 2rem;
margin-bottom: 2rem;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.sdk-section h2 {
font-size: 2rem;
margin-bottom: 1rem;
color: #667eea;
}
.sdk-section h3 {
font-size: 1.5rem;
margin-top: 1.5rem;
margin-bottom: 0.5rem;
color: #a78bfa;
}
.code-block {
background: rgba(0, 0, 0, 0.3);
border-radius: 0.5rem;
padding: 1rem;
margin: 1rem 0;
overflow-x: auto;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.code-block code {
color: #a5f3fc;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
white-space: pre;
}
.download-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.download-card {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
border-radius: 1rem;
padding: 1.5rem;
border: 1px solid rgba(102, 126, 234, 0.3);
transition: all 0.3s ease;
text-decoration: none;
display: block;
}
.download-card:hover {
transform: translateY(-4px);
border-color: rgba(102, 126, 234, 0.6);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.2);
}
.download-card h3 {
margin: 0 0 0.5rem 0;
color: #fff;
}
.download-card p {
color: rgba(255, 255, 255, 0.7);
margin: 0.5rem 0;
}
.download-btn {
display: inline-block;
margin-top: 1rem;
padding: 0.75rem 1.5rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 0.5rem;
color: white;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
}
.download-btn:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.steps-list {
counter-reset: step-counter;
list-style: none;
padding-left: 0;
}
.steps-list li {
counter-increment: step-counter;
position: relative;
padding-left: 3rem;
margin-bottom: 1.5rem;
}
.steps-list li::before {
content: counter(step-counter);
position: absolute;
left: 0;
top: 0;
width: 2rem;
height: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
}
.info-box {
background: rgba(102, 126, 234, 0.1);
border-left: 4px solid #667eea;
padding: 1rem;
margin: 1rem 0;
border-radius: 0.5rem;
}
.back-link {
display: inline-block;
margin-top: 2rem;
color: #667eea;
text-decoration: none;
font-size: 1.1rem;
}
.back-link:hover {
color: #764ba2;
}
.sdk-container ul {
color: rgba(255, 255, 255, 0.8);
line-height: 1.8;
}
.sdk-container p {
color: rgba(255, 255, 255, 0.8);
line-height: 1.8;
}
.sdk-container ol {
color: rgba(255, 255, 255, 0.8);
line-height: 1.8;
}

View File

@@ -1,16 +1,26 @@
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
# Copy project file and restore dependencies
COPY BlazorApp/SilverLabs.Website.csproj BlazorApp/
RUN dotnet restore "BlazorApp/SilverLabs.Website.csproj"
# Copy all source files
COPY BlazorApp/ BlazorApp/
# Build and publish the application
WORKDIR /src/BlazorApp
RUN dotnet publish "SilverLabs.Website.csproj" -c Release -o /app/publish
# Runtime stage
FROM nginx:alpine FROM nginx:alpine
# Copy website files to nginx html directory # Copy the published Blazor app to nginx html directory
COPY index.html /usr/share/nginx/html/ COPY --from=build /app/publish/wwwroot /usr/share/nginx/html
COPY styles.css /usr/share/nginx/html/
COPY script.js /usr/share/nginx/html/
COPY logo.png /usr/share/nginx/html/
# Copy SDK directory with templates
COPY sdk/ /usr/share/nginx/html/sdk/
# Copy custom nginx configuration # Copy custom nginx configuration
COPY nginx-site.conf /etc/nginx/conf.d/default.conf COPY nginx-blazor.conf /etc/nginx/conf.d/default.conf
# Expose port 80 # Expose port 80
EXPOSE 80 EXPOSE 80

View File

@@ -1,73 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SilverLabs - Innovation Gateway</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<!-- Loading Screen -->
<div id="loading-screen" class="loading-screen">
<div class="loading-content">
<img src="logo.png" alt="SilverLabs Logo" class="loading-logo">
<div class="loading-spinner"></div>
</div>
</div>
<!-- Main Content -->
<div id="main-content" class="main-content">
<header class="header">
<img src="logo.png" alt="SilverLabs Logo" class="logo">
</header>
<main class="main">
<h1 class="title">Welcome to SilverLabs</h1>
<p class="subtitle">Your Innovation Gateway</p>
<div class="gateway-grid">
<a href="https://helpdesk.silverlabs.uk" class="gateway-card">
<div class="card-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
</div>
<h2 class="card-title">Help Desk</h2>
<p class="card-description">Support & Assistance</p>
</a>
<a href="https://appstore.silverlabs.uk" class="gateway-card">
<div class="card-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7"></rect>
<rect x="14" y="3" width="7" height="7"></rect>
<rect x="14" y="14" width="7" height="7"></rect>
<rect x="3" y="14" width="7" height="7"></rect>
</svg>
</div>
<h2 class="card-title">App Store</h2>
<p class="card-description">Applications & Tools</p>
</a>
<a href="https://cloud.silverlabs.uk" class="gateway-card">
<div class="card-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 10h-1.26A8 8 0 1 0 9 20h9a5 5 0 0 0 0-10z"></path>
</svg>
</div>
<h2 class="card-title">Cloud</h2>
<p class="card-description">Storage & Collaboration</p>
</a>
</div>
</main>
<footer class="footer">
<p>&copy; 2025 SilverLabs. All rights reserved.</p>
</footer>
</div>
<script src="script.js"></script>
</body>
</html>

41
nginx-blazor.conf Normal file
View File

@@ -0,0 +1,41 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_types text/css application/javascript application/json image/jpeg image/png application/wasm;
gzip_min_length 1000;
# Cache static assets
location ~* \.(jpg|jpeg|png|gif|ico|css|js|wasm|json)$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
}
# Blazor framework files - longer cache
location /_framework/ {
expires 1y;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# MIME types for Blazor WebAssembly
types {
application/wasm wasm;
application/octet-stream dll;
application/json json;
}
# Main location - support client-side routing
location / {
try_files $uri $uri/ /index.html;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}

View File

@@ -1,28 +0,0 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Enable gzip compression
gzip on;
gzip_types text/css application/javascript image/jpeg image/png;
gzip_min_length 1000;
# Cache static assets (reduced for development)
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
}
# Main location
location / {
try_files $uri $uri/index.html /index.html;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}

139
script.js
View File

@@ -1,139 +0,0 @@
// Loading screen functionality
document.addEventListener('DOMContentLoaded', function() {
const loadingScreen = document.getElementById('loading-screen');
const mainContent = document.getElementById('main-content');
// Minimum loading time (in milliseconds) to show the loading screen
const minLoadingTime = 2000;
const startTime = Date.now();
// Function to hide loading screen and show main content
function hideLoadingScreen() {
const elapsedTime = Date.now() - startTime;
const remainingTime = Math.max(0, minLoadingTime - elapsedTime);
setTimeout(() => {
loadingScreen.classList.add('fade-out');
mainContent.classList.add('visible');
// Remove loading screen from DOM after transition
setTimeout(() => {
loadingScreen.style.display = 'none';
}, 500);
}, remainingTime);
}
// Hide loading screen when page is fully loaded
if (document.readyState === 'complete') {
hideLoadingScreen();
} else {
window.addEventListener('load', hideLoadingScreen);
}
// Add floating particles animation to background
createFloatingParticles();
});
// Create floating particles for background effect
function createFloatingParticles() {
const mainContent = document.getElementById('main-content');
const particleCount = 20;
for (let i = 0; i < particleCount; i++) {
createParticle(mainContent);
}
}
function createParticle(container) {
const particle = document.createElement('div');
particle.className = 'particle';
// Random size between 2px and 6px
const size = Math.random() * 4 + 2;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
// Random position
particle.style.left = `${Math.random() * 100}%`;
particle.style.top = `${Math.random() * 100}%`;
// Random animation duration between 10s and 30s
const duration = Math.random() * 20 + 10;
particle.style.animationDuration = `${duration}s`;
// Random delay
const delay = Math.random() * 5;
particle.style.animationDelay = `${delay}s`;
// Random opacity
const opacity = Math.random() * 0.3 + 0.1;
particle.style.opacity = opacity;
// Apply styles
particle.style.position = 'absolute';
particle.style.borderRadius = '50%';
particle.style.background = 'rgba(255, 255, 255, 0.5)';
particle.style.pointerEvents = 'none';
particle.style.zIndex = '0';
particle.style.animation = 'float ' + particle.style.animationDuration + ' ease-in-out infinite';
container.appendChild(particle);
}
// Add CSS animation for particles
const style = document.createElement('style');
style.textContent = `
@keyframes float {
0%, 100% {
transform: translate(0, 0) rotate(0deg);
}
25% {
transform: translate(10px, -20px) rotate(90deg);
}
50% {
transform: translate(-15px, -10px) rotate(180deg);
}
75% {
transform: translate(-10px, 20px) rotate(270deg);
}
}
.particle {
filter: blur(1px);
}
`;
document.head.appendChild(style);
// Add smooth scroll behavior for potential internal links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth'
});
}
});
});
// Add interactive hover effect to cards
document.querySelectorAll('.gateway-card').forEach(card => {
card.addEventListener('mousemove', function(e) {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const rotateX = (y - centerY) / 10;
const rotateY = (centerX - x) / 10;
card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) translateY(-10px)`;
});
card.addEventListener('mouseleave', function() {
card.style.transform = '';
});
});

View File

@@ -1,427 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SilverSHELL SDK - SilverLabs</title>
<link rel="stylesheet" href="../styles.css">
<style>
.sdk-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.sdk-header {
text-align: center;
margin-bottom: 3rem;
}
.sdk-header h1 {
font-size: 3rem;
margin-bottom: 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sdk-section {
background: rgba(255, 255, 255, 0.05);
border-radius: 1rem;
padding: 2rem;
margin-bottom: 2rem;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.sdk-section h2 {
font-size: 2rem;
margin-bottom: 1rem;
color: #667eea;
}
.sdk-section h3 {
font-size: 1.5rem;
margin-top: 1.5rem;
margin-bottom: 0.5rem;
color: #a78bfa;
}
.code-block {
background: rgba(0, 0, 0, 0.3);
border-radius: 0.5rem;
padding: 1rem;
margin: 1rem 0;
overflow-x: auto;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.code-block code {
color: #a5f3fc;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
white-space: pre;
}
.download-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.download-card {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
border-radius: 1rem;
padding: 1.5rem;
border: 1px solid rgba(102, 126, 234, 0.3);
transition: all 0.3s ease;
text-decoration: none;
display: block;
}
.download-card:hover {
transform: translateY(-4px);
border-color: rgba(102, 126, 234, 0.6);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.2);
}
.download-card h3 {
margin: 0 0 0.5rem 0;
color: #fff;
}
.download-card p {
color: rgba(255, 255, 255, 0.7);
margin: 0.5rem 0;
}
.download-btn {
display: inline-block;
margin-top: 1rem;
padding: 0.75rem 1.5rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 0.5rem;
color: white;
text-decoration: none;
font-weight: 600;
transition: all 0.3s ease;
}
.download-btn:hover {
transform: scale(1.05);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.steps-list {
counter-reset: step-counter;
list-style: none;
padding-left: 0;
}
.steps-list li {
counter-increment: step-counter;
position: relative;
padding-left: 3rem;
margin-bottom: 1.5rem;
}
.steps-list li::before {
content: counter(step-counter);
position: absolute;
left: 0;
top: 0;
width: 2rem;
height: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: white;
}
.info-box {
background: rgba(102, 126, 234, 0.1);
border-left: 4px solid #667eea;
padding: 1rem;
margin: 1rem 0;
border-radius: 0.5rem;
}
.back-link {
display: inline-block;
margin-top: 2rem;
color: #667eea;
text-decoration: none;
font-size: 1.1rem;
}
.back-link:hover {
color: #764ba2;
}
ul {
color: rgba(255, 255, 255, 0.8);
line-height: 1.8;
}
p {
color: rgba(255, 255, 255, 0.8);
line-height: 1.8;
}
</style>
</head>
<body>
<div id="main-content" class="main-content visible">
<header class="header">
<img src="../logo.png" alt="SilverLabs Logo" class="logo">
</header>
<div class="sdk-container">
<div class="sdk-header">
<h1>SilverSHELL SDK</h1>
<p style="font-size: 1.2rem; color: rgba(255, 255, 255, 0.7);">Build modular Blazor WebAssembly applications with ease</p>
</div>
<!-- Quick Start Section -->
<div class="sdk-section">
<h2>🚀 Quick Start</h2>
<p>Get started with SilverSHELL in just a few minutes. Follow these simple steps:</p>
<ol class="steps-list">
<li>
<strong>Download the templates</strong> (see downloads below)
</li>
<li>
<strong>Extract the templates</strong> to a directory of your choice
</li>
<li>
<strong>Install the templates</strong>
<div class="code-block"><code>dotnet new install ./path/to/SilverSHELL.Starter.Template
dotnet new install ./path/to/SilverSHELL.AppModule.Template</code></div>
</li>
<li>
<strong>Create your first project</strong>
<div class="code-block"><code>dotnet new silvershell-starter -n MyApp --pwa true --module-repository true
cd MyApp
dotnet run</code></div>
</li>
<li>
<strong>Access your application</strong> at <code>https://localhost:5001</code>
</li>
</ol>
</div>
<!-- Downloads Section -->
<div class="sdk-section">
<h2>📦 Downloads</h2>
<p>Download the SilverSHELL project templates to get started:</p>
<div class="download-grid">
<div class="download-card">
<h3>Starter Template</h3>
<p>Create a new SilverSHELL application with minimal configuration</p>
<p><strong>Size:</strong> 6.3 KB</p>
<p><strong>Includes:</strong> Blazor WebAssembly setup, module loading, PWA support</p>
<a href="downloads/silvershell-starter-template.tar.gz" class="download-btn" download>
Download Starter Template
</a>
</div>
<div class="download-card">
<h3>Module Template</h3>
<p>Create reusable SilverSHELL modules with best practices</p>
<p><strong>Size:</strong> 14 KB</p>
<p><strong>Includes:</strong> Module structure, configuration, CI/CD templates</p>
<a href="downloads/silvershell-module-template.tar.gz" class="download-btn" download>
Download Module Template
</a>
</div>
</div>
</div>
<!-- Creating Applications Section -->
<div class="sdk-section">
<h2>🏗️ Creating Applications</h2>
<h3>Using the Starter Template</h3>
<div class="code-block"><code># Create a new application
dotnet new silvershell-starter -n MyAwesomeApp
# With PWA support
dotnet new silvershell-starter -n MyAwesomeApp --pwa true
# With Module Repository integration
dotnet new silvershell-starter -n MyAwesomeApp --module-repository true</code></div>
<h3>Adding Modules</h3>
<p>SilverSHELL supports multiple ways to add modules to your application:</p>
<h3>Option 1: Configuration File</h3>
<p>Edit <code>wwwroot/appsettings.json</code>:</p>
<div class="code-block"><code>{
"AMS": {
"Deployment": {
"PreloadModules": [
"SilverLabs.SilverSHELL.Auth.Login",
"SilverSHELL.Modules.ModuleBrowser"
]
}
}
}</code></div>
<h3>Option 2: Module Browser UI</h3>
<ol>
<li>Navigate to <code>/modules/browse</code> in your application</li>
<li>Search for modules from <strong>library.silverlabs.uk</strong></li>
<li>Click "Install" on any module</li>
<li>Modules are downloaded and installed automatically</li>
</ol>
<h3>Option 3: Manual Installation</h3>
<div class="code-block"><code># Copy module DLLs to the modules directory
cp SomeModule.dll wwwroot/modules/
dotnet run
# Module is automatically discovered and loaded!</code></div>
</div>
<!-- Creating Modules Section -->
<div class="sdk-section">
<h2>🔧 Creating Modules</h2>
<h3>Using the Module Template</h3>
<div class="code-block"><code># Create a basic module
dotnet new silvershell-module -n MyModule
# Create a module with widgets
dotnet new silvershell-module -n MyModule --includeWidgets true
# Create a module with search provider
dotnet new silvershell-module -n MyModule --includeSearchProvider true
# Create a module with everything
dotnet new silvershell-module -n MyModule \
--includeWidgets true \
--includeSearchProvider true \
--includeTests true</code></div>
<h3>Module Structure</h3>
<p>The template creates an organized structure:</p>
<div class="code-block"><code>MyModule/
├── Configuration/
│ ├── ModuleMetadata.cs # Module identity and version
│ ├── EndpointConfiguration.cs # Navigation routes
│ └── WidgetConfiguration.cs # Dashboard widgets
├── Pages/
│ └── Index.razor # Razor pages
├── Components/
│ └── ... # Razor components
├── .gitlab-ci.yml # GitLab CI/CD
├── .github/workflows/
│ └── publish.yml # GitHub Actions
└── MyModuleMain.cs # Module entry point</code></div>
</div>
<!-- Publishing Modules Section -->
<div class="sdk-section">
<h2>🚀 Publishing Modules</h2>
<div class="info-box">
<strong>CI/CD Included!</strong> The module template includes ready-to-use CI/CD pipelines for both GitLab and GitHub.
</div>
<h3>Automated Publishing (Recommended)</h3>
<p>The templates include CI/CD pipelines for automatic publishing:</p>
<ol class="steps-list">
<li>
<strong>Configure CI/CD variables</strong>
<div class="code-block"><code># In GitLab: Settings > CI/CD > Variables
# In GitHub: Settings > Secrets > Actions
# Add variable:
MODULE_REPO_TOKEN: [your token from library.silverlabs.uk]</code></div>
</li>
<li>
<strong>Commit and push your code</strong>
<div class="code-block"><code>git add .
git commit -m "feat: Initial module implementation"
git push</code></div>
</li>
<li>
<strong>Create a release tag</strong>
<div class="code-block"><code>git tag v1.0.0
git push --tags</code></div>
</li>
<li>
<strong>Trigger publish</strong> from your CI/CD pipeline UI
</li>
</ol>
<h3>Manual Publishing</h3>
<div class="code-block"><code># Build and package
dotnet pack --configuration Release -o dist/
# Upload to repository
curl -X POST "https://library.silverlabs.uk/api/modules/publish" \
-F "id=MyModule" \
-F "name=My Awesome Module" \
-F "version=1.0.0" \
-F "description=A great module" \
-F "author=Your Name" \
-F "package=@dist/MyModule.1.0.0.nupkg"</code></div>
</div>
<!-- Available Modules Section -->
<div class="sdk-section">
<h2>📚 Available Modules</h2>
<p>Browse and install modules from the SilverSHELL module repository:</p>
<div class="info-box">
<strong>Module Repository:</strong> <a href="https://library.silverlabs.uk" target="_blank" style="color: #a78bfa;">library.silverlabs.uk</a>
</div>
<h3>Featured Modules:</h3>
<ul>
<li><strong>Auth.Login</strong> - User authentication and login UI</li>
<li><strong>Auth.Registration</strong> - User registration system</li>
<li><strong>Auth.UserManagement</strong> - User administration</li>
<li><strong>Auth.MyAccount</strong> - User profile management</li>
<li><strong>ModuleBrowser</strong> - Browse and install modules from the UI</li>
</ul>
<h3>Explore All Modules</h3>
<div class="code-block"><code># List all available modules via API
curl https://library.silverlabs.uk/api/modules
# Search for specific modules
curl https://library.silverlabs.uk/api/modules/search?q=auth</code></div>
</div>
<!-- Resources Section -->
<div class="sdk-section">
<h2>📖 Resources</h2>
<ul>
<li><strong>Module Repository API:</strong> <a href="https://library.silverlabs.uk/api/modules" target="_blank" style="color: #a78bfa;">https://library.silverlabs.uk/api/modules</a></li>
<li><strong>Demo Application:</strong> <a href="https://demo.silverlabs.uk" target="_blank" style="color: #a78bfa;">https://demo.silverlabs.uk</a></li>
<li><strong>GitLab Repository:</strong> <a href="https://gitlab.silverlabs.uk/silverlabs/silvershell" target="_blank" style="color: #a78bfa;">GitLab</a></li>
</ul>
</div>
<!-- Support Section -->
<div class="sdk-section">
<h2>💬 Support</h2>
<p>Need help? We're here for you:</p>
<ul>
<li><strong>Help Desk:</strong> <a href="https://helpdesk.silverlabs.uk" target="_blank" style="color: #a78bfa;">helpdesk.silverlabs.uk</a></li>
<li><strong>Issues:</strong> <a href="https://gitlab.silverlabs.uk/silverlabs/silvershell/-/issues" target="_blank" style="color: #a78bfa;">GitLab Issues</a></li>
</ul>
</div>
<a href="/" class="back-link">← Back to SilverLabs Home</a>
</div>
</div>
</body>
</html>