설정창에서 런처 색인 진행률과 남은 시간을 직접 표시하도록 개선
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- IndexService에 LauncherIndexProgressInfo와 진행 이벤트를 추가해 전체 색인 중 진행률, 상태 문구, 예상 남은 시간을 계산하도록 변경 - 설정창 일반 탭의 인덱싱 속도 아래에 프로그레스바와 상태/상세 문구 패널을 추가하고 실시간으로 바인딩되도록 연결 - 전체 색인 완료 후 최근 색인 완료 요약과 검색 가능 항목 수를 같은 위치에 유지해 런처 하단 완료 문구 의존도를 낮춤 - README와 DEVELOPMENT 문서에 변경 이력 및 반영 시각을 기록 검증 - 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:
@@ -48,9 +48,11 @@ public class IndexService : IDisposable
|
||||
public IReadOnlyList<IndexEntry> Entries => _index;
|
||||
|
||||
public event EventHandler? IndexRebuilt;
|
||||
public event EventHandler<LauncherIndexProgressInfo>? IndexProgressChanged;
|
||||
public TimeSpan LastIndexDuration { get; private set; }
|
||||
public int LastIndexCount { get; private set; }
|
||||
public bool HasCachedIndexLoaded { get; private set; }
|
||||
public LauncherIndexProgressInfo CurrentProgress { get; private set; } = LauncherIndexProgressInfo.Idle();
|
||||
|
||||
public IndexService(SettingsService settings)
|
||||
{
|
||||
@@ -110,16 +112,30 @@ public class IndexService : IDisposable
|
||||
try
|
||||
{
|
||||
var fileSystemEntries = new List<IndexEntry>();
|
||||
var paths = GetExpandedIndexPaths();
|
||||
var paths = GetExpandedIndexPaths()
|
||||
.Where(Directory.Exists)
|
||||
.ToList();
|
||||
var allowedExts = GetAllowedExtensions();
|
||||
var indexSpeed = _settings.Settings.IndexSpeed ?? "normal";
|
||||
|
||||
foreach (var dir in paths)
|
||||
ReportIndexProgress(LauncherIndexProgressInfo.Running(0, "색인 준비 중...", "남은 시간 계산 중"));
|
||||
|
||||
for (var index = 0; index < paths.Count; index++)
|
||||
{
|
||||
if (!Directory.Exists(dir))
|
||||
continue;
|
||||
var dir = paths[index];
|
||||
var progressBase = paths.Count == 0 ? 0 : (double)index / paths.Count;
|
||||
ReportIndexProgress(LauncherIndexProgressInfo.Running(
|
||||
progressBase * 100,
|
||||
$"스캔 중: {Path.GetFileName(dir)}",
|
||||
EstimateRemainingText(progressBase, sw.Elapsed)));
|
||||
|
||||
await ScanDirectoryAsync(dir, fileSystemEntries, allowedExts, indexSpeed, ct);
|
||||
|
||||
var completedProgress = paths.Count == 0 ? 1 : (double)(index + 1) / paths.Count;
|
||||
ReportIndexProgress(LauncherIndexProgressInfo.Running(
|
||||
completedProgress * 100,
|
||||
$"스캔 완료: {Path.GetFileName(dir)}",
|
||||
EstimateRemainingText(completedProgress, sw.Elapsed)));
|
||||
}
|
||||
|
||||
ComputeAllSearchCaches(fileSystemEntries);
|
||||
@@ -135,6 +151,10 @@ public class IndexService : IDisposable
|
||||
LastIndexCount = _index.Count;
|
||||
HasCachedIndexLoaded = fileSystemEntries.Count > 0;
|
||||
PersistCache();
|
||||
ReportIndexProgress(LauncherIndexProgressInfo.Completed(
|
||||
LastIndexCount,
|
||||
LastIndexDuration,
|
||||
$"최근 색인 완료 · {LastIndexCount:N0}개 항목 · {LastIndexDuration.TotalSeconds:F1}초"));
|
||||
LogService.Info($"런처 인덱싱 완료: {fileSystemEntries.Count}개 파일 시스템 항목 ({sw.Elapsed.TotalSeconds:F1}초)");
|
||||
IndexRebuilt?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
@@ -291,10 +311,34 @@ public class IndexService : IDisposable
|
||||
|
||||
PersistCacheDeferred();
|
||||
LastIndexCount = _index.Count;
|
||||
IndexRebuilt?.Invoke(this, EventArgs.Empty);
|
||||
CurrentProgress = LauncherIndexProgressInfo.Completed(
|
||||
LastIndexCount,
|
||||
LastIndexDuration,
|
||||
$"최근 색인 완료 · {LastIndexCount:N0}개 항목");
|
||||
LogService.Info($"런처 인덱스 증분 갱신: {triggerPath}");
|
||||
}
|
||||
|
||||
private void ReportIndexProgress(LauncherIndexProgressInfo progress)
|
||||
{
|
||||
CurrentProgress = progress;
|
||||
IndexProgressChanged?.Invoke(this, progress);
|
||||
}
|
||||
|
||||
private static string EstimateRemainingText(double progressRatio, TimeSpan elapsed)
|
||||
{
|
||||
if (progressRatio <= 0.01)
|
||||
return "남은 시간 계산 중";
|
||||
|
||||
var remaining = TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds * ((1 - progressRatio) / progressRatio));
|
||||
if (remaining.TotalSeconds < 1)
|
||||
return "곧 완료";
|
||||
|
||||
if (remaining.TotalMinutes >= 1)
|
||||
return $"예상 남은 시간 {remaining.Minutes + (remaining.Hours * 60)}분 {remaining.Seconds}초";
|
||||
|
||||
return $"예상 남은 시간 {Math.Max(1, (int)Math.Round(remaining.TotalSeconds))}초";
|
||||
}
|
||||
|
||||
private bool AddPathEntryIncrementally(string fullPath, string rootPath)
|
||||
{
|
||||
if (ShouldIgnorePath(fullPath))
|
||||
@@ -926,3 +970,37 @@ public enum IndexEntryType
|
||||
Alias,
|
||||
Folder
|
||||
}
|
||||
|
||||
public sealed class LauncherIndexProgressInfo
|
||||
{
|
||||
public bool IsRunning { get; init; }
|
||||
public double ProgressPercent { get; init; }
|
||||
public string StatusText { get; init; } = "";
|
||||
public string DetailText { get; init; } = "";
|
||||
|
||||
public static LauncherIndexProgressInfo Idle() => new()
|
||||
{
|
||||
IsRunning = false,
|
||||
ProgressPercent = 0,
|
||||
StatusText = "색인 대기 중",
|
||||
DetailText = "필요할 때 전체 색인을 시작합니다."
|
||||
};
|
||||
|
||||
public static LauncherIndexProgressInfo Running(double progressPercent, string statusText, string detailText) => new()
|
||||
{
|
||||
IsRunning = true,
|
||||
ProgressPercent = Math.Clamp(progressPercent, 0, 100),
|
||||
StatusText = statusText,
|
||||
DetailText = detailText
|
||||
};
|
||||
|
||||
public static LauncherIndexProgressInfo Completed(int count, TimeSpan duration, string statusText) => new()
|
||||
{
|
||||
IsRunning = false,
|
||||
ProgressPercent = 100,
|
||||
StatusText = statusText,
|
||||
DetailText = count > 0
|
||||
? $"현재 검색 가능 항목 {count:N0}개 · 최근 전체 색인 {duration.TotalSeconds:F1}초"
|
||||
: "검색 가능한 항목이 없습니다."
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1056,6 +1056,26 @@
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<Border x:Name="IndexProgressPanel"
|
||||
Style="{StaticResource SettingsRow}"
|
||||
Visibility="Collapsed">
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="IndexProgressStatusText"
|
||||
Text="색인 대기 중"
|
||||
Style="{StaticResource RowLabel}"/>
|
||||
<ProgressBar x:Name="IndexProgressBar"
|
||||
Height="8"
|
||||
Margin="0,10,0,0"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Value="0"/>
|
||||
<TextBlock x:Name="IndexProgressDetailText"
|
||||
Text="필요할 때 전체 색인을 시작합니다."
|
||||
Margin="0,8,0,0"
|
||||
Style="{StaticResource RowHint}"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<!-- 알림 패널 (하위 탭) — 기존 알림 탭 내용을 여기로 통합 -->
|
||||
|
||||
@@ -15,6 +15,7 @@ public partial class SettingsWindow : Window
|
||||
private readonly SettingsViewModel _vm;
|
||||
private readonly Action<string> _previewCallback;
|
||||
private readonly Action _revertCallback;
|
||||
private IndexService? _indexService;
|
||||
private bool _saved;
|
||||
private bool _isDisplayModeSyncing;
|
||||
|
||||
@@ -59,9 +60,15 @@ public partial class SettingsWindow : Window
|
||||
catch { /* 인덱싱 실패해도 설정 저장은 완료 */ }
|
||||
});
|
||||
};
|
||||
Closed += (_, _) =>
|
||||
{
|
||||
if (_indexService != null)
|
||||
_indexService.IndexProgressChanged -= IndexService_IndexProgressChanged;
|
||||
};
|
||||
|
||||
Loaded += async (_, _) =>
|
||||
{
|
||||
BindIndexProgress();
|
||||
RefreshHotkeyBadges();
|
||||
SetVersionText();
|
||||
EnsureHotkeyInCombo();
|
||||
@@ -104,6 +111,35 @@ public partial class SettingsWindow : Window
|
||||
};
|
||||
}
|
||||
|
||||
private void BindIndexProgress()
|
||||
{
|
||||
_indexService = (System.Windows.Application.Current as App)?.IndexService;
|
||||
if (_indexService == null)
|
||||
return;
|
||||
|
||||
_indexService.IndexProgressChanged -= IndexService_IndexProgressChanged;
|
||||
_indexService.IndexProgressChanged += IndexService_IndexProgressChanged;
|
||||
ApplyIndexProgress(_indexService.CurrentProgress);
|
||||
}
|
||||
|
||||
private void IndexService_IndexProgressChanged(object? sender, LauncherIndexProgressInfo e)
|
||||
{
|
||||
Dispatcher.BeginInvoke(() => ApplyIndexProgress(e));
|
||||
}
|
||||
|
||||
private void ApplyIndexProgress(LauncherIndexProgressInfo progress)
|
||||
{
|
||||
if (IndexProgressPanel == null || IndexProgressStatusText == null || IndexProgressBar == null || IndexProgressDetailText == null)
|
||||
return;
|
||||
|
||||
IndexProgressPanel.Visibility = Visibility.Visible;
|
||||
IndexProgressStatusText.Text = progress.StatusText;
|
||||
IndexProgressDetailText.Text = progress.DetailText;
|
||||
IndexProgressBar.IsIndeterminate = progress.IsRunning && progress.ProgressPercent <= 0.01;
|
||||
if (!IndexProgressBar.IsIndeterminate)
|
||||
IndexProgressBar.Value = progress.ProgressPercent;
|
||||
}
|
||||
|
||||
private bool HasLegacyAgentTab()
|
||||
=> MainSettingsTab != null && AgentTabItem != null && MainSettingsTab.Items.Contains(AgentTabItem);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user