fix(ui): make Files-tab system prompts view-only, drop external-editor edit path
Editing a prompt file (even externally via "Open in editor") freezes it as a user customization and blocks future default updates. The Files tab no longer opens an external editor; it shows each prompt's content read-only in-app via a new ViewPromptCommand, dropping the file-seeding side effect. Reset to default and the customized-prompts deviation list are unchanged.
This commit is contained in:
@@ -62,7 +62,7 @@
|
||||
"retryPrompt": "Wiederholung",
|
||||
"dailyPrepPrompt": "Tagesplanung",
|
||||
"weeklyReportPrompt": "Wochenbericht",
|
||||
"openInEditor": "Im Editor öffnen",
|
||||
"viewPrompt": "Ansehen",
|
||||
"customizedSection": "ANGEPASST",
|
||||
"customizedHint": "Diese Prompts weichen vom mitgelieferten Standard ab und werden unverändert beibehalten. Zurücksetzen, um künftige Standard-Verbesserungen zu erhalten.",
|
||||
"resetToDefault": "Auf Standard zurücksetzen"
|
||||
@@ -604,7 +604,7 @@
|
||||
"loadFailed": "Nutzungsdaten konnten nicht geladen werden: {0}",
|
||||
"refreshFailed": "Nutzung konnte nicht aktualisiert werden: {0}"
|
||||
},
|
||||
"filesTab": { "workerOffline": "Worker offline.", "noneBundled": "Keine Standard-Agenten mitgeliefert.", "allPresent": "Alle Standard-Agenten bereits vorhanden.", "restored": "{0} Standard-Agent(en) wiederhergestellt.", "restoreFailed": "Wiederherstellung fehlgeschlagen: {0}", "openFailed": "Öffnen fehlgeschlagen: {0}", "resetToDefault": "Auf den mitgelieferten Standard zurückgesetzt." },
|
||||
"filesTab": { "workerOffline": "Worker offline.", "noneBundled": "Keine Standard-Agenten mitgeliefert.", "allPresent": "Alle Standard-Agenten bereits vorhanden.", "restored": "{0} Standard-Agent(en) wiederhergestellt.", "restoreFailed": "Wiederherstellung fehlgeschlagen: {0}", "resetToDefault": "Auf den mitgelieferten Standard zurückgesetzt." },
|
||||
"sessionSkillsTab": { "installed": "Installiert: {0}", "installFailed": "Installation fehlgeschlagen: {0}", "updated": "Aktualisiert.", "updateFailed": "Aktualisierung fehlgeschlagen: {0}", "removed": "Entfernt.", "removeFailed": "Entfernen fehlgeschlagen: {0}" },
|
||||
"worktreesTab": { "workerOffline": "Worker offline.", "removed": "{0} Worktree(s) entfernt.", "blocked": "Zwangsentfernung nicht möglich: {0} Aufgabe(n) laufen noch. Brich sie zuerst ab.", "removedFrom": "{0} Worktree(s) von {1} Aufgabe(n) entfernt.", "cleanupFailed": "Aufräumen fehlgeschlagen: {0}", "resetFailed": "Zurücksetzen fehlgeschlagen: {0}" },
|
||||
"worktreesOverview": { "titleAll": "Worktrees", "titleList": "Worktrees — {0}", "listFallback": "Liste", "cleanupFailed": "Aufräumen fehlgeschlagen.", "cleanupFailedDetailed": "Aufräumen fehlgeschlagen: {0}", "removed": "{0} Worktree(s) entfernt.", "discardFailed": "Worktree konnte nicht verworfen werden.", "keepFailed": "Worktree konnte nicht behalten werden.", "cannotForceRunning": "Eine laufende Aufgabe kann nicht zwangsweise entfernt werden.", "forceRemoveFailed": "Zwangsentfernung fehlgeschlagen.", "forceRemoveFailedDetailed": "Zwangsentfernung fehlgeschlagen: {0}", "batchProgress": "Merge {0}/{1}…", "batchDone": "{0} gemergt, {1} zu lösen." },
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"retryPrompt": "Retry",
|
||||
"dailyPrepPrompt": "Daily prep",
|
||||
"weeklyReportPrompt": "Weekly report",
|
||||
"openInEditor": "Open in editor",
|
||||
"viewPrompt": "View",
|
||||
"customizedSection": "CUSTOMIZED",
|
||||
"customizedHint": "These prompts differ from the bundled default and are kept as-is. Reset to pick up future default improvements.",
|
||||
"resetToDefault": "Reset to default"
|
||||
@@ -604,7 +604,7 @@
|
||||
"loadFailed": "Couldn't load usage data: {0}",
|
||||
"refreshFailed": "Couldn't refresh usage: {0}"
|
||||
},
|
||||
"filesTab": { "workerOffline": "Worker offline.", "noneBundled": "No default agents bundled.", "allPresent": "All default agents already present.", "restored": "Restored {0} default agent(s).", "restoreFailed": "Restore failed: {0}", "openFailed": "Open failed: {0}", "resetToDefault": "Reset to the bundled default." },
|
||||
"filesTab": { "workerOffline": "Worker offline.", "noneBundled": "No default agents bundled.", "allPresent": "All default agents already present.", "restored": "Restored {0} default agent(s).", "restoreFailed": "Restore failed: {0}", "resetToDefault": "Reset to the bundled default." },
|
||||
"sessionSkillsTab": { "installed": "Installed: {0}", "installFailed": "Install failed: {0}", "updated": "Updated.", "updateFailed": "Update failed: {0}", "removed": "Removed.", "removeFailed": "Remove failed: {0}" },
|
||||
"worktreesTab": { "workerOffline": "Worker offline.", "removed": "Removed {0} worktree(s).", "blocked": "Cannot force-remove: {0} task(s) still running. Cancel them first.", "removedFrom": "Removed {0} worktree(s) from {1} task(s).", "cleanupFailed": "Cleanup failed: {0}", "resetFailed": "Reset failed: {0}" },
|
||||
"worktreesOverview": { "titleAll": "Worktrees", "titleList": "Worktrees — {0}", "listFallback": "list", "cleanupFailed": "Cleanup failed.", "cleanupFailedDetailed": "Cleanup failed: {0}", "removed": "Removed {0} worktree(s).", "discardFailed": "Failed to discard worktree.", "keepFailed": "Failed to keep worktree.", "cannotForceRunning": "Cannot force-remove a running task.", "forceRemoveFailed": "Force remove failed.", "forceRemoveFailedDetailed": "Force remove failed: {0}", "batchProgress": "Merging {0}/{1}…", "batchDone": "Merged {0}, {1} need resolution." },
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using ClaudeDo.Data;
|
||||
using ClaudeDo.Ui.Localization;
|
||||
using ClaudeDo.Ui.Services;
|
||||
@@ -15,6 +14,8 @@ public sealed partial class FilesSettingsTabViewModel : ViewModelBase
|
||||
[ObservableProperty] private string _statusMessage = "";
|
||||
[ObservableProperty] private bool _isBusy;
|
||||
[ObservableProperty] private bool _hasCustomizedPrompts;
|
||||
[ObservableProperty] private string? _viewedKindName;
|
||||
[ObservableProperty] private string _viewedContent = "";
|
||||
|
||||
public string SystemPromptPath { get; } = PromptFiles.PathFor(PromptKind.System);
|
||||
public string PlanningPromptPath { get; } = PromptFiles.PathFor(PromptKind.Planning);
|
||||
@@ -49,20 +50,11 @@ public sealed partial class FilesSettingsTabViewModel : ViewModelBase
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void OpenPrompt(string? kindName)
|
||||
private void ViewPrompt(string? kindName)
|
||||
{
|
||||
if (!Enum.TryParse<PromptKind>(kindName, ignoreCase: true, out var kind)) return;
|
||||
try
|
||||
{
|
||||
// No override file yet: seed it with today's bundled default (hash-tracked, so a later
|
||||
// default change reconciles this file away automatically unless the user actually edits it)
|
||||
// rather than leaving a plain file that would freeze the prompt forever.
|
||||
if (!File.Exists(PromptFiles.PathFor(kind)))
|
||||
PromptFiles.Save(kind, PromptFiles.DefaultFor(kind));
|
||||
Process.Start(new ProcessStartInfo(PromptFiles.PathFor(kind)) { UseShellExecute = true });
|
||||
}
|
||||
catch (Exception ex) { StatusMessage = Loc.T("vm.filesTab.openFailed", ex.Message); }
|
||||
finally { RefreshCustomizedPrompts(); }
|
||||
ViewedKindName = kindName;
|
||||
ViewedContent = PromptFiles.ReadOrDefault(kind);
|
||||
}
|
||||
|
||||
private void RefreshCustomizedPrompts()
|
||||
|
||||
@@ -278,30 +278,39 @@
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto" ColumnDefinitions="120,*,Auto" RowSpacing="8">
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Classes="field-label" Text="{loc:Tr settings.files.systemPrompt}" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Row="0" Grid.Column="1" Classes="path-mono" Text="{Binding Files.SystemPromptPath}" VerticalAlignment="Center"/>
|
||||
<Button Classes="btn" Grid.Row="0" Grid.Column="2" Content="{loc:Tr settings.files.openInEditor}"
|
||||
Command="{Binding Files.OpenPromptCommand}" CommandParameter="System"/>
|
||||
<Button Classes="btn" Grid.Row="0" Grid.Column="2" Content="{loc:Tr settings.files.viewPrompt}"
|
||||
Command="{Binding Files.ViewPromptCommand}" CommandParameter="System"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Classes="field-label" Text="{loc:Tr settings.files.planningPrompt}" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="1" Classes="path-mono" Text="{Binding Files.PlanningPromptPath}" VerticalAlignment="Center"/>
|
||||
<Button Classes="btn" Grid.Row="1" Grid.Column="2" Content="{loc:Tr settings.files.openInEditor}"
|
||||
Command="{Binding Files.OpenPromptCommand}" CommandParameter="Planning"/>
|
||||
<Button Classes="btn" Grid.Row="1" Grid.Column="2" Content="{loc:Tr settings.files.viewPrompt}"
|
||||
Command="{Binding Files.ViewPromptCommand}" CommandParameter="Planning"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Classes="field-label" Text="{loc:Tr settings.files.planningInitialPrompt}" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Row="2" Grid.Column="1" Classes="path-mono" Text="{Binding Files.PlanningInitialPromptPath}" VerticalAlignment="Center"/>
|
||||
<Button Classes="btn" Grid.Row="2" Grid.Column="2" Content="{loc:Tr settings.files.openInEditor}"
|
||||
Command="{Binding Files.OpenPromptCommand}" CommandParameter="PlanningInitial"/>
|
||||
<Button Classes="btn" Grid.Row="2" Grid.Column="2" Content="{loc:Tr settings.files.viewPrompt}"
|
||||
Command="{Binding Files.ViewPromptCommand}" CommandParameter="PlanningInitial"/>
|
||||
<TextBlock Grid.Row="3" Grid.Column="0" Classes="field-label" Text="{loc:Tr settings.files.retryPrompt}" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Row="3" Grid.Column="1" Classes="path-mono" Text="{Binding Files.RetryPromptPath}" VerticalAlignment="Center"/>
|
||||
<Button Classes="btn" Grid.Row="3" Grid.Column="2" Content="{loc:Tr settings.files.openInEditor}"
|
||||
Command="{Binding Files.OpenPromptCommand}" CommandParameter="Retry"/>
|
||||
<Button Classes="btn" Grid.Row="3" Grid.Column="2" Content="{loc:Tr settings.files.viewPrompt}"
|
||||
Command="{Binding Files.ViewPromptCommand}" CommandParameter="Retry"/>
|
||||
<TextBlock Grid.Row="4" Grid.Column="0" Classes="field-label" Text="{loc:Tr settings.files.dailyPrepPrompt}" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Row="4" Grid.Column="1" Classes="path-mono" Text="{Binding Files.DailyPrepPromptPath}" VerticalAlignment="Center"/>
|
||||
<Button Classes="btn" Grid.Row="4" Grid.Column="2" Content="{loc:Tr settings.files.openInEditor}"
|
||||
Command="{Binding Files.OpenPromptCommand}" CommandParameter="DailyPrep"/>
|
||||
<Button Classes="btn" Grid.Row="4" Grid.Column="2" Content="{loc:Tr settings.files.viewPrompt}"
|
||||
Command="{Binding Files.ViewPromptCommand}" CommandParameter="DailyPrep"/>
|
||||
<TextBlock Grid.Row="5" Grid.Column="0" Classes="field-label" Text="{loc:Tr settings.files.weeklyReportPrompt}" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Row="5" Grid.Column="1" Classes="path-mono" Text="{Binding Files.WeeklyReportPromptPath}" VerticalAlignment="Center"/>
|
||||
<Button Classes="btn" Grid.Row="5" Grid.Column="2" Content="{loc:Tr settings.files.openInEditor}"
|
||||
Command="{Binding Files.OpenPromptCommand}" CommandParameter="WeeklyReport"/>
|
||||
<Button Classes="btn" Grid.Row="5" Grid.Column="2" Content="{loc:Tr settings.files.viewPrompt}"
|
||||
Command="{Binding Files.ViewPromptCommand}" CommandParameter="WeeklyReport"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="6" IsVisible="{Binding Files.ViewedKindName, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||
<TextBlock Classes="section-label" Text="{Binding Files.ViewedKindName}"/>
|
||||
<Border BorderBrush="{DynamicResource LineBrush}" BorderThickness="1" CornerRadius="6" Padding="10"
|
||||
Background="{DynamicResource DeepBrush}">
|
||||
<ScrollViewer MaxHeight="260">
|
||||
<ctl:MarkdownView Markdown="{Binding Files.ViewedContent}"/>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<StackPanel Spacing="6" IsVisible="{Binding Files.HasCustomizedPrompts}">
|
||||
<TextBlock Classes="section-label" Text="{loc:Tr settings.files.customizedSection}"/>
|
||||
<TextBlock Classes="meta" Text="{loc:Tr settings.files.customizedHint}" TextWrapping="Wrap"/>
|
||||
|
||||
Reference in New Issue
Block a user