From 161a1093e8aaf6c9ce3d8806e7194d47b5f85f78 Mon Sep 17 00:00:00 2001 From: TC Date: Fri, 12 Jun 2026 11:20:59 +1000 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20past=20progressive,=20weak=20verb,?= =?UTF-8?q?=20and=20cliche=20analysis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/aiService.ts | 28 +++++++ .../components/Toolbar/AnalysisToolbar.tsx | 55 ++++++++++++-- src/renderer/types/editor.ts | 6 +- src/renderer/utils/annotationParser.ts | 3 + src/renderer/utils/pastProgressive.ts | 76 +++++++++++++++++++ tsconfig.node.tsbuildinfo | 2 +- 6 files changed, 160 insertions(+), 10 deletions(-) create mode 100644 src/renderer/utils/pastProgressive.ts diff --git a/src/main/aiService.ts b/src/main/aiService.ts index c9cf010..5b9524e 100644 --- a/src/main/aiService.ts +++ b/src/main/aiService.ts @@ -160,6 +160,34 @@ PASSAGE: "[exact quoted text from the chapter]" PROBLEM: [one sentence explaining what is being told that should be shown] SUGGESTION: "[a concrete rewrite that shows the same moment through action, sensation, or dialogue]"`, + past_progressive: `Identify every instance of past progressive tense (was/were + verb-ing) in this chapter that would read more strongly in simple past tense. + +Do not flag past progressive that is grammatically necessary (e.g. interrupted actions: "She was reading when he entered"). Only flag cases where simple past would be tighter. + +For each instance: +ISSUE: Past Progressive +PASSAGE: "[exact quoted sentence from the chapter]" +PROBLEM: [one sentence on why simple past would be stronger] +SUGGESTION: "[rewritten in simple past]"`, + + weak_verbs: `Identify sentences in this chapter where the main verb is weak — relying on "to be" (was, were, is, are), "to have" (had, has), "to get" (got), "to seem" (seemed, appeared), "to look" (looked), or "to feel" (felt) as the primary predicate when a stronger, more specific verb would make the prose more vivid. + +Do not flag auxiliary uses (passive voice, perfect aspect) — only flag cases where these are the main, load-bearing verb in the sentence. + +For each instance: +ISSUE: Weak Verb +PASSAGE: "[exact quoted sentence from the chapter]" +PROBLEM: [explain what the weak verb is and why a stronger verb would serve better] +SUGGESTION: "[rewritten with a more specific, active verb]"`, + + cliches: `Identify every cliché, hackneyed phrase, or overused expression in this chapter. This includes worn-out metaphors, stock phrases, predictable comparisons, and any language that has become so common it has lost its impact. + +For each instance: +ISSUE: Cliché +PASSAGE: "[exact quoted phrase or sentence from the chapter]" +PROBLEM: [name the cliché and briefly explain why it's stale] +SUGGESTION: "[a fresher, more original alternative]"`, + critique: `Give an honest, detailed critique of this chapter as a whole. Structure your response as follows: **Overall impression** (2–3 sentences on what the chapter achieves and its most significant weakness) diff --git a/src/renderer/components/Toolbar/AnalysisToolbar.tsx b/src/renderer/components/Toolbar/AnalysisToolbar.tsx index 481fd8e..cfa10dd 100644 --- a/src/renderer/components/Toolbar/AnalysisToolbar.tsx +++ b/src/renderer/components/Toolbar/AnalysisToolbar.tsx @@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react' import { createPortal } from 'react-dom' import { useEditorStore } from '../../store/editorStore' import { detectPassiveVoice } from '../../utils/passiveVoice' +import { detectPastProgressive } from '../../utils/pastProgressive' import { parseAnnotationsFromAIResponse } from '../../utils/annotationParser' import { tooltipAnalysisCache } from '../Editor/MarkdownEditor' import '../FileTree/ContextMenu.css' @@ -256,7 +257,15 @@ export function AnalysisToolbar(): JSX.Element { setAnalysisMode('passive_voice') } - const runAIAnalysis = async (mode: 'consistency' | 'style' | 'show_tell' | 'critique'): Promise => { + const runPastProgressive = (): void => { + if (!hasFile) return + const found = detectPastProgressive(activeFileContent) + const existing = useEditorStore.getState().annotations.filter((a) => a.type !== 'past_progressive') + setAnnotations([...existing, ...found]) + setAnalysisMode('past_progressive') + } + + const runAIAnalysis = async (mode: 'consistency' | 'style' | 'show_tell' | 'critique' | 'weak_verbs' | 'cliches' | 'past_progressive'): Promise => { if (!activeFilePath || isAILoading) return setAnalysisMode(mode) @@ -269,7 +278,13 @@ export function AnalysisToolbar(): JSX.Element { ? 'Please analyze the style and pacing of this chapter and suggest improvements.' : mode === 'show_tell' ? 'Please identify every passage in this chapter where I am telling rather than showing.' - : 'Please give me an honest critique of this chapter.' + : mode === 'weak_verbs' + ? 'Please identify sentences in this chapter that use weak verbs (was, were, had, got, seemed, appeared, looked, felt) as the main predicate.' + : mode === 'cliches' + ? 'Please identify every cliché and overused phrase in this chapter.' + : mode === 'past_progressive' + ? 'Please identify every past progressive construction (was/were + verb-ing) in this chapter that would be stronger in simple past.' + : 'Please give me an honest critique of this chapter.' addUserMessage(prompt) startAssistantMessage() @@ -295,9 +310,13 @@ export function AnalysisToolbar(): JSX.Element { const currentHistory = useEditorStore.getState().chatHistory const lastMsg = currentHistory[currentHistory.length - 1] if (lastMsg?.role === 'assistant' && lastMsg.content.length > 0) { - // For show_tell mode, force all annotations to the show_tell type so the - // classifier doesn't accidentally mis-label them as 'style' or 'consistency'. - const overrideType = mode === 'show_tell' ? 'show_tell' : undefined + // Force annotation type for modes where the classifier might mis-label. + const overrideType = + mode === 'show_tell' ? 'show_tell' : + mode === 'weak_verbs' ? 'weak_verbs' : + mode === 'cliches' ? 'cliches' : + mode === 'past_progressive' ? 'past_progressive' : + undefined const { annotations: newAnnotations } = parseAnnotationsFromAIResponse(lastMsg.content, activeFileContent, overrideType) if (newAnnotations.length > 0) { const existing = useEditorStore.getState().annotations.filter((a) => a.type !== mode) @@ -313,11 +332,14 @@ export function AnalysisToolbar(): JSX.Element { } const passiveCount = annotations.filter((a) => a.type === 'passive_voice').length + const pastProgressiveCount = annotations.filter((a) => a.type === 'past_progressive').length + const weakVerbsCount = annotations.filter((a) => a.type === 'weak_verbs').length + const clichesCount = annotations.filter((a) => a.type === 'cliches').length const consistencyCount = annotations.filter((a) => a.type === 'consistency').length const styleCount = annotations.filter((a) => a.type === 'style').length const showTellCount = annotations.filter((a) => a.type === 'show_tell').length const critiqueCount = annotations.filter((a) => a.type === 'critique').length - const totalCount = passiveCount + consistencyCount + styleCount + showTellCount + critiqueCount + const totalCount = passiveCount + pastProgressiveCount + weakVerbsCount + clichesCount + consistencyCount + styleCount + showTellCount + critiqueCount const anyActive = Boolean(analysisMode) const docWordCount = countWords(activeFileContent) const sentenceStats = activeFileContent ? computeSentenceStats(activeFileContent) : null @@ -405,6 +427,27 @@ export function AnalysisToolbar(): JSX.Element { Passive Voice {passiveCount > 0 && {passiveCount}} + + +