1
0
Fork 0

feat(ui): 디자인 마무리 — AI 작업종류 옵션 + 큐 accent bar + 빈 상태 + polish

디자인 세션 마지막 단계(순차 workflow). Linear/Geist 영감 polish + AI 작업 선택 UI.

- AI 작업종류 ComboBox(요약/번역/교정) + 번역 대상언어 → BuildOptions의 opts.Ai.Task/TargetLanguage 연결. 이제 Codex로 번역·교정도 가능
- 큐 row 좌측 형식색 accent bar + hover, 빈 상태(Browse + 안내) 개선
- FormatShiftTheme: FsNumStyle/FsLossPillStyle/FsListRowStyle/FsIconButtonStyle 재사용 스타일
This commit is contained in:
Yun Chan 2026-06-01 16:03:50 +09:00
parent 5c7a4a9ee3
commit e30559b3cd
3 changed files with 226 additions and 61 deletions

View file

@ -375,4 +375,82 @@
<Setter Property="Height" Value="8"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- ============================================================
UX polish 스타일 (단계 1) — Linear/Geist 영감, 절제된 확장
============================================================ -->
<!-- 숫자/통계용: 모노 + tabular 숫자 정렬(자리 흔들림 방지) -->
<Style x:Key="FsNumStyle" TargetType="TextBlock" BasedOn="{StaticResource FsMonoStyle}">
<Setter Property="Typography.NumeralAlignment" Value="Tabular"/>
<Setter Property="Typography.NumeralStyle" Value="Lining"/>
<Setter Property="Foreground" Value="{StaticResource FsTextSecondary}"/>
<Setter Property="TextOptions.TextFormattingMode" Value="Display"/>
</Style>
<!-- 손실 등급 pill: Background는 코드에서 FsLoss*Bg 주입 -->
<Style x:Key="FsLossPillStyle" TargetType="Border">
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Padding" Value="6,1"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- 큐/이력 row 공통 컨테이너: 투명 → hover 시 surface, 부드러운 fade -->
<Style x:Key="FsListRowStyle" TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{StaticResource FsBgSurfaceHover}"/>
<Setter Property="BorderBrush" Value="{StaticResource FsBorderHairline}"/>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="0.85" To="1" Duration="0:0:0.12"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
<!-- 32x32 아이콘 버튼(설정 ⚙, 삭제 ×): 투명 → hover surface, radius 6 -->
<Style x:Key="FsIconButtonStyle" TargetType="Button">
<Setter Property="Width" Value="32"/>
<Setter Property="Height" Value="32"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource FsTextSecondary}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="PART_Bd"
Background="{TemplateBinding Background}"
CornerRadius="6">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
TextElement.Foreground="{TemplateBinding Foreground}"
TextElement.FontSize="{TemplateBinding FontSize}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="PART_Bd" Property="Background" Value="{StaticResource FsBgSurfaceHover}"/>
<Setter Property="Foreground" Value="{StaticResource FsTextPrimary}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="PART_Bd" Property="Opacity" Value="0.4"/>
<Setter Property="Cursor" Value="Arrow"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View file

