AX Commander 비교본 런처 기능 대량 이식

변경 목적: Agent Compare 아래 비교본의 개발 문서와 런처 소스를 기준으로 현재 AX Commander에 빠져 있던 신규 런처 기능을 동일한 흐름으로 옮겨, 비교본 수준의 기능 폭을 현재 제품에 반영했습니다.

핵심 수정사항: 비교본의 신규 런처 핸들러 다수를 src/AxCopilot/Handlers로 이식하고 App.xaml.cs 등록 흐름에 연결했습니다. 빠른 링크, 파일 태그, 알림 센터, 포모도로, 파일 브라우저, 핫키 관리, OCR, 세션/스케줄/매크로, Git/정규식/네트워크/압축/해시/UUID/JWT/QR 등 AX Commander 기능을 추가했습니다.

핵심 수정사항: 신규 기능이 실제 동작하도록 AppSettings 확장, SchedulerService/FileTagService/NotificationCenterService/IconCacheService/UrlTemplateEngine/PomodoroService 추가, 배치 이름변경/세션/스케줄/매크로 편집 창 추가, NotificationService와 Symbols 보강, QR/OCR용 csproj 의존성과 Windows 타겟 프레임워크를 반영했습니다.

문서 반영: README.md와 docs/DEVELOPMENT.md에 비교본 기반 런처 기능 이식 이력과 검증 결과를 업데이트했습니다.

검증 결과: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 실행 기준 경고 0개, 오류 0개를 확인했습니다.
This commit is contained in:
2026-04-05 00:59:45 +09:00
parent 0929778ca7
commit 0336904258
115 changed files with 30749 additions and 1 deletions

View File

