feat: refine branded operator experience

Add the MBN-branded startup intro, semantic sidebar icons, and refined card-based operator visuals. Strengthen packaged UI and input smoke coverage for the updated experience.
This commit is contained in:
2026-07-24 01:26:13 +09:00
parent f43483533a
commit b050f2f06c
25 changed files with 2316 additions and 164 deletions

View File

@@ -301,9 +301,15 @@ public sealed class LegacyWorkspaceLayoutContractTests
public void WorkspaceHeaderUsesModernSemanticLockupAndStableNativeStatusTarget()
{
Assert.Matches(
@"class=""brand""[^>]*role=""img""[^>]*aria-label=""매일경제TV VRi""[\s\S]*?" +
@"class=""brand-symbol""[\s\S]*?<svg",
@"class=""brand""[\s\S]*?class=""brand-wordmark-plate""[\s\S]*?" +
@"<img class=""brand-wordmark"" src=""Brand/logo\.png"" width=""176"" height=""43""[\s\S]*?" +
@"alt=""매일경제TV"" draggable=""false"">[\s\S]*?" +
@"class=""brand-divider"" aria-hidden=""true""[\s\S]*?" +
@"class=""brand-product"">V-STOCK</span>",
Markup);
Assert.DoesNotContain("class=\"brand-symbol\"", Markup, StringComparison.Ordinal);
Assert.DoesNotContain("class=\"brand-lockup\"", Markup, StringComparison.Ordinal);
Assert.DoesNotContain(">VRi<", Markup, StringComparison.Ordinal);
Assert.Matches(
@"class=""workspace-heading""[\s\S]*?class=""workspace-heading-icon""" +
@"[\s\S]*?<div class=""workspace-heading-copy"">[\s\S]*?" +
@@ -491,6 +497,70 @@ public sealed class LegacyWorkspaceLayoutContractTests
Navigation);
}
[Fact]
public void VisualCardModeReflowsOnlyGraphicActionsAndKeepsNativeIntentButtons()
{
var viewModes = Slice(
Styles,
"/* View modes affect graphic actions only.",
"/* End view-mode presentation. */");
Assert.Contains("[data-visual-card-list=\"true\"]", viewModes,
StringComparison.Ordinal);
Assert.Contains(
"@container operator-workspace (min-width: 520px)",
viewModes,
StringComparison.Ordinal);
Assert.Contains("grid-template-columns: repeat(2, minmax(0, 1fr))", viewModes,
StringComparison.Ordinal);
Assert.Contains("@container operator-workspace (min-width: 820px)", viewModes,
StringComparison.Ordinal);
Assert.Contains("grid-template-columns: repeat(3, minmax(0, 1fr))", viewModes,
StringComparison.Ordinal);
Assert.Contains("button.visual-card", viewModes, StringComparison.Ordinal);
Assert.Contains("min-height: 62px", viewModes, StringComparison.Ordinal);
Assert.DoesNotContain("grid-auto-rows: minmax(62px", viewModes,
StringComparison.Ordinal);
Assert.DoesNotContain(".visual-card-preview", viewModes,
StringComparison.Ordinal);
Assert.DoesNotContain(".visual-card-kicker", viewModes,
StringComparison.Ordinal);
Assert.DoesNotContain(".visual-card-state", viewModes,
StringComparison.Ordinal);
Assert.Matches(@"\.comparison-actions\s*\{\s*display:\s*block;", viewModes);
Assert.Contains(
"summary[data-tree-root].tree-node-selected",
viewModes,
StringComparison.Ordinal);
Assert.Contains("@container operator-workspace (max-width: 759px)", viewModes,
StringComparison.Ordinal);
Assert.DoesNotContain(".stock-result", viewModes, StringComparison.Ordinal);
Assert.DoesNotContain(".cut-row", viewModes, StringComparison.Ordinal);
Assert.DoesNotContain(".playlist-data-row", viewModes, StringComparison.Ordinal);
Assert.DoesNotContain(".operator-catalog-draft", viewModes, StringComparison.Ordinal);
Assert.DoesNotContain(".overseas-period-actions", viewModes, StringComparison.Ordinal);
Assert.Matches(@"\.industry-action-tree\s*\{\s*overflow:\s*auto;", viewModes);
Assert.Contains("function setVisualCardContent(button, options)", App,
StringComparison.Ordinal);
Assert.Contains("button.dataset.actionId = action.id", App,
StringComparison.Ordinal);
Assert.Contains("button.dataset.industryActionId = action.actionId", App,
StringComparison.Ordinal);
Assert.Contains("button.dataset.comparisonActionId = action.actionId", App,
StringComparison.Ordinal);
Assert.Contains("button.dataset.themeActionId = action.actionId", App,
StringComparison.Ordinal);
Assert.Contains("button.dataset.expertActionId = action.actionId", App,
StringComparison.Ordinal);
Assert.Contains("button.dataset.haltActionId = action.actionId", App,
StringComparison.Ordinal);
Assert.Matches(
@"button\.dataset\.overseasActionId = action\.actionId;\s*" +
@"button\.textContent = label;",
App);
}
private static string Slice(string source, string startMarker, string endMarker)
{
var start = source.IndexOf(startMarker, StringComparison.Ordinal);