1
0
Fork 0

feat: imagegen2 아이콘·글리프·일러스트 생성 후 GUI 전면 적용 (투명 배경)

codex noninteractive + imagegen2로 앱 비주얼 에셋 일괄 생성, 코드 생성 차단
(gen-one.sh: FORBID 블록 + 코드파일 감지 + 재시도). 모든 산출물은
플러드필 배경 제거로 진짜 알파 투명 PNG화.

생성 에셋 (assets-gen/):
- app-master: 앱 아이콘 마스터 (사이클 화살표, 솔리드 타일 유지)
- 카테고리 글리프 8종: image/video/audio/document/data/vector/archive/ai
- 일러스트 2종: illus-empty(드롭존), illus-done(완료)
- logo-mark: 브랜드 마크

GUI 적용:
- 사이드바 헤더 브랜드 → logo-mark
- 드롭존 빈 상태 → illus-empty
- QuickProgressWindow 완료 → illus-done
- AI 작업 패널 → glyph-ai 스파클
- 큐/과거결과 행 라벨 아이콘 → 카테고리 글리프 (CategoryGlyphs 매핑)
- 미리보기 placeholder → 형식별 카테고리 글리프 (경고 대신)

앱 아이콘:
- app.ico (멀티 크기 16~256) + 윈도우 Icon
- MSIX 타일 4종(Square44/150·Wide310·Store) app-master 기반 갱신
- Package 버전 1.0.2.0
This commit is contained in:
Yun Chan 2026-06-01 22:00:42 +09:00
parent ef10c139cd
commit acb3fdbfd1
37 changed files with 189 additions and 34 deletions

BIN
assets-gen/app-master.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

55
assets-gen/gen-one.sh Normal file
View file

@ -0,0 +1,55 @@
#!/usr/bin/env bash
# gen-one.sh NAME "PROMPT BODY"
# codex imagegen2로 단일 PNG 생성. 코드 파일(.py/.svg/.html/.js)을 쓰면 실패로 보고 최대 3회 재시도.
set -u
NAME="$1"
BODY="$2"
ROOT="D:/workspace/Everything2Everthing/assets-gen"
DEST="$ROOT/$NAME.png"
for attempt in 1 2 3 4; do
WORK="$(mktemp -d)"
# 깨끗한 작업 폴더에서만 쓰게 하고, 결과는 out.png 한 장으로 강제
PROMPT=$(cat <<PROMPT
You have an image generation tool available (imagegen / image_gen). Use it now.
TASK: Generate a single PNG image and save it in the current working directory as exactly "out.png".
IMAGE DESCRIPTION:
$BODY
ABSOLUTE RULES — violating any of these is a hard failure:
- You MUST call the image generation tool to produce the picture.
- DO NOT write, create, or edit ANY code file of ANY kind: no .py, .svg, .html, .js, .ts, .sh, .json, no Python, no PIL/Pillow, no matplotlib, no cairo, no SVG markup, no canvas, no ASCII art.
- DO NOT try to "draw" or "render" the image with code. If you are even slightly tempted to script it, STOP and use the image generation tool instead.
- The ONLY file that may exist when you finish is out.png produced by the image generation tool.
- After saving, reply with just: DONE
PROMPT
)
echo "$PROMPT" | codex exec --skip-git-repo-check --sandbox workspace-write -C "$WORK" --ephemeral - >/dev/null 2>&1
# 코드 파일을 썼는지 검사
CODE=$(find "$WORK" -type f \( -name '*.py' -o -name '*.svg' -o -name '*.html' -o -name '*.js' -o -name '*.ts' -o -name '*.sh' \) 2>/dev/null | head -1)
PNG=$(find "$WORK" -type f -name '*.png' 2>/dev/null | head -1)
if [ -n "$CODE" ]; then
echo "[$NAME] attempt $attempt: codex wrote CODE ($CODE) — reject, retry"
rm -rf "$WORK"; continue
fi
if [ -z "$PNG" ]; then
echo "[$NAME] attempt $attempt: no PNG produced — retry"
rm -rf "$WORK"; continue
fi
# 너무 작은 PNG(코드로 만든 단색/플레이스홀더)도 거부
SZ=$(stat -c%s "$PNG" 2>/dev/null || echo 0)
if [ "$SZ" -lt 120000 ]; then
echo "[$NAME] attempt $attempt: PNG too small ($SZ bytes) — likely synthetic, retry"
rm -rf "$WORK"; continue
fi
cp "$PNG" "$DEST"
echo "[$NAME] OK ($SZ bytes) -> $DEST"
rm -rf "$WORK"
exit 0
done
echo "[$NAME] FAILED after retries"
exit 1

