From 2d1b474931a8070f1634b15df421cf9ea95cdcf6 Mon Sep 17 00:00:00 2001 From: Twest2 Date: Wed, 24 Jun 2026 11:27:24 -0500 Subject: [PATCH] fix generate-all-steps: never overwrite existing content, right target per step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous version called target:'all' on every step unconditionally, which caused the AI to rewrite steps that already had good titles and descriptions — often making them worse or inaccurate. New behaviour: - Build a queue of only the steps that are actually missing content (placeholder title and/or empty description). - Determine target per step: 'title', 'description', or 'all' — only fill what is blank; leave existing user-written text completely alone. - If all steps already have titles and descriptions, show a toast and stop. - Call reload() once at the end instead of patching this.steps mid-loop, so the editor and step list both update atomically from the store. Co-Authored-By: Claude Sonnet 4.6 --- app/renderer/editor.js | 50 ++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/app/renderer/editor.js b/app/renderer/editor.js index 0f0e28b..420425b 100644 --- a/app/renderer/editor.js +++ b/app/renderer/editor.js @@ -228,36 +228,48 @@ class GuideEditor { return; } if (this.pendingSave) await this.flushStep(); + + // Only fill fields that are actually empty — never overwrite user-written content. + const PLACEHOLDER_TITLES = new Set([ + '', 'screen capture', 'window capture', 'region capture', 'capture', 'untitled step', + ]); + const isEmptyDesc = (html) => !(html || '').replace(/<[^>]*>/g, '').trim(); + + const queue = this.steps + .map((step) => { + const titleEmpty = !step.title || PLACEHOLDER_TITLES.has(step.title.toLowerCase()); + const descEmpty = isEmptyDesc(step.descriptionHtml); + if (!titleEmpty && !descEmpty) return null; + const target = titleEmpty && descEmpty ? 'all' : titleEmpty ? 'title' : 'description'; + return { stepId: step.stepId, target }; + }) + .filter(Boolean); + + if (!queue.length) { + this.onToast('All steps already have titles and descriptions.'); + return; + } + if (button) setButtonLoading(button, true, 'Generating…'); let done = 0; let failed = 0; - const total = this.steps.length; + const total = queue.length; try { - for (const step of this.steps) { - this.onToast(`AI: filling step ${done + 1} of ${total}…`); + for (const { stepId, target } of queue) { + this.onToast(`AI: filling step ${done + failed + 1} of ${total}…`); try { - const result = await api.ai.fillStep({ - guideId: this.guideId, - stepId: step.stepId, - target: 'all', - }); - if (result?.ok) { - done++; - // Keep the in-memory steps list fresh so subsequent steps see updated guide context. - const idx = this.steps.findIndex((s) => s.stepId === step.stepId); - if (idx >= 0) this.steps[idx] = result.step; - } else { - failed++; - } + const result = await api.ai.fillStep({ guideId: this.guideId, stepId, target }); + if (result?.ok) done++; + else failed++; } catch { failed++; } } - // Reload the currently-visible step so the editor reflects its new text. - if (this.selectedStepId) await this.reload(this.selectedStepId); + // Reload re-fetches all steps from the store so the editor and list both reflect the new text. + await this.reload(this.selectedStepId); const msg = failed ? `AI filled ${done} step${done === 1 ? '' : 's'} (${failed} failed).` - : `AI filled all ${done} step${done === 1 ? '' : 's'}.`; + : `AI filled ${done} step${done === 1 ? '' : 's'}.`; this.onToast(msg, failed ? { error: true } : undefined); } finally { if (button) setButtonLoading(button, false);