📝 explanatory tooltips

This commit is contained in:
TC
2026-06-14 20:40:27 +10:00
parent 497c29c792
commit 2f584f669c
2 changed files with 70 additions and 1 deletions

View File

@@ -628,3 +628,33 @@
text-align: right;
flex-shrink: 0;
}
.pm-dim-info {
display: inline-block;
margin-left: 4px;
font-size: 9px;
opacity: 0.4;
cursor: default;
vertical-align: middle;
line-height: 1;
}
.pm-dim-info:hover {
opacity: 0.9;
}
.pm-tooltip-fixed {
position: fixed;
transform: translate(-50%, -100%);
width: 220px;
background: var(--bg-panel, #1a1a1a);
border: 1px solid var(--border);
border-radius: 5px;
padding: 7px 9px;
font-size: 11px;
line-height: 1.5;
color: var(--text-primary);
white-space: normal;
z-index: 9999;
pointer-events: none;
}

View File

@@ -274,8 +274,26 @@ function scoreColor(score: number): string {
return 'var(--polish-low)'
}
const DIM_DESCRIPTIONS: Record<string, string> = {
passiveVoice: 'Flags "was/were + past participle" constructions. Penalises above 12 per 1,000 words.',
weaselWords: 'Intensifiers like very, quite, clearly that weaken prose. Penalises above 10 per 1,000 words.',
adverbDensity: 'ly adverbs per 1,000 words. Strong verbs rarely need adverb support. Penalises above 20 per 1,000.',
filterWords: '"She felt", "he noticed" — constructions that put a layer between reader and action. Penalises above 8 per 1,000 words.',
showTell: '"Was/were + emotion adjective" (was angry, were afraid). Suggests telling the reader rather than dramatising the moment.',
sentenceRhythm: 'Coefficient of variation in sentence lengths. High variance = good pacing. Also flags runs of 3+ sentences within 2 words of each other in length.',
paragraphRhythm: 'Variation in paragraph lengths. Flags walls of text over 150 words and measures overall length variance.',
repeatedStarters: 'Penalises 3+ consecutive sentences opening with the same word, and chapters heavy with pronoun openers (he/she/they).',
wordVariety: 'Type-token ratio over 100-word windows of content words. Low ratio = repetitive vocabulary. Also flags words repeated 3+ times within 50 words.',
cliches: 'Matches against a list of common literary clichés (heart pounding, blood ran cold, etc.). Penalises above 6 per 1,000 words.',
overusedWords: 'Drafting filler: just, suddenly, somehow, began to, started to. Penalises above 25 per 1,000 words.',
dialogueTags: 'Said-bookisms (hissed, snarled, barked) and adverb-modified tags (said softly). Penalises above 8 per 1,000 words.',
}
interface TooltipState { text: string; x: number; y: number }
function PolishMeter({ score }: { score: PolishScore }): JSX.Element {
const [activeKey, setActiveKey] = useState<string | null>(null)
const [tooltip, setTooltip] = useState<TooltipState | null>(null)
const { setAnnotations, clearAnnotations } = useEditorStore()
const dims = Object.entries(score.dimensions) as [string, PolishDimension][]
@@ -325,7 +343,20 @@ function PolishMeter({ score }: { score: PolishScore }): JSX.Element {
onClick={() => clickable && handleDimClick(key, dim)}
title={clickable ? `Click to highlight ${dim.issueCount} instance${dim.issueCount !== 1 ? 's' : ''}` : undefined}
>
<span className="pm-dim-label">{dim.label}</span>
<span className="pm-dim-label">
{dim.label}
{DIM_DESCRIPTIONS[key] && (
<span
className="pm-dim-info"
onClick={e => e.stopPropagation()}
onMouseEnter={e => {
const r = (e.currentTarget as HTMLElement).getBoundingClientRect()
setTooltip({ text: DIM_DESCRIPTIONS[key], x: r.left + r.width / 2, y: r.top - 8 })
}}
onMouseLeave={() => setTooltip(null)}
></span>
)}
</span>
<div className="pm-bar-track">
<div
className="pm-bar-fill"
@@ -337,6 +368,14 @@ function PolishMeter({ score }: { score: PolishScore }): JSX.Element {
)
})}
</div>
{tooltip && (
<div
className="pm-tooltip-fixed"
style={{ left: tooltip.x, top: tooltip.y }}
>
{tooltip.text}
</div>
)}
</div>
)
}