Add initial WinUI automation shell
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
bin/
|
||||
obj/
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
*.user
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
TestResults/
|
||||
*.nupkg
|
||||
*.snupkg
|
||||
|
||||
publish/
|
||||
artifacts/
|
||||
9
TornadoAce_CJOnStyle.slnx
Normal file
@@ -0,0 +1,9 @@
|
||||
<Solution>
|
||||
<Configurations>
|
||||
<Platform Name="x64" />
|
||||
</Configurations>
|
||||
<Project Path="TornadoAce_CJOnStyle/TornadoAce_CJOnStyle.csproj">
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
<Deploy />
|
||||
</Project>
|
||||
</Solution>
|
||||
83
TornadoAce_CJOnStyle/App.xaml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<Application
|
||||
x:Class="TornadoAce_CJOnStyle.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:TornadoAce_CJOnStyle"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<!-- Other merged dictionaries here -->
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<SolidColorBrush x:Key="AppBackgroundBrush" Color="#0A0D12" />
|
||||
<SolidColorBrush x:Key="AppChromeBrush" Color="#111827" />
|
||||
<SolidColorBrush x:Key="PanelBrush" Color="#151A22" />
|
||||
<SolidColorBrush x:Key="PanelAltBrush" Color="#1E2631" />
|
||||
<SolidColorBrush x:Key="PanelStrokeBrush" Color="#303946" />
|
||||
<SolidColorBrush x:Key="TextPrimaryBrush" Color="#F6F8FB" />
|
||||
<SolidColorBrush x:Key="TextSecondaryBrush" Color="#C2CAD6" />
|
||||
<SolidColorBrush x:Key="TextMutedBrush" Color="#8290A3" />
|
||||
<SolidColorBrush x:Key="AccentCyanBrush" Color="#45D6D1" />
|
||||
<SolidColorBrush x:Key="AccentGreenBrush" Color="#37D67A" />
|
||||
<SolidColorBrush x:Key="AccentAmberBrush" Color="#FFB547" />
|
||||
<SolidColorBrush x:Key="AccentRedBrush" Color="#FF5B61" />
|
||||
<SolidColorBrush x:Key="AccentVioletBrush" Color="#B48CFF" />
|
||||
|
||||
<Style x:Key="PageTitleTextStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="Bahnschrift SemiBold" />
|
||||
<Setter Property="FontSize" Value="30" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="SectionTitleTextStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="Bahnschrift SemiBold" />
|
||||
<Setter Property="FontSize" Value="18" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="LabelTextStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="Bahnschrift" />
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextMutedBrush}" />
|
||||
<Setter Property="TextWrapping" Value="WrapWholeWords" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="BodyTextStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="Bahnschrift" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextSecondaryBrush}" />
|
||||
<Setter Property="TextWrapping" Value="WrapWholeWords" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="MonoTextStyle" TargetType="TextBlock">
|
||||
<Setter Property="FontFamily" Value="Cascadia Mono" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ActionButtonStyle" TargetType="Button">
|
||||
<Setter Property="Padding" Value="12,8" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="FontFamily" Value="Bahnschrift SemiBold" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="PrimaryActionButtonStyle"
|
||||
TargetType="Button"
|
||||
BasedOn="{StaticResource ActionButtonStyle}">
|
||||
<Setter Property="Background" Value="{StaticResource AccentCyanBrush}" />
|
||||
<Setter Property="Foreground" Value="#051012" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="GhostActionButtonStyle"
|
||||
TargetType="Button"
|
||||
BasedOn="{StaticResource ActionButtonStyle}">
|
||||
<Setter Property="Background" Value="{StaticResource PanelAltBrush}" />
|
||||
<Setter Property="BorderBrush" Value="{StaticResource PanelStrokeBrush}" />
|
||||
<Setter Property="Foreground" Value="{StaticResource TextPrimaryBrush}" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
52
TornadoAce_CJOnStyle/App.xaml.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
namespace TornadoAce_CJOnStyle
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
private Window? window;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the singleton application object. This is the first line of authored code
|
||||
/// executed, and as such is the logical equivalent of main() or WinMain().
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user. Other entry points
|
||||
/// will be used such as when the application is launched to open a specific file.
|
||||
/// </summary>
|
||||
/// <param name="e">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs e)
|
||||
{
|
||||
window ??= new Window
|
||||
{
|
||||
Title = "TornadoAce CJ OnStyle CG Automation"
|
||||
};
|
||||
|
||||
if (window.Content is not Frame rootFrame)
|
||||
{
|
||||
rootFrame = new Frame();
|
||||
rootFrame.NavigationFailed += OnNavigationFailed;
|
||||
window.Content = rootFrame;
|
||||
}
|
||||
|
||||
_ = rootFrame.Navigate(typeof(MainPage), e.Arguments);
|
||||
window.Activate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when Navigation to a certain page fails
|
||||
/// </summary>
|
||||
/// <param name="sender">The Frame which failed navigation</param>
|
||||
/// <param name="e">Details about the navigation failure</param>
|
||||
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
|
||||
{
|
||||
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
TornadoAce_CJOnStyle/Assets/LockScreenLogo.scale-200.png
Normal file
|
After Width: | Height: | Size: 432 B |
BIN
TornadoAce_CJOnStyle/Assets/SplashScreen.scale-200.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
TornadoAce_CJOnStyle/Assets/Square150x150Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
TornadoAce_CJOnStyle/Assets/Square44x44Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 637 B |
|
After Width: | Height: | Size: 283 B |
BIN
TornadoAce_CJOnStyle/Assets/StoreLogo.png
Normal file
|
After Width: | Height: | Size: 456 B |
BIN
TornadoAce_CJOnStyle/Assets/Wide310x150Logo.scale-200.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
11
TornadoAce_CJOnStyle/Domain/CueSheetModels.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace TornadoAce_CJOnStyle.Domain
|
||||
{
|
||||
public sealed record CueSheetRecord(
|
||||
string CueSheetId,
|
||||
DateTime BroadcastTime,
|
||||
string ProgramTitle,
|
||||
string ProductName,
|
||||
string PriceText,
|
||||
string CompositionText,
|
||||
string PromotionText);
|
||||
}
|
||||
36
TornadoAce_CJOnStyle/Domain/SceneModels.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace TornadoAce_CJOnStyle.Domain
|
||||
{
|
||||
public sealed record SceneTemplateDefinition(
|
||||
string TemplateId,
|
||||
string TemplateName,
|
||||
string TornadoVersion,
|
||||
IReadOnlyList<FieldMappingDefinition> Mappings);
|
||||
|
||||
public sealed record FieldMappingDefinition(
|
||||
string CueSheetField,
|
||||
string SceneObjectName,
|
||||
string Attribute,
|
||||
string Rule);
|
||||
|
||||
public sealed record SceneDataSnapshot(
|
||||
string SceneName,
|
||||
string TemplateId,
|
||||
IReadOnlyDictionary<string, string> VisibleTextObjects,
|
||||
IReadOnlyList<string> ImagePaths)
|
||||
{
|
||||
public string ToPreviewJson()
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
scene = SceneName,
|
||||
templateId = TemplateId,
|
||||
text = VisibleTextObjects,
|
||||
images = ImagePaths
|
||||
};
|
||||
|
||||
return JsonSerializer.Serialize(payload, new JsonSerializerOptions { WriteIndented = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
10
TornadoAce_CJOnStyle/Domain/VerificationModels.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace TornadoAce_CJOnStyle.Domain
|
||||
{
|
||||
public sealed record InspectionResult(
|
||||
string CueSheetId,
|
||||
string ProgramTitle,
|
||||
string SceneName,
|
||||
double MatchRate,
|
||||
string Severity,
|
||||
string Decision);
|
||||
}
|
||||
10
TornadoAce_CJOnStyle/Imports.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
global using CommunityToolkit.Mvvm.ComponentModel;
|
||||
global using CommunityToolkit.Mvvm.Input;
|
||||
global using CommunityToolkit.Mvvm.Messaging;
|
||||
|
||||
global using TornadoAce_CJOnStyle.ViewModels;
|
||||
global using TornadoAce_CJOnStyle.Views;
|
||||
|
||||
global using Microsoft.UI.Xaml;
|
||||
global using Microsoft.UI.Xaml.Controls;
|
||||
global using Microsoft.UI.Xaml.Navigation;
|
||||
51
TornadoAce_CJOnStyle/Package.appxmanifest
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
IgnorableNamespaces="uap rescap">
|
||||
|
||||
<Identity
|
||||
Name="e3cb6767-25dc-4651-b094-b6efbe33b802"
|
||||
Publisher="CN=Comtropy"
|
||||
Version="1.0.0.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="e3cb6767-25dc-4651-b094-b6efbe33b802" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>TornadoAce CJ OnStyle</DisplayName>
|
||||
<PublisherDisplayName>Comtropy</PublisherDisplayName>
|
||||
<Logo>Assets\StoreLogo.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App"
|
||||
Executable="$targetnametoken$.exe"
|
||||
EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="TornadoAce CJ OnStyle"
|
||||
Description="CJ OnStyle CG production automation console"
|
||||
BackgroundColor="transparent"
|
||||
Square150x150Logo="Assets\Square150x150Logo.png"
|
||||
Square44x44Logo="Assets\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
|
||||
<uap:SplashScreen Image="Assets\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||
-->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PublishProtocol>FileSystem</PublishProtocol>
|
||||
<Platform>x64</Platform>
|
||||
<RuntimeIdentifier Condition="$([MSBuild]::GetTargetFrameworkVersion('$(TargetFramework)')) >= 8">win-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifier Condition="$([MSBuild]::GetTargetFrameworkVersion('$(TargetFramework)')) < 8">win10-x64</RuntimeIdentifier>
|
||||
<PublishDir>bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\</PublishDir>
|
||||
<SelfContained>true</SelfContained>
|
||||
<PublishSingleFile>False</PublishSingleFile>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun>
|
||||
<PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun>
|
||||
<PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed>
|
||||
<PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
10
TornadoAce_CJOnStyle/Properties/launchSettings.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"profiles": {
|
||||
"TornadoAce_CJOnStyle (Package)": {
|
||||
"commandName": "MsixPackage"
|
||||
},
|
||||
"TornadoAce_CJOnStyle (Unpackaged)": {
|
||||
"commandName": "Project"
|
||||
}
|
||||
}
|
||||
}
|
||||
70
TornadoAce_CJOnStyle/Services/AutomationWorkspace.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using TornadoAce_CJOnStyle.Domain;
|
||||
|
||||
namespace TornadoAce_CJOnStyle.Services
|
||||
{
|
||||
public sealed class AutomationWorkspace
|
||||
{
|
||||
private AutomationWorkspace(
|
||||
IReadOnlyList<CueSheetRecord> cueSheets,
|
||||
SceneTemplateDefinition activeTemplate,
|
||||
SceneDataSnapshot activeScene,
|
||||
IReadOnlyList<InspectionResult> inspectionResults)
|
||||
{
|
||||
CueSheets = cueSheets;
|
||||
ActiveTemplate = activeTemplate;
|
||||
ActiveScene = activeScene;
|
||||
InspectionResults = inspectionResults;
|
||||
}
|
||||
|
||||
public IReadOnlyList<CueSheetRecord> CueSheets { get; }
|
||||
|
||||
public SceneTemplateDefinition ActiveTemplate { get; }
|
||||
|
||||
public SceneDataSnapshot ActiveScene { get; }
|
||||
|
||||
public IReadOnlyList<InspectionResult> InspectionResults { get; }
|
||||
|
||||
public static AutomationWorkspace CreateDemo()
|
||||
{
|
||||
var cueSheets = new[]
|
||||
{
|
||||
new CueSheetRecord("CJ-20260817-001", DateTime.Today.AddHours(9), "최화정쇼", "비비안 컴포트 패키지", "79,900원", "브라 4종 + 팬티 4종", "ARS 10% 할인"),
|
||||
new CueSheetRecord("CJ-20260817-002", DateTime.Today.AddHours(11), "동가게", "프리미엄 한우 세트", "69,900원", "1등급 등심 1.2kg", "무이자 5개월"),
|
||||
new CueSheetRecord("CJ-20260817-003", DateTime.Today.AddHours(14), "스타일 라이브", "썸머 린넨 셋업", "59,900원", "자켓 + 팬츠", "앱 주문 추가 적립")
|
||||
};
|
||||
|
||||
var mappings = new[]
|
||||
{
|
||||
new FieldMappingDefinition("programTitle", "TXT_PROGRAM", "Text", "trim"),
|
||||
new FieldMappingDefinition("productName", "TXT_PRODUCT", "Text", "normalize-space"),
|
||||
new FieldMappingDefinition("priceText", "TXT_PRICE", "Text", "currency-text"),
|
||||
new FieldMappingDefinition("compositionText", "TXT_COMPOSITION", "Text", "line-break-safe"),
|
||||
new FieldMappingDefinition("promotionText", "TXT_PROMOTION", "Text", "special-char-exception")
|
||||
};
|
||||
|
||||
var template = new SceneTemplateDefinition("CJ_PRICE_LOWER_01", "가격형 하단 자막 / Tornado Scene", "Tornado2 primary, Tornado3 ready", mappings);
|
||||
|
||||
var scene = new SceneDataSnapshot(
|
||||
"CJ_PRICE_LOWER_01_0900.tscn",
|
||||
template.TemplateId,
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["TXT_PROGRAM"] = "최화정쇼",
|
||||
["TXT_PRODUCT"] = "비비안 컴포트 패키지",
|
||||
["TXT_PRICE"] = "79,900원",
|
||||
["TXT_COMPOSITION"] = "브라 4종 + 팬티 4종",
|
||||
["TXT_PROMOTION"] = "ARS 10% 할인"
|
||||
},
|
||||
new[] { @"D:\CJOnStyle\Products\VIVIEN\main.png" });
|
||||
|
||||
var inspections = new[]
|
||||
{
|
||||
new InspectionResult("CJ-20260817-001", "최화정쇼", "CJ_PRICE_LOWER_01_0900.tscn", 100, "Low", "1차 통과"),
|
||||
new InspectionResult("CJ-20260817-002", "동가게", "CJ_PRICE_LOWER_01_1100.tscn", 96.4, "High", "가격 불일치 확인"),
|
||||
new InspectionResult("CJ-20260817-003", "스타일 라이브", "CJ_PRICE_LOWER_01_1400.tscn", 98.1, "Medium", "특수문자 예외 후보")
|
||||
};
|
||||
|
||||
return new AutomationWorkspace(cueSheets, template, scene, inspections);
|
||||
}
|
||||
}
|
||||
}
|
||||
21
TornadoAce_CJOnStyle/Services/IntegrationInterfaces.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using TornadoAce_CJOnStyle.Domain;
|
||||
|
||||
namespace TornadoAce_CJOnStyle.Services
|
||||
{
|
||||
public interface ICueSheetClient
|
||||
{
|
||||
Task<IReadOnlyList<CueSheetRecord>> GetCueSheetsAsync(DateOnly broadcastDate, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface ITornadoSceneAdapter
|
||||
{
|
||||
Task<SceneDataSnapshot> CreateOrUpdateSceneAsync(CueSheetRecord cueSheet, SceneTemplateDefinition template, CancellationToken cancellationToken);
|
||||
|
||||
Task<SceneDataSnapshot> ExtractSceneDataAsync(string scenePath, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IInspectionService
|
||||
{
|
||||
Task<InspectionResult> InspectAsync(CueSheetRecord cueSheet, SceneDataSnapshot sceneSnapshot, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
67
TornadoAce_CJOnStyle/TornadoAce_CJOnStyle.csproj
Normal file
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
|
||||
<OutputType>WinExe</OutputType>
|
||||
|
||||
<!-- WinUI 3 -->
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<!-- <WindowsSdkPackageVersion>10.0.19041.38</WindowsSdkPackageVersion> -->
|
||||
|
||||
<!-- Project Options -->
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>TornadoAce_CJOnStyle</RootNamespace>
|
||||
|
||||
<!-- App Options -->
|
||||
<UseRidGraph>true</UseRidGraph>
|
||||
<Platforms>x64</Platforms>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<PublishProfile>win-$(Platform).pubxml</PublishProfile>
|
||||
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\SplashScreen.scale-200.png" />
|
||||
<Content Include="Assets\LockScreenLogo.scale-200.png" />
|
||||
<Content Include="Assets\Square150x150Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.scale-200.png" />
|
||||
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
|
||||
<Content Include="Assets\StoreLogo.png" />
|
||||
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="TornadoAce_CJOnStyle.slnx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.260209005" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.4654" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
Tools extension to be activated for this project even if the Windows App SDK Nuget
|
||||
package has not yet been restored.
|
||||
-->
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
|
||||
Explorer "Package and Publish" context menu entry to be enabled for this project even if
|
||||
the Windows App SDK Nuget package has not yet been restored.
|
||||
-->
|
||||
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
|
||||
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
13
TornadoAce_CJOnStyle/ViewModels/BaseViewModel.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace TornadoAce_CJOnStyle.ViewModels
|
||||
{
|
||||
public partial class BaseViewModel : ObservableObject
|
||||
{
|
||||
private string title = string.Empty;
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => title;
|
||||
set => SetProperty(ref title, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
202
TornadoAce_CJOnStyle/ViewModels/MainViewModel.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System.Collections.ObjectModel;
|
||||
using TornadoAce_CJOnStyle.Services;
|
||||
using Windows.UI;
|
||||
|
||||
namespace TornadoAce_CJOnStyle.ViewModels
|
||||
{
|
||||
public partial class MainViewModel : BaseViewModel
|
||||
{
|
||||
private readonly AutomationWorkspace workspace;
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
Title = "CG 제작 자동화";
|
||||
workspace = AutomationWorkspace.CreateDemo();
|
||||
|
||||
BuildPhase = "INITIAL BUILD";
|
||||
OperatorMessage = "큐시트 플랫폼, Tornado Scene 템플릿, 자동 검수 결과를 한 화면에서 확인하는 운영 콘솔 초안입니다.";
|
||||
LastCueSheetSyncText = "목 데이터 기준 13:25";
|
||||
ActiveTemplateName = workspace.ActiveTemplate.TemplateName;
|
||||
InspectionSummary = "1차 diff 실패 2건 / LLM 검토 대기 1건";
|
||||
SceneSnapshotLabel = $"{workspace.ActiveScene.SceneName} / visible text objects";
|
||||
SceneSnapshotText = workspace.ActiveScene.ToPreviewJson();
|
||||
LlmPolicyText = "사내 LLM 우선, 외부 서비스는 정책 승인 후";
|
||||
LlmPolicyDetail = "1차 Text Diffing 실패 건만 2차 LLM 판정으로 이동하고, 가격·구성·방송시간 변경은 높은 중요도로 표시합니다.";
|
||||
|
||||
LoadStatusCards();
|
||||
LoadPipelineSteps();
|
||||
LoadFieldMappings();
|
||||
LoadEndpoints();
|
||||
LoadInspectionQueue();
|
||||
}
|
||||
|
||||
private string buildPhase = string.Empty;
|
||||
private string operatorMessage = string.Empty;
|
||||
private string lastCueSheetSyncText = string.Empty;
|
||||
private string activeTemplateName = string.Empty;
|
||||
private string inspectionSummary = string.Empty;
|
||||
private string sceneSnapshotLabel = string.Empty;
|
||||
private string sceneSnapshotText = string.Empty;
|
||||
private string llmPolicyText = string.Empty;
|
||||
private string llmPolicyDetail = string.Empty;
|
||||
|
||||
public string BuildPhase
|
||||
{
|
||||
get => buildPhase;
|
||||
set => SetProperty(ref buildPhase, value);
|
||||
}
|
||||
|
||||
public string OperatorMessage
|
||||
{
|
||||
get => operatorMessage;
|
||||
set => SetProperty(ref operatorMessage, value);
|
||||
}
|
||||
|
||||
public string LastCueSheetSyncText
|
||||
{
|
||||
get => lastCueSheetSyncText;
|
||||
set => SetProperty(ref lastCueSheetSyncText, value);
|
||||
}
|
||||
|
||||
public string ActiveTemplateName
|
||||
{
|
||||
get => activeTemplateName;
|
||||
set => SetProperty(ref activeTemplateName, value);
|
||||
}
|
||||
|
||||
public string InspectionSummary
|
||||
{
|
||||
get => inspectionSummary;
|
||||
set => SetProperty(ref inspectionSummary, value);
|
||||
}
|
||||
|
||||
public string SceneSnapshotLabel
|
||||
{
|
||||
get => sceneSnapshotLabel;
|
||||
set => SetProperty(ref sceneSnapshotLabel, value);
|
||||
}
|
||||
|
||||
public string SceneSnapshotText
|
||||
{
|
||||
get => sceneSnapshotText;
|
||||
set => SetProperty(ref sceneSnapshotText, value);
|
||||
}
|
||||
|
||||
public string LlmPolicyText
|
||||
{
|
||||
get => llmPolicyText;
|
||||
set => SetProperty(ref llmPolicyText, value);
|
||||
}
|
||||
|
||||
public string LlmPolicyDetail
|
||||
{
|
||||
get => llmPolicyDetail;
|
||||
set => SetProperty(ref llmPolicyDetail, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<StatusCardViewModel> StatusCards { get; } = [];
|
||||
|
||||
public ObservableCollection<PipelineStepViewModel> PipelineSteps { get; } = [];
|
||||
|
||||
public ObservableCollection<FieldMappingViewModel> FieldMappings { get; } = [];
|
||||
|
||||
public ObservableCollection<IntegrationEndpointViewModel> Endpoints { get; } = [];
|
||||
|
||||
public ObservableCollection<InspectionQueueItemViewModel> InspectionQueue { get; } = [];
|
||||
|
||||
[RelayCommand]
|
||||
private void RefreshCueSheet()
|
||||
{
|
||||
LastCueSheetSyncText = $"목 데이터 동기화 {DateTime.Now:HH:mm:ss}";
|
||||
OperatorMessage = "큐시트 목 데이터를 다시 읽었습니다. 실제 API 연결 전까지 이 버튼은 화면 흐름 확인용으로 동작합니다.";
|
||||
StatusCards[0].Metric = "12";
|
||||
StatusCards[0].Detail = "동기화 완료";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void GenerateScene()
|
||||
{
|
||||
OperatorMessage = "선택된 큐시트 레코드를 Tornado Scene 템플릿에 매핑하는 흐름을 확인했습니다.";
|
||||
StatusCards[1].Metric = "7";
|
||||
StatusCards[1].Detail = "생성 후보";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void RunInspection()
|
||||
{
|
||||
OperatorMessage = "Scene 데이터와 큐시트 데이터를 비교하는 1차 검수 흐름을 확인했습니다.";
|
||||
InspectionSummary = "1차 diff 실패 2건 / LLM 검토 대기 1건 / 통과 5건";
|
||||
StatusCards[2].Metric = "99.2%";
|
||||
StatusCards[2].Detail = "평균 일치율";
|
||||
}
|
||||
|
||||
private void LoadStatusCards()
|
||||
{
|
||||
StatusCards.Add(new StatusCardViewModel("큐시트 API", "12", "수신 대기", Symbol.Sync, Brush(0xFF45D6D1)));
|
||||
StatusCards.Add(new StatusCardViewModel("Scene 매핑", "5", "템플릿 정의", Symbol.Map, Brush(0xFFB48CFF)));
|
||||
StatusCards.Add(new StatusCardViewModel("자동 검수", "98.7%", "목표 99%+", Symbol.Accept, Brush(0xFFFFB547)));
|
||||
StatusCards.Add(new StatusCardViewModel("Tornado", "Mock", "SDK 어댑터 자리", Symbol.Repair, Brush(0xFF37D67A)));
|
||||
}
|
||||
|
||||
private void LoadPipelineSteps()
|
||||
{
|
||||
PipelineSteps.Add(new PipelineStepViewModel("01", "큐시트 데이터 수신", "AI ON Studio / 자막 큐시트 API", "설계", 35, Brush(0xFF45D6D1)));
|
||||
PipelineSteps.Add(new PipelineStepViewModel("02", "Data Field Mapping", "큐시트 필드 -> Tornado Scene 객체", "초안", 45, Brush(0xFFB48CFF)));
|
||||
PipelineSteps.Add(new PipelineStepViewModel("03", "Scene 생성/갱신", "Tornado2 우선, Tornado3 어댑터 준비", "대기", 25, Brush(0xFF37D67A)));
|
||||
PipelineSteps.Add(new PipelineStepViewModel("04", "Scene Data API 추출", "Text/Image visible object snapshot", "초안", 30, Brush(0xFFFFB547)));
|
||||
PipelineSteps.Add(new PipelineStepViewModel("05", "자동 검수 피드백", "Text Diffing + LLM 중요도 판정", "초안", 40, Brush(0xFFFF5B61)));
|
||||
}
|
||||
|
||||
private void LoadFieldMappings()
|
||||
{
|
||||
foreach (var mapping in workspace.ActiveTemplate.Mappings)
|
||||
{
|
||||
FieldMappings.Add(new FieldMappingViewModel(mapping.CueSheetField, mapping.SceneObjectName, mapping.Rule, mapping.Attribute));
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadEndpoints()
|
||||
{
|
||||
Endpoints.Add(new IntegrationEndpointViewModel("큐시트 플랫폼", "GET /api/cuesheets/{broadcastDate}", "AI ON Studio", "Mock", Brush(0xFF45D6D1)));
|
||||
Endpoints.Add(new IntegrationEndpointViewModel("ERP 편성", "GET /api/schedules/{programCode}", "CJ OnStyle ERP", "대기", Brush(0xFFFFB547)));
|
||||
Endpoints.Add(new IntegrationEndpointViewModel("Tornado Scene API", "POST /api/scenes/extract", "Tornado Adapter", "설계", Brush(0xFF37D67A)));
|
||||
Endpoints.Add(new IntegrationEndpointViewModel("LLM 검수", "POST /api/inspection/llm", "Internal LLM", "정책 확인", Brush(0xFFB48CFF)));
|
||||
}
|
||||
|
||||
private void LoadInspectionQueue()
|
||||
{
|
||||
foreach (var item in workspace.InspectionResults)
|
||||
{
|
||||
InspectionQueue.Add(new InspectionQueueItemViewModel(
|
||||
item.ProgramTitle,
|
||||
item.SceneName,
|
||||
$"{item.MatchRate:0.0}%",
|
||||
item.Severity,
|
||||
item.Decision,
|
||||
SeverityBrush(item.Severity)));
|
||||
}
|
||||
}
|
||||
|
||||
private static SolidColorBrush SeverityBrush(string severity)
|
||||
{
|
||||
return severity switch
|
||||
{
|
||||
"High" => Brush(0xFFFF5B61),
|
||||
"Medium" => Brush(0xFFFFB547),
|
||||
_ => Brush(0xFF37D67A)
|
||||
};
|
||||
}
|
||||
|
||||
private static SolidColorBrush Brush(uint argb)
|
||||
{
|
||||
return new SolidColorBrush(Color.FromArgb(
|
||||
(byte)(argb >> 24),
|
||||
(byte)(argb >> 16),
|
||||
(byte)(argb >> 8),
|
||||
(byte)argb));
|
||||
}
|
||||
}
|
||||
}
|
||||
70
TornadoAce_CJOnStyle/ViewModels/ShellModels.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
namespace TornadoAce_CJOnStyle.ViewModels
|
||||
{
|
||||
public sealed partial class StatusCardViewModel : ObservableObject
|
||||
{
|
||||
private string metric = string.Empty;
|
||||
private string detail = string.Empty;
|
||||
|
||||
public StatusCardViewModel(string label, string metric, string detail, Symbol icon, SolidColorBrush accentBrush)
|
||||
{
|
||||
Label = label;
|
||||
Metric = metric;
|
||||
Detail = detail;
|
||||
Icon = icon;
|
||||
AccentBrush = accentBrush;
|
||||
}
|
||||
|
||||
public string Label { get; }
|
||||
|
||||
public Symbol Icon { get; }
|
||||
|
||||
public SolidColorBrush AccentBrush { get; }
|
||||
|
||||
public string Metric
|
||||
{
|
||||
get => metric;
|
||||
set => SetProperty(ref metric, value);
|
||||
}
|
||||
|
||||
public string Detail
|
||||
{
|
||||
get => detail;
|
||||
set => SetProperty(ref detail, value);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record PipelineStepViewModel(
|
||||
string StepNumber,
|
||||
string Title,
|
||||
string SystemName,
|
||||
string Status,
|
||||
int Completion,
|
||||
SolidColorBrush AccentBrush);
|
||||
|
||||
public sealed record FieldMappingViewModel(
|
||||
string CueSheetField,
|
||||
string SceneObject,
|
||||
string BindingRule,
|
||||
string Attribute);
|
||||
|
||||
public sealed record IntegrationEndpointViewModel(
|
||||
string Name,
|
||||
string Endpoint,
|
||||
string Owner,
|
||||
string Status,
|
||||
SolidColorBrush StatusBrush)
|
||||
{
|
||||
public string OwnerAndStatus => $"{Owner} / {Status}";
|
||||
}
|
||||
|
||||
public sealed record InspectionQueueItemViewModel(
|
||||
string ProgramTitle,
|
||||
string SceneName,
|
||||
string MatchRate,
|
||||
string Severity,
|
||||
string Decision,
|
||||
SolidColorBrush SeverityBrush);
|
||||
}
|
||||
499
TornadoAce_CJOnStyle/Views/MainPage.xaml
Normal file
@@ -0,0 +1,499 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<Page
|
||||
x:Class="TornadoAce_CJOnStyle.Views.MainPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:TornadoAce_CJOnStyle.ViewModels"
|
||||
mc:Ignorable="d">
|
||||
<Page.Content>
|
||||
<NavigationView
|
||||
AlwaysShowHeader="False"
|
||||
Background="{StaticResource AppChromeBrush}"
|
||||
IsBackButtonVisible="Collapsed"
|
||||
IsSettingsVisible="False"
|
||||
PaneDisplayMode="LeftCompact"
|
||||
RequestedTheme="Dark">
|
||||
<NavigationView.PaneHeader>
|
||||
<StackPanel Margin="12,18,12,12" Spacing="4">
|
||||
<TextBlock Style="{StaticResource LabelTextStyle}" Text="TORNADOACE" />
|
||||
<TextBlock
|
||||
FontFamily="Bahnschrift SemiBold"
|
||||
FontSize="16"
|
||||
Foreground="{StaticResource TextPrimaryBrush}"
|
||||
Text="CJ OnStyle" />
|
||||
<TextBlock
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind ViewModel.BuildPhase, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</NavigationView.PaneHeader>
|
||||
<NavigationView.MenuItems>
|
||||
<NavigationViewItem Content="대시보드" IsSelected="True">
|
||||
<NavigationViewItem.Icon>
|
||||
<SymbolIcon Symbol="Home" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="큐시트">
|
||||
<NavigationViewItem.Icon>
|
||||
<SymbolIcon Symbol="Library" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="매핑">
|
||||
<NavigationViewItem.Icon>
|
||||
<SymbolIcon Symbol="Map" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="검수">
|
||||
<NavigationViewItem.Icon>
|
||||
<SymbolIcon Symbol="Accept" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="연동">
|
||||
<NavigationViewItem.Icon>
|
||||
<SymbolIcon Symbol="Sync" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="설정">
|
||||
<NavigationViewItem.Icon>
|
||||
<SymbolIcon Symbol="Setting" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
</NavigationView.MenuItems>
|
||||
|
||||
<ScrollViewer
|
||||
Background="{StaticResource AppBackgroundBrush}"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
HorizontalScrollMode="Auto"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
VerticalScrollMode="Auto">
|
||||
<Grid
|
||||
MinWidth="1180"
|
||||
Margin="24"
|
||||
RowSpacing="18">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid ColumnSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock
|
||||
Style="{StaticResource PageTitleTextStyle}"
|
||||
Text="CG 제작 자동화 컨트롤" />
|
||||
<TextBlock
|
||||
MaxWidth="880"
|
||||
Style="{StaticResource BodyTextStyle}"
|
||||
Text="{x:Bind ViewModel.OperatorMessage, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
Spacing="10"
|
||||
VerticalAlignment="Top">
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.RefreshCueSheetCommand}"
|
||||
Style="{StaticResource GhostActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="7">
|
||||
<SymbolIcon Symbol="Refresh" />
|
||||
<TextBlock Text="큐시트 동기화" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.GenerateSceneCommand}"
|
||||
Style="{StaticResource PrimaryActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="7">
|
||||
<SymbolIcon Symbol="Play" />
|
||||
<TextBlock Text="Scene 생성" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button
|
||||
Command="{x:Bind ViewModel.RunInspectionCommand}"
|
||||
Style="{StaticResource GhostActionButtonStyle}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="7">
|
||||
<SymbolIcon Symbol="Accept" />
|
||||
<TextBlock Text="검수 실행" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<ItemsControl
|
||||
Grid.Row="1"
|
||||
ItemsSource="{x:Bind ViewModel.StatusCards, Mode=OneWay}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:StatusCardViewModel">
|
||||
<Border
|
||||
Width="268"
|
||||
Margin="0,0,12,0"
|
||||
Padding="14"
|
||||
Background="{StaticResource PanelBrush}"
|
||||
BorderBrush="{StaticResource PanelStrokeBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Width="34"
|
||||
Height="34"
|
||||
Background="{x:Bind AccentBrush}"
|
||||
CornerRadius="8">
|
||||
<SymbolIcon
|
||||
Foreground="#071013"
|
||||
Symbol="{x:Bind Icon}" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Spacing="4">
|
||||
<TextBlock
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind Label}" />
|
||||
<TextBlock
|
||||
FontFamily="Bahnschrift SemiBold"
|
||||
FontSize="24"
|
||||
Foreground="{StaticResource TextPrimaryBrush}"
|
||||
Text="{x:Bind Metric}" />
|
||||
<TextBlock
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind Detail}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<Grid
|
||||
Grid.Row="2"
|
||||
ColumnSpacing="16"
|
||||
RowSpacing="16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1.2*" />
|
||||
<ColumnDefinition Width="1.4*" />
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="380" />
|
||||
<RowDefinition Height="330" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border
|
||||
Padding="16"
|
||||
Background="{StaticResource PanelBrush}"
|
||||
BorderBrush="{StaticResource PanelStrokeBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<StackPanel Spacing="14">
|
||||
<Grid>
|
||||
<TextBlock Style="{StaticResource SectionTitleTextStyle}" Text="업무 파이프라인" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind ViewModel.LastCueSheetSyncText, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<ListView
|
||||
ItemsSource="{x:Bind ViewModel.PipelineSteps, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:PipelineStepViewModel">
|
||||
<Grid
|
||||
Margin="0,0,0,8"
|
||||
ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="42" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border
|
||||
Width="34"
|
||||
Height="34"
|
||||
Background="{x:Bind AccentBrush}"
|
||||
CornerRadius="8">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="Cascadia Mono"
|
||||
Foreground="#071013"
|
||||
Text="{x:Bind StepNumber}" />
|
||||
</Border>
|
||||
<StackPanel Grid.Column="1" Spacing="5">
|
||||
<Grid>
|
||||
<TextBlock
|
||||
FontFamily="Bahnschrift SemiBold"
|
||||
Foreground="{StaticResource TextPrimaryBrush}"
|
||||
Text="{x:Bind Title}" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind Status}" />
|
||||
</Grid>
|
||||
<TextBlock
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind SystemName}" />
|
||||
<ProgressBar
|
||||
Height="6"
|
||||
Maximum="100"
|
||||
Value="{x:Bind Completion}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Grid.Column="1"
|
||||
Padding="16"
|
||||
Background="{StaticResource PanelBrush}"
|
||||
BorderBrush="{StaticResource PanelStrokeBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<StackPanel Spacing="14">
|
||||
<Grid>
|
||||
<TextBlock Style="{StaticResource SectionTitleTextStyle}" Text="Scene 필드 매핑" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind ViewModel.ActiveTemplateName, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Border
|
||||
Padding="10,8"
|
||||
Background="{StaticResource PanelAltBrush}"
|
||||
CornerRadius="8">
|
||||
<Grid ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
<ColumnDefinition Width="1.2*" />
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
<ColumnDefinition Width="0.8*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Style="{StaticResource LabelTextStyle}" Text="큐시트 필드" />
|
||||
<TextBlock Grid.Column="1" Style="{StaticResource LabelTextStyle}" Text="Tornado 객체" />
|
||||
<TextBlock Grid.Column="2" Style="{StaticResource LabelTextStyle}" Text="매핑 규칙" />
|
||||
<TextBlock Grid.Column="3" Style="{StaticResource LabelTextStyle}" Text="속성" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<ListView
|
||||
ItemsSource="{x:Bind ViewModel.FieldMappings, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:FieldMappingViewModel">
|
||||
<Border Padding="10,7">
|
||||
<Grid ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
<ColumnDefinition Width="1.2*" />
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
<ColumnDefinition Width="0.8*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Style="{StaticResource MonoTextStyle}"
|
||||
Text="{x:Bind CueSheetField}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource BodyTextStyle}"
|
||||
Text="{x:Bind SceneObject}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Style="{StaticResource BodyTextStyle}"
|
||||
Text="{x:Bind BindingRule}" />
|
||||
<TextBlock
|
||||
Grid.Column="3"
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind Attribute}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Grid.Column="2"
|
||||
Padding="16"
|
||||
Background="{StaticResource PanelBrush}"
|
||||
BorderBrush="{StaticResource PanelStrokeBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Style="{StaticResource SectionTitleTextStyle}" Text="연동 엔드포인트" />
|
||||
<ItemsControl ItemsSource="{x:Bind ViewModel.Endpoints, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:IntegrationEndpointViewModel">
|
||||
<Grid
|
||||
Margin="0,0,0,10"
|
||||
ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Ellipse
|
||||
Width="10"
|
||||
Height="10"
|
||||
Margin="0,5,0,0"
|
||||
Fill="{x:Bind StatusBrush}" />
|
||||
<StackPanel Grid.Column="1" Spacing="3">
|
||||
<TextBlock
|
||||
FontFamily="Bahnschrift SemiBold"
|
||||
Foreground="{StaticResource TextPrimaryBrush}"
|
||||
Text="{x:Bind Name}" />
|
||||
<TextBlock
|
||||
Style="{StaticResource MonoTextStyle}"
|
||||
Text="{x:Bind Endpoint}" />
|
||||
<TextBlock
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind OwnerAndStatus}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Padding="16"
|
||||
Background="{StaticResource PanelBrush}"
|
||||
BorderBrush="{StaticResource PanelStrokeBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<StackPanel Spacing="14">
|
||||
<Grid>
|
||||
<TextBlock Style="{StaticResource SectionTitleTextStyle}" Text="자동 검수 큐" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind ViewModel.InspectionSummary, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Border
|
||||
Padding="10,8"
|
||||
Background="{StaticResource PanelAltBrush}"
|
||||
CornerRadius="8">
|
||||
<Grid ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
<ColumnDefinition Width="1.2*" />
|
||||
<ColumnDefinition Width="0.7*" />
|
||||
<ColumnDefinition Width="0.9*" />
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Style="{StaticResource LabelTextStyle}" Text="프로그램" />
|
||||
<TextBlock Grid.Column="1" Style="{StaticResource LabelTextStyle}" Text="Scene" />
|
||||
<TextBlock Grid.Column="2" Style="{StaticResource LabelTextStyle}" Text="일치율" />
|
||||
<TextBlock Grid.Column="3" Style="{StaticResource LabelTextStyle}" Text="중요도" />
|
||||
<TextBlock Grid.Column="4" Style="{StaticResource LabelTextStyle}" Text="판정" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<ListView
|
||||
ItemsSource="{x:Bind ViewModel.InspectionQueue, Mode=OneWay}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:InspectionQueueItemViewModel">
|
||||
<Border Padding="10,8">
|
||||
<Grid ColumnSpacing="10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
<ColumnDefinition Width="1.2*" />
|
||||
<ColumnDefinition Width="0.7*" />
|
||||
<ColumnDefinition Width="0.9*" />
|
||||
<ColumnDefinition Width="1.1*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Style="{StaticResource BodyTextStyle}"
|
||||
Text="{x:Bind ProgramTitle}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Style="{StaticResource MonoTextStyle}"
|
||||
Text="{x:Bind SceneName}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
FontFamily="Cascadia Mono"
|
||||
Foreground="{StaticResource TextPrimaryBrush}"
|
||||
Text="{x:Bind MatchRate}" />
|
||||
<Border
|
||||
Grid.Column="3"
|
||||
Padding="8,3"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{x:Bind SeverityBrush}"
|
||||
CornerRadius="8">
|
||||
<TextBlock
|
||||
FontFamily="Bahnschrift SemiBold"
|
||||
FontSize="12"
|
||||
Foreground="#071013"
|
||||
Text="{x:Bind Severity}" />
|
||||
</Border>
|
||||
<TextBlock
|
||||
Grid.Column="4"
|
||||
Style="{StaticResource BodyTextStyle}"
|
||||
Text="{x:Bind Decision}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Padding="16"
|
||||
Background="{StaticResource PanelBrush}"
|
||||
BorderBrush="{StaticResource PanelStrokeBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<StackPanel Spacing="14">
|
||||
<TextBlock Style="{StaticResource SectionTitleTextStyle}" Text="Scene 데이터 API 샘플" />
|
||||
<Border
|
||||
Padding="14"
|
||||
Background="#101419"
|
||||
BorderBrush="{StaticResource PanelStrokeBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="8">
|
||||
<StackPanel Spacing="8">
|
||||
<TextBlock
|
||||
Style="{StaticResource LabelTextStyle}"
|
||||
Text="{x:Bind ViewModel.SceneSnapshotLabel, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
FontFamily="Cascadia Mono"
|
||||
FontSize="13"
|
||||
Foreground="{StaticResource AccentCyanBrush}"
|
||||
Text="{x:Bind ViewModel.SceneSnapshotText, Mode=OneWay}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock Style="{StaticResource LabelTextStyle}" Text="LLM 검수 정책" />
|
||||
<TextBlock
|
||||
FontFamily="Bahnschrift SemiBold"
|
||||
FontSize="18"
|
||||
Foreground="{StaticResource TextPrimaryBrush}"
|
||||
Text="{x:Bind ViewModel.LlmPolicyText, Mode=OneWay}" />
|
||||
<TextBlock
|
||||
Style="{StaticResource BodyTextStyle}"
|
||||
Text="{x:Bind ViewModel.LlmPolicyDetail, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</NavigationView>
|
||||
</Page.Content>
|
||||
</Page>
|
||||
17
TornadoAce_CJOnStyle/Views/MainPage.xaml.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace TornadoAce_CJOnStyle.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// A simple page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public partial class MainPage : Page
|
||||
{
|
||||
private readonly MainViewModel viewModel = new();
|
||||
|
||||
public MainPage()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
public MainViewModel ViewModel => viewModel;
|
||||
}
|
||||
}
|
||||
24
TornadoAce_CJOnStyle/app.manifest
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="TornadoAce_CJOnStyle.app"/>
|
||||
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!--The ID below informs the system that this application is compatible with OS features first introduced in Windows 8.
|
||||
For more info see https://learn.microsoft.com/windows/win32/sysinfo/targeting-your-application-at-windows-8-1
|
||||
|
||||
It is also necessary to support features in unpackaged applications, for example the custom titlebar implementation.-->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<!-- The combination of below two tags have the following effect:
|
||||
1) Per-Monitor for >= Windows 10 Anniversary Update
|
||||
2) System < Windows 10 Anniversary Update
|
||||
-->
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
24
docs/initial-architecture.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# TornadoAce CJ OnStyle 초기 구조
|
||||
|
||||
## RFP에서 잡은 핵심 흐름
|
||||
|
||||
1. 큐시트 플랫폼과 ERP 편성 데이터를 API로 수신한다.
|
||||
2. 큐시트 필드를 사전 정의된 Tornado Scene 템플릿 객체에 매핑한다.
|
||||
3. Tornado Scene에서 화면에 노출되는 Text/Image 데이터를 추출하는 API를 둔다.
|
||||
4. 큐시트 데이터와 Scene 데이터를 1차 Text Diffing으로 비교한다.
|
||||
5. 1차 실패 건은 LLM 검수로 중요도를 판정하고 제작자에게 피드백한다.
|
||||
|
||||
## 현재 초기 빌드 범위
|
||||
|
||||
- `Domain`: 큐시트, Scene 템플릿, Scene 데이터 스냅샷, 검수 결과 모델.
|
||||
- `Services`: 실제 API/Tornado SDK 연결을 받을 인터페이스와 목 워크스페이스.
|
||||
- `ViewModels`: 운영 콘솔에 표시할 상태 카드, 파이프라인, 매핑표, 검수 큐.
|
||||
- `Views`: WinUI 3 기반 첫 화면. 예제 프로젝트와 같은 데스크톱 앱 방향으로 유지.
|
||||
|
||||
## 다음 설계 결정 지점
|
||||
|
||||
- Tornado2와 Tornado3의 어댑터 경계를 같은 인터페이스로 유지할지 여부.
|
||||
- Scene 내부 비노출 객체를 구분할 메타데이터 규칙.
|
||||
- 큐시트 API 원문 필드명과 화면용 정규화 필드명 분리 방식.
|
||||
- 특수문자 예외 처리 목록을 룰 파일로 둘지 관리자 UI에서 편집할지 여부.
|
||||
- LLM이 반환할 중요도 스키마와 감사 로그 보관 범위.
|
||||