@ -190,6 +190,30 @@
</Border>
</StackPanel>
<!-- AI Task -->
<StackPanel x:Name="AiTaskPanel" Margin="0,0,0,32">
<TextBlock Text="AI 작업" Style="{StaticResource FsLabelStyle}"/>
<ComboBox x:Name="AiTaskCombo" Margin="0,8,0,0"
SelectedIndex="0"
SelectionChanged="OnAiTaskChanged">
<ComboBoxItem Content="요약"/>
<ComboBoxItem Content="번역"/>
<ComboBoxItem Content="교정"/>
</ComboBox>
<TextBlock x:Name="AiTaskHint" Margin="0,6,0,0"
Style="{StaticResource FsCaptionStyle}"
Text="텍스트(txt·md·docx) 출력 시 LLM이 적용합니다"/>
<StackPanel x:Name="AiTargetLangPanel" Margin="0,10,0,0"
Visibility="Collapsed">
<TextBlock Text="대상 언어" Style="{StaticResource FsCaptionStyle}"
Margin="0,0,0,4"/>
<TextBox x:Name="AiTargetLangBox"
Style="{StaticResource FsPathInputStyle}"
Text="영어"
ToolTip="번역 대상 언어 (예: 영어)"/>
</StackPanel>
</StackPanel>
<!-- Stats Box -->
<Border Background="{StaticResource FsBgInput}"
BorderBrush="{StaticResource FsBorderSubtle}"
@ -365,20 +389,43 @@
<!-- Active Queue view -->
<Grid x:Name="ActiveQueueView" Visibility="Collapsed">
<Grid x:Name="DropZoneEmpty">
<Border Background="{StaticResource FsBgBase}">
<Border Background="{StaticResource FsBgBase}" Padding="32">
<!-- 점선 드롭존: 절제된 dashed 보더 + 중앙 정렬 안내 -->
<Grid MaxWidth="440"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<!-- 점선 테두리(드롭존 느낌) -->
<Rectangle RadiusX="10" RadiusY="10"
Stroke="{StaticResource FsBorderHairline}"
StrokeThickness="1.25"
StrokeDashArray="5 4"
Fill="#04FFFFFF"/>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="48">
<Path Width="48" Height="48" Stretch="Uniform"
Margin="48,56">
<Path Width="44" Height="44" Stretch="Uniform"
HorizontalAlignment="Center"
Stroke="{StaticResource FsAccentBlue}" StrokeThickness="1.5"
Data="M21,15 L21,19 C21,20.1 20.1,21 19,21 L5,21 C3.9,21 3,20.1 3,19 L3,15 M7,10 L12,15 L17,10 M12,15 L12,3"/>
<TextBlock Margin="0,16,0,0" FontSize="16" FontWeight="SemiBold"
<TextBlock Margin="0,18,0,0" FontSize="16" FontWeight="SemiBold"
Foreground="{StaticResource FsTextPrimary}"
HorizontalAlignment="Center"
Text="Drop files anywhere to queue"/>
<TextBlock Margin="0,4,0,0" Style="{StaticResource FsCaptionStyle}"
<TextBlock Margin="0,6,0,0" Style="{StaticResource FsCaptionStyle}"
HorizontalAlignment="Center"
Text="PNG · JPG · GIF · HEIC · RAW · PDF · DOCX · HTML · HWP/HWPX"/>
TextAlignment="Center"
Text="이미지 · 문서 · 데이터 · 영상 · 오디오 등 무엇이든"/>
<Button Margin="0,22,0,0"
HorizontalAlignment="Center"
Style="{StaticResource FsSecondaryButtonStyle}"
Padding="18,8"
Content="Browse…"
Command="{Binding AddFilesCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
<TextBlock Margin="0,12,0,0" Style="{StaticResource FsCaptionStyle}"
HorizontalAlignment="Center"
Foreground="{StaticResource FsTextTertiary}"
Text="또는 Ctrl+O"/>
</StackPanel>
</Grid>
</Border>
</Grid>
<ScrollViewer x:Name="ActiveQueueScroll" VerticalScrollBarVisibility="Auto"
@ -386,7 +433,8 @@
<ItemsControl x:Name="ActiveQueueList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="0,0,0,8" Padding="12,10" CornerRadius="6"
<!-- row hover: FsListRowStyle 느낌(투명→surface hover, hairline 보더) + 좌측 형식색 accent bar -->
<Border x:Name="QueueRow" Margin="0,0,0,8" Padding="0" CornerRadius="6"
Background="{StaticResource FsBgPanel}"
BorderBrush="{StaticResource FsBorderSubtle}"
BorderThickness="1"
@ -394,6 +442,16 @@
PreviewMouseLeftButtonUp="OnQueueRowClick"
Tag="{Binding}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 좌측 2px 세로 accent bar: 해당 파일 형식색 -->
<Border Grid.Column="0"
Width="2"
Background="{Binding FormatBrush}"
CornerRadius="6,0,0,6"/>
<Grid Grid.Column="1" Margin="12,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
@ -434,19 +492,30 @@
Foreground="{Binding StateBrush}"
VerticalAlignment="Center"/>
<Button Grid.Column="4"
Style="{StaticResource FsSecondaryButtonStyle}"
Background="Transparent"
BorderThickness="0"
Padding="6"
Style="{StaticResource FsIconButtonStyle}"
Width="28" Height="28"
VerticalAlignment="Center"
HorizontalAlignment="Right"
ToolTip="큐에서 제거"
Click="OnRemoveQueueItem"
Tag="{Binding}">
<Path Width="14" Height="14" Stretch="Uniform"
<Path Width="13" Height="13" Stretch="Uniform"
Stroke="{StaticResource FsTextTertiary}"
StrokeThickness="2"
Data="M18,6 L6,18 M6,6 L18,18"/>
</Button>
</Grid>
</Grid>
</Border>
<DataTemplate.Triggers>
<!-- row hover: 배경 살짝 밝게 + hairline 보더 -->
<Trigger SourceName="QueueRow" Property="IsMouseOver" Value="True">
<Setter TargetName="QueueRow" Property="Background"
Value="{StaticResource FsBgSurfaceHover}"/>
<Setter TargetName="QueueRow" Property="BorderBrush"
Value="{StaticResource FsBorderHairline}"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

View file

@ -258,6 +258,14 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
};
}
private void OnAiTaskChanged(object sender, SelectionChangedEventArgs e)
{
// 번역(인덱스 1)일 때만 대상 언어 입력을 표시
if (AiTargetLangPanel is null) return;
AiTargetLangPanel.Visibility =
(AiTaskCombo?.SelectedIndex == 1) ? Visibility.Visible : Visibility.Collapsed;
}
private void OnPickOutputFolderClick(object sender, RoutedEventArgs e)
{
var dlg = new Microsoft.Win32.OpenFolderDialog { Title = "출력 폴더 선택" };
@ -286,6 +294,16 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
opts.OutputLocation = OutputLocation.SubfolderBesideSource;
}
// AI 작업 종류 (txt↔txt / md↔md 등 텍스트 변환 시 LlmProvider가 사용)
opts.Ai.Task = (AiTaskCombo?.SelectedIndex ?? 0) switch
{
1 => "translate",
2 => "proofread",
_ => "summarize",
};
var targetLang = AiTargetLangBox?.Text?.Trim();
opts.Ai.TargetLanguage = string.IsNullOrEmpty(targetLang) ? null : targetLang;
return opts;
}