fix: apply direct runtime paths and respect taskbar

This commit is contained in:
2026-07-28 00:35:47 +09:00
parent e3b174eec7
commit 347900701b
16 changed files with 522 additions and 401 deletions

View File

@@ -126,7 +126,7 @@ public sealed partial class FirstRunSetupWindow : Window
SetSetupRunning(true);
SetupStatusBar.Title = "설정 중";
SetupStatusBar.Message =
"자산·DB 설정·K3D를 검증하고 있습니다. 완료될 때까지 Visual Studio 실행을 중지하지 마세요.";
"선택한 경로와 DB 설정·K3D를 적용하고 있습니다. 완료될 때까지 Visual Studio 실행을 중지하지 마세요.";
SetupStatusBar.Severity = InfoBarSeverity.Informational;
try
@@ -253,7 +253,7 @@ public sealed partial class FirstRunSetupWindow : Window
InstallButton.IsEnabled = true;
SetupStatusBar.Title = "준비 완료";
SetupStatusBar.Message =
"설정 시작을 누르면 검증된 파일만 이 PC의 로컬 실행 영역에 구성합니다.";
"설정 시작을 누르면 선택한 Cuts/Res 경로를 이 PC에 적용합니다.";
SetupStatusBar.Severity = InfoBarSeverity.Success;
}
@@ -302,6 +302,13 @@ public sealed partial class FirstRunSetupWindow : Window
return false;
}
if (cutsParent.IndexOfAny(['$', '%', '@', ';', '\'']) >= 0)
{
warningMessage =
"선택한 폴더 경로에 설정용 예약 문자가 포함되어 있습니다. 다른 위치의 Cuts/Res를 선택해 주세요.";
return false;
}
runtimeRoot = cutsParent;
return true;
}

View File

@@ -31,9 +31,9 @@
-->
<SourceRepositoryRoot>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..\..'))</SourceRepositoryRoot>
<!--
Keep the legacy runtime assets outside this repository while reproducing the
original executable-folder layout. A fresh source checkout always builds the
safe source-only first-run app until its generated local binding exists.
Keep the legacy runtime assets outside this repository. A fresh source
checkout always builds the safe source-only first-run app until its
generated local path binding exists.
Supplying an explicit root or creating a Release MSIX makes the assets
required unless the caller explicitly selects Auto mode:
/p:LegacyRuntimeSourceRoot="D:\path\to\MBN_STOCK_N\bin\Debug"
@@ -56,9 +56,9 @@
<LegacyRuntimeAssetsEnabled
Condition="'$(LegacyRuntimeAssetsEnabled)' == ''">false</LegacyRuntimeAssetsEnabled>
<!--
Never share an executable directory with a previous full-runtime build.
Otherwise omitted Cuts/Res (including the local DB INI) could remain stale
and be consumed by a nominal source-only launch.
Never share an executable directory with a previous runtime-enabled build.
Otherwise omitted Res content (including a historical local DB INI) could
remain stale and be consumed by a nominal source-only launch.
-->
<BaseOutputPath
Condition="'$(LegacyRuntimeAssetsEnabled)' != 'true'">bin\SourceOnly\</BaseOutputPath>
@@ -83,7 +83,7 @@
</ItemGroup>
<ItemGroup>
<!-- Closed runtime contract: every active alias and non-external scene asset must exist. -->
<!-- Runtime coverage metadata. Individual Cuts files are resolved on demand. -->
<LegacyRequiredScene Include="5001;N5001;5006;5011;5016;50160;5023;5024;5025;5026;5029;8018;8032;5032;5037;5074;5076;5077;5078;5079;5080;5081;5082;5083;5084;5085;5086;50860;5087;5088;6001;6067;8001;8002;8003;8035;8061;8040;8046;8051;8056;8067;5068;5070;5072" />
<LegacyRequiredBuiltInAsset Include="images\주유기merge.png;images\35752913_l.jpg;Images\그림_빨강.png;Images\그림_검정.png;Images\그림_파랑.png;Images\프리마켓.png;Images\애프터마켓.png;Images\KRX.png;Images\NXT.png" />
</ItemGroup>
@@ -162,13 +162,6 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="$(LegacyCutsSourceRoot)\**\*"
Condition="'$(LegacyRuntimeAssetsEnabled)' == 'true'">
<Link>Cuts\%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Cuts\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Include="@(LegacyPackagedResAsset)"
Condition="'$(LegacyRuntimeAssetsEnabled)' == 'true'">
<Link>Res\%(Filename)%(Extension)</Link>
@@ -209,7 +202,7 @@
Text="Legacy runtime assets were not found. Building the safe first-run setup app. Start Debug x64 and select the existing Cuts and Res folders." />
<Message Condition="'$(LegacyRuntimeAssetsEnabled)' == 'true'"
Importance="high"
Text="Verified full legacy runtime assets are enabled. Database credentials remain in the current user's LocalAppData overlay." />
Text="External legacy runtime paths are enabled. Cuts files remain in the selected folder and database credentials remain in the current user's LocalAppData overlay." />
<Error Condition="'$(LegacyRuntimeAssetsValidationEnabled)' == 'true' and
!Exists('$(LegacyRuntimeSourceRoot)')"
Text="Legacy runtime source root was not found: $(LegacyRuntimeSourceRoot). Run scripts\Initialize-ExistingDevelopmentPc.ps1 to create the verified local Required binding, or use /p:LegacyRuntimeAssetsMode=Auto for a source-only DryRun build." />
@@ -228,12 +221,6 @@
<Error Condition="'$(LegacyRuntimeAssetsValidationEnabled)' == 'true' and
!Exists('$(LegacyResSourceRoot)\logo.png')"
Text="The required legacy brand wordmark is missing: Res\logo.png." />
<Error Condition="'$(LegacyRuntimeAssetsValidationEnabled)' == 'true' and
!Exists('$(LegacyCutsSourceRoot)\%(LegacyRequiredScene.Identity).t2s')"
Text="A required active legacy scene file is missing: Cuts\%(LegacyRequiredScene.Identity).t2s." />
<Error Condition="'$(LegacyRuntimeAssetsValidationEnabled)' == 'true' and
!Exists('$(LegacyCutsSourceRoot)\%(LegacyRequiredBuiltInAsset.Identity)')"
Text="A required built-in legacy scene asset is missing: Cuts\%(LegacyRequiredBuiltInAsset.Identity)." />
<Error Condition="'$(LegacyRuntimeAssetsValidationEnabled)' == 'true' and
!Exists('$(LegacyResSourceRoot)\종목.ini')"
Text="The required legacy stock menu file Res\종목.ini is missing from $(LegacyRuntimeSourceRoot)." />
@@ -264,6 +251,12 @@
Condition="Exists('$(TargetDir)Res\MmoneyCoder.ini')" />
</Target>
<Target Name="ScrubCopiedCutsFromBuildOutput"
BeforeTargets="PrepareForBuild">
<RemoveDir Directories="$(TargetDir)Cuts"
Condition="Exists('$(TargetDir)Cuts')" />
</Target>
<Target Name="RejectLegacyDatabaseIniFromPackagePayload"
BeforeTargets="_ComputeAppxPackagePayload">
<ItemGroup>

View File

@@ -38,7 +38,9 @@ public sealed partial class MainWindow
_isDevelopmentLiveLaunch &&
!_developmentLiveStartupBlocked
? PlayoutOptionsLoader.LoadDevelopmentLive(
baseDirectory: AppContext.BaseDirectory)
baseDirectory: AppContext.BaseDirectory,
operatorSceneDirectory:
_appliedOperatorSettings.SceneDirectory)
: PlayoutOptionsLoader.Load(
operatorSceneDirectory: _appliedOperatorSettings.SceneDirectory,
operatorBackgroundDirectory: _appliedOperatorSettings.BackgroundDirectory,

View File

@@ -87,13 +87,9 @@ public sealed partial class MainWindow : Window
"MBN_STOCK_WEBVIEW",
"Res",
"종목.ini");
var selectedResourceDirectory = _isDevelopmentLiveLaunch
? null
: _appliedOperatorSettings.ResourceDirectory;
var cutMenuComposition = _isDevelopmentLiveLaunch
? LegacyRuntimeStockCutMenuLoader.ResolveFromBaseDirectory(
AppContext.BaseDirectory)
: selectedResourceDirectory is null
var selectedResourceDirectory =
_appliedOperatorSettings.ResourceDirectory;
var cutMenuComposition = selectedResourceDirectory is null
? LegacyRuntimeStockCutMenuLoader.ResolveFromCandidates(
runtimeCutMenuOverridePath,
AppContext.BaseDirectory)
@@ -135,7 +131,9 @@ public sealed partial class MainWindow : Window
// A Gate A plan pins database.local.json by path, size and SHA-256.
// Never inspect either packaged or operator-selected legacy INI files
// during that one-shot validation process.
var selectedLegacyDatabaseIni = selectedResourceDirectory is null
var selectedLegacyDatabaseIni =
_isDevelopmentLiveLaunch ||
selectedResourceDirectory is null
? null
: Path.Combine(selectedResourceDirectory, "MmoneyCoder.ini");
_databaseRuntime = _playoutLaunchAuthorization.IsGateA
@@ -375,17 +373,28 @@ public sealed partial class MainWindow : Window
appWindow.SetIcon(iconPath);
}
// Preserve the legacy restore size when it fits, but clamp the complete
// window (client plus caption and borders) to the monitor work area.
// Clamping the client size alone can still place the bottom controls
// behind the taskbar.
var displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Primary);
var workArea = displayArea.WorkArea;
appWindow.ResizeClient(new SizeInt32(1905, 1015));
var requestedWindowSize = appWindow.Size;
appWindow.MoveAndResize(new RectInt32(
workArea.X,
workArea.Y,
Math.Min(requestedWindowSize.Width, workArea.Width),
Math.Min(requestedWindowSize.Height, workArea.Height)));
if (appWindow.Presenter is OverlappedPresenter presenter)
{
presenter.IsMaximizable = false;
// Keep the operator window fixed, while allowing Windows to use its
// standard work-area-aware maximize path on startup and after restore.
presenter.IsMaximizable = true;
presenter.IsResizable = false;
presenter.Maximize();
}
// WinForms stored 1905 x 1015 as ClientSize. AppWindow.Resize would include
// the caption and borders and silently make the WebView viewport smaller.
appWindow.ResizeClient(new SizeInt32(1905, 1015));
var displayArea = DisplayArea.GetFromWindowId(windowId, DisplayAreaFallback.Primary);
appWindow.Move(new PointInt32(displayArea.WorkArea.X, displayArea.WorkArea.Y));
}
private async Task InitializeWebViewAsync()