improve AI rewrite UX and title quality

**AI button: dynamic tooltip hints**
- titleAiBtn and descAiBtn now show "Rewrite with AI" when the field
  already has user content, "Generate with AI" when empty.
- updateAiButtonHints() fires on every title/description input event
  and whenever syncStepFields() runs to keep hints current.

**Stronger rewrite prompt**
- When the user has typed a draft title or description, the prompt now
  shows it explicitly: "User's draft title (rewrite this): '...'"
- Rules changed from "improve its wording" to "Your only job is to
  polish its grammar and phrasing. Do NOT replace it with something
  different." — prevents the model from ignoring the user's text and
  generating fresh content from capture context.
- The suggested-title hint is suppressed when a draft title exists so
  the model doesn't silently swap the user's text for the auto-title.

**Title quality: generic window title filter**
- GENERIC_WINDOW_TITLES Set filters "New Tab", "Untitled", "Loading"
  etc. from the window-title path so they no longer produce titles
  like "Open New Tab in Chrome".

**Title quality: app name stripping for non-browser apps**
- stripBrowserNameSuffix now accepts an optional appName; it strips
  the app's display name and process name from the window title suffix
  using the same pattern as browser names.
- "Document1.docx - Word" with appName "winword" → "Document1.docx".
- buildCaptureTitle passes metadata.appName into the strip call.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
2026-06-24 10:42:56 -05:00
co-authored by Claude Sonnet 4.6
parent 879434f14f
commit 162ae0ae05
2 changed files with 69 additions and 19 deletions
+18
View File
@@ -227,6 +227,21 @@ class GuideEditor {
return this.runAiGeneration('block', { blockId: block.id, button });
}
updateAiButtonHints() {
const PLACEHOLDER_TITLES = new Set([
'', 'screen capture', 'window capture', 'region capture', 'capture',
]);
if (this.dom.titleAiBtn) {
const val = (this.dom.titleInput?.value || '').trim();
const hasDraft = Boolean(val) && !PLACEHOLDER_TITLES.has(val.toLowerCase());
this.dom.titleAiBtn.title = hasDraft ? 'Rewrite step title with AI' : 'Generate step title with AI';
}
if (this.dom.descAiBtn) {
const hasDesc = Boolean((this.dom.descEditor?.innerText || '').trim());
this.dom.descAiBtn.title = hasDesc ? 'Rewrite description with AI' : 'Generate description with AI';
}
}
get currentStep() {
return this.stepMap.get(this.selectedStepId) || null;
}
@@ -504,6 +519,7 @@ class GuideEditor {
this.saveStepDebounced();
this.renderStepList();
this.emitMeta();
this.updateAiButtonHints();
});
this.dom.titleAiBtn.addEventListener('click', () => this.generateTitleWithAi(this.dom.titleAiBtn));
@@ -575,6 +591,7 @@ class GuideEditor {
this.saveStepDebounced();
this.emitMeta();
this.updateToolbarState();
this.updateAiButtonHints();
});
this.dom.descEditor.addEventListener('keyup', () => this.updateToolbarState());
this.dom.descEditor.addEventListener('mouseup', () => this.updateToolbarState());
@@ -1019,6 +1036,7 @@ class GuideEditor {
if (document.activeElement !== this.dom.titleInput) this.dom.titleInput.value = step.title || '';
if (document.activeElement !== this.dom.descEditor) this.dom.descEditor.innerHTML = step.descriptionHtml || '';
this.dom.statusSelect.value = step.status || 'todo';
this.updateAiButtonHints();
this.dom.hiddenToggle.querySelector('input').checked = Boolean(step.hidden);
this.dom.skippedToggle.querySelector('input').checked = Boolean(step.skipped);
this.dom.forceNewPageToggle.querySelector('input').checked = Boolean(step.forceNewPage);