@@ -0,0 +1,343 @@
<Window x:Class="AxCopilot.Views.ScheduleEditorWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AX Commander — 스케줄 편집"
Width="520" Height="480"
MinWidth="440" MinHeight="400"
WindowStyle="None" AllowsTransparency="True"
Background="Transparent"
WindowStartupLocation="CenterScreen"
ResizeMode="NoResize"
ShowInTaskbar="False">
<Border Background="{DynamicResource LauncherBackground}" CornerRadius="12"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
Margin="6">
<Border.Effect>
<DropShadowEffect BlurRadius="22" ShadowDepth="4" Opacity="0.32" Color="Black" Direction="270"/>
</Border.Effect>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="44"/> <!-- 타이틀바 -->
<RowDefinition Height="*"/> <!-- 콘텐츠 -->
<RowDefinition Height="Auto"/> <!-- 하단 버튼 -->
</Grid.RowDefinitions>
<!-- ─── 타이틀바 ─────────────────────────────────────────────── -->
<Border Grid.Row="0" CornerRadius="12,12,0,0"
Background="{DynamicResource ItemBackground}"
MouseLeftButtonDown="TitleBar_MouseDown">
<Grid Margin="14,0,8,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="&#xE916;"
FontFamily="Segoe MDL2 Assets" FontSize="15"
Foreground="{DynamicResource AccentColor}"
VerticalAlignment="Center" Margin="0,1,10,0"/>
<TextBlock Text="스케줄 편집"
FontSize="13" FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"
VerticalAlignment="Center"/>
</StackPanel>
<Border HorizontalAlignment="Right" VerticalAlignment="Center"
CornerRadius="4" Padding="8,4" Cursor="Hand"
MouseLeftButtonUp="BtnClose_Click">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#40C05050"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="&#xE711;" FontFamily="Segoe MDL2 Assets" FontSize="13"
Foreground="{DynamicResource SecondaryText}"/>
</Border>
</Grid>
</Border>
<!-- ─── 콘텐츠 ───────────────────────────────────────────────── -->
<ScrollViewer Grid.Row="1"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<StackPanel Margin="18,14,18,10">
<!-- 스케줄 이름 -->
<TextBlock Text="스케줄 이름" FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}" Margin="0,0,0,4"/>
<TextBox x:Name="NameBox"
FontSize="13" FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryText}"
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
Padding="8,6" Margin="0,0,0,14"/>
<!-- ── 트리거 유형 ── -->
<TextBlock Text="실행 주기" FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}" Margin="0,0,0,6"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border x:Name="BtnDaily" CornerRadius="4,0,0,4" Padding="14,5" Cursor="Hand" MouseLeftButtonUp="TriggerType_Click" Tag="daily">
<TextBlock x:Name="TxtDaily" Text="매일" FontSize="12"/>
</Border>
<Border x:Name="BtnWeekdays" CornerRadius="0" Padding="14,5" Cursor="Hand" MouseLeftButtonUp="TriggerType_Click" Tag="weekdays">
<TextBlock x:Name="TxtWeekdays" Text="주중(월~금)" FontSize="12"/>
</Border>
<Border x:Name="BtnWeekly" CornerRadius="0" Padding="14,5" Cursor="Hand" MouseLeftButtonUp="TriggerType_Click" Tag="weekly">
<TextBlock x:Name="TxtWeekly" Text="매주" FontSize="12"/>
</Border>
<Border x:Name="BtnOnce" CornerRadius="0,4,4,0" Padding="14,5" Cursor="Hand" MouseLeftButtonUp="TriggerType_Click" Tag="once">
<TextBlock x:Name="TxtOnce" Text="한번" FontSize="12"/>
</Border>
</StackPanel>
<!-- 실행 시각 -->
<Grid Margin="0,0,0,12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="실행 시각 (HH:mm)"
FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}"
VerticalAlignment="Center" Margin="0,0,12,0" Width="130"/>
<TextBox Grid.Column="1" x:Name="TimeBox"
Text="09:00"
FontFamily="Cascadia Code, Consolas"
FontSize="13"
Foreground="{DynamicResource PrimaryText}"
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
Padding="8,5" MaxWidth="100" HorizontalAlignment="Left"/>
</Grid>
<!-- 요일 선택 (weekly일 때만 표시) -->
<StackPanel x:Name="WeekDaysPanel" Visibility="Collapsed" Margin="0,0,0,12">
<TextBlock Text="요일 선택" FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}" Margin="0,0,0,6"/>
<StackPanel Orientation="Horizontal">
<Border x:Name="BtnSun" Tag="0" CornerRadius="4" Width="38" Height="32" Margin="0,0,4,0" Cursor="Hand" MouseLeftButtonUp="WeekDay_Click">
<TextBlock Text="일" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border x:Name="BtnMon" Tag="1" CornerRadius="4" Width="38" Height="32" Margin="0,0,4,0" Cursor="Hand" MouseLeftButtonUp="WeekDay_Click">
<TextBlock Text="월" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border x:Name="BtnTue" Tag="2" CornerRadius="4" Width="38" Height="32" Margin="0,0,4,0" Cursor="Hand" MouseLeftButtonUp="WeekDay_Click">
<TextBlock Text="화" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border x:Name="BtnWed" Tag="3" CornerRadius="4" Width="38" Height="32" Margin="0,0,4,0" Cursor="Hand" MouseLeftButtonUp="WeekDay_Click">
<TextBlock Text="수" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border x:Name="BtnThu" Tag="4" CornerRadius="4" Width="38" Height="32" Margin="0,0,4,0" Cursor="Hand" MouseLeftButtonUp="WeekDay_Click">
<TextBlock Text="목" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border x:Name="BtnFri" Tag="5" CornerRadius="4" Width="38" Height="32" Margin="0,0,4,0" Cursor="Hand" MouseLeftButtonUp="WeekDay_Click">
<TextBlock Text="금" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Border x:Name="BtnSat" Tag="6" CornerRadius="4" Width="38" Height="32" Cursor="Hand" MouseLeftButtonUp="WeekDay_Click">
<TextBlock Text="토" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</StackPanel>
</StackPanel>
<!-- 날짜 선택 (once일 때만 표시) -->
<Grid x:Name="DatePanel" Visibility="Collapsed" Margin="0,0,0,12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="실행 날짜 (yyyy-MM-dd)"
FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}"
VerticalAlignment="Center" Margin="0,0,12,0" Width="130"/>
<TextBox Grid.Column="1" x:Name="DateBox"
FontFamily="Cascadia Code, Consolas"
FontSize="13"
Foreground="{DynamicResource PrimaryText}"
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
Padding="8,5" MaxWidth="140" HorizontalAlignment="Left"/>
</Grid>
<!-- 구분선 -->
<Border Height="1" Background="{DynamicResource BorderColor}" Margin="0,4,0,14"/>
<!-- ── 액션 유형 ── -->
<TextBlock Text="실행 액션" FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}" Margin="0,0,0,6"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border x:Name="BtnActionApp" CornerRadius="4,0,0,4" Padding="14,5" Cursor="Hand" MouseLeftButtonUp="ActionType_Click" Tag="app">
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xECAA;" FontFamily="Segoe MDL2 Assets" FontSize="12"
VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBlock x:Name="TxtActionApp" Text="앱 실행" FontSize="12"/>
</StackPanel>
</Border>
<Border x:Name="BtnActionNotif" CornerRadius="0,4,4,0" Padding="14,5" Cursor="Hand" MouseLeftButtonUp="ActionType_Click" Tag="notification">
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xEA8F;" FontFamily="Segoe MDL2 Assets" FontSize="12"
VerticalAlignment="Center" Margin="0,0,5,0"/>
<TextBlock x:Name="TxtActionNotif" Text="알림 표시" FontSize="12"/>
</StackPanel>
</Border>
</StackPanel>
<!-- 앱 경로 (app 모드) -->
<StackPanel x:Name="AppPathPanel" Visibility="Visible">
<TextBlock Text="앱 경로" FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}" Margin="0,0,0,4"/>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" x:Name="AppPathBox"
FontSize="11"
Foreground="{DynamicResource PrimaryText}"
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
Padding="8,5" Margin="0,0,6,0"/>
<Border Grid.Column="1" CornerRadius="4" Padding="10,5" Cursor="Hand"
MouseLeftButtonUp="BtnBrowseApp_Click">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#18FFFFFF"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#28FFFFFF"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="찾아보기" FontSize="11"
Foreground="{DynamicResource SecondaryText}"/>
</Border>
</Grid>
<TextBlock Text="실행 인자 (선택)" FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}" Margin="0,0,0,4"/>
<TextBox x:Name="AppArgsBox"
FontSize="11"
Foreground="{DynamicResource SecondaryText}"
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
Padding="8,5"/>
</StackPanel>
<!-- 알림 메시지 (notification 모드) -->
<StackPanel x:Name="NotifPanel" Visibility="Collapsed">
<TextBlock Text="알림 메시지" FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}" Margin="0,0,0,4"/>
<TextBox x:Name="NotifMsgBox"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
Padding="8,6"
AcceptsReturn="False"/>
</StackPanel>
<!-- ── 조건 (L6-4) ── -->
<Border Height="1" Background="{DynamicResource BorderColor}" Margin="0,14,0,14"/>
<TextBlock Text="실행 조건 (선택)" FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}" Margin="0,0,0,6"/>
<Grid Margin="0,0,0,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="프로세스 이름"
FontSize="10" FontWeight="Medium"
Foreground="{DynamicResource SecondaryText}"
VerticalAlignment="Center" Margin="0,0,12,0" Width="100"/>
<TextBox Grid.Column="1" x:Name="ConditionProcessBox"
FontFamily="Cascadia Code, Consolas"
FontSize="12"
Foreground="{DynamicResource PrimaryText}"
Background="{DynamicResource LauncherBackground}"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="1"
Padding="8,5" MaxWidth="180" HorizontalAlignment="Left"/>
</Grid>
<StackPanel Orientation="Horizontal" Margin="0,0,0,4">
<Border x:Name="BtnCondRun" CornerRadius="4,0,0,4" Padding="12,4" Cursor="Hand"
MouseLeftButtonUp="ConditionMode_Click" Tag="run">
<TextBlock x:Name="TxtCondRun" Text="실행 중일 때" FontSize="11"/>
</Border>
<Border x:Name="BtnCondNotRun" CornerRadius="0,4,4,0" Padding="12,4" Cursor="Hand"
MouseLeftButtonUp="ConditionMode_Click" Tag="notrun">
<TextBlock x:Name="TxtCondNotRun" Text="실행 중 아닐 때" FontSize="11"/>
</Border>
</StackPanel>
<TextBlock Text="비어 있으면 조건 없이 항상 실행합니다 · 예: chrome, code, slack"
FontSize="10"
Foreground="{DynamicResource SecondaryText}"
Margin="0,4,0,0"/>
</StackPanel>
</ScrollViewer>
<!-- ─── 하단 버튼 바 ─────────────────────────────────────────── -->
<Border Grid.Row="2" CornerRadius="0,0,12,12"
Background="{DynamicResource ItemBackground}"
BorderBrush="{DynamicResource BorderColor}" BorderThickness="0,1,0,0"
Padding="12,8">
<Grid>
<!-- 활성화 토글 -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<TextBlock Text="활성화" FontSize="11"
Foreground="{DynamicResource SecondaryText}"
VerticalAlignment="Center" Margin="0,0,8,0"/>
<Border x:Name="EnabledToggle"
Width="36" Height="20" CornerRadius="10"
Background="{DynamicResource AccentColor}"
Cursor="Hand" MouseLeftButtonUp="EnabledToggle_Click">
<Border x:Name="EnabledThumb"
Width="16" Height="16" CornerRadius="8"
Background="White"
HorizontalAlignment="Right"
Margin="0,0,1,0"/>
</Border>
</StackPanel>
<!-- 취소·저장 -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Center">
<Border CornerRadius="4" Padding="14,5" Cursor="Hand" Margin="0,0,8,0"
MouseLeftButtonUp="BtnCancel_Click">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#18FFFFFF"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#28FFFFFF"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="취소" FontSize="12"
Foreground="{DynamicResource SecondaryText}"/>
</Border>
<Border CornerRadius="4" Padding="16,5" Cursor="Hand"
Background="{DynamicResource AccentColor}"
MouseLeftButtonUp="BtnSave_Click">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="{DynamicResource AccentColor}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="0.85"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Horizontal">
<TextBlock Text="&#xE74E;" FontFamily="Segoe MDL2 Assets" FontSize="12"
Foreground="White" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="저장" FontSize="12" FontWeight="SemiBold" Foreground="White"/>
</StackPanel>
</Border>
</StackPanel>
</Grid>
</Border>
</Grid>
</Border>
</Window>