BIN
assets-gen/glyph-ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
assets-gen/glyph-audio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

BIN
assets-gen/glyph-data.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 MiB

BIN
assets-gen/glyph-image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
assets-gen/glyph-vector.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 MiB

BIN
assets-gen/glyph-video.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
assets-gen/illus-done.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
assets-gen/illus-empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

BIN
assets-gen/logo-mark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 B

After

Width:  |  Height:  |  Size: 4.7 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 729 B

After

Width:  |  Height:  |  Size: 5.9 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before After
Before After

View file

@ -12,7 +12,7 @@
<Identity
Name="Everything2Everything.YunChan"
Publisher="CN=Everything2EverythingDev"
Version="1.0.0.0"
Version="1.0.2.0"
ProcessorArchitecture="x64" />
<Properties>

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View file

@ -9,6 +9,7 @@
<AssemblyName>Everything2Everything</AssemblyName>
<RootNamespace>Everything2Everything.App</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>app.ico</ApplicationIcon>
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<NoWarn>$(NoWarn);NU1901;NU1902;NU1903;NU1904</NoWarn>
@ -26,4 +27,9 @@
<Using Include="System.IO" />
</ItemGroup>
<!-- 인앱 일러스트·글리프·브랜드 마크 (imagegen2 생성) -->
<ItemGroup>
<Resource Include="Assets\**\*.png" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,63 @@
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Everything2Everything.App.Views;
/// <summary>
/// 파일 확장자 → 카테고리 글리프(imagegen2 생성 PNG) 매핑.
/// 큐 행·미리보기 placeholder 등에서 형식을 한눈에 보여주기 위해 사용한다.
/// </summary>
public static class CategoryGlyphs
{
private static readonly Dictionary<string, string> ExtToCategory = new(StringComparer.OrdinalIgnoreCase)
{
// image
[".jpg"] = "image", [".jpeg"] = "image", [".jpe"] = "image", [".png"] = "image",
[".webp"] = "image", [".avif"] = "image", [".bmp"] = "image", [".tif"] = "image",
[".tiff"] = "image", [".gif"] = "image", [".heic"] = "image", [".heif"] = "image",
[".cr2"] = "image", [".nef"] = "image", [".arw"] = "image", [".dng"] = "image", [".raw"] = "image",
// vector
[".svg"] = "vector", [".eps"] = "vector", [".ai"] = "vector",
// video
[".mp4"] = "video", [".webm"] = "video", [".mkv"] = "video", [".mov"] = "video", [".avi"] = "video",
// audio
[".mp3"] = "audio", [".aac"] = "audio", [".m4a"] = "audio", [".opus"] = "audio",
[".ogg"] = "audio", [".flac"] = "audio", [".wav"] = "audio",
// document
[".pdf"] = "document", [".docx"] = "document", [".doc"] = "document",
[".hwp"] = "document", [".hwpx"] = "document", [".html"] = "document", [".htm"] = "document",
[".md"] = "document", [".txt"] = "document", [".rtf"] = "document",
// data
[".csv"] = "data", [".json"] = "data", [".xlsx"] = "data", [".xls"] = "data",
[".tsv"] = "data", [".xml"] = "data", [".yaml"] = "data", [".yml"] = "data",
// archive
[".zip"] = "archive", [".7z"] = "archive", [".rar"] = "archive",
[".tar"] = "archive", [".gz"] = "archive",
};
private static readonly Dictionary<string, ImageSource> Cache = new(StringComparer.OrdinalIgnoreCase);
/// <summary>확장자(.jpg 등)에 해당하는 카테고리 글리프 이미지. 매핑이 없으면 document로 폴백.</summary>
public static ImageSource ForExtension(string? extension)
{
var ext = string.IsNullOrWhiteSpace(extension) ? "" : extension.Trim();
if (ext.Length > 0 && ext[0] != '.') ext = "." + ext;
var category = ExtToCategory.TryGetValue(ext, out var c) ? c : "document";
return ForCategory(category);
}
/// <summary>카테고리명(image/video/audio/document/data/vector/archive/ai)에 해당하는 글리프.</summary>
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 img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnLoad;
img.UriSource = uri;
img.EndInit();
img.Freeze();
Cache[category] = img;
return img;
}
}

