docs(09): create phase plan — 5 plans in 3 waves for scan cancellation & keyboard shortcuts
This commit is contained in:
1 parent
71c200bc5b
commit
08ff00f029
6 files changed
+1793
-2
No files matched your search
@@ -0,0 +1,319 @@
|
||||
---
|
||||
phase: 09-scan-cancellation-keyboard-shortcuts
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on:
|
||||
- 09-01
|
||||
files_modified:
|
||||
- frontend/src/components/config-page/config-page.ts
|
||||
autonomous: true
|
||||
requirements:
|
||||
- SCAN-01
|
||||
- SCAN-02
|
||||
- SCAN-03
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "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"
|
||||
artifacts:
|
||||
- path: "frontend/src/components/config-page/config-page.ts"
|
||||
provides: "Pause/Cancel/Resume buttons, cancel confirmation dialog, event handling for scan control"
|
||||
contains: "handleCancelScan"
|
||||
key_links:
|
||||
- from: "frontend/src/components/config-page/config-page.ts"
|
||||
to: "backend/library/scan_control.go"
|
||||
via: "Wails bindings CancelScan/PauseScan/ResumeScan"
|
||||
pattern: "CancelScan|PauseScan|ResumeScan"
|
||||
- from: "frontend/src/components/config-page/config-page.ts"
|
||||
to: "backend/events/events.go"
|
||||
via: "EventsOn for LibraryScanCancelled/Paused/Resumed"
|
||||
pattern: "LibraryScanCancelled|LibraryScanPaused|LibraryScanResumed"
|
||||
---
|
||||
|
||||
<objective>
|
||||
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.
|
||||
</objective>
|
||||
|
||||
<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>
|
||||
|
||||
<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
|
||||
|
||||
<interfaces>
|
||||
<!-- Scan control Wails bindings (from Plan 01) -->
|
||||
// From wailsjs/go/library/Library:
|
||||
export function CancelScan(): Promise<void>;
|
||||
export function PauseScan(): Promise<void>;
|
||||
export function ResumeScan(): Promise<void>;
|
||||
export function IsScanActive(): Promise<boolean>;
|
||||
export function IsScanPaused(): Promise<boolean>;
|
||||
|
||||
<!-- New events (from Plan 01) -->
|
||||
export const LibraryScanCancelled = "LibraryScanCancelled";
|
||||
export const LibraryScanPaused = "LibraryScanPaused";
|
||||
export const LibraryScanResumed = "LibraryScanResumed";
|
||||
|
||||
<!-- ScanMetrics now has Cancelled bool (from Plan 01) -->
|
||||
interface ScanMetrics {
|
||||
// ... existing fields ...
|
||||
cancelled: boolean;
|
||||
added: number;
|
||||
// ...
|
||||
}
|
||||
|
||||
<!-- Existing scan UI state in config-page.ts -->
|
||||
@state() scanning = false;
|
||||
@state() statusMessage = '';
|
||||
@state() scanProgress: ScanProgress | null = null;
|
||||
@state() metrics: any = null;
|
||||
@state() scanErrors = '';
|
||||
|
||||
<!-- Existing scan buttons location (config-page.ts:1327-1346) -->
|
||||
<div class="scan-actions">
|
||||
<button class="btn-warning" ?disabled=${this.scanning} @click=${this.handleSoftScan}>
|
||||
${this.scanning ? 'Scanning...' : 'Soft Scan'}
|
||||
</button>
|
||||
<button class="btn-danger" ?disabled=${this.scanning} @click=${this.handleFullRescan}>
|
||||
${this.scanning ? 'Scanning...' : 'Full Rescan'}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Status bar (config-page.ts:1348-1354) -->
|
||||
<div class="status-bar ${this.scanning ? 'active' : ''}">
|
||||
${this.scanProgress ? this.renderScanProgress() : this.statusMessage || 'Ready.'}
|
||||
</div>
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Add scan control state, event handlers, and UI buttons</name>
|
||||
<files>frontend/src/components/config-page/config-page.ts</files>
|
||||
<action>
|
||||
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;
|
||||
```
|
||||
|
||||
2. **Register event listeners** in `connectedCallback()` (find where existing scan events are registered and add alongside them):
|
||||
```typescript
|
||||
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.';
|
||||
});
|
||||
```
|
||||
|
||||
3. **Add scan control handler methods:**
|
||||
|
||||
```typescript
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
4. **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:
|
||||
```typescript
|
||||
<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>
|
||||
```
|
||||
|
||||
5. **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):
|
||||
|
||||
```typescript
|
||||
${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>
|
||||
` : ''}
|
||||
```
|
||||
|
||||
6. **Update the status bar** to show paused state:
|
||||
In the existing status bar rendering, update to show "Paused" when paused:
|
||||
```typescript
|
||||
<div class="status-bar ${this.scanning ? 'active' : ''} ${this.scanPaused ? 'paused' : ''}">
|
||||
${this.scanPaused
|
||||
? 'Scan paused.'
|
||||
: this.scanProgress
|
||||
? this.renderScanProgress()
|
||||
: this.statusMessage || 'Ready.'}
|
||||
</div>
|
||||
```
|
||||
|
||||
7. **Add CSS styles** for the cancel dialog and paused state. Add to the component's static styles:
|
||||
```css
|
||||
.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);
|
||||
}
|
||||
```
|
||||
|
||||
8. **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`).
|
||||
|
||||
9. **Reset scanPaused** in the existing `LibraryScanComplete` handler (the scan finished normally):
|
||||
Add `this.scanPaused = false;` to the existing handler.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd frontend && npx tsc --noEmit 2>&1 | head -30</automated>
|
||||
</verify>
|
||||
<done>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.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
```bash
|
||||
cd frontend && npx tsc --noEmit
|
||||
```
|
||||
TypeScript compiles with no errors. Scan control UI renders correctly.
|
||||
</verification>
|
||||
|
||||
<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>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/09-scan-cancellation-keyboard-shortcuts/09-03-SUMMARY.md`
|
||||
</output>
|
||||
Reference in new issue
Block a user