feat(ui): default to active queue, scope search toolbar, add double-bezel inspector, optimize queue columns, and achieve 100% pure Korean UI
This commit is contained in:
parent
455c8b1324
commit
4ca7352dc3
27 changed files with 2477 additions and 659 deletions
|
|
@ -50,7 +50,7 @@ public static class CategoryGlyphs
|
|||
public static ImageSource ForCategory(string category)
|
||||
{
|
||||
if (Cache.TryGetValue(category, out var cached)) return cached;
|
||||
var uri = new Uri($"pack://application:,,,/Assets/glyph-{category}.png", UriKind.Absolute);
|
||||
var uri = new Uri($"pack://application:,,,/Everything2Everything;component/Assets/glyph-{category}.png", UriKind.Absolute);
|
||||
var img = new BitmapImage();
|
||||
img.BeginInit();
|
||||
img.CacheOption = BitmapCacheOption.OnLoad;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ControlsDictionary/>
|
||||
<ResourceDictionary Source="FormatShiftTheme.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
|
|
|||
|
|
@ -521,6 +521,100 @@
|
|||
<Setter Property="Margin" Value="0,0,0,16"/>
|
||||
</Style>
|
||||
|
||||
<!-- Double-Bezel (Doppelrand) Machine-Hardware Container Styles (high-end-visual-design Section 4) -->
|
||||
<Style x:Key="FsDoubleBezelShellStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="#10131B"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource FsBorderHairline}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="14"/>
|
||||
<Setter Property="Padding" Value="4"/>
|
||||
<Setter Property="Margin" Value="0,0,0,16"/>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="FsDoubleBezelCoreStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="{StaticResource FsBgSurface}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource FsGlassBorderBrush}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="CornerRadius" Value="10"/>
|
||||
<Setter Property="Padding" Value="14"/>
|
||||
</Style>
|
||||
|
||||
<!-- Physical <kbd> Keycap Shortcut Style (minimalist-ui Section 5) -->
|
||||
<Style x:Key="FsKbdStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="{StaticResource FsBgInput}"/>
|
||||
<Setter Property="BorderBrush" Value="{StaticResource FsBorderStrong}"/>
|
||||
<Setter Property="BorderThickness" Value="1,1,2,1"/>
|
||||
<Setter Property="CornerRadius" Value="4"/>
|
||||
<Setter Property="Padding" Value="6,2"/>
|
||||
<Setter Property="Margin" Value="0,0,4,0"/>
|
||||
<Setter Property="VerticalAlignment" Value="Center"/>
|
||||
</Style>
|
||||
|
||||
<!-- Island Button-in-Button Primary Launch CTA Style (high-end-visual-design Section 4.B) -->
|
||||
<LinearGradientBrush x:Key="FsIslandButtonGlow" StartPoint="0,0" EndPoint="1,1">
|
||||
<GradientStop Color="#0284C7" Offset="0.0"/>
|
||||
<GradientStop Color="#0369A1" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
<LinearGradientBrush x:Key="FsIslandButtonGlowHover" StartPoint="0,0" EndPoint="1,1">
|
||||
<GradientStop Color="#0EA5E9" Offset="0.0"/>
|
||||
<GradientStop Color="#0284C7" Offset="1.0"/>
|
||||
</LinearGradientBrush>
|
||||
|
||||
<Style x:Key="FsIslandPrimaryButtonStyle" TargetType="Button">
|
||||
<Setter Property="Background" Value="{StaticResource FsIslandButtonGlow}"/>
|
||||
<Setter Property="Foreground" Value="#FFFFFF"/>
|
||||
<Setter Property="BorderBrush" Value="#4038BDF8"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Height" Value="44"/>
|
||||
<Setter Property="Padding" Value="16,0,10,0"/>
|
||||
<Setter Property="FontSize" Value="13"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border x:Name="PART_Bd"
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderBrush="{TemplateBinding BorderBrush}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CornerRadius="10"
|
||||
Padding="{TemplateBinding Padding}">
|
||||
<Grid VerticalAlignment="Center">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<ContentPresenter Grid.Column="0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
TextElement.Foreground="{TemplateBinding Foreground}"
|
||||
TextElement.FontSize="{TemplateBinding FontSize}"
|
||||
TextElement.FontWeight="{TemplateBinding FontWeight}"/>
|
||||
<Border Grid.Column="1" Margin="8,0,0,0"
|
||||
Width="26" Height="26" CornerRadius="13"
|
||||
Background="#22FFFFFF"
|
||||
VerticalAlignment="Center" HorizontalAlignment="Right">
|
||||
<ui:SymbolIcon Symbol="ArrowRight24" FontSize="13"
|
||||
Foreground="#FFFFFF"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="False">
|
||||
<Setter TargetName="PART_Bd" Property="Opacity" Value="0.45"/>
|
||||
<Setter Property="Cursor" Value="Arrow"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="PART_Bd" Property="Background" Value="{StaticResource FsIslandButtonGlowHover}"/>
|
||||
<Setter TargetName="PART_Bd" Property="BorderBrush" Value="#8038BDF8"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- 실시간 기술 스펙 칩 (Live Spec Chip) 캡슐 스타일 -->
|
||||
<Style x:Key="FsSpecChipStyle" TargetType="Border">
|
||||
<Setter Property="Background" Value="{StaticResource FsAccentCyanBg}"/>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -145,7 +145,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
ToggleInspectorCommand = new RelayCommand(_ => ToggleInspector());
|
||||
RemoveQueueItemCommand = new RelayCommand(p => RemoveQueueItem(p as QueueItem));
|
||||
OpenFolderCommand = new RelayCommand(p => OpenFolderForPath(p as string));
|
||||
TabCommand = new RelayCommand(p => ShowTab(p as string ?? "Past"));
|
||||
TabCommand = new RelayCommand(p => ShowTab(p as string ?? "Active"));
|
||||
ConflictRuleCommand = new RelayCommand(p => SetConflictRule(p as string));
|
||||
CombineToggleCommand = new RelayCommand(_ => UpdateCombineState(SelectedOutputExtension));
|
||||
OutputFormatChangedCommand = new RelayCommand(_ => OnOutputFormatSelected());
|
||||
|
|
@ -156,12 +156,26 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
PastRowCommand = new RelayCommand(p => HandlePastRowClick(p as MouseButtonEventArgs));
|
||||
|
||||
InitializeComponent();
|
||||
Title = "Everything2Everything";
|
||||
if (AppTitleBar is not null) AppTitleBar.Title = "Everything2Everything";
|
||||
|
||||
if (AdvancedOptionsExpander is not null)
|
||||
{
|
||||
AdvancedOptionsExpander.IsExpanded = true;
|
||||
AdvancedOptionsExpander.Expanded += (_, _) => _options.IsAdvancedExpanded = true;
|
||||
AdvancedOptionsExpander.Collapsed += (_, _) => _options.IsAdvancedExpanded = false;
|
||||
}
|
||||
|
||||
if (SmartPresetCombo is not null)
|
||||
{
|
||||
SmartPresetCombo.SelectionChanged += OnSmartPresetChanged;
|
||||
}
|
||||
|
||||
if (OutputFormatCombo is not null)
|
||||
{
|
||||
OutputFormatCombo.SelectionChanged += (_, _) => OnOutputFormatSelected();
|
||||
}
|
||||
|
||||
// ActiveQueueList/PastResultsList의 ItemsSource는 XAML이 ActiveQueue/PastResults에 바인딩(선언적).
|
||||
|
||||
InitializeOutputFormats();
|
||||
|
|
@ -202,7 +216,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
return;
|
||||
}
|
||||
|
||||
CapabilityStatusText.Text = $"⚠ {notReady.Count}개 형식이 외부 도구를 기다립니다 (Diagnose 참조)";
|
||||
CapabilityStatusText.Text = $"⚠ {notReady.Count}개 형식이 외부 도구를 기다립니다 (진단 도구 참조)";
|
||||
CapabilityStatusText.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
|
|
@ -343,22 +357,26 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
var count = _activeQueue.Count;
|
||||
if (_cts is not null)
|
||||
{
|
||||
ProcessQueueButton.Content = $"변환 처리 중… ({count}개 파일)";
|
||||
ProcessQueueButton.Content = $"변환 처리 중… ({count}개)";
|
||||
ProcessQueueButton.ToolTip = "파일 변환이 진행 중입니다.";
|
||||
ProcessQueueButton.IsEnabled = false;
|
||||
}
|
||||
else if (count == 0)
|
||||
{
|
||||
ProcessQueueButton.Content = "대기 중 — 파일을 드래그하여 추가하세요";
|
||||
ProcessQueueButton.Content = "파일을 드래그하여 추가";
|
||||
ProcessQueueButton.ToolTip = "대기열에 변환할 파일을 추가하세요 (단축키: Ctrl + O)";
|
||||
ProcessQueueButton.IsEnabled = false;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(SelectedOutputExtension))
|
||||
{
|
||||
ProcessQueueButton.Content = "변환 불가 (공통 형식 없음)";
|
||||
ProcessQueueButton.Content = "공통 형식 없음";
|
||||
ProcessQueueButton.ToolTip = "선택된 파일들 간에 호환 가능한 공통 출력 형식이 없습니다.";
|
||||
ProcessQueueButton.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessQueueButton.Content = $"대기열 일괄 변환 시작 ({count}개) [Ctrl + Enter]";
|
||||
ProcessQueueButton.Content = $"변환 시작 ({count}개 파일)";
|
||||
ProcessQueueButton.ToolTip = $"대기열 일괄 변환 시작 ({count}개 파일) [단축키: Ctrl + Enter]";
|
||||
ProcessQueueButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -508,7 +526,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
PreviewFormatText.Text = string.IsNullOrEmpty(formatLabel) || formatLabel == "—" ? info.Extension.TrimStart('.').ToUpperInvariant() : formatLabel;
|
||||
PreviewSizeText.Text = string.IsNullOrEmpty(sizeText) || sizeText == "—" ? info.FormattedSize : sizeText;
|
||||
PreviewDimText.Text = info.DimensionsOrMeta;
|
||||
PreviewPageText.Text = info.Category.ToString();
|
||||
PreviewPageText.Text = info.Category.ToKoreanLabel();
|
||||
}
|
||||
|
||||
private void ShowPreviewLoading()
|
||||
|
|
@ -1043,10 +1061,9 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
private static string FormatDateLabel(DateOnly date)
|
||||
{
|
||||
var today = DateOnly.FromDateTime(DateTime.Today);
|
||||
var label = date == today ? "Today"
|
||||
: date == today.AddDays(-1) ? "Yesterday"
|
||||
: date.ToString("dddd", CultureInfo.GetCultureInfo("en-US"));
|
||||
return $"{label}, {date:MMM d}";
|
||||
if (date == today) return $"오늘 ({date:M월 d일})";
|
||||
if (date == today.AddDays(-1)) return $"어제 ({date:M월 d일})";
|
||||
return date.ToString("yyyy년 M월 d일 (ddd)", CultureInfo.GetCultureInfo("ko-KR"));
|
||||
}
|
||||
|
||||
private void ApplyAppDataStats()
|
||||
|
|
@ -1098,7 +1115,7 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
else if (TabPastBtn.IsChecked == true)
|
||||
{
|
||||
var confirm = MessageBox.Show(this,
|
||||
"Past Results 전체를 삭제하시겠습니까?\n영구 저장된 이력도 함께 삭제됩니다.",
|
||||
"변환 기록 전체를 삭제하시겠습니까?\n영구 저장된 이력도 함께 삭제됩니다.",
|
||||
"Everything2Everything",
|
||||
MessageBoxButton.OKCancel, MessageBoxImage.Question);
|
||||
if (confirm != MessageBoxResult.OK) return;
|
||||
|
|
@ -1386,17 +1403,33 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
|
|||
|
||||
private void UpdateQualityPanelForFormat(string? extension)
|
||||
{
|
||||
if (QualityPanel is null || QualityLabelText is null) return;
|
||||
var ext = extension?.ToLowerInvariant();
|
||||
var supportsQuality = ext is ".jpg" or ".jpeg" or ".webp" or ".avif";
|
||||
QualityPanel.Visibility = supportsQuality ? Visibility.Visible : Visibility.Collapsed;
|
||||
QualityLabelText.Text = ext switch
|
||||
var isImageQuality = ext is ".jpg" or ".jpeg" or ".webp" or ".avif";
|
||||
var isVideo = ext is ".mp4" or ".mkv" or ".webm" or ".mov" or ".avi";
|
||||
var isAudio = ext is ".mp3" or ".aac" or ".m4a" or ".opus" or ".ogg" or ".flac" or ".wav";
|
||||
var isPdf = ext is ".pdf";
|
||||
|
||||
if (QualityPanel is not null)
|
||||
{
|
||||
".jpg" or ".jpeg" => "JPEG QUALITY",
|
||||
".webp" => "WEBP QUALITY",
|
||||
".avif" => "AVIF QUALITY",
|
||||
_ => "ENCODING QUALITY",
|
||||
};
|
||||
QualityPanel.Visibility = isImageQuality ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (QualityLabelText is not null)
|
||||
{
|
||||
QualityLabelText.Text = ext switch
|
||||
{
|
||||
".jpg" or ".jpeg" => "JPEG 압축 품질",
|
||||
".webp" => "WebP 압축 품질",
|
||||
".avif" => "AVIF 압축 품질",
|
||||
_ => "압축 품질",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (VideoQuickPanel is not null)
|
||||
VideoQuickPanel.Visibility = isVideo ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (AudioQuickPanel is not null)
|
||||
AudioQuickPanel.Visibility = isAudio ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (PdfQuickPanel is not null)
|
||||
PdfQuickPanel.Visibility = isPdf ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
UpdateMediaPanelForFormat(extension);
|
||||
}
|
||||
|
|
@ -1471,9 +1504,17 @@ public sealed class QueueItem : INotifyPropertyChanged
|
|||
public string StateText
|
||||
{
|
||||
get => _state;
|
||||
set { _state = value; Raise(nameof(StateText)); Raise(nameof(IsDone)); }
|
||||
set { _state = value; Raise(nameof(StateText)); Raise(nameof(DisplayStateText)); Raise(nameof(IsDone)); }
|
||||
}
|
||||
|
||||
/// <summary>사용자에게 표시되는 정제된 한국어 상태 텍스트 (AGENTS.md Rule 3).</summary>
|
||||
public string DisplayStateText => _state switch
|
||||
{
|
||||
"queued" => "대기 중",
|
||||
"done" => "변환 완료",
|
||||
_ => _state,
|
||||
};
|
||||
|
||||
public Brush StateBrush => _state switch
|
||||
{
|
||||
"queued" => (Application.Current?.TryFindResource("FsTextTertiary") as Brush) ?? Brushes.Gray,
|
||||
|
|
@ -1547,7 +1588,7 @@ public sealed class DateGroup : INotifyPropertyChanged
|
|||
public string DateTitle { get; }
|
||||
public ObservableCollection<HistoryRow> Entries { get; } = new();
|
||||
public long SessionSavingsBytes { get; set; }
|
||||
public string SessionSavingsText => $"Session Savings: {MainWindow.HumanizeBytes(SessionSavingsBytes)}";
|
||||
public string SessionSavingsText => $"세션 절감: {MainWindow.HumanizeBytes(SessionSavingsBytes)}";
|
||||
|
||||
public DateGroup(string dateTitle) { DateTitle = dateTitle; }
|
||||
|
||||
|
|
@ -1591,7 +1632,7 @@ public sealed record HistoryRow(
|
|||
FormatLabel: label,
|
||||
FormatBrush: brush,
|
||||
FileName: Path.GetFileName(e.SourcePath),
|
||||
MetaLine: $"{e.Timestamp:HH:mm:ss} • {e.OutputCount} output(s)",
|
||||
MetaLine: $"{e.Timestamp:HH:mm:ss} • {e.OutputCount}개 파일",
|
||||
SizeText: MainWindow.HumanizeBytes(e.SourceSizeBytes),
|
||||
SavingsText: $"{arrow} {MainWindow.HumanizeBytes(Math.Abs(saved))}",
|
||||
SourcePath: e.SourcePath,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ControlsDictionary/>
|
||||
<ResourceDictionary Source="FormatShiftTheme.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ControlsDictionary/>
|
||||
<ResourceDictionary Source="FormatShiftTheme.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ControlsDictionary/>
|
||||
<ResourceDictionary Source="FormatShiftTheme.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
|
|
@ -29,132 +30,132 @@
|
|||
<StackPanel>
|
||||
|
||||
<!-- ===== AI 카드 ===== -->
|
||||
<Border Background="{StaticResource FsBgSurface}"
|
||||
BorderBrush="{StaticResource FsBorderHairline}" BorderThickness="1"
|
||||
CornerRadius="10" Padding="20" Margin="0,0,0,16">
|
||||
<StackPanel>
|
||||
<TextBlock Text="AI 텍스트 변환" Style="{StaticResource FsBodyStyle}" FontWeight="SemiBold" FontSize="15"/>
|
||||
<TextBlock Text="요약 · 번역 · 교정에 사용됩니다 (종량 과금 · 네트워크 필요). 키가 없으면 AI 변환만 비활성됩니다."
|
||||
Style="{StaticResource FsCaptionStyle}" TextWrapping="Wrap" Margin="0,4,0,16"/>
|
||||
<Border Style="{StaticResource FsDoubleBezelShellStyle}" Margin="0,0,0,16">
|
||||
<Border Style="{StaticResource FsDoubleBezelCoreStyle}" Padding="20">
|
||||
<StackPanel>
|
||||
<TextBlock Text="AI 텍스트 변환" Style="{StaticResource FsBodyStyle}" FontWeight="SemiBold" FontSize="15"/>
|
||||
<TextBlock Text="요약 · 번역 · 교정에 사용됩니다 (종량 과금 · 네트워크 필요). 키가 없으면 AI 변환만 비활성됩니다."
|
||||
Style="{StaticResource FsCaptionStyle}" TextWrapping="Wrap" Margin="0,4,0,16"/>
|
||||
|
||||
<TextBlock Text="기본 백엔드" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<ComboBox x:Name="BackendCombo" Margin="0,0,0,16">
|
||||
<ComboBoxItem Content="자동 (API 키 우선, 없으면 Codex)"/>
|
||||
<ComboBoxItem Content="OpenAI (API 키)"/>
|
||||
<ComboBoxItem Content="Anthropic (API 키)"/>
|
||||
<ComboBoxItem Content="Codex CLI (ChatGPT 구독 OAuth)"/>
|
||||
</ComboBox>
|
||||
<TextBlock Text="기본 백엔드" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<ComboBox x:Name="BackendCombo" Margin="0,0,0,16">
|
||||
<ComboBoxItem Content="자동 (API 키 우선, 없으면 Codex)"/>
|
||||
<ComboBoxItem Content="OpenAI (API 키)"/>
|
||||
<ComboBoxItem Content="Anthropic (API 키)"/>
|
||||
<ComboBoxItem Content="Codex CLI (ChatGPT 구독 OAuth)"/>
|
||||
</ComboBox>
|
||||
|
||||
<!-- OpenAI -->
|
||||
<TextBlock Text="OpenAI API Key" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<Grid Margin="0,0,0,4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<PasswordBox x:Name="OpenAiKeyBox" Grid.Column="0"
|
||||
Style="{StaticResource FsPasswordInputStyle}"
|
||||
PasswordChanged="OnOpenAiKeyChanged"/>
|
||||
<Button x:Name="OpenAiVerifyBtn" Grid.Column="1" Content="확인"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}" Margin="8,0,0,0"
|
||||
IsEnabled="False" Click="OnVerifyOpenAi"/>
|
||||
</Grid>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,0,16">
|
||||
<Ellipse x:Name="OpenAiDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsTextTertiary}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="OpenAiStatus" Text="키 미설정" Style="{StaticResource FsCaptionStyle}" VerticalAlignment="Center"/>
|
||||
<!-- OpenAI -->
|
||||
<TextBlock Text="OpenAI API Key" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<Grid Margin="0,0,0,4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<PasswordBox x:Name="OpenAiKeyBox" Grid.Column="0"
|
||||
Style="{StaticResource FsPasswordInputStyle}"
|
||||
PasswordChanged="OnOpenAiKeyChanged"/>
|
||||
<Button x:Name="OpenAiVerifyBtn" Grid.Column="1" Content="확인"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}" Margin="8,0,0,0"
|
||||
IsEnabled="False" Click="OnVerifyOpenAi"/>
|
||||
</Grid>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,0,16">
|
||||
<Ellipse x:Name="OpenAiDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsTextTertiary}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="OpenAiStatus" Text="키 미설정" Style="{StaticResource FsCaptionStyle}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Anthropic -->
|
||||
<TextBlock Text="Anthropic API Key" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<Grid Margin="0,0,0,4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<PasswordBox x:Name="AnthropicKeyBox" Grid.Column="0"
|
||||
Style="{StaticResource FsPasswordInputStyle}"
|
||||
PasswordChanged="OnAnthropicKeyChanged"/>
|
||||
<Button x:Name="AnthropicVerifyBtn" Grid.Column="1" Content="확인"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}" Margin="8,0,0,0"
|
||||
IsEnabled="False" Click="OnVerifyAnthropic"/>
|
||||
</Grid>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,0,16">
|
||||
<Ellipse x:Name="AnthropicDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsTextTertiary}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="AnthropicStatus" Text="키 미설정" Style="{StaticResource FsCaptionStyle}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Codex CLI (OAuth) -->
|
||||
<TextBlock Text="Codex CLI (OAuth · API 키 불필요)" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,0,16">
|
||||
<Ellipse x:Name="CodexDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsTextTertiary}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="CodexStatus" Text="확인 중…" Style="{StaticResource FsCaptionStyle}" VerticalAlignment="Center"/>
|
||||
<Button x:Name="CodexVerifyBtn" Content="테스트" Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Margin="12,0,0,0" Padding="10,4" IsEnabled="False" Click="OnVerifyCodex" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 모델 -->
|
||||
<TextBlock Text="모델 (선택)" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<TextBox x:Name="ModelBox" Style="{StaticResource FsPathInputStyle}"/>
|
||||
<TextBlock Text="비워두면 기본 모델 (OpenAI: gpt-4o-mini, Anthropic: claude-3-5-sonnet-latest)"
|
||||
Style="{StaticResource FsCaptionStyle}" Margin="0,6,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Anthropic -->
|
||||
<TextBlock Text="Anthropic API Key" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<Grid Margin="0,0,0,4">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<PasswordBox x:Name="AnthropicKeyBox" Grid.Column="0"
|
||||
Style="{StaticResource FsPasswordInputStyle}"
|
||||
PasswordChanged="OnAnthropicKeyChanged"/>
|
||||
<Button x:Name="AnthropicVerifyBtn" Grid.Column="1" Content="확인"
|
||||
Style="{StaticResource FsSecondaryButtonStyle}" Margin="8,0,0,0"
|
||||
IsEnabled="False" Click="OnVerifyAnthropic"/>
|
||||
</Grid>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,0,16">
|
||||
<Ellipse x:Name="AnthropicDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsTextTertiary}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="AnthropicStatus" Text="키 미설정" Style="{StaticResource FsCaptionStyle}" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Codex CLI (OAuth) -->
|
||||
<TextBlock Text="Codex CLI (OAuth · API 키 불필요)" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,0,16">
|
||||
<Ellipse x:Name="CodexDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsTextTertiary}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="CodexStatus" Text="확인 중…" Style="{StaticResource FsCaptionStyle}" VerticalAlignment="Center"/>
|
||||
<Button x:Name="CodexVerifyBtn" Content="테스트" Style="{StaticResource FsSecondaryButtonStyle}"
|
||||
Margin="12,0,0,0" Padding="10,4" IsEnabled="False" Click="OnVerifyCodex" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
|
||||
<!-- 모델 -->
|
||||
<TextBlock Text="모델 (선택)" Style="{StaticResource FsLabelStyle}" Margin="0,0,0,6"/>
|
||||
<TextBox x:Name="ModelBox" Style="{StaticResource FsPathInputStyle}"/>
|
||||
<TextBlock Text="비워두면 기본 모델 (OpenAI: gpt-4o-mini, Anthropic: claude-3-5-sonnet-latest)"
|
||||
Style="{StaticResource FsCaptionStyle}" Margin="0,6,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
<!-- ===== 외부 도구 카드 ===== -->
|
||||
<Border Background="{StaticResource FsBgSurface}"
|
||||
BorderBrush="{StaticResource FsBorderHairline}" BorderThickness="1"
|
||||
CornerRadius="10" Padding="20">
|
||||
<StackPanel>
|
||||
<TextBlock Text="외부 도구" Style="{StaticResource FsBodyStyle}" FontWeight="SemiBold" FontSize="15"/>
|
||||
<TextBlock Text="설치하면 영상/오디오·한글/Word 변환이 자동 활성화됩니다."
|
||||
Style="{StaticResource FsCaptionStyle}" TextWrapping="Wrap" Margin="0,4,0,12"/>
|
||||
<CheckBox x:Name="GpuToggle" Content="영상 변환 시 GPU 가속(NVENC) 시도 — 없으면 CPU 자동 전환"
|
||||
Foreground="{StaticResource FsTextSecondary}" IsChecked="True" Margin="0,0,0,16"/>
|
||||
<Border Style="{StaticResource FsDoubleBezelShellStyle}">
|
||||
<Border Style="{StaticResource FsDoubleBezelCoreStyle}" Padding="20">
|
||||
<StackPanel>
|
||||
<TextBlock Text="외부 도구" Style="{StaticResource FsBodyStyle}" FontWeight="SemiBold" FontSize="15"/>
|
||||
<TextBlock Text="설치하면 영상/오디오·한글/Word 변환이 자동 활성화됩니다."
|
||||
Style="{StaticResource FsCaptionStyle}" TextWrapping="Wrap" Margin="0,4,0,12"/>
|
||||
<CheckBox x:Name="GpuToggle" Content="영상 변환 시 GPU 가속(NVENC) 시도 — 없으면 CPU 자동 전환"
|
||||
Foreground="{StaticResource FsTextSecondary}" IsChecked="True" Margin="0,0,0,16"/>
|
||||
|
||||
<!-- FFmpeg -->
|
||||
<Grid Margin="0,0,0,14">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Ellipse x:Name="FfmpegDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsStatusWarn}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="FFmpeg" Style="{StaticResource FsBodyStyle}" FontWeight="Medium" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="FfmpegStatus" Text="미설치" Style="{StaticResource FsCaptionStyle}" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
||||
<!-- FFmpeg -->
|
||||
<Grid Margin="0,0,0,14">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Ellipse x:Name="FfmpegDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsStatusWarn}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="FFmpeg" Style="{StaticResource FsBodyStyle}" FontWeight="Medium" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="FfmpegStatus" Text="미설치" Style="{StaticResource FsCaptionStyle}" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="영상/오디오 변환 (mp4·webm·mp3·flac…)" Style="{StaticResource FsCaptionStyle}" Margin="15,3,0,0"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="영상/오디오 변환 (mp4·webm·mp3·flac…)" Style="{StaticResource FsCaptionStyle}" Margin="15,3,0,0"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Button Content="다운로드" Style="{StaticResource FsSecondaryButtonStyle}" Click="OnDownloadFfmpeg"/>
|
||||
<Button Content="폴더 열기" Style="{StaticResource FsSecondaryButtonStyle}" Margin="6,0,0,0" Click="OnOpenFfmpegFolder"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Button Content="다운로드" Style="{StaticResource FsSecondaryButtonStyle}" Click="OnDownloadFfmpeg"/>
|
||||
<Button Content="폴더 열기" Style="{StaticResource FsSecondaryButtonStyle}" Margin="6,0,0,0" Click="OnOpenFfmpegFolder"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- LibreOffice -->
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Ellipse x:Name="LibreDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsStatusWarn}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="LibreOffice" Style="{StaticResource FsBodyStyle}" FontWeight="Medium" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="LibreStatus" Text="미설치" Style="{StaticResource FsCaptionStyle}" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
||||
<!-- LibreOffice -->
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Ellipse x:Name="LibreDot" Style="{StaticResource FsLossDotStyle}"
|
||||
Fill="{StaticResource FsStatusWarn}" Margin="0,0,7,0" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="LibreOffice" Style="{StaticResource FsBodyStyle}" FontWeight="Medium" VerticalAlignment="Center"/>
|
||||
<TextBlock x:Name="LibreStatus" Text="미설치" Style="{StaticResource FsCaptionStyle}" Margin="8,0,0,0" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="한글/Word/문서 변환 (HWP·DOCX→PDF/이미지). 한글은 H2Orestart 확장 필요." Style="{StaticResource FsCaptionStyle}" Margin="15,3,0,0" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="한글/Word/문서 변환 (HWP·DOCX→PDF/이미지). 한글은 H2Orestart 확장 필요." Style="{StaticResource FsCaptionStyle}" Margin="15,3,0,0" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Button Content="다운로드" Style="{StaticResource FsSecondaryButtonStyle}" Click="OnDownloadLibre"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<Button Content="다운로드" Style="{StaticResource FsSecondaryButtonStyle}" Click="OnDownloadLibre"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue