Skip to content

Commit d3bab18

Browse files
phmatrayclaude
andcommitted
feat(demo): add GitHub PAT authentication and PR creation support
Add comprehensive GitHub integration allowing users to authenticate with Personal Access Tokens for higher rate limits and enable pull request creation for edited files imported from GitHub repositories. - Add PAT storage in localStorage with validation via Octokit API - Track GitHub metadata (owner, repo, branch, SHA) for imported files - Create GitHubWriteService for fork, branch, commit, and PR operations - Add pending changes system for batching file edits into single PR - Enhance FileViewer with GitHub badge and Create PR button - Add GitHubSettingsDialog for token management and rate limit display - Add CreatePullRequestDialog for reviewing changes and submitting PRs - Add status indicators showing auth state and pending change count Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1cb3df2 commit d3bab18

25 files changed

+2886
-10
lines changed

src/Atypical.VirtualFileSystem.DemoBlazorApp/Atypical.VirtualFileSystem.DemoBlazorApp.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
<ImplicitUsings>enable</ImplicitUsings>
77
</PropertyGroup>
88

9+
<ItemGroup>
10+
<PackageReference Include="Octokit" Version="14.0.0" />
11+
</ItemGroup>
12+
913
<ItemGroup>
1014
<ProjectReference Include="../Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj" />
1115
<ProjectReference Include="../Atypical.VirtualFileSystem.GitHub/Atypical.VirtualFileSystem.GitHub.csproj" />
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
@* GitHub Authentication Status Indicator *@
2+
@inject GitHubAuthService AuthService
3+
@implements IDisposable
4+
5+
<button class="relative flex items-center gap-1.5 px-2 py-1 text-xs rounded-md transition-colors @GetButtonClasses()"
6+
@onclick="OnClick"
7+
title="@GetTooltip()">
8+
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
9+
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.17 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.463-1.11-1.463-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.831.092-.646.35-1.086.636-1.336-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.564 9.564 0 0112 6.844c.85.004 1.705.114 2.504.336 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.743 0 .267.18.579.688.481C19.137 20.167 22 16.42 22 12c0-5.523-4.477-10-10-10z" />
10+
</svg>
11+
@if (AuthService.IsAuthenticated)
12+
{
13+
<span>@AuthService.CurrentCredentials.Username</span>
14+
<span class="w-1.5 h-1.5 bg-green-500 rounded-full"></span>
15+
}
16+
else
17+
{
18+
<span>Anonymous</span>
19+
}
20+
</button>
21+
22+
@code {
23+
[Parameter] public EventCallback OnClick { get; set; }
24+
25+
protected override async Task OnInitializedAsync()
26+
{
27+
AuthService.OnCredentialsChanged += StateHasChanged;
28+
AuthService.OnRateLimitUpdated += StateHasChanged;
29+
await AuthService.InitializeAsync();
30+
}
31+
32+
private string GetButtonClasses()
33+
{
34+
return AuthService.IsAuthenticated
35+
? "bg-green-50 text-green-700 hover:bg-green-100 border border-green-200"
36+
: "bg-gray-100 text-gray-600 hover:bg-gray-200";
37+
}
38+
39+
private string GetTooltip()
40+
{
41+
if (AuthService.IsAuthenticated)
42+
{
43+
return $"GitHub: {AuthService.CurrentCredentials.Username}\n" +
44+
$"Rate Limit: {AuthService.CurrentCredentials.RateLimitDisplay}\n" +
45+
"Click to manage settings";
46+
}
47+
48+
return $"Anonymous (60 requests/hr)\n" +
49+
"Click to sign in with PAT";
50+
}
51+
52+
public void Dispose()
53+
{
54+
AuthService.OnCredentialsChanged -= StateHasChanged;
55+
AuthService.OnRateLimitUpdated -= StateHasChanged;
56+
}
57+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
@* Pending GitHub Changes Indicator *@
2+
@inject GitHubPendingChangesService PendingChangesService
3+
@inject GitHubAuthService AuthService
4+
@implements IDisposable
5+
6+
@if (PendingChangesService.HasPendingChanges)
7+
{
8+
<button class="relative flex items-center gap-2 px-3 py-1.5 text-sm bg-cloud-50 text-cloud-700 rounded-lg hover:bg-cloud-100 transition-colors border border-cloud-200"
9+
@onclick="OpenDialog"
10+
title="@PendingChangesService.PendingChangeCount pending change(s) ready for PR">
11+
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
12+
<path stroke-linecap="round" stroke-linejoin="round" d="M7.217 10.907a2.25 2.25 0 1 0 0 2.186m0-2.186c.18.324.283.696.283 1.093s-.103.77-.283 1.093m0-2.186 9.566-5.314m-9.566 7.5 9.566 5.314m0 0a2.25 2.25 0 1 0 3.935 2.186 2.25 2.25 0 0 0-3.935-2.186Zm0-12.814a2.25 2.25 0 1 0 3.933-2.185 2.25 2.25 0 0 0-3.933 2.185Z" />
13+
</svg>
14+
<span class="font-medium">@PendingChangesService.PendingChangeCount PR</span>
15+
@if (!AuthService.IsAuthenticated)
16+
{
17+
<svg class="w-3.5 h-3.5 text-amber-500" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" title="Sign in to create PR">
18+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" />
19+
</svg>
20+
}
21+
</button>
22+
}
23+
24+
<CreatePullRequestDialog @bind-IsOpen="_showDialog" />
25+
26+
@code {
27+
private bool _showDialog;
28+
29+
protected override void OnInitialized()
30+
{
31+
PendingChangesService.OnPendingChangesChanged += StateHasChanged;
32+
AuthService.OnCredentialsChanged += StateHasChanged;
33+
}
34+
35+
private void OpenDialog()
36+
{
37+
_showDialog = true;
38+
}
39+
40+
public void Dispose()
41+
{
42+
PendingChangesService.OnPendingChangesChanged -= StateHasChanged;
43+
AuthService.OnCredentialsChanged -= StateHasChanged;
44+
}
45+
}

0 commit comments

Comments
 (0)