Skip to content

Commit 2f0e8e4

Browse files
committed
Harden viewer flow around paid synopses and series picker
- LLM Settings Save now closes the modal and, if the user has no saved results, routes to welcome so the Analyze form is visible instead of leaving them on the BB demo grid with no obvious next step. - Series picker: selecting "+ Analyze another…" resets the select value to the current series so clicking it again re-fires change (otherwise it was a no-op when it had been the prior selection). - Analyze: if a paid synopses draft is pending for a different show, confirm before overwriting rather than silently discarding work the user has already paid the LLM for. - Analyze failure path: return to welcome and re-render the resume banner instead of stranding the user on an empty grid. - Review cancel: ask before discarding the draft (safe-by-default — accidental Enter preserves synopses rather than throwing them away).
1 parent b6a542c commit 2f0e8e4

2 files changed

Lines changed: 32 additions & 2 deletions

File tree

src/tvplot/html/parts/app.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,14 @@ function _initApp() {
783783
if (btnTabAnalytics) btnTabAnalytics.addEventListener('click', () => _setTab('analytics'));
784784

785785
if (seriesSelect) {
786-
seriesSelect.addEventListener('change', () => _switchSeries(seriesSelect.value));
786+
// "+ Analyze another…" is a one-shot action, not a selection — when the
787+
// user picks it we reset the value so the next pick always fires `change`
788+
// (fixes the case where it was the only option and clicking it was a no-op).
789+
seriesSelect.addEventListener('change', () => {
790+
const v = seriesSelect.value;
791+
if (v === '__add__') seriesSelect.value = _currentSeriesName || '';
792+
_switchSeries(v);
793+
});
787794
}
788795

789796
// Export dropdown — all .btn-export-trigger buttons open the shared menu

src/tvplot/html/parts/pipeline.js

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,18 @@ async function _analyzeSeries(show, season) {
726726
return;
727727
}
728728

729+
// Protect existing draft — starting a new analysis would silently overwrite
730+
// synopses the user already paid for. Ask first.
731+
const existingDraft = Store.getSynopsesDraft();
732+
if (existingDraft && existingDraft.show && existingDraft.synopses && existingDraft.synopses.length > 0) {
733+
const draftLabel = `${existingDraft.show} S${String(existingDraft.season).padStart(2, '0')}`;
734+
const replace = window.confirm(
735+
`You have unfinished synopses for ${draftLabel} (generated previously at API cost).\n\n` +
736+
`Starting a new analysis for ${show} S${String(season).padStart(2, '0')} will replace them. Continue?`,
737+
);
738+
if (!replace) return;
739+
}
740+
729741
// Move from welcome screen into the viewer shell so the progress overlay
730742
// fits the normal layout. The grid may be empty (no data yet) — that's
731743
// fine, the progress overlay covers it until the pipeline finishes.
@@ -744,6 +756,11 @@ async function _analyzeSeries(show, season) {
744756
if (synopses.length === 0) throw new Error('All returned synopses were empty.');
745757
} catch (err) {
746758
_hidePipelineProgress();
759+
// Return to welcome so the user isn't stranded on an empty grid with a
760+
// dropdown whose only option is "+ Analyze another…" (which, being the
761+
// current value, no longer fires a `change` event on re-selection).
762+
showScreen('welcome');
763+
_renderResumeBanner && _renderResumeBanner();
747764
alert(`Couldn't fetch synopses: ${err.message}`);
748765
return;
749766
}
@@ -769,7 +786,13 @@ async function _analyzeSeries(show, season) {
769786
async function _reviewAndRun(show, season, synopses, provider, apiKey) {
770787
const confirmed = await _confirmSynopses(show, season, synopses);
771788
if (!confirmed) {
772-
Store.clearSynopsesDraft();
789+
// Synopses were generated at API cost — ask to keep by default (OK = save,
790+
// Cancel = discard). Safe-by-default: accidental Enter preserves the draft.
791+
const save = window.confirm(
792+
`Save the generated synopses for ${show} S${String(season).padStart(2, '0')}?\n\n` +
793+
`They were produced by a paid LLM call. Saving lets you resume later without regenerating.`,
794+
);
795+
if (!save) Store.clearSynopsesDraft();
773796
return;
774797
}
775798

0 commit comments

Comments
 (0)