12 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 09-scan-cancellation-keyboard-shortcuts | 03 | execute | 2 |
|
|
true |
|
|
Purpose: Frontend UX for SCAN-01/02/03. Wires to backend scan control methods from Plan 01. Output: Modified config-page.ts with scan control UI, event handling, and cancel confirmation.
<execution_context> @/home/caleb/.config/opencode/get-shit-done/workflows/execute-plan.md @/home/caleb/.config/opencode/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-RESEARCH.md @.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-CONTEXT.md @.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-01-SUMMARY.md@frontend/src/components/config-page/config-page.ts @frontend/src/events.ts
// From wailsjs/go/library/Library: export function CancelScan(): Promise; export function PauseScan(): Promise; export function ResumeScan(): Promise; export function IsScanActive(): Promise; export function IsScanPaused(): Promise;export const LibraryScanCancelled = "LibraryScanCancelled"; export const LibraryScanPaused = "LibraryScanPaused"; export const LibraryScanResumed = "LibraryScanResumed";
interface ScanMetrics { // ... existing fields ... cancelled: boolean; added: number; // ... }
@state() scanning = false; @state() statusMessage = ''; @state() scanProgress: ScanProgress | null = null; @state() metrics: any = null; @state() scanErrors = '';
-
Register event listeners in
connectedCallback()(find where existing scan events are registered and add alongside them):EventsOn(events.LibraryScanPaused, () => { this.scanPaused = true; }); EventsOn(events.LibraryScanResumed, () => { this.scanPaused = false; }); EventsOn(events.LibraryScanCancelled, (metrics: any) => { this.scanning = false; this.scanPaused = false; this.scanProgress = null; this.metrics = metrics; this.statusMessage = metrics?.cancelled ? 'Scan cancelled.' : 'Scan complete.'; }); -
Add scan control handler methods:
private handlePauseScan() { PauseScan(); } private handleResumeScan() { ResumeScan(); } private handleCancelScan() { // Show confirmation dialog with current progress const added = this.scanProgress?.added ?? 0; this.cancelMetrics = { added }; this.showCancelDialog = true; } private async handleCancelKeep() { this.showCancelDialog = false; this.cancelMetrics = null; CancelScan(); } private async handleCancelDiscard() { this.showCancelDialog = false; this.cancelMetrics = null; CancelScan(); // After cancel completes, trigger a full rescan to clear partial data. // The simpler approach: use the library's FullRescan which clears tables first. // Wait briefly for cancel to take effect, then initiate full rescan. // Alternatively, just cancel — the user can manually rescan if they want clean state. // Per research: "discard" clears the entire library since partial state is unreliable. // Call the existing clearLibraryTables equivalent via FullRescan. // For simplicity and safety: cancel + emit a status message saying "Partial results discarded. Run Full Rescan to start fresh." this.statusMessage = 'Scan cancelled. Partial results discarded — run Full Rescan for a clean library.'; // Note: A more sophisticated approach would track added IDs and delete them. // For v1.1, the simple discard = cancel + inform user approach is safer. } private handleCancelDialogDismiss() { this.showCancelDialog = false; this.cancelMetrics = null; } -
Modify the scan buttons area (around line 1327). Add Pause/Resume and Cancel buttons that appear ONLY during scanning. Place them between the existing scan buttons and the status bar:
Per user decision: "Pause and Cancel buttons placed next to the existing status label, above the existing progress bar."
Replace the
.scan-actionsdiv content when scanning is active:<div class="scan-actions"> ${this.scanning ? html` ${this.scanPaused ? html`<button class="btn-warning" @click=${this.handleResumeScan}>Resume</button>` : html`<button class="btn-warning" @click=${this.handlePauseScan}>Pause</button>` } <button class="btn-danger" @click=${this.handleCancelScan}>Cancel Scan</button> ` : html` <button class="btn-warning" @click=${this.handleSoftScan}>Soft Scan</button> <button class="btn-danger" @click=${this.handleFullRescan}>Full Rescan</button> ` } </div> -
Add cancel confirmation dialog — render it conditionally when
showCancelDialogis true. Place the dialog render at the end of the library section's render method (after the metrics tree, before the closing</config-section>tag):${this.showCancelDialog ? html` <div class="cancel-dialog-overlay" @click=${this.handleCancelDialogDismiss}> <div class="cancel-dialog" @click=${(e: Event) => e.stopPropagation()}> <div class="cancel-dialog-title">Cancel Scan</div> <div class="cancel-dialog-message"> ${this.cancelMetrics?.added ? `Keep ${this.cancelMetrics.added} tracks found so far, or discard?` : 'Cancel the current scan?'} </div> <div class="cancel-dialog-actions"> <button class="btn-primary" @click=${this.handleCancelKeep}> ${this.cancelMetrics?.added ? `Keep ${this.cancelMetrics.added} tracks` : 'Cancel Scan'} </button> <button class="btn-danger" @click=${this.handleCancelDiscard}> Discard </button> <button class="btn-ghost" @click=${this.handleCancelDialogDismiss}> Continue Scanning </button> </div> </div> </div> ` : ''} -
Update the status bar to show paused state: In the existing status bar rendering, update to show "Paused" when paused:
<div class="status-bar ${this.scanning ? 'active' : ''} ${this.scanPaused ? 'paused' : ''}"> ${this.scanPaused ? 'Scan paused.' : this.scanProgress ? this.renderScanProgress() : this.statusMessage || 'Ready.'} </div> -
Add CSS styles for the cancel dialog and paused state. Add to the component's static styles:
.cancel-dialog-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); display: flex; align-items: center; justify-content: center; z-index: 1000; } .cancel-dialog { background: var(--yj-bg-surface, #2a2a2a); border: 1px solid var(--yj-border, #444); border-radius: 8px; padding: 24px; max-width: 420px; width: 90%; } .cancel-dialog-title { font-size: var(--yj-text-lg, 18px); font-weight: 600; margin-bottom: 12px; } .cancel-dialog-message { font-size: var(--yj-text-sm, 14px); color: var(--yj-text-secondary, #aaa); margin-bottom: 20px; } .cancel-dialog-actions { display: flex; gap: 8px; justify-content: flex-end; } .status-bar.paused { color: var(--yj-accent, #ffd43b); } -
Import Wails bindings — add imports for
CancelScan,PauseScan,ResumeScanfrom the Wails generated bindings path. Check the actual import path by looking at how existing Library bindings are imported (e.g.,ScanandFullRescan). -
Reset scanPaused in the existing
LibraryScanCompletehandler (the scan finished normally): Addthis.scanPaused = false;to the existing handler. cd frontend && npx tsc --noEmit 2>&1 | head -30 Config page shows Pause/Cancel buttons during active scan. Pause toggles to Resume when paused. Cancel shows confirmation dialog with "Keep X tracks / Discard / Continue Scanning" options. All scan control events update UI state correctly. CSS styles render the dialog overlay properly.
<success_criteria>
- Pause button visible during scan, calls PauseScan()
- Resume button replaces Pause when paused, calls ResumeScan()
- Cancel button visible during scan, shows confirmation dialog
- Confirmation dialog shows track count and offers Keep/Discard/Continue
- LibraryScanPaused/Resumed/Cancelled events update component state
- Status bar shows "Scan paused." when paused
- Dialog overlay dismissible by clicking outside or "Continue Scanning" </success_criteria>