feat(phase7.1): add HelpTooltip system with contextual help icons

- New HelpTooltip component: hover-triggered floating panel, themed via
  CSS variables, supports top/right/bottom/left positioning, no deps
- New helpTexts.ts registry: 14 entries covering render settings,
  admin actions, template fields, and wizard fields
- Admin.tsx: tooltips on Cycles/EEVEE samples, smooth angle, regenerate
  thumbnails, process unprocessed
- RenderTemplateTable.tsx: tooltips on material replace, lighting only,
  shadow catcher column headers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 20:16:42 +01:00
parent 206672a858
commit 10d05bd2e7
4 changed files with 234 additions and 24 deletions
+95
View File
@@ -0,0 +1,95 @@
import { HelpCircle } from 'lucide-react'
import { useState } from 'react'
import { HELP_TEXTS, type HelpText } from '../help/helpTexts'
interface HelpTooltipProps {
helpKey?: string
help?: HelpText
position?: 'top' | 'right' | 'bottom' | 'left'
size?: number
}
export function HelpTooltip({
helpKey,
help: directHelp,
position = 'right',
size = 14,
}: HelpTooltipProps) {
const [visible, setVisible] = useState(false)
const help = directHelp ?? (helpKey ? HELP_TEXTS[helpKey] : undefined)
if (!help) return null
const tooltipPositionStyle: React.CSSProperties = (() => {
switch (position) {
case 'top':
return { bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: 6 }
case 'bottom':
return { top: '100%', left: '50%', transform: 'translateX(-50%)', marginTop: 6 }
case 'left':
return { right: '100%', top: '50%', transform: 'translateY(-50%)', marginRight: 6 }
case 'right':
default:
return { left: '100%', top: '50%', transform: 'translateY(-50%)', marginLeft: 6 }
}
})()
return (
<span
className="relative inline-flex items-center"
onMouseEnter={() => setVisible(true)}
onMouseLeave={() => setVisible(false)}
>
<HelpCircle
size={size}
className="text-content-muted hover:text-content-secondary transition-colors cursor-help"
/>
{visible && (
<div
className="absolute z-50 rounded-lg border shadow-lg p-3 text-sm"
style={{
...tooltipPositionStyle,
backgroundColor: 'var(--color-bg-surface, var(--color-surface, #fff))',
borderColor: 'var(--color-border-default, #e5e7eb)',
color: 'var(--color-text-content, var(--color-content, #111827))',
maxWidth: 280,
minWidth: 180,
whiteSpace: 'normal',
pointerEvents: 'none',
}}
>
<p className="font-semibold mb-1" style={{ color: 'var(--color-text-content, inherit)' }}>
{help.title}
</p>
<p className="text-xs leading-relaxed" style={{ color: 'var(--color-content-secondary, #6b7280)' }}>
{help.body}
</p>
{help.unit && help.range && (
<p className="text-xs mt-1" style={{ color: 'var(--color-content-muted, #9ca3af)' }}>
Range: {help.range[0]}{help.range[1]} {help.unit}
</p>
)}
{help.recommendation && (
<p
className="text-xs mt-1.5 px-2 py-1 rounded"
style={{ backgroundColor: 'rgba(16, 185, 129, 0.1)', color: 'var(--color-status-success-text, #065f46)' }}
>
Recommended: {help.recommendation}
</p>
)}
{help.warning && (
<p
className="text-xs mt-1.5 px-2 py-1 rounded"
style={{ backgroundColor: 'rgba(245, 158, 11, 0.1)', color: 'var(--color-status-warning-text, #92400e)' }}
>
Warning: {help.warning}
</p>
)}
</div>
)}
</span>
)
}
export default HelpTooltip
@@ -1,6 +1,7 @@
import { useState, useRef } from 'react'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { Pencil, Trash2, Plus, Check, X, Upload, Download } from 'lucide-react'
import HelpTooltip from '../HelpTooltip'
import { toast } from 'sonner'
import {
listRenderTemplates,
@@ -165,9 +166,24 @@ export default function RenderTemplateTable() {
<th className="px-3 py-2 font-medium">Category</th>
<th className="px-3 py-2 font-medium">Output Type</th>
<th className="px-3 py-2 font-medium">Collection</th>
<th className="px-3 py-2 font-medium">Mat. Replace</th>
<th className="px-3 py-2 font-medium">Lighting Only</th>
<th className="px-3 py-2 font-medium" title="Enable Shadowcatcher collection (Cycles only)">Shadow Catcher</th>
<th className="px-3 py-2 font-medium">
<span className="inline-flex items-center gap-1">
Mat. Replace
<HelpTooltip helpKey="template.material_replace_enabled" position="bottom" size={12} />
</span>
</th>
<th className="px-3 py-2 font-medium">
<span className="inline-flex items-center gap-1">
Lighting Only
<HelpTooltip helpKey="template.lighting_only" position="bottom" size={12} />
</span>
</th>
<th className="px-3 py-2 font-medium">
<span className="inline-flex items-center gap-1">
Shadow Catcher
<HelpTooltip helpKey="template.shadow_catcher" position="bottom" size={12} />
</span>
</th>
<th className="px-3 py-2 font-medium" title="Rotate camera around product instead of product rotation (faster GPU rendering)">Cam Orbit</th>
<th className="px-3 py-2 font-medium">.blend File</th>
<th className="px-3 py-2 font-medium">Active</th>