Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@
}
},
"forwardPorts": [
2137,
2053,
2227,
4298,
2169,
2232,
2160,
4097,
5000,
5174
5215
],
"features": {
"ghcr.io/devcontainers/features/python": {},
Expand Down
1 change: 1 addition & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[*]
indent_style = space
indent_size = 4
spelling_exclusion_path = vs-spell.dic
# Code files
[*.{cs,csx,vb,vbx}]
insert_final_newline = true
Expand Down
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Carefully analyze the user's prompt. Identify the core objectives, whether it is
Before writing code, investigate thoroughly.
* If the user provides a **URL**, you **MUST** use the `fetch` tool to retrieve its content.
* If the user provides a **git commit id/hash**, you **MUST** run the `git --no-pager show <commit-id>` command to retrieve its details.
* If the user taked about current changes in the codebase, you **MUST** run the `git --no-pager diff` and `git --no-pager diff --staged` commands to see the differences.
* If the user talked about current changes in the codebase, you **MUST** run the `git --no-pager diff` and `git --no-pager diff --staged` commands to see the differences.
* For UI-related tasks, you **MUST** first ask `DeepWiki`: *"What features does BitPlatform offer to help me complete this task? [USER'S ORIGINAL REQUEST]"*
* For anything related to `Bit.BlazorUI`, `bit Bswup`, `bit Butil`, `bit Besql`, or the bit project template, you **MUST** use the `DeepWiki_ask_question` tool with repository `bitfoundation/bitplatform` to find relevant information.
* For mapper/mapping entity/dto related tasks, you **MUST** use the `DeepWiki_ask_question` tool with repository `riok/mapperly` to find correct implementation and usage patterns focusing on its static classes and extension methods approach.
Expand Down
3 changes: 1 addition & 2 deletions .github/prompts/scaffold.prompt.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
```prompt
# Scaffold Complete Entity with Full CRUD

You are an expert at scaffolding complete entity implementations for the BitPlatform bit.templateplayground project.
You are an expert at scaffolding complete entity implementations for the project.

## Instructions

Expand Down
22 changes: 13 additions & 9 deletions .github/workflows/cd-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ on:
required: true
type: string

env:
DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: true

permissions:
contents: read

Expand All @@ -19,10 +22,10 @@ jobs:
steps:

- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json

Expand All @@ -42,6 +45,7 @@ jobs:
env:
WebAppRender.BlazorMode: 'BlazorWebAssembly'
ServerAddress: ${{ vars.SERVER_ADDRESS }}
AdsPushVapid.PublicKey: ${{ secrets.PUBLIC_VAPIDKEY }}

- name: Install wasm
run: cd src && dotnet workload install wasm-tools
Expand Down Expand Up @@ -71,7 +75,7 @@ jobs:
steps:

- name: Retrieve server bundle
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: server-bundle

Expand All @@ -97,10 +101,10 @@ jobs:
steps:

- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json

Expand Down Expand Up @@ -144,10 +148,10 @@ jobs:

steps:
- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json

Expand Down Expand Up @@ -202,10 +206,10 @@ jobs:
steps:

- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json

Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
workflow_dispatch:
pull_request:

env:
DOTNET_SYSTEM_CONSOLE_ALLOW_ANSI_COLOR_REDIRECTION: true

jobs:

build_blazor_server:
Expand All @@ -15,10 +18,10 @@ jobs:
steps:

- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v5

- name: Setup .NET
uses: actions/setup-dotnet@v4
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json

Expand Down
4 changes: 4 additions & 0 deletions Bit.ResxTranslator.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

"ResxPaths": [ "/src/**/*.resx" ],

"ChatOptions": {
"Temperature": "0"
},

"OpenAI": {
"Model": "gpt-4.1-mini",
"Endpoint": "https://models.inference.ai.azure.com",
Expand Down
1 change: 1 addition & 0 deletions Bit.TemplatePlayground.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".SolutionItems", ".Solution
src\Directory.Build.props = src\Directory.Build.props
src\Directory.Packages.props = src\Directory.Packages.props
global.json = global.json
vs-spell.dic = vs-spell.dic
.vscode\mcp.json=.vscode\mcp.json
README.md = README.md
EndProjectSection
Expand Down
12 changes: 1 addition & 11 deletions Bit.TemplatePlayground.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -10,42 +10,32 @@
<File Path=".vsconfig" />
<File Path="Clean.bat" />
<File Path="global.json" />
<File Path="vs-spell.dic" />
<File Path=".vscode/mcp.json" />
<File Path="README.md" />
<File Path="settings.VisualStudio.json" />
<File Path="Bit.ResxTranslator.json" />
<File Path="src/Directory.Build.props" />
<File Path="src/Directory.Packages.props" />
</Folder>
<!--#if (pipeline == "Azure")-->
<Folder Name="/.SolutionItems/.azure-devops/" />
<Folder Name="/.SolutionItems/.azure-devops/workflows/">
<File Path=".azure-devops/workflows/cd.yml" />
<File Path=".azure-devops/workflows/ci.yml" />
</Folder>
<!--#endif-->
<Folder Name="/.SolutionItems/.github/">
<File Path=".github/copilot-instructions.md" />
</Folder>
<Folder Name="/.SolutionItems/.github/prompts/">
<File Path=".github/prompts/resx.prompt.md" />
<File Path=".github/prompts/scaffold.prompt.md" />
</Folder>
<!--#if (pipeline == "GitHub")-->
<Folder Name="/.SolutionItems/.github/workflows/">
<File Path=".github/workflows/cd-production.yml" />
<File Path=".github/workflows/cd-test.yml" />
<File Path=".github/workflows/cd-template.yml" />
<File Path=".github/workflows/ci.yml" />
</Folder>
<!--#endif-->
<Folder Name="/Server/">
<Project Path="src/Server/Bit.TemplatePlayground.Server.Web/Bit.TemplatePlayground.Server.Web.csproj" />
<Project Path="src/Server/Bit.TemplatePlayground.Server.Api/Bit.TemplatePlayground.Server.Api.csproj" />
<Project Path="src/Server/Bit.TemplatePlayground.Server.Shared/Bit.TemplatePlayground.Server.Shared.csproj" />
<!--#if (aspire == true)-->
<Project Path="src/Server/Bit.TemplatePlayground.Server.AppHost/Bit.TemplatePlayground.Server.AppHost.csproj" />
<!--#endif-->
</Folder>
<Folder Name="/Client/">
<Project Path="src/Client/Bit.TemplatePlayground.Client.Core/Bit.TemplatePlayground.Client.Core.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@

<PackageReference Include="Bit.Butil" />
<PackageReference Include="Bit.BlazorUI" />
<PackageReference Include="Bit.BlazorES2019" />
<PackageReference Include="Bit.BlazorUI.Icons" />
<PackageReference Include="Bit.BlazorUI.Assets" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" />
<PackageReference Include="Bit.BlazorUI.Extras" />
<PackageReference Include="Bit.BlazorUI.Extras" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public partial class AppClientCoordinator : AppComponentBase
[AutoInject] private ILogger<Navigator> navigatorLogger = default!;
[AutoInject] private ILogger<AppClientCoordinator> logger = default!;
[AutoInject] private IBitDeviceCoordinator bitDeviceCoordinator = default!;
[AutoInject] private IPushNotificationService pushNotificationService = default!;

private Action? unsubscribe;

Expand Down Expand Up @@ -113,6 +114,7 @@ public async Task PropagateAuthState(bool firstRun, Task<AuthenticationState> ta

await EnsureSignalRStarted();

await pushNotificationService.Subscribe(CurrentCancellationToken);

if (isAuthenticated)
{
Expand Down Expand Up @@ -225,9 +227,16 @@ private async Task HubConnectionStateChange(Exception? exception)
{
logger.LogWarning(exception, "SignalR connection lost.");

if (exception is HubException && exception.Message.EndsWith(nameof(AppStrings.UnauthorizedException)))
if (exception is HubException)
{
await AuthManager.RefreshToken(requestedBy: nameof(HubException));
if (exception.Message.EndsWith(nameof(AppStrings.UnauthorizedException)))
{
await AuthManager.RefreshToken(requestedBy: nameof(HubException));
}
else if (exception.Message.EndsWith(nameof(AppStrings.ForceUpdateTitle)))
{
PubSubService.Publish(ClientPubSubMessages.FORCE_UPDATE);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@
<BitButton Float
Draggable
FloatOffset="2rem"
Size="BitSize.Large"
Class="open-panel-button"
Color="BitColor.Secondary"
Variant="BitVariant.Outline"
OnClick="() => isOpen = true"
Color="BitColor.PrimaryBackground"
FloatPosition="BitPosition.BottomRight"
IconUrl="@(CurrentTheme.HasValue ? $"_content/Bit.TemplatePlayground.Client.Core/images/icons/ai-icon-{CurrentTheme?.ToString().ToLowerInvariant()}.png" : null)" />
IconUrl="_content/Bit.TemplatePlayground.Client.Core/images/icons/ai-chat-icon-64.webp" />

<BitProPanel ShowCloseButton
@bind-IsOpen="isOpen"
ModeFull="isSmallScreen is true"
Modeless="isSmallScreen is false"
OnDismiss="WrapHandled(HandleOnDismissPanel)">
OnDismiss="WrapHandled(HandleOnDismissPanel)"
Classes="@(new() { Container = "panel-cnt" })">
<Header>
<BitStack Horizontal Alignment="BitAlignment.Center">
<BitText Typography="BitTypography.H5">@Localizer[nameof(AppStrings.AiChatPanelTitle)]</BitText>
Expand Down Expand Up @@ -99,6 +99,24 @@
</BitStack>
}

@if (followUpSuggestions.Any())
{
<BitStack Alignment=" BitAlignment.Center" FitHeight FillContent Class="default-prompt-container">

@foreach (var suggestion in followUpSuggestions)
{
<BitButton FixedColor
Variant="BitVariant.Outline"
Class="default-prompt-button"
Color="BitColor.SecondaryBackground"
OnClick="() => SendPromptMessage(suggestion)">
@suggestion
</BitButton>
}

</BitStack>
}

<BitStack FitHeight Style="position:relative">
<BitTextField Rows="1"
Immediate
Expand All @@ -110,7 +128,7 @@
@bind-Value="@userInput"
OnEnter="WrapHandled((KeyboardEventArgs e) => HandleOnUserInputEnter(e))"
Styles="@(new() { FieldGroup = "padding:0.5rem; padding-inline-end:2.5rem", Input = "min-height:unset" })"
Placeholder="@(Localizer[isLoading ? nameof(AppStrings.AiChatPanelCommunicatingPlaceholder) : nameof(AppStrings.AiChatPanelPlaceholder)])" />
Placeholder="@(Localizer[nameof(AppStrings.AiChatPanelPlaceholder)])" />

<BitButton Float
AutoLoading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public partial class AppAiChatPanel
private Channel<string>? channel;
private AiChatMessage? lastAssistantMessage;
private List<AiChatMessage> chatMessages = []; // TODO: Persist these values in client-side storage to retain them across app restarts.
private List<string> followUpSuggestions = [];


protected override Task OnInitAsync()
Expand Down Expand Up @@ -50,7 +51,9 @@ private async Task HubConnection_Reconnected(string? _)

private async Task SendPromptMessage(string message)
{
followUpSuggestions = [];
userInput = message;
StateHasChanged();
await SendMessage();
}

Expand Down Expand Up @@ -86,6 +89,7 @@ private void SetDefaultValues()
{
isLoading = false;
responseCounter = 0;
followUpSuggestions = [];
lastAssistantMessage = new() { Role = AiChatMessageRole.Assistant };
chatMessages = [
new()
Expand Down Expand Up @@ -125,25 +129,32 @@ private async Task StartChannel()
{
int expectedResponsesCount = chatMessages.Count(c => c.Role is AiChatMessageRole.User);

if (response is SharedChatProcessMessages.MESSAGE_RPOCESS_SUCESS)
if (response.Contains(nameof(AiChatFollowUpList.FollowUpSuggestions)))
{
responseCounter++;
isLoading = false;
followUpSuggestions = JsonSerializer.Deserialize<AiChatFollowUpList>(response)?.FollowUpSuggestions ?? [];
}
else if (response is SharedChatProcessMessages.MESSAGE_RPOCESS_ERROR)
else
{
responseCounter++;
if (responseCounter == expectedResponsesCount)
if (response is SharedChatProcessMessages.MESSAGE_RPOCESS_SUCESS)
{
isLoading = false; // Hide loading only if this is an error for the last user's message.
responseCounter++;
isLoading = false;
}
chatMessages[responseCounter * 2].Successful = false;
}
else
{
if ((responseCounter + 1) == expectedResponsesCount)
else if (response is SharedChatProcessMessages.MESSAGE_RPOCESS_ERROR)
{
responseCounter++;
if (responseCounter == expectedResponsesCount)
{
isLoading = false; // Hide loading only if this is an error for the last user's message.
}
chatMessages[responseCounter * 2].Successful = false;
}
else
{
lastAssistantMessage!.Content += response;
if ((responseCounter + 1) == expectedResponsesCount)
{
lastAssistantMessage!.Content += response;
}
}
}

Expand Down
Loading
Loading