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

@@ -1798,11 +1798,14 @@ function Assert-DebugAppXMatchesLatestPackage([string]$InstallLocation) {
$archive = [IO.Compression.ZipFile]::OpenRead($msix)
try {
$expectedFiles = @(
'AppxManifest.xml',
'Assets/StartupHero.png',
'MBN_STOCK_WEBVIEW.Core.dll',
'MBN_STOCK_WEBVIEW.Infrastructure.dll',
'MBN_STOCK_WEBVIEW.LegacyApplication.dll',
'MBN_STOCK_WEBVIEW.LegacyBridge.dll',
'MBN_STOCK_WEBVIEW.LegacyParityApp.dll',
'Web/Brand/logo.png',
'Web/app.js',
'Web/index.html',
'Web/playout-ui.js',
@@ -1833,12 +1836,19 @@ function Assert-DebugAppXMatchesLatestPackage([string]$InstallLocation) {
$archive.Dispose()
}
$sourceAppJs = Join-Path $projectRoot 'Web\app.js'
$installedAppJs = Join-Path $InstallLocation 'Web\app.js'
if (-not [IO.File]::Exists($sourceAppJs) -or
(Get-FileHash -LiteralPath $sourceAppJs -Algorithm SHA256).Hash -cne
(Get-FileHash -LiteralPath $installedAppJs -Algorithm SHA256).Hash) {
Throw-SafeFailure 'The registered Debug AppX does not contain the current Web application source.'
foreach ($relativeSource in @(
'Web\app.js',
'Web\index.html',
'Web\styles.css',
'Web\workspace-navigation.js')) {
$sourcePath = Join-Path $projectRoot $relativeSource
$installedPath = Join-Path $InstallLocation $relativeSource
if (-not [IO.File]::Exists($sourcePath) -or
-not [IO.File]::Exists($installedPath) -or
(Get-FileHash -LiteralPath $sourcePath -Algorithm SHA256).Hash -cne
(Get-FileHash -LiteralPath $installedPath -Algorithm SHA256).Hash) {
Throw-SafeFailure 'The registered Debug AppX does not contain the current Web application source.'
}
}
}
@@ -3061,6 +3071,60 @@ function Assert-ExactApplicationStillRunning(
}
}
function Wait-StartupIntroDismissed(
[Diagnostics.Process]$Application,
[string]$ExpectedPath) {
Add-Type -AssemblyName UIAutomationClient
Add-Type -AssemblyName UIAutomationTypes
$deadline = [datetime]::UtcNow.AddSeconds(4)
$nameCondition = [Windows.Automation.PropertyCondition]::new(
[Windows.Automation.AutomationElement]::NameProperty,
'매일경제TV V-STOCK 시작 화면')
while ($true) {
Assert-ExactApplicationStillRunning $Application $ExpectedPath
Assert-NoTornadoConnection $Application.Id
$Application.Refresh()
$root = [Windows.Automation.AutomationElement]::FromHandle(
$Application.MainWindowHandle)
if ($null -eq $root) {
Throw-SafeFailure 'The main window is unavailable while waiting for the startup intro.'
}
$intro = $root.FindFirst(
[Windows.Automation.TreeScope]::Descendants,
$nameCondition)
$introIsVisible = $false
if ($null -ne $intro) {
try {
$introIsVisible = -not $intro.Current.IsOffscreen
}
catch [System.Windows.Automation.ElementNotAvailableException] {
$introIsVisible = $false
}
}
if (-not $introIsVisible) {
Assert-ExactApplicationStillRunning $Application $ExpectedPath
Assert-NoTornadoConnection $Application.Id
return
}
if ([datetime]::UtcNow -ge $deadline) {
break
}
$remainingMilliseconds = [int][Math]::Max(
1,
[Math]::Min(50, ($deadline - [datetime]::UtcNow).TotalMilliseconds))
Start-Sleep -Milliseconds $remainingMilliseconds
}
Assert-ExactApplicationStillRunning $Application $ExpectedPath
Assert-NoTornadoConnection $Application.Id
Throw-SafeFailure 'The startup intro remained visible beyond its four-second bound.'
}
function Assert-NoExistingContentDialog([IntPtr]$MainWindowHandle) {
Add-Type -AssemblyName UIAutomationClient
Add-Type -AssemblyName UIAutomationTypes
@@ -4091,6 +4155,9 @@ try {
$script:ApplicationProcess `
([datetime]::UtcNow.AddSeconds($LaunchTimeoutSeconds)))
Assert-NoTornadoConnection $script:ApplicationProcess.Id
Wait-StartupIntroDismissed `
$script:ApplicationProcess `
$registration.Executable
$script:Phase = 'real input harness'
$harnessRun = Invoke-NodeHarnessUnderTcpWatch `