View file

@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Title="FormatShift Utility"
Icon="pack://application:,,,/Assets/app-icon.png"
Width="1280" Height="960"
MinWidth="1080" MinHeight="640"
ExtendsContentIntoTitleBar="True"
@ -62,10 +63,10 @@
<Border Grid.Row="0" BorderBrush="{StaticResource FsBorderSubtle}"
BorderThickness="0,0,0,1" Padding="24,0">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Path Width="18" Height="18" Stretch="Uniform"
Stroke="{StaticResource FsAccentBlue}" StrokeThickness="2"
Data="M3,3 L21,3 L21,21 L3,21 Z M12,8 L12,16 M8,12 L16,12"/>
<TextBlock Margin="8,0,0,0" FontSize="14" FontWeight="SemiBold"
<Image Width="22" Height="22"
RenderOptions.BitmapScalingMode="HighQuality"
Source="pack://application:,,,/Assets/logo-mark.png"/>
<TextBlock Margin="9,0,0,0" FontSize="14" FontWeight="SemiBold"
Foreground="{StaticResource FsTextPrimary}"
FontFamily="{StaticResource FsFontSans}">
FormatShift
@ -192,7 +193,12 @@
<!-- AI Task -->
<StackPanel x:Name="AiTaskPanel" Margin="0,0,0,32">
<TextBlock Text="AI 작업" Style="{StaticResource FsLabelStyle}"/>
<StackPanel Orientation="Horizontal">
<Image Width="15" Height="15" VerticalAlignment="Center" Margin="0,0,6,0"
RenderOptions.BitmapScalingMode="HighQuality"
Source="pack://application:,,,/Assets/glyph-ai.png"/>
<TextBlock Text="AI 작업" Style="{StaticResource FsLabelStyle}" VerticalAlignment="Center"/>
</StackPanel>
<ComboBox x:Name="AiTaskCombo" Margin="0,8,0,0"
SelectedIndex="0"
SelectionChanged="OnAiTaskChanged">
@ -401,12 +407,12 @@
StrokeDashArray="5 4"
Fill="#04FFFFFF"/>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"
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,18,0,0" FontSize="16" FontWeight="SemiBold"
Margin="48,52">
<Image Width="128" Height="128"
HorizontalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Source="pack://application:,,,/Assets/illus-empty.png"/>
<TextBlock Margin="0,16,0,0" FontSize="16" FontWeight="SemiBold"
Foreground="{StaticResource FsTextPrimary}"
HorizontalAlignment="Center"
Text="Drop files anywhere to queue"/>
@ -459,15 +465,9 @@
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<Border Width="32" Height="32" CornerRadius="4"
Background="{Binding FormatBrush}"
VerticalAlignment="Center">
<TextBlock Text="{Binding FormatLabel}"
Foreground="White" FontSize="10"
FontWeight="SemiBold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<Image Width="34" Height="34" VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding GlyphSource}"/>
<StackPanel Grid.Column="1" Margin="16,0,0,0"
VerticalAlignment="Center">
<TextBlock Text="{Binding FileName}"
@ -570,15 +570,9 @@
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<Border Width="32" Height="32" CornerRadius="4"
Background="{Binding FormatBrush}"
VerticalAlignment="Center">
<TextBlock Text="{Binding FormatLabel}"
Foreground="White" FontSize="10"
FontWeight="SemiBold"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
<Image Width="34" Height="34" VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Source="{Binding GlyphSource}"/>
<StackPanel Grid.Column="1" Margin="16,0,0,0"
VerticalAlignment="Center">
@ -707,7 +701,10 @@
<!-- Reason -->
<StackPanel x:Name="PreviewReason" Visibility="Collapsed"
HorizontalAlignment="Center" VerticalAlignment="Center" Margin="20">
<Path Width="32" Height="32" Stretch="Uniform"
<Image x:Name="PreviewGlyph" Width="84" Height="84"
HorizontalAlignment="Center" Visibility="Collapsed"
RenderOptions.BitmapScalingMode="HighQuality"/>
<Path x:Name="PreviewWarnIcon" Width="32" Height="32" Stretch="Uniform"
Stroke="{StaticResource FsAccentAmber}" StrokeThickness="1.5"
Data="M12,2 L22,20 L2,20 Z M12,9 L12,14 M12,17 L12,17.01"/>
<TextBlock x:Name="PreviewReasonText" Margin="0,12,0,0"

View file

@ -393,8 +393,10 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
}
else
{
PreviewReasonText.Text = result.Reason ?? "미리보기를 생성하지 못했습니다.";
PreviewReason.Visibility = Visibility.Visible;
// 래스터 미리보기가 없는 형식(영상·오디오·문서·데이터 등)은 경고 대신 카테고리 글리프로 표현
ShowPreviewGlyph(
System.IO.Path.GetExtension(sourcePath),
result.Reason ?? "이 형식은 미리보기를 만들 수 없습니다. 변환은 정상 동작합니다.");
}
PreviewDimText.Text = result.Dimensions ?? "—";
@ -431,6 +433,21 @@ public partial class MainWindow : Wpf.Ui.Controls.FluentWindow
PreviewEmpty.Visibility = Visibility.Collapsed;
PreviewImage.Visibility = Visibility.Collapsed;
PreviewLoading.Visibility = Visibility.Collapsed;
PreviewGlyph.Visibility = Visibility.Collapsed;
PreviewWarnIcon.Visibility = Visibility.Visible;
PreviewReasonText.Text = reason;
PreviewReason.Visibility = Visibility.Visible;
}
/// <summary>래스터 미리보기가 없는 형식은 경고 대신 카테고리 글리프로 안내한다.</summary>
private void ShowPreviewGlyph(string? extension, string reason)
{
PreviewEmpty.Visibility = Visibility.Collapsed;
PreviewImage.Visibility = Visibility.Collapsed;
PreviewLoading.Visibility = Visibility.Collapsed;
PreviewGlyph.Source = CategoryGlyphs.ForExtension(extension);
PreviewGlyph.Visibility = Visibility.Visible;
PreviewWarnIcon.Visibility = Visibility.Collapsed;
PreviewReasonText.Text = reason;
PreviewReason.Visibility = Visibility.Visible;
}
@ -1120,6 +1137,9 @@ public sealed class QueueItem : INotifyPropertyChanged
public required string MetaLine { get; init; }
public required long SourceSizeBytes { get; init; }
/// <summary>형식 카테고리 글리프(라벨 아이콘).</summary>
public System.Windows.Media.ImageSource GlyphSource => CategoryGlyphs.ForExtension(Path.GetExtension(SourcePath));
public string StateText
{
get => _state;
@ -1225,6 +1245,9 @@ public sealed record HistoryRow(
{
public string RevealPath => OutputPath ?? SourcePath;
/// <summary>형식 카테고리 글리프(라벨 아이콘).</summary>
public System.Windows.Media.ImageSource GlyphSource => CategoryGlyphs.ForExtension(Path.GetExtension(SourcePath));
public static HistoryRow From(HistoryEntry e)
{
var ext = Path.GetExtension(e.SourcePath).TrimStart('.').ToLowerInvariant();

View file

@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
Title="빠른 변환"
Width="560" Height="220"
Width="560" Height="240"
ExtendsContentIntoTitleBar="True"
WindowBackdropType="Mica"
WindowCornerPreference="Round"
@ -36,6 +36,11 @@
Style="{StaticResource TextCaption}" TextTrimming="CharacterEllipsis"/>
<ProgressBar x:Name="OverallProgress" Grid.Row="2" Height="6"
Minimum="0" Maximum="1"/>
<Image x:Name="DoneIllustration" Grid.Row="3" Visibility="Collapsed"
Width="96" Height="96" Margin="0,8,0,0"
HorizontalAlignment="Center" VerticalAlignment="Center"
RenderOptions.BitmapScalingMode="HighQuality"
Source="pack://application:,,,/Assets/illus-done.png"/>
<StackPanel Grid.Row="4" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,12,0,0">
<ui:Button x:Name="OpenFolderButton" Content="결과 폴더 열기"
Icon="{ui:SymbolIcon Folder24}"

View file

@ -69,6 +69,12 @@ public partial class QuickProgressWindow : FluentWindow
OpenInExplorer(_firstSuccessOutput);
Close();
}
else if (failed == 0 && success > 0)
{
// 전부 성공했지만 일부 건너뛰어 창이 남는 경우 — 완료 일러스트로 마무리
DoneIllustration.Visibility = Visibility.Visible;
Height = 360;
}
}
private void OnOpenFolderClick(object sender, RoutedEventArgs e)

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB