Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9b9eb28
Move GridValue copy button inside a menu
Jul 17, 2024
1aeb0de
add empty dialog
Jul 17, 2024
10f7cd2
Merge branch 'refs/heads/main' into dev/adamint/4412-multiline-string…
Jul 22, 2024
1714da7
test
Jul 22, 2024
eebf7a4
Style dialog, work around fluentui onclick issue for FluentMenuItem
adamint Jul 22, 2024
86c1a5c
Remove hardcoded string
adamint Jul 22, 2024
ce36464
Merge branch 'refs/heads/main' into dev/adamint/4412-multiline-string…
adamint Jul 23, 2024
633250a
Update unformatted name, use JsonSerializer.Serialize, use key compar…
adamint Jul 23, 2024
2b9ebab
Display dialog like in console logs
adamint Jul 23, 2024
fa0b2ea
Format based on language
adamint Jul 29, 2024
7a6336e
stream data
adamint Jul 29, 2024
3afba4c
Add TextVisualizerDialog component tests
adamint Jul 30, 2024
8cbdad8
fix logical error
adamint Jul 30, 2024
987c800
Merge branch 'refs/heads/main' into dev/adamint/4412-multiline-string…
adamint Jul 30, 2024
1c618f2
fix console log css
adamint Jul 30, 2024
fd2dc69
Don't loose comments when formatting JSON
JamesNK Jul 31, 2024
e7ebf22
Merge branch 'refs/heads/main' into dev/adamint/4412-multiline-string…
adamint Jul 31, 2024
1b8d8be
run custom tool after merge
adamint Jul 31, 2024
813cb75
Update ThemeManager to obtain effective theme, conditionally highligh…
adamint Aug 1, 2024
665b30b
remove unnecessary top margin, re-highlight if container content has …
adamint Aug 1, 2024
681789f
Merge branch 'refs/heads/main' into dev/adamint/4412-multiline-string…
adamint Aug 1, 2024
78eac50
Add more tests
adamint Aug 1, 2024
68edbfa
Merge branch 'refs/heads/main' into dev/adamint/4412-multiline-string…
adamint Aug 2, 2024
729889c
start at line 1, move copy button to top of dialog
adamint Aug 2, 2024
4b262ab
Merge branch 'refs/heads/main' into dev/adamint/4412-multiline-string…
adamint Aug 7, 2024
6cd0349
change width/height display of dialog
adamint Aug 7, 2024
1593d0d
Update src/Aspire.Dashboard/Components/Layout/MainLayout.razor.cs
JamesNK Aug 8, 2024
1892bc8
Style fixes
JamesNK Aug 8, 2024
bb2d8d9
Change text visualizer theming from server to client side
JamesNK Aug 8, 2024
d52819f
Move parameter to codebehind, properly disconnect observer
adamint Aug 8, 2024
18cdf6c
Allow users to click the menu item icons to prompt copy/open text vis…
adamint Aug 8, 2024
e29961f
Merge branch 'refs/heads/main' into dev/adamint/4412-multiline-string…
adamint Aug 10, 2024
bdedc1e
set width to 0 on cell menubutton to show as much column content as p…
adamint Aug 10, 2024
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
1 change: 1 addition & 0 deletions src/Aspire.Dashboard/Components/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<link rel="stylesheet" href="_content/Microsoft.FluentUI.AspNetCore.Components/Microsoft.FluentUI.AspNetCore.Components.bundle.scp.css" />
<link rel="stylesheet" href="css/app.css" />
<link rel="stylesheet" href="Aspire.Dashboard.styles.css" />

<HeadOutlet @rendermode="@(new InteractiveServerRenderMode(prerender: false))" />
<!-- Make the body background dark if dark mode will be enabled to prevent a flash of white
before fully rendered. This value is from --neutral-layer-2, but that custom
Expand Down
33 changes: 27 additions & 6 deletions src/Aspire.Dashboard/Components/Controls/GridValue.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@using Aspire.Dashboard.Extensions
@using Aspire.Dashboard.Resources
@inject IStringLocalizer<ControlsStrings> Loc
@inject IStringLocalizer<Dialogs> DialogsLoc

<div class="@GetContainerClass()" style="width: inherit;">
@if (EnableMasking && IsMasked)
Expand Down Expand Up @@ -34,7 +35,7 @@
}

@{
(string, object)[] uncapturedAttributes = [
(string, object)[] uncapturedCopyAttributes = [
("alt", PreCopyToolTip),
("title", string.Empty),
("aria-label", Loc[nameof(ControlsStrings.GridValueCopyToClipboard)]),
Expand All @@ -43,12 +44,32 @@
}

<FluentButton Appearance="Appearance.Lightweight"
Id="@_anchorId"
Id="@_menuAnchorId"
Class="defaultHidden"
Style="float: right; flex-shrink: 0"
AdditionalAttributes="@FluentUIExtensions.GetClipboardCopyAdditionalAttributes(ValueToCopy ?? Value, PreCopyToolTip, PostCopyToolTip, uncapturedAttributes)">
<FluentIcon Class="copy-icon" Style="display:inline;" Icon="Icons.Regular.Size16.Copy" />
<FluentIcon Class="checkmark-icon" Style="display:none;" Icon="Icons.Regular.Size16.Checkmark" />
OnClick="@ToggleMenuOpen">
<FluentIcon Style="display:inline;" Icon="Icons.Regular.Size16.MoreVertical" />
</FluentButton>
<FluentTooltip @ref="_tooltipComponent" Anchor="@_anchorId" Position="TooltipPosition.Top" HideTooltipOnCursorLeave="true">@PreCopyToolTip</FluentTooltip>
</div>

<FluentMenu Anchor="@_menuAnchorId" @bind-Open="_isMenuOpen" VerticalThreshold="170" HorizontalPosition="HorizontalPosition.End">
<FluentMenuItem
Id="@_copyId"
AdditionalAttributes="@FluentUIExtensions.GetClipboardCopyAdditionalAttributes(ValueToCopy ?? Value, PreCopyToolTip, PostCopyToolTip, uncapturedCopyAttributes)">
<span slot="start">
<FluentIcon Class="copy-icon" Style="display:inline; vertical-align: text-bottom" Icon="Icons.Regular.Size16.Copy" Slot="start" />
<FluentIcon Class="checkmark-icon" Style="display:none; vertical-align: text-bottom" Icon="Icons.Regular.Size16.Checkmark" Slot="start" />
</span>
@PreCopyToolTip
</FluentMenuItem>

<FluentMenuItem
Disabled="@(Value is null)"
AdditionalAttributes="@FluentUIExtensions.GetOpenTextVisualizerAdditionalAttributes(Value!, ValueDescription)">
<span slot="start">
<FluentIcon Style="display:inline; vertical-align: text-bottom" Icon="Icons.Regular.Size16.Open" Slot="start"/>
</span>

@DialogsLoc[nameof(Dialogs.OpenInTextVisualizer)]
</FluentMenuItem>
</FluentMenu>
34 changes: 25 additions & 9 deletions src/Aspire.Dashboard/Components/Controls/GridValue.razor.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Aspire.Dashboard.Components.Resize;
using Aspire.Dashboard.Resources;
using Microsoft.AspNetCore.Components;
using Microsoft.FluentUI.AspNetCore.Components;
using Microsoft.JSInterop;

namespace Aspire.Dashboard.Components.Controls;

public partial class GridValue : IDisposable
public partial class GridValue
{
[Parameter, EditorRequired]
public string? Value { get; set; }

[Parameter, EditorRequired]
public required string ValueDescription { get; set; }

/// <summary>
/// Content to include, if any, after the Value string
/// </summary>
Expand Down Expand Up @@ -54,23 +59,34 @@ public partial class GridValue : IDisposable
[Parameter]
public string? ToolTip { get; set; }

[Parameter] public string PreCopyToolTip { get; set; } = null!;
[Parameter]
public string PreCopyToolTip { get; set; } = null!;

[Parameter]
public string PostCopyToolTip { get; set; } = null!;

[Inject]
public required IDialogService DialogService { get; init; }

[Parameter] public string PostCopyToolTip { get; set; } = null!;
[Inject]
public required IJSRuntime JS { get; init; }

[CascadingParameter]
public required ViewportInformation ViewportInformation { get; init; }

private readonly Icon _maskIcon = new Icons.Regular.Size16.EyeOff();
private readonly Icon _unmaskIcon = new Icons.Regular.Size16.Eye();
private readonly string _anchorId = $"copy-{Guid.NewGuid():N}";

private FluentTooltip? _tooltipComponent;
private readonly string _copyId = $"copy-{Guid.NewGuid():N}";
private readonly string _menuAnchorId = $"menu-{Guid.NewGuid():N}";
private bool _isMenuOpen;

protected override void OnInitialized()
{
PreCopyToolTip = Loc[nameof(ControlsStrings.GridValueCopyToClipboard)];
PostCopyToolTip = Loc[nameof(ControlsStrings.GridValueCopied)];
}

private string GetContainerClass() => EnableMasking ? "container masking-enabled wrap" : "container wrap";
private string GetContainerClass() => EnableMasking ? "container masking-enabled" : "container";

private async Task ToggleMaskStateAsync()
=> await IsMaskedChanged.InvokeAsync(!IsMasked);
Expand All @@ -85,8 +101,8 @@ private string TrimLength(string? text)
return text ?? "";
}

public void Dispose()
private void ToggleMenuOpen()
{
_tooltipComponent?.Dispose();
_isMenuOpen = !_isMenuOpen;
}
}
14 changes: 7 additions & 7 deletions src/Aspire.Dashboard/Components/Controls/LogViewer.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
@inject IJSRuntime JS
@implements IAsyncDisposable

<div class="log-overflow continuous-scroll-overflow">
<div class="log-container" id="logContainer">
<div class="log-overflow console-overflow continuous-scroll-overflow">
<div class="log-container console-container" id="logContainer">
<Virtualize Items="@LogEntries.GetEntries()" ItemSize="20" OverscanCount="100" TItem="LogEntry">
<div class="line-row-container">
<div class="line-row">
<span class="line-area" role="log">
<span class="line-number">@context.LineNumber</span>
<span class="content">
<div class="log-line-row-container">
<div class="log-line-row console-line-row">
<span class="log-line-area" role="log">
<span class="log-line-number">@context.LineNumber</span>
<span class="log-content">
@if (context.Timestamp is { } timestamp)
{
<span class="timestamp">@GetDisplayTimestamp(timestamp)</span>
Expand Down
102 changes: 9 additions & 93 deletions src/Aspire.Dashboard/Components/Controls/LogViewer.razor.css
Original file line number Diff line number Diff line change
@@ -1,117 +1,33 @@
.log-overflow {
--console-theme-black: #0C0C0C;
--console-theme-blue: #0037DA;
--console-theme-cyan: #3A96DD;
--console-theme-green: #13A10E;
--console-theme-magenta: #CD13E8;
--console-theme-bg-red: #F80F24;
--console-theme-fg-red: #C50F1F;
--console-theme-white: #CCCCCC;
--console-theme-yellow: #C19C00;

--console-theme-bright-black: #767676;
--console-theme-bright-blue: #3B78FF;
--console-theme-bright-cyan: #61D6D6;
--console-theme-bright-green: #16C60C;
--console-theme-bright-magenta: #DA01FA;
--console-theme-bright-red: #E74856;
--console-theme-bright-white: #F2F2F2;
--console-theme-bright-yellow: #F9F1A5;

--line-number-color: #848484;
--timestamp-color: var(--line-number-color);
--console-background-color: var(--console-theme-black);
--console-background-color-hover: #1D1D1D;
--console-font-color: var(--console-theme-white);

height: 100%;
width: 100%;
overflow: auto;
background-color: var(--console-background-color);
}

::deep .log-container {
::deep .console-container {
background: var(--console-background-color);
color: var(--console-font-color);
font-family: 'Cascadia Mono', Consolas, monospace;
font-size: 13px;
margin: 16px 0 0 0;
padding-bottom: 24px;
line-height: 20px;
overflow: visible;
display: flex;
flex-direction: column;
width: 100%;
}

::deep .line-row-container {
width: 100%;
overflow: hidden;
}

::deep .line-row {
cursor: text;
padding: 0 12px 0 12px;
white-space: pre-wrap;

display: flex;
flex-direction: row;
flex-grow: 1;
}

::deep .line-row:hover {
::deep .console-line-row:hover {
background-color: var(--console-background-color-hover);
}

::deep .line-area {
flex-grow: 1;
justify-content: flex-start;
display: flex;
flex-direction: row;
}

::deep .line-number {
padding-left: 10px;
min-width: 43px;
text-align: right;
align-self: flex-start;
flex-shrink: 0;
color: var(--line-number-color);
user-select: none;
cursor: default;
}

::deep .content {
word-break: break-all;
margin-left: 20px;
position: relative;
margin-right: 6px;
user-select: text;
cursor: text;
white-space: pre-wrap;
overflow-wrap: anywhere;
}

::deep .content .timestamp {
::deep .log-content .timestamp {
color: var(--timestamp-color);
}

::deep .content a,
::deep .content a:active,
::deep .content a:visited {
::deep .log-content a,
::deep .log-content a:active,
::deep .log-content a:visited {
color: var(--console-font-color);
text-decoration: none;
}

::deep .content a:hover {
::deep .log-content a:hover {
text-decoration: underline;
}

::deep .content > fluent-badge {
::deep .log-content > fluent-badge {
user-select: none;
}

::deep .content > .timestamp + fluent-badge {
::deep .log-content > .timestamp + fluent-badge {
margin-left: 1ch;
}

Expand Down
13 changes: 9 additions & 4 deletions src/Aspire.Dashboard/Components/Controls/PropertyGrid.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,17 @@
GridTemplateColumns="@GridTemplateColumns"
ShowHover="true">
<TemplateColumn Title="@(NameColumnTitle ?? Loc[nameof(ControlsStrings.NameColumnHeader)])" Class="nameColumn" SortBy="@NameSort" Sortable="@IsNameSortable">
<GridValue Value="@NameColumnValue(context)" HighlightText="@HighlightText" />
<GridValue
ValueDescription="@(NameColumnTitle ?? Loc[nameof(ControlsStrings.NameColumnHeader)])"
Value="@NameColumnValue(context)"
HighlightText="@HighlightText" />
</TemplateColumn>
<TemplateColumn Title="@(ValueColumnTitle ?? Loc[nameof(ControlsStrings.PropertyGridValueColumnHeader)])" Class="valueColumn" SortBy="@ValueSort" Sortable="@IsValueSortable">
<GridValue Value="@ValueColumnValue(context)" HighlightText="@HighlightText"
EnableMasking="@EnableValueMasking" IsMasked="@GetIsItemMasked(context)"
IsMaskedChanged="(newValue) => OnIsMaskedChanged(context, newValue)" />
<GridValue
ValueDescription="@(ValueColumnTitle ?? Loc[nameof(ControlsStrings.PropertyGridValueColumnHeader)])"
Value="@ValueColumnValue(context)" HighlightText="@HighlightText"
EnableMasking="@EnableValueMasking" IsMasked="@GetIsItemMasked(context)"
IsMaskedChanged="(newValue) => OnIsMaskedChanged(context, newValue)"/>
@ExtraValueContent(context)
</TemplateColumn>
</FluentDataGrid>
Expand Down
17 changes: 13 additions & 4 deletions src/Aspire.Dashboard/Components/Controls/ResourceDetails.razor
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,16 @@
GridTemplateColumns="1fr 1.5fr"
ShowHover="true">
<TemplateColumn Title="@ControlStringsLoc[nameof(ControlsStrings.NameColumnHeader)]" Class="nameColumn">
<GridValue Value="@(context.KnownProperty?.DisplayName ?? context.Key)" ToolTip="@context.Key" />
<GridValue
Value="@(context.KnownProperty?.DisplayName ?? context.Key)"
ValueDescription="@ControlStringsLoc[nameof(ControlsStrings.NameColumnHeader)]"
ToolTip="@context.Key" />
</TemplateColumn>
<TemplateColumn Title="@ControlStringsLoc[nameof(ControlsStrings.PropertyGridValueColumnHeader)]" Class="valueColumn">
<GridValue Value="@GetDisplayedValue(TimeProvider, context)" ToolTip="@context.Tooltip" />
<GridValue
Value="@GetDisplayedValue(TimeProvider, context)"
ValueDescription="@(context.KnownProperty?.DisplayName ?? context.Key)"
ToolTip="@context.Tooltip" />
</TemplateColumn>
</FluentDataGrid>
</FluentAccordionItem>
Expand All @@ -70,10 +76,13 @@
GridTemplateColumns="1fr 1.5fr"
ShowHover="true">
<TemplateColumn Title="@ControlStringsLoc[nameof(ControlsStrings.NameColumnHeader)]" Class="nameColumn">
<GridValue Value="@context.Name" />
<GridValue Value="@context.Name" ValueDescription="@ControlStringsLoc[nameof(ControlsStrings.NameColumnHeader)]" />
</TemplateColumn>
<TemplateColumn Title="@ControlStringsLoc[nameof(ControlsStrings.PropertyGridValueColumnHeader)]" Class="valueColumn">
<GridValue Value="@context.Text" MaxDisplayLength="0">
<GridValue
Value="@context.Text"
ValueDescription="@context.Name"
MaxDisplayLength="0">
<ContentAfterValue>
@if (context.Url != null)
{
Expand Down
Loading