GitBrowser/Components/Pages/Repo.razor
+50
-34
diff --git a/GitBrowser/Components/Pages/Repo.razor b/GitBrowser/Components/Pages/Repo.razor
index a3e81a6..25e22b9 100644
@@ -10,47 +10,63 @@
</SectionContent>
<div class="container">
<nav class="breadcrumb">
@if (availableBranches.Count > 1)
<nav>
<div class="breadcrumb">
@if (availableBranches.Count > 1)
{
<div class="branch-dropdown">
<input type="checkbox" id="branch-dropdown-toggle" class="branch-dropdown-checkbox" />
<label for="branch-dropdown-toggle" class="dropdown-overlay"></label>
<label for="branch-dropdown-toggle" class="branch-indicator">
<span class="branch-name">@currentBranch</span>
<span class="dropdown-arrow">▼</span>
</label>
<div class="dropdown-list">
@foreach (var branch in availableBranches.Where(b => b != currentBranch))
{
<a href="/@Uri.EscapeDataString(RepoName)/tree/@Uri.EscapeDataString(branch)" class="dropdown-item">@branch</a>
}
</div>
</div>
}
else
{
<span class="branch-indicator">@currentBranch</span>
}
@if (!string.IsNullOrEmpty(currentPath))
{
var pathParts = currentPath.Split('/');
var accumulatedPath = "";
foreach (var part in pathParts)
{
accumulatedPath += (string.IsNullOrEmpty(accumulatedPath) ? "" : "/") + part;
var isLast = accumulatedPath == currentPath;
<span class="separator">/</span>
@if (isLast)
{
<span class="current">@part</span>
}
else
{
<a href="/@Uri.EscapeDataString(RepoName)/tree/@Uri.EscapeDataString(currentBranch)/@accumulatedPath">@part</a>
}
}
}
</div>
@if (!string.IsNullOrEmpty(cloneCommand))
{
<div class="branch-dropdown">
<input type="checkbox" id="branch-dropdown-toggle" class="branch-dropdown-checkbox" />
<label for="branch-dropdown-toggle" class="dropdown-overlay"></label>
<label for="branch-dropdown-toggle" class="branch-indicator">
<span class="branch-name">@currentBranch</span>
<div class="clone-dropdown">
<input type="checkbox" id="clone-dropdown-toggle" class="clone-dropdown-checkbox" />
<label for="clone-dropdown-toggle" class="dropdown-overlay"></label>
<label for="clone-dropdown-toggle" class="branch-indicator">
<span>Clone</span>
<span class="dropdown-arrow">▼</span>
</label>
<div class="dropdown-list">
@foreach (var branch in availableBranches.Where(b => b != currentBranch))
{
<a href="/@Uri.EscapeDataString(RepoName)/tree/@Uri.EscapeDataString(branch)" class="dropdown-item">@branch</a>
}
<input type="text" readonly value="@cloneCommand" />
</div>
</div>
}
else
{
<span class="branch-indicator">@currentBranch</span>
}
@if (!string.IsNullOrEmpty(currentPath))
{
var pathParts = currentPath.Split('/');
var accumulatedPath = "";
foreach (var part in pathParts)
{
accumulatedPath += (string.IsNullOrEmpty(accumulatedPath) ? "" : "/") + part;
var isLast = accumulatedPath == currentPath;
<span class="separator">/</span>
@if (isLast)
{
<span class="current">@part</span>
}
else
{
<a href="/@Uri.EscapeDataString(RepoName)/tree/@Uri.EscapeDataString(currentBranch)/@accumulatedPath">@part</a>
}
}
}
</nav>
@if (!entries.Any() && !isViewingFile)
GitBrowser/Components/Pages/Repo.razor.cs
+13
-1
diff --git a/GitBrowser/Components/Pages/Repo.razor.cs b/GitBrowser/Components/Pages/Repo.razor.cs
index bfcf6ff..46f47d3 100644
@@ -6,13 +6,16 @@ using GitBrowser.Services;
using LibGit2Sharp;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Options;
namespace GitBrowser.Components.Pages;
public sealed partial class Repo(
RepositoryService repositoryService,
IHttpContextAccessor httpContextAccessor,
NavigationManager navigationManager
NavigationManager navigationManager,
IOptions<RepositoryConfiguration> options
) : IDisposable
{
[Parameter]
@@ -24,10 +27,13 @@ public sealed partial class Repo(
[Parameter]
public required string Path { get; init; }
private readonly bool cloningEnabled = options.Value.CloningEnabled;
private TreeEntry[] entries = [];
private string currentBranch = "main";
private string currentPath = "";
private string displayPath = "";
private string? cloneCommand;
private bool isViewingFile = false;
private Stream? fileContent = null;
private string? fileName = null;
@@ -103,6 +109,12 @@ public sealed partial class Repo(
return;
}
if (cloningEnabled && httpContextAccessor.HttpContext?.Request is { } request)
{
cloneCommand =
$"git clone {request.Scheme}{Uri.SchemeDelimiter}{request.Host}{request.PathBase}/{Uri.EscapeDataString(repoInfo.Name)}.git";
}
// Check if the path is a file or directory
if (!string.IsNullOrEmpty(currentPath))
{
GitBrowser/Components/Pages/Repo.razor.css
+44
-8
diff --git a/GitBrowser/Components/Pages/Repo.razor.css b/GitBrowser/Components/Pages/Repo.razor.css
index f5b723a..e88d859 100644
@@ -11,24 +11,39 @@
margin: 2rem 0 1.5rem 0;
}
nav {
display: grid;
grid-template-areas: "breadcrumb clone";
grid-template-columns: 1fr auto;
padding: 0.75rem 0;
margin-bottom: 1rem;
.breadcrumb {
grid-area: breadcrumb;
}
.clone-dropdown {
grid-area: clone;
}
}
.breadcrumb {
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0.75rem 0;
margin-bottom: 1rem;
font-size: 0.875rem;
color: var(--gh-color-fg-default);
}
/* Branch dropdown container */
.branch-dropdown {
.branch-dropdown,
.clone-dropdown {
position: relative;
display: inline-block;
}
/* Hide the checkbox */
.branch-dropdown-checkbox {
.branch-dropdown-checkbox,
.clone-dropdown-checkbox {
display: none;
}
@@ -45,7 +60,8 @@
}
/* Show overlay when dropdown is open */
.branch-dropdown-checkbox:checked ~ .dropdown-overlay {
.branch-dropdown-checkbox:checked ~ .dropdown-overlay,
.clone-dropdown-checkbox:checked ~ .dropdown-overlay {
display: block;
}
@@ -80,7 +96,8 @@ label.branch-indicator:hover {
}
/* Rotate arrow when dropdown is open */
.branch-dropdown-checkbox:checked ~ .branch-indicator .dropdown-arrow {
.branch-dropdown-checkbox:checked ~ .branch-indicator .dropdown-arrow,
.clone-dropdown-checkbox:checked ~ .branch-indicator .dropdown-arrow {
transform: rotate(180deg);
}
@@ -102,14 +119,32 @@ label.branch-indicator:hover {
transform: translateY(-0.5rem);
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s;
}
.clone-dropdown .dropdown-list {
left: unset;
right: 0;
}
/* Show dropdown when checkbox is checked */
.branch-dropdown-checkbox:checked ~ .dropdown-list {
.branch-dropdown-checkbox:checked ~ .dropdown-list,
.clone-dropdown-checkbox:checked ~ .dropdown-list {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.clone-dropdown-checkbox ~ .dropdown-list {
container-type: content-size;
input {
width: 100%;
background: none;
border: none;
color: var(--gh-color-fg-default);
outline: none;
padding: 0.25em 0.5em;
}
}
/* Dropdown items */
.dropdown-item {
display: block;
@@ -269,7 +304,8 @@ tbody td {
border-top: none;
padding: 0.75rem;
display: grid;
grid-template-areas: "name name"
grid-template-areas:
"name name"
"message date";
grid-template-columns: 1fr auto;
}
GitBrowser/RepositoryConfiguration.cs
+2
-0
diff --git a/GitBrowser/RepositoryConfiguration.cs b/GitBrowser/RepositoryConfiguration.cs
index 7395823..694bd81 100644
@@ -8,6 +8,8 @@ public sealed class RepositoryConfiguration
{
[Required]
public required string Path { get; set; }
public bool CloningEnabled { get; set; } = false;
}
[OptionsValidator]
GitBrowser/appsettings.Development.json
+2
-1
diff --git a/GitBrowser/appsettings.Development.json b/GitBrowser/appsettings.Development.json
index ec34a84..aa90ffb 100644
@@ -6,6 +6,7 @@
}
},
"Repository": {
"Path": "../.."
"Path": "../..",
"CloningEnabled": true
}
}