Files
yellowjacket/.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-03-PLAN.md
T

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
09-01
frontend/src/components/config-page/config-page.ts
true
SCAN-01
SCAN-02
SCAN-03
truths artifacts key_links
Cancel button appears during an active scan and calls CancelScan() Wails binding
Pause button appears during an active scan and calls PauseScan() Wails binding
Resume button replaces Pause when paused and calls ResumeScan() Wails binding
On cancel, a confirmation dialog asks 'Keep X tracks found so far, or discard?'
Keep option: scan stops, partial results remain in library
Discard option: scan stops, added tracks from this scan are removed
LibraryScanCancelled, LibraryScanPaused, LibraryScanResumed events update UI state
path provides contains
frontend/src/components/config-page/config-page.ts Pause/Cancel/Resume buttons, cancel confirmation dialog, event handling for scan control handleCancelScan
from to via pattern
frontend/src/components/config-page/config-page.ts backend/library/scan_control.go Wails bindings CancelScan/PauseScan/ResumeScan CancelScan|PauseScan|ResumeScan
from to via pattern
frontend/src/components/config-page/config-page.ts backend/events/events.go EventsOn for LibraryScanCancelled/Paused/Resumed LibraryScanCancelled|LibraryScanPaused|LibraryScanResumed
Add scan control buttons (Pause, Resume, Cancel) and a cancel confirmation dialog to the config page's library scan section.

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 = '';

${this.scanning ? 'Scanning...' : 'Soft Scan'} ${this.scanning ? 'Scanning...' : 'Full Rescan'}
${this.scanProgress ? this.renderScanProgress() : this.statusMessage || 'Ready.'}
Task 1: Add scan control state, event handlers, and UI buttons frontend/src/components/config-page/config-page.ts 1. **Add new state properties** to the config-page component class: ```typescript @state() private scanPaused = false; @state() private showCancelDialog = false; @state() private cancelMetrics: { added: number } | null = null; ```
  1. 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.';
    });
    
  2. 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;
    }
    
  3. 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-actions div 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>
    
  4. Add cancel confirmation dialog — render it conditionally when showCancelDialog is 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>
    ` : ''}
    
  5. 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>
    
  6. 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);
    }
    
  7. Import Wails bindings — add imports for CancelScan, PauseScan, ResumeScan from the Wails generated bindings path. Check the actual import path by looking at how existing Library bindings are imported (e.g., Scan and FullRescan).

  8. Reset scanPaused in the existing LibraryScanComplete handler (the scan finished normally): Add this.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.

```bash cd frontend && npx tsc --noEmit ``` TypeScript compiles with no errors. Scan control UI renders correctly.

<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>
After completion, create `.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-03-SUMMARY.md`