analytica-frontend-lib 1.4.77 → 1.4.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AccessibilityWidget/index.js +1 -2
- package/dist/AccessibilityWidget/index.js.map +1 -1
- package/dist/AccessibilityWidget/index.mjs +1 -2
- package/dist/AccessibilityWidget/index.mjs.map +1 -1
- package/dist/ActivitiesHistory/index.js.map +1 -1
- package/dist/ActivitiesHistory/index.mjs.map +1 -1
- package/dist/ActivityCardQuestionBanks/index.js +1 -2
- package/dist/ActivityCardQuestionBanks/index.js.map +1 -1
- package/dist/ActivityCardQuestionBanks/index.mjs +1 -2
- package/dist/ActivityCardQuestionBanks/index.mjs.map +1 -1
- package/dist/ActivityPageLayout/index.js.map +1 -1
- package/dist/ActivityPageLayout/index.mjs.map +1 -1
- package/dist/AppLayout/index.js +6 -0
- package/dist/AppLayout/index.js.map +1 -1
- package/dist/AppLayout/index.mjs +6 -0
- package/dist/AppLayout/index.mjs.map +1 -1
- package/dist/BreadcrumbMenu/index.js.map +1 -1
- package/dist/BreadcrumbMenu/index.mjs.map +1 -1
- package/dist/Menu/index.d.ts.map +1 -1
- package/dist/Menu/index.js +6 -0
- package/dist/Menu/index.js.map +1 -1
- package/dist/Menu/index.mjs +6 -0
- package/dist/Menu/index.mjs.map +1 -1
- package/dist/Quiz/index.js +1 -2
- package/dist/Quiz/index.js.map +1 -1
- package/dist/Quiz/index.mjs +1 -2
- package/dist/Quiz/index.mjs.map +1 -1
- package/dist/RecommendedLessonsHistory/index.js.map +1 -1
- package/dist/RecommendedLessonsHistory/index.mjs.map +1 -1
- package/dist/RichEditor/index.js.map +1 -1
- package/dist/RichEditor/index.mjs.map +1 -1
- package/dist/Select/index.js +1 -2
- package/dist/Select/index.js.map +1 -1
- package/dist/Select/index.mjs +1 -2
- package/dist/Select/index.mjs.map +1 -1
- package/dist/StatisticsCard/index.js +1 -2
- package/dist/StatisticsCard/index.js.map +1 -1
- package/dist/StatisticsCard/index.mjs +1 -2
- package/dist/StatisticsCard/index.mjs.map +1 -1
- package/dist/Support/index.js +1 -2
- package/dist/Support/index.js.map +1 -1
- package/dist/Support/index.mjs +1 -2
- package/dist/Support/index.mjs.map +1 -1
- package/dist/index.js +36 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +36 -11
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/AccessibilityWidget/AccessibilityWidget.tsx","../../src/components/Button/Button.tsx","../../src/utils/utils.ts","../../src/components/Tooltip/Tooltip.tsx","../../src/components/Text/Text.tsx","../../src/components/AccessibilityWidget/fabPositioning.ts","../../src/components/AccessibilityWidget/AccessibilityFab.tsx","../../src/components/AccessibilityWidget/AccessibilityPanel.tsx","../../src/components/IconButton/IconButton.tsx","../../src/components/ToggleSwitch/ToggleSwitch.tsx","../../src/components/AccessibilityWidget/AccessibilityToggleRow.tsx","../../src/components/AccessibilityWidget/TTSSection.tsx","../../src/components/Select/Select.tsx","../../src/store/accessibilityStore.ts","../../src/hooks/useTTS.ts","../../src/components/AccessibilityWidget/tts/WebSpeechProvider.ts","../../src/components/AccessibilityWidget/LibrasFab.tsx","../../src/components/AccessibilityWidget/ReadingAid.tsx","../../src/components/AccessibilityWidget/ColorBlindFilters.tsx","../../src/components/AccessibilityWidget/TTSController.tsx","../../src/components/AccessibilityWidget/VLibrasLoader.tsx","../../src/hooks/useA11yPreferences.ts","../../src/hooks/useA11yKeyboardShortcut.ts"],"sourcesContent":["import AccessibilityFab, {\n type AccessibilityFabPosition,\n} from './AccessibilityFab';\nimport AccessibilityPanel from './AccessibilityPanel';\nimport LibrasFab from './LibrasFab';\nimport ReadingAid from './ReadingAid';\nimport ColorBlindFilters from './ColorBlindFilters';\nimport TTSController from './TTSController';\nimport VLibrasLoader from './VLibrasLoader';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\nimport { useA11yPreferences } from '../../hooks/useA11yPreferences';\nimport { useA11yKeyboardShortcut } from '../../hooks/useA11yKeyboardShortcut';\nimport './accessibility.css';\n\nexport interface AccessibilityWidgetProps {\n /** Lado da viewport onde o botão fica colado (default: 'right') */\n position?: AccessibilityFabPosition;\n /** Classes extras para o botão flutuante */\n fabClassName?: string;\n /** Classes extras para o painel */\n panelClassName?: string;\n /**\n * Mostra o botão de Libras empilhado abaixo do botão de\n * acessibilidade (default: true). Plataformas que já injetam o\n * VLibras de forma própria podem desabilitar passando `false`.\n */\n showLibras?: boolean;\n}\n\n/**\n * Widget de acessibilidade — botão flutuante que abre um painel\n * com controles de contraste, fonte, espaçamento e outras\n * preferências visuais. As preferências são persistidas em\n * localStorage e aplicadas via classes no `<html>`.\n *\n * Renderize uma única instância no shell da aplicação. Tudo fica\n * dentro de `.a11y-widget-root` para que o próprio widget não\n * sofra com os filtros que aplica na página.\n *\n * @example\n * ```tsx\n * <AccessibilityWidget position=\"left\" />\n * ```\n */\nexport default function AccessibilityWidget({\n position = 'right',\n fabClassName,\n panelClassName,\n showLibras = true,\n}: Readonly<AccessibilityWidgetProps>) {\n useA11yPreferences();\n useA11yKeyboardShortcut();\n\n const isPanelOpen = useAccessibilityStore((s) => s.isPanelOpen);\n const togglePanel = useAccessibilityStore((s) => s.togglePanel);\n const closePanel = useAccessibilityStore((s) => s.closePanel);\n const librasEnabled = useAccessibilityStore((s) => s.librasEnabled);\n const setLibrasEnabled = useAccessibilityStore((s) => s.setLibrasEnabled);\n\n // Quando o Libras é exibido, os dois FABs ficam empilhados\n // (acessibilidade acima do meio, libras abaixo). Sem Libras,\n // o de acessibilidade ocupa o centro como antes.\n const accessibilityVerticalAlign = showLibras ? 'above-center' : 'center';\n\n /**\n * Clique no FAB de Libras:\n * - Primeira vez (não ativado): ativa o widget VLibras (que se\n * auto-abre no painel pela primeira vez).\n * - Demais vezes: dispara o click no botão de acesso nativo do\n * VLibras, que abre/fecha o painel sem precisar reinjetar o widget.\n * Isso evita o ciclo de remoção/reinjeção que deixava o VLibras\n * em estado inconsistente após fechá-lo.\n */\n const handleLibrasClick = () => {\n if (!librasEnabled) {\n setLibrasEnabled(true);\n return;\n }\n document.querySelector<HTMLElement>('[vw-access-button]')?.click();\n };\n\n return (\n <>\n {!isPanelOpen && (\n <AccessibilityFab\n onClick={togglePanel}\n isOpen={isPanelOpen}\n position={position}\n verticalAlign={accessibilityVerticalAlign}\n className={fabClassName}\n />\n )}\n {!isPanelOpen && showLibras && (\n <LibrasFab onClick={handleLibrasClick} position={position} />\n )}\n <AccessibilityPanel\n isOpen={isPanelOpen}\n onClose={closePanel}\n position={position}\n className={panelClassName}\n />\n <ReadingAid />\n <ColorBlindFilters />\n <TTSController />\n {showLibras && <VLibrasLoader />}\n </>\n );\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Lookup table for variant and action class combinations\n */\nconst VARIANT_ACTION_CLASSES = {\n solid: {\n primary:\n 'bg-primary-950 text-text border border-primary-950 hover:bg-primary-800 hover:border-primary-800 focus-visible:outline-none focus-visible:bg-primary-950 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-primary-700 active:border-primary-700 disabled:bg-primary-500 disabled:border-primary-500 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-text-950 text-text border border-text-800 hover:bg-text-800 hover:border-text-950 focus-visible:outline-none focus-visible:bg-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-text-700 active:border-text-700 disabled:bg-text-500 disabled:border-text-500 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-success-500 text-text border border-success-500 hover:bg-success-600 hover:border-success-600 focus-visible:outline-none focus-visible:bg-success-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-success-700 active:border-success-700 disabled:bg-success-500 disabled:border-success-500 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-error-500 text-text border border-error-500 hover:bg-error-600 hover:border-error-600 focus-visible:outline-none focus-visible:bg-error-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-error-700 active:border-error-700 disabled:bg-error-500 disabled:border-error-500 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n outline: {\n primary:\n 'bg-transparent text-primary-950 border border-primary-950 hover:bg-background-50 hover:text-primary-400 hover:border-primary-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 active:border-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-transparent text-text-950 border border-text-800 hover:bg-background-50 hover:text-text-700 hover:border-text-700 focus-visible:border-0 focus-visible:outline-none focus-visible:text-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-text-700 active:border-text-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 border border-success-300 hover:bg-background-50 hover:text-success-400 hover:border-success-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 active:border-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 border border-error-300 hover:bg-background-50 hover:text-error-400 hover:border-error-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 active:border-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n link: {\n primary:\n 'bg-transparent text-primary-950 hover:text-primary-400 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-transparent text-text-950 hover:text-text-800 focus-visible:outline-none focus-visible:text-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-text-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 hover:text-success-400 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 hover:text-error-400 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n} as const;\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n 'extra-small': 'text-xs px-3.5 py-2',\n small: 'text-sm px-4 py-2.5',\n medium: 'text-md px-5 py-2.5',\n large: 'text-lg px-6 py-3',\n 'extra-large': 'text-lg px-7 py-3.5',\n} as const;\n\n/**\n * Button component props interface\n */\ntype ButtonProps = {\n /** Content to be displayed inside the button */\n children: ReactNode;\n /** Ícone à esquerda do texto */\n iconLeft?: ReactNode;\n /** Ícone à direita do texto */\n iconRight?: ReactNode;\n /** Size of the button */\n size?: 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large';\n /** Visual variant of the button. Use 'raw' for no default styling */\n variant?: 'solid' | 'outline' | 'link' | 'raw';\n /** Action type of the button */\n action?: 'primary' | 'secondary' | 'positive' | 'negative';\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * Button component for Analytica Ensino platforms\n *\n * A flexible button component with multiple variants, sizes and actions.\n *\n * @param children - The content to display inside the button\n * @param size - The size variant (extra-small, small, medium, large, extra-large)\n * @param variant - The visual style variant (solid, outline, link)\n * @param action - The action type (primary, secondary, positive, negative)\n * @param className - Additional CSS classes\n * @param props - All other standard button HTML attributes\n * @returns A styled button element\n *\n * @example\n * ```tsx\n * <Button variant=\"solid\" action=\"primary\" size=\"medium\" onClick={() => console.log('clicked')}>\n * Click me\n * </Button>\n * ```\n */\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n children,\n iconLeft,\n iconRight,\n size = 'medium',\n variant = 'solid',\n action = 'primary',\n className = '',\n disabled,\n type = 'button',\n ...props\n },\n ref\n ) => {\n // Raw variant: no default styling, only className\n if (variant === 'raw') {\n return (\n <button\n ref={ref}\n className={className}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && (\n <span className=\"mr-2 flex items-center\">{iconLeft}</span>\n )}\n {children}\n {iconRight && (\n <span className=\"ml-2 flex items-center\">{iconRight}</span>\n )}\n </button>\n );\n }\n\n // Get classes from lookup tables\n const sizeClasses = SIZE_CLASSES[size];\n const variantClasses = VARIANT_ACTION_CLASSES[variant][action];\n\n const baseClasses =\n 'inline-flex items-center justify-center rounded-full cursor-pointer font-medium';\n\n return (\n <button\n ref={ref}\n className={cn(baseClasses, variantClasses, sizeClasses, className)}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && <span className=\"mr-2 flex items-center\">{iconLeft}</span>}\n {children}\n {iconRight && (\n <span className=\"ml-2 flex items-center\">{iconRight}</span>\n )}\n </button>\n );\n }\n);\n\nButton.displayName = 'Button';\n\nexport default Button;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n formatActivityDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Format a number as a rounded percentage string\n * @param value - Number to format (0-100)\n * @returns Formatted string with % suffix (e.g., \"72%\")\n */\nexport function formatPercentageRounded(value: number): string {\n return `${Math.round(value)}%`;\n}\n\nexport { formatScore } from './formatScore';\n\n/**\n * Convert hex color to rgba with opacity for background\n * @param hex - Hex color (e.g., \"#4B0082\")\n * @param opacity - Opacity value (0-1)\n * @returns rgba string\n */\nexport function hexToRgba(hex: string, opacity: number): string {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n if (!result) return `rgba(107, 114, 128, ${opacity})`; // fallback gray\n const r = Number.parseInt(result[1], 16);\n const g = Number.parseInt(result[2], 16);\n const b = Number.parseInt(result[3], 16);\n return `rgba(${r}, ${g}, ${b}, ${opacity})`;\n}\n\n/**\n * Maps Tailwind bg-* class to CSS variable\n * @param bgClass - Tailwind background class (e.g., \"bg-error-600\")\n * @returns CSS variable string (e.g., \"var(--color-error-600)\")\n */\nexport function bgClassToCssVar(bgClass: string): string {\n return `var(--color-${bgClass.replace('bg-', '')})`;\n}\n\n/**\n * Converts polar coordinates to Cartesian for SVG arc path calculations.\n * Angles are in degrees, with 0° at the top (12 o'clock position).\n * @param cx - Center X coordinate\n * @param cy - Center Y coordinate\n * @param r - Radius\n * @param angleDeg - Angle in degrees\n * @returns Cartesian coordinates { x, y }\n */\nexport function polarToCartesian(\n cx: number,\n cy: number,\n r: number,\n angleDeg: number\n): { x: number; y: number } {\n const rad = ((angleDeg - 90) * Math.PI) / 180;\n return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };\n}\n\n/**\n * Generates an SVG filled arc (pie slice) path string.\n * @param cx - Center X coordinate\n * @param cy - Center Y coordinate\n * @param r - Radius\n * @param startAngle - Start angle in degrees\n * @param endAngle - End angle in degrees\n * @returns SVG path string for the arc\n */\nexport function describeArc(\n cx: number,\n cy: number,\n r: number,\n startAngle: number,\n endAngle: number\n): string {\n const span = endAngle - startAngle;\n\n // For full circle (or near-full), use two arcs to avoid SVG arc degeneracy\n if (span >= 359.99) {\n const midAngle = startAngle + 180;\n const s = polarToCartesian(cx, cy, r, startAngle);\n const m = polarToCartesian(cx, cy, r, midAngle);\n return `M ${cx} ${cy} L ${s.x} ${s.y} A ${r} ${r} 0 1 1 ${m.x} ${m.y} A ${r} ${r} 0 1 1 ${s.x} ${s.y} Z`;\n }\n\n const s = polarToCartesian(cx, cy, r, endAngle);\n const e = polarToCartesian(cx, cy, r, startAngle);\n const largeArc = span > 180 ? 1 : 0;\n return `M ${cx} ${cy} L ${s.x} ${s.y} A ${r} ${r} 0 ${largeArc} 0 ${e.x} ${e.y} Z`;\n}\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import {\n ReactNode,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Tooltip position options\n */\ntype TooltipPosition = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Tooltip component props interface\n */\nexport interface TooltipProps {\n /** Content to display in the tooltip */\n content: ReactNode;\n /** Element that triggers the tooltip */\n children: ReactNode;\n /** Position of the tooltip relative to the trigger */\n position?: TooltipPosition;\n /** Additional className for the tooltip container */\n className?: string;\n /** Additional className for the tooltip content */\n contentClassName?: string;\n /** Whether the tooltip is disabled */\n disabled?: boolean;\n /**\n * Render the tooltip inside a React Portal attached to document.body.\n * Use this when the trigger lives inside an ancestor with `overflow:hidden`\n * (e.g. scroll containers) that would otherwise clip the tooltip.\n */\n usePortal?: boolean;\n}\n\n/**\n * Position classes for tooltip placement (non-portal mode, CSS-only)\n */\nconst POSITION_CLASSES: Record<TooltipPosition, string> = {\n top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',\n bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',\n left: 'right-full top-1/2 -translate-y-1/2 mr-2',\n right: 'left-full top-1/2 -translate-y-1/2 ml-2',\n};\n\nconst TOOLTIP_GAP_PX = 8;\n\n/**\n * Compute fixed-position coordinates relative to the viewport for a given\n * trigger element and desired tooltip position.\n */\nconst computePortalCoords = (\n triggerRect: DOMRect,\n position: TooltipPosition,\n tooltipRect: { width: number; height: number }\n): { top: number; left: number } => {\n switch (position) {\n case 'bottom':\n return {\n top: triggerRect.bottom + TOOLTIP_GAP_PX,\n left: triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2,\n };\n case 'left':\n return {\n top: triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2,\n left: triggerRect.left - tooltipRect.width - TOOLTIP_GAP_PX,\n };\n case 'right':\n return {\n top: triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2,\n left: triggerRect.right + TOOLTIP_GAP_PX,\n };\n case 'top':\n default:\n return {\n top: triggerRect.top - tooltipRect.height - TOOLTIP_GAP_PX,\n left: triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2,\n };\n }\n};\n\nconst TOOLTIP_CONTENT_CLASSES = cn(\n 'whitespace-nowrap',\n 'px-4 py-2 rounded-lg',\n 'bg-background-dark text-white',\n 'text-sm font-medium',\n 'shadow-[0px_3px_10px_0px_rgba(38,38,38,0.2)]',\n 'transition-opacity duration-150'\n);\n\n/**\n * Tooltip component - Displays contextual information on hover/focus\n *\n * By default uses a CSS-only approach with `group-hover` for performance.\n * When `usePortal` is true, the tooltip is rendered in a React Portal so it\n * escapes ancestors with `overflow:hidden`.\n *\n * @example\n * ```tsx\n * <Tooltip content=\"Desempenho baseado nas atividades\">\n * <Info size={18} weight=\"bold\" className=\"text-text-950 cursor-pointer\" />\n * </Tooltip>\n * ```\n */\nexport function Tooltip({\n content,\n children,\n position = 'top',\n className,\n contentClassName,\n disabled = false,\n usePortal = false,\n}: Readonly<TooltipProps>) {\n const triggerRef = useRef<HTMLSpanElement>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const [open, setOpen] = useState(false);\n const [coords, setCoords] = useState<{ top: number; left: number }>({\n top: 0,\n left: 0,\n });\n\n const updatePosition = useCallback(() => {\n if (!triggerRef.current || !tooltipRef.current) return;\n const triggerRect = triggerRef.current.getBoundingClientRect();\n const tooltipRect = {\n width: tooltipRef.current.offsetWidth,\n height: tooltipRef.current.offsetHeight,\n };\n setCoords(computePortalCoords(triggerRect, position, tooltipRect));\n }, [position]);\n\n useLayoutEffect(() => {\n if (!usePortal || !open) return;\n updatePosition();\n const onViewportChange = () => updatePosition();\n window.addEventListener('resize', onViewportChange);\n window.addEventListener('scroll', onViewportChange, true);\n return () => {\n window.removeEventListener('resize', onViewportChange);\n window.removeEventListener('scroll', onViewportChange, true);\n };\n }, [usePortal, open, updatePosition, content]);\n\n /**\n * Attach hover/focus listeners via the DOM API (not JSX props) so the\n * wrapper element stays semantically non-interactive. This satisfies the\n * SonarQube `S6848`/`jsx-a11y/no-static-element-interactions` rule which\n * fires on static elements (`<span>`/`<div>`) that carry interactive JSX\n * handlers (`onMouseEnter`/`onFocus`/etc.).\n */\n useEffect(() => {\n if (!usePortal) return;\n const node = triggerRef.current;\n if (!node) return;\n\n const handleOpen = () => setOpen(true);\n const handleClose = () => setOpen(false);\n\n node.addEventListener('mouseenter', handleOpen);\n node.addEventListener('mouseleave', handleClose);\n node.addEventListener('focusin', handleOpen);\n node.addEventListener('focusout', handleClose);\n\n return () => {\n node.removeEventListener('mouseenter', handleOpen);\n node.removeEventListener('mouseleave', handleClose);\n node.removeEventListener('focusin', handleOpen);\n node.removeEventListener('focusout', handleClose);\n };\n }, [usePortal]);\n\n if (disabled) {\n return <>{children}</>;\n }\n\n if (usePortal) {\n return (\n <Text\n as=\"span\"\n ref={triggerRef}\n className={cn('relative inline-flex', className)}\n aria-describedby={open ? 'tooltip-portal' : undefined}\n >\n {children}\n {open &&\n typeof document !== 'undefined' &&\n createPortal(\n <div\n ref={tooltipRef}\n id=\"tooltip-portal\"\n role=\"tooltip\"\n style={{\n position: 'fixed',\n top: coords.top,\n left: coords.left,\n zIndex: 9999,\n }}\n className={cn(TOOLTIP_CONTENT_CLASSES, contentClassName)}\n >\n {content}\n </div>,\n document.body\n )}\n </Text>\n );\n }\n\n return (\n <div className={cn('relative inline-flex group', className)}>\n {children}\n\n {/* Tooltip content - shown on hover/focus via CSS */}\n <div\n role=\"tooltip\"\n className={cn(\n 'absolute z-50',\n TOOLTIP_CONTENT_CLASSES,\n 'opacity-0 invisible',\n 'group-hover:opacity-100 group-hover:visible',\n 'group-focus-within:opacity-100 group-focus-within:visible',\n POSITION_CLASSES[position],\n contentClassName\n )}\n >\n {content}\n </div>\n </div>\n );\n}\n\nexport default Tooltip;\n","import { ComponentPropsWithRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses: string;\n let weightClasses: string;\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n","/**\n * Constantes de posicionamento compartilhadas entre os FABs do widget\n * (`AccessibilityFab` e `LibrasFab`). Centralizadas aqui para evitar\n * que mudanças de gap/lateral precisem ser replicadas em cada FAB.\n */\n\nexport type FabPosition = 'right' | 'left';\n\n/**\n * Alinhamento vertical do FAB. Quando o widget renderiza só um botão\n * (sem Libras), `center` deixa-o no meio da viewport. Quando há também\n * o LibrasFab empilhado abaixo, o de acessibilidade fica `above-center`\n * e o Libras fica `below-center` — formando um par grudado no meio.\n */\nexport type FabVerticalAlign = 'center' | 'above-center' | 'below-center';\n\nexport const FAB_POSITION_CLASSES: Record<FabPosition, string> = {\n right: 'right-0 rounded-l-lg',\n left: 'left-0 rounded-r-lg',\n};\n\nexport const FAB_VERTICAL_ALIGN_CLASSES: Record<FabVerticalAlign, string> = {\n center: 'top-1/2 -translate-y-1/2',\n // Pares empilhados (acessibilidade + Libras) com gap mínimo entre si,\n // como no widget do HandTalk. Cada FAB tem 40px (h-10).\n 'above-center': 'top-[calc(50%-1px)] -translate-y-full',\n 'below-center': 'top-[calc(50%+1px)]',\n};\n\n/** Tooltip aparece do lado oposto à borda em que o FAB está colado. */\nexport const FAB_TOOLTIP_POSITION: Record<FabPosition, 'left' | 'right'> = {\n right: 'left',\n left: 'right',\n};\n","import Button from '../Button/Button';\nimport { Tooltip } from '../Tooltip/Tooltip';\nimport { cn } from '../../utils/utils';\nimport accessibilityIcon from '../../assets/img/accessibility.png';\nimport {\n FAB_POSITION_CLASSES,\n FAB_TOOLTIP_POSITION,\n FAB_VERTICAL_ALIGN_CLASSES,\n type FabPosition,\n type FabVerticalAlign,\n} from './fabPositioning';\n\n/** Re-exports mantidos para compatibilidade com a API pública do widget. */\nexport type AccessibilityFabPosition = FabPosition;\nexport type AccessibilityFabVerticalAlign = FabVerticalAlign;\n\nexport interface AccessibilityFabProps {\n /** Click handler — alterna o painel */\n onClick: () => void;\n /** Indica se o painel está aberto (controla aria-expanded) */\n isOpen?: boolean;\n /** Lado da viewport onde o botão fica colado */\n position?: AccessibilityFabPosition;\n /** Alinhamento vertical (default: `center`) */\n verticalAlign?: AccessibilityFabVerticalAlign;\n /** Classes extras */\n className?: string;\n}\n\n/**\n * Botão flutuante (FAB) que abre o painel de acessibilidade.\n * Inspirado no padrão HandTalk: quadrado azul escuro colado na\n * lateral da viewport (direita por padrão), com o ícone universal\n * de acessibilidade. Verticalmente centralizado.\n */\nexport default function AccessibilityFab({\n onClick,\n isOpen = false,\n position = 'right',\n verticalAlign = 'center',\n className,\n}: Readonly<AccessibilityFabProps>) {\n const label = isOpen\n ? 'Fechar opções de acessibilidade'\n : 'Opções de acessibilidade';\n\n return (\n <Tooltip\n content={label}\n position={FAB_TOOLTIP_POSITION[position]}\n className={cn(\n 'fixed z-40',\n FAB_VERTICAL_ALIGN_CLASSES[verticalAlign],\n FAB_POSITION_CLASSES[position]\n )}\n >\n <Button\n variant=\"raw\"\n onClick={onClick}\n aria-label={label}\n aria-expanded={isOpen}\n data-testid=\"accessibility-fab\"\n className={cn(\n 'a11y-widget-shield',\n FAB_POSITION_CLASSES[position],\n 'flex h-10 w-10 cursor-pointer items-center justify-center',\n // `text-text-50` flipa junto com `bg-info-900` (claro no light,\n // escuro no dark). `text-white` deixaria o ícone branco sumindo\n // no tema escuro, onde `bg-info-900` resolve pra azul-claro.\n 'bg-info-900 text-text-50 shadow-lg',\n 'transition-all duration-200 hover:scale-110 hover:bg-info-800',\n 'focus:outline-none focus:ring-4 focus:ring-info-300',\n className\n )}\n >\n <img\n src={accessibilityIcon}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"h-7 w-7\"\n />\n </Button>\n </Tooltip>\n );\n}\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport {\n ArrowCounterClockwiseIcon,\n CaretDownIcon,\n EyeIcon,\n FilmReelIcon,\n KeyboardIcon,\n NavigationArrowIcon,\n SpeakerHighIcon,\n TextTIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport accessibilityIcon from '../../assets/img/accessibility.png';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport IconButton from '../IconButton/IconButton';\nimport AccessibilityToggleRow from './AccessibilityToggleRow';\nimport TTSSection from './TTSSection';\nimport { cn } from '../../utils/utils';\nimport {\n useAccessibilityStore,\n type ContrastMode,\n type SaturationMode,\n type ReadingAid,\n} from '../../store/accessibilityStore';\nimport type { AccessibilityFabPosition } from './AccessibilityFab';\n\nconst CONTRAST_OPTIONS: { value: ContrastMode; label: string }[] = [\n { value: 'normal', label: 'Normal' },\n { value: 'high', label: 'Alto' },\n { value: 'inverted', label: 'Invertido' },\n];\n\nconst SATURATION_OPTIONS: { value: SaturationMode; label: string }[] = [\n { value: 'normal', label: 'Normal' },\n { value: 'low', label: 'Baixa' },\n { value: 'grayscale', label: 'Tons de cinza' },\n];\n\nconst READING_AID_OPTIONS: { value: ReadingAid; label: string }[] = [\n { value: 'none', label: 'Nenhum' },\n { value: 'ruler', label: 'Régua' },\n { value: 'mask', label: 'Máscara' },\n];\n\nconst LEVEL_LABELS = ['Padrão', 'Pequeno', 'Médio', 'Grande'] as const;\n\n/** Tamanho de fonte do preview \"Aa\" em cada botão (Padrão → Grande). */\nconst FONT_SIZE_PREVIEW_CLASSES = [\n 'text-base', // Padrão\n 'text-lg', // Pequeno (= maior que Padrão, segue a escala incremental do widget)\n 'text-2xl', // Médio\n 'text-4xl', // Grande\n] as const;\n\n/** Letter-spacing do preview \"Aa\" em cada botão. */\nconst LETTER_SPACING_PREVIEW_CLASSES = [\n 'tracking-tighter', // Padrão (junto)\n 'tracking-normal',\n 'tracking-wider',\n 'tracking-widest',\n] as const;\n\n/** Gap vertical entre as 3 barras do preview de espaçamento entre linhas. */\nconst LINE_SPACING_PREVIEW_GAPS = [\n 'gap-0.5', // Padrão (2px) — linhas próximas\n 'gap-1.5', // Pequeno (6px)\n 'gap-2.5', // Médio (10px)\n 'gap-3.5', // Grande (14px)\n] as const;\n\n/**\n * `<dialog open>` aplica `left: 0; right: 0` como user-agent style. Sem\n * resetar o lado oposto para `auto`, o painel acaba grudando no lado\n * errado mesmo com `right-6` ou `left-6` aplicado.\n */\nconst PANEL_EDGE_CLASSES: Record<AccessibilityFabPosition, string> = {\n right: 'right-10 left-auto',\n left: 'left-10 right-auto',\n};\n\ninterface AccordionSectionProps {\n title: string;\n icon: ReactNode;\n defaultExpanded?: boolean;\n testId?: string;\n children: ReactNode;\n}\n\n/**\n * Item de accordion estilo lista (sem cantos arredondados nem card),\n * com ícone + título à esquerda e caret à direita. Cada item separado\n * do próximo por uma linha fina. Mantém a estrutura visual do design\n * do Figma sem usar o `CardAccordation` da lib (que tem visual de card).\n */\nconst AccordionSection = ({\n title,\n icon,\n defaultExpanded = false,\n testId,\n children,\n}: Readonly<AccordionSectionProps>) => {\n const [expanded, setExpanded] = useState(defaultExpanded);\n return (\n <section className=\"border-b border-background-200 last:border-b-0\">\n <Button\n variant=\"raw\"\n aria-expanded={expanded}\n onClick={() => setExpanded((v) => !v)}\n data-testid={testId}\n className={cn(\n 'flex w-full cursor-pointer items-center justify-between gap-2 px-4 py-3',\n 'text-left transition-colors hover:bg-background-50',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-info-500 focus-visible:ring-inset'\n )}\n >\n <span className=\"flex items-center gap-2\">\n <span className=\"text-text-800\" aria-hidden=\"true\">\n {icon}\n </span>\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"leading-none text-text-800\"\n >\n {title}\n </Text>\n </span>\n <CaretDownIcon\n size={16}\n weight=\"bold\"\n aria-hidden=\"true\"\n className={cn(\n 'text-text-700 transition-transform duration-200',\n expanded ? 'rotate-180' : 'rotate-0'\n )}\n />\n </Button>\n {expanded && (\n <div className=\"flex flex-col gap-6 px-4 pb-4 pt-2\">{children}</div>\n )}\n </section>\n );\n};\n\ninterface SubControlProps {\n label: string;\n children: ReactNode;\n}\n\n/**\n * Wrapper para uma control (segmented/toggle) com título dentro do\n * accordion. Tipografia do label segue o Figma: Roboto 12px/500/100%\n * uppercase, cor `text-text-600` (#737373).\n */\nconst SubControl = ({ label, children }: Readonly<SubControlProps>) => (\n <div className=\"flex flex-col gap-2\">\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"uppercase leading-none text-text-600\"\n >\n {label}\n </Text>\n {children}\n </div>\n);\n\ninterface SegmentedProps<T extends string | number> {\n value: T;\n options: { value: T; label: string }[];\n onChange: (value: T) => void;\n ariaLabel: string;\n}\n\n/**\n * Estilo de segmented baseado no `SelectionButton` da lib: cada opção é\n * um botão independente com `border` + `bg-background`, e o selecionado\n * ganha `ring-primary-950` (mesma identidade visual do componente da lib,\n * sem o overhead de ícone obrigatório do SelectionButton).\n */\nconst Segmented = <T extends string | number>({\n value,\n options,\n onChange,\n ariaLabel,\n}: Readonly<SegmentedProps<T>>) => (\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n className=\"flex flex-wrap gap-2\"\n >\n {options.map((opt) => {\n const isActive = opt.value === value;\n return (\n <Button\n key={String(opt.value)}\n variant=\"raw\"\n role=\"radio\"\n aria-checked={isActive}\n onClick={() => onChange(opt.value)}\n className={cn(\n 'flex-1 cursor-pointer rounded-xl border border-border-50 bg-background',\n // Texto segue spec do Figma: Roboto 14px/700/100%/0.2px, cor\n // #525252 (text-700). Quando ativo, troca pra primary-950\n // (azul escuro), mas mantém a mesma typography.\n 'px-4 py-4 text-sm font-bold leading-none tracking-[0.2px] text-text-700',\n 'shadow-soft-shadow-1 transition-all duration-150',\n 'hover:bg-background-100',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indicator-info',\n isActive &&\n 'ring-2 ring-primary-950 ring-offset-0 shadow-none text-primary-950'\n )}\n >\n {opt.label}\n </Button>\n );\n })}\n </div>\n);\n\ninterface PreviewSegmentedOption<T extends string | number> {\n value: T;\n label: string;\n /** Conteúdo visual exibido DENTRO do botão (preview do efeito). */\n preview: ReactNode;\n}\n\ninterface PreviewSegmentedProps<T extends string | number> {\n value: T;\n options: PreviewSegmentedOption<T>[];\n onChange: (value: T) => void;\n ariaLabel: string;\n}\n\n/**\n * Variante do Segmented onde o botão mostra um preview visual do efeito\n * (ex.: \"Aa\" em tamanhos diferentes para \"Tamanho da fonte\") e o label\n * textual fica abaixo do botão como legenda.\n */\nconst PreviewSegmented = <T extends string | number>({\n value,\n options,\n onChange,\n ariaLabel,\n}: Readonly<PreviewSegmentedProps<T>>) => (\n <div role=\"radiogroup\" aria-label={ariaLabel} className=\"flex gap-2\">\n {options.map((opt) => {\n const isActive = opt.value === value;\n return (\n <div\n key={String(opt.value)}\n className=\"flex flex-1 flex-col items-center gap-2\"\n >\n <Button\n variant=\"raw\"\n role=\"radio\"\n aria-checked={isActive}\n aria-label={opt.label}\n onClick={() => onChange(opt.value)}\n className={cn(\n 'flex aspect-square w-full items-center justify-center',\n 'cursor-pointer rounded-xl border border-border-50 bg-background',\n 'text-text-700 shadow-soft-shadow-1 transition-all duration-150',\n 'hover:bg-background-100',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indicator-info',\n isActive &&\n 'ring-2 ring-primary-950 ring-offset-0 shadow-none text-primary-950'\n )}\n >\n {opt.preview}\n </Button>\n <Text\n size=\"xs\"\n weight=\"normal\"\n className=\"leading-none text-text-700\"\n >\n {opt.label}\n </Text>\n </div>\n );\n })}\n </div>\n);\n\nexport interface AccessibilityPanelProps {\n isOpen: boolean;\n onClose: () => void;\n /** Lado da viewport onde o painel aparece (default: 'right') */\n position?: AccessibilityFabPosition;\n className?: string;\n}\n\n/**\n * Painel lateral com os controles de acessibilidade organizados em\n * accordions colapsáveis (Visão, Leitura, Leitor de texto, Animação,\n * Navegação, Atalho de teclado).\n *\n * O próprio painel fica dentro de `.a11y-widget-shield`, então\n * filtros aplicados no `<html>` (alto contraste, etc.) não afetam\n * sua legibilidade.\n */\nexport default function AccessibilityPanel({\n isOpen,\n onClose,\n position = 'right',\n className,\n}: Readonly<AccessibilityPanelProps>) {\n const closeButtonRef = useRef<HTMLButtonElement>(null);\n const previouslyFocusedRef = useRef<Element | null>(null);\n\n const {\n contrastMode,\n saturationMode,\n fontSize,\n letterSpacing,\n lineSpacing,\n highlightLinks,\n pauseAnimations,\n bigCursor,\n dyslexiaFont,\n readingAid,\n keyboardShortcut,\n setContrastMode,\n setSaturationMode,\n setFontSize,\n setLetterSpacing,\n setLineSpacing,\n setHighlightLinks,\n setPauseAnimations,\n setBigCursor,\n setDyslexiaFont,\n setReadingAid,\n setKeyboardShortcut,\n resetPreferences,\n } = useAccessibilityStore();\n\n // Foco e Escape: ao abrir, lembra o elemento anteriormente focado,\n // move o foco para o botão de fechar e escuta a tecla Esc. Restaura\n // o foco anterior ao fechar.\n useEffect(() => {\n if (!isOpen) return;\n previouslyFocusedRef.current = document.activeElement;\n closeButtonRef.current?.focus();\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n event.stopPropagation();\n onClose();\n }\n };\n globalThis.addEventListener('keydown', handleKeyDown);\n\n return () => {\n globalThis.removeEventListener('keydown', handleKeyDown);\n const previous = previouslyFocusedRef.current as HTMLElement | null;\n if (previous && typeof previous.focus === 'function') {\n previous.focus();\n }\n };\n }, [isOpen, onClose]);\n\n if (!isOpen) return null;\n\n const levelOptions = LEVEL_LABELS.map((label, index) => ({\n value: index as 0 | 1 | 2 | 3,\n label,\n }));\n\n return (\n <dialog\n open\n aria-label=\"Opções de acessibilidade\"\n data-testid=\"accessibility-panel\"\n // `<dialog>` não estica automaticamente com `top` + `bottom`, por\n // isso a altura fica explícita: `100vh - 5rem` (80px de respiro do\n // topo). Combinado com `bottom-0` no className, o painel encosta\n // na base da viewport como no Figma.\n style={{ height: 'calc(100vh - 5rem)' }}\n className={cn(\n 'a11y-widget-shield',\n 'fixed bottom-0 z-40 m-0',\n PANEL_EDGE_CLASSES[position],\n 'flex w-[calc(100vw-3rem)] max-w-110 flex-col overflow-hidden rounded-2xl p-0',\n 'border border-background-200 bg-background shadow-2xl',\n className\n )}\n >\n {/* Header — visual do Figma: bg azul-claro (Paraná) / rosa-claro\n (Paraíba) usando `primary-50`. Preferido em vez de `map-attention`\n porque `primary-50` flipa corretamente no dark mode em todos os\n temas (ex.: Paraná dark = #27344a), enquanto `map-attention`\n ficou idêntico em light/dark no theme do Paraná, deixando o\n texto branco invisível sobre o azul-claro no dark. */}\n <header className=\"flex items-center justify-between gap-3 bg-primary-50 px-4 py-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <img\n src={accessibilityIcon}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"h-10 w-10 shrink-0\"\n />\n <div className=\"flex min-w-0 flex-col gap-1\">\n <Text\n size=\"md\"\n weight=\"bold\"\n className=\"leading-none tracking-[0.2px] text-text-950\"\n >\n Acessibilidade\n </Text>\n <Text\n size=\"xs\"\n weight=\"normal\"\n className=\"leading-none text-text-800\"\n >\n Personalize a leitura e navegação\n </Text>\n </div>\n </div>\n <IconButton\n ref={closeButtonRef}\n size=\"sm\"\n aria-label=\"Fechar opções de acessibilidade\"\n onClick={onClose}\n icon={<XIcon size={18} />}\n />\n </header>\n\n <div className=\"flex-1 overflow-y-auto\">\n <AccordionSection\n title=\"Visão\"\n icon={<EyeIcon size={18} />}\n testId=\"a11y-section-vision\"\n >\n <SubControl label=\"Contraste\">\n <Segmented\n ariaLabel=\"Modo de contraste\"\n value={contrastMode}\n options={CONTRAST_OPTIONS}\n onChange={setContrastMode}\n />\n </SubControl>\n <SubControl label=\"Saturação\">\n <Segmented\n ariaLabel=\"Modo de saturação\"\n value={saturationMode}\n options={SATURATION_OPTIONS}\n onChange={setSaturationMode}\n />\n </SubControl>\n {/* Daltonismo removido por enquanto — store/store actions\n mantidos para reativação futura sem refactor. */}\n </AccordionSection>\n\n <AccordionSection\n title=\"Leitura\"\n icon={<TextTIcon size={18} />}\n testId=\"a11y-section-reading\"\n >\n <SubControl label=\"Fonte para dislexia\">\n <AccessibilityToggleRow\n ariaLabel=\"Trocar para Comic Sans MS\"\n rowTestId=\"a11y-toggle-dyslexia-font-row\"\n switchTestId=\"a11y-toggle-dyslexia-font\"\n checked={dyslexiaFont}\n onChange={() => setDyslexiaFont(!dyslexiaFont)}\n label={\n <>\n Trocar para{' '}\n <span style={{ fontFamily: '\"Comic Sans MS\", cursive' }}>\n Comic Sans MS\n </span>\n </>\n }\n />\n </SubControl>\n\n <SubControl label=\"Tamanho da fonte\">\n <PreviewSegmented\n ariaLabel=\"Tamanho da fonte\"\n value={fontSize}\n options={levelOptions.map((opt) => ({\n ...opt,\n preview: (\n <span\n className={cn(\n 'font-semibold leading-none',\n FONT_SIZE_PREVIEW_CLASSES[opt.value]\n )}\n >\n Aa\n </span>\n ),\n }))}\n onChange={setFontSize}\n />\n </SubControl>\n\n <SubControl label=\"Espaçamento entre letras\">\n <PreviewSegmented\n ariaLabel=\"Espaçamento entre letras\"\n value={letterSpacing}\n options={levelOptions.map((opt) => ({\n ...opt,\n preview: (\n <span\n className={cn(\n // Mesmo tamanho do \"Aa\" no botão Padrão de \"Tamanho\n // da fonte\" (text-base), pra que essa scale visualize\n // só a variação de letter-spacing.\n 'text-base font-semibold leading-none',\n LETTER_SPACING_PREVIEW_CLASSES[opt.value]\n )}\n >\n Aa\n </span>\n ),\n }))}\n onChange={setLetterSpacing}\n />\n </SubControl>\n\n <SubControl label=\"Espaçamento entre linhas\">\n <PreviewSegmented\n ariaLabel=\"Espaçamento entre linhas\"\n value={lineSpacing}\n options={levelOptions.map((opt) => ({\n ...opt,\n preview: (\n <span\n className={cn(\n 'flex flex-col items-stretch',\n LINE_SPACING_PREVIEW_GAPS[opt.value]\n )}\n >\n <span className=\"h-0.5 w-7 rounded-full bg-current\" />\n <span className=\"h-0.5 w-7 rounded-full bg-current\" />\n <span className=\"h-0.5 w-7 rounded-full bg-current\" />\n </span>\n ),\n }))}\n onChange={setLineSpacing}\n />\n </SubControl>\n\n <SubControl label=\"Auxiliar de leitura\">\n <Segmented\n ariaLabel=\"Auxiliar de leitura\"\n value={readingAid}\n options={READING_AID_OPTIONS}\n onChange={setReadingAid}\n />\n </SubControl>\n </AccordionSection>\n\n <AccordionSection\n title=\"Leitor de texto\"\n icon={<SpeakerHighIcon size={18} />}\n testId=\"a11y-section-tts\"\n >\n <TTSSection PreviewSegmented={PreviewSegmented} />\n </AccordionSection>\n\n <AccordionSection\n title=\"Animação\"\n icon={<FilmReelIcon size={18} />}\n testId=\"a11y-section-animation\"\n >\n <AccessibilityToggleRow\n label=\"Pausar animações para conforto visual\"\n checked={pauseAnimations}\n onChange={() => setPauseAnimations(!pauseAnimations)}\n switchTestId=\"a11y-toggle-pause-animations\"\n />\n </AccordionSection>\n\n <AccordionSection\n title=\"Navegação\"\n icon={<NavigationArrowIcon size={18} />}\n testId=\"a11y-section-navigation\"\n >\n <SubControl label=\"Destacar links e botões\">\n <AccessibilityToggleRow\n label=\"Adicionar contorno em elementos clicáveis\"\n checked={highlightLinks}\n onChange={() => setHighlightLinks(!highlightLinks)}\n switchTestId=\"a11y-toggle-highlight-links\"\n />\n </SubControl>\n <SubControl label=\"Cursor maior\">\n <AccessibilityToggleRow\n label=\"Aumentar e escurecer cursor\"\n checked={bigCursor}\n onChange={() => setBigCursor(!bigCursor)}\n switchTestId=\"a11y-toggle-big-cursor\"\n />\n </SubControl>\n </AccordionSection>\n\n <AccordionSection\n title=\"Atalho de teclado\"\n icon={<KeyboardIcon size={18} />}\n testId=\"a11y-section-shortcut\"\n >\n <AccessibilityToggleRow\n label=\"Alt+A para abrir o painel\"\n checked={keyboardShortcut}\n onChange={() => setKeyboardShortcut(!keyboardShortcut)}\n switchTestId=\"a11y-toggle-keyboard-shortcut\"\n />\n </AccordionSection>\n </div>\n\n <footer className=\"border-t border-background-200 px-4 py-3\">\n <Button\n variant=\"outline\"\n action=\"primary\"\n size=\"medium\"\n iconLeft={\n <ArrowCounterClockwiseIcon\n size={16}\n weight=\"bold\"\n aria-hidden=\"true\"\n />\n }\n onClick={resetPreferences}\n data-testid=\"a11y-reset\"\n className=\"w-full justify-center\"\n >\n Redefinir ajustes\n </Button>\n </footer>\n </dialog>\n );\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * IconButton component props interface\n */\nexport type IconButtonProps = {\n /** Ícone a ser exibido no botão */\n icon: ReactNode;\n /** Tamanho do botão */\n size?: 'sm' | 'md';\n /** Estado de seleção/ativo do botão - permanece ativo até ser clicado novamente ou outro botão ser ativado */\n active?: boolean;\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * IconButton component for Analytica Ensino platforms\n *\n * Um botão compacto apenas com ícone, ideal para menus dropdown,\n * barras de ferramentas e ações secundárias.\n * Oferece dois tamanhos com estilo consistente.\n * Estado ativo permanece até ser clicado novamente ou outro botão ser ativado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param icon - O ícone a ser exibido no botão\n * @param size - Tamanho do botão (sm, md)\n * @param active - Estado ativo/selecionado do botão\n * @param className - Classes CSS adicionais\n * @param props - Todos os outros atributos HTML padrão de button\n * @returns Um elemento button compacto estilizado apenas com ícone\n *\n * @example\n * ```tsx\n * <IconButton\n * icon={<MoreVerticalIcon />}\n * size=\"sm\"\n * onClick={() => openMenu()}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Botão ativo em uma barra de ferramentas - permanece ativo até outro clique\n * <IconButton\n * icon={<BoldIcon />}\n * active={isBold}\n * onClick={toggleBold}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usando ref para controle programático\n * const buttonRef = useRef<HTMLButtonElement>(null);\n *\n * <IconButton\n * ref={buttonRef}\n * icon={<EditIcon />}\n * size=\"md\"\n * onClick={() => startEditing()}\n * />\n * ```\n */\nconst IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(\n (\n { icon, size = 'md', active = false, className = '', disabled, ...props },\n ref\n ) => {\n // Classes base para todos os estados\n const baseClasses = [\n 'inline-flex',\n 'items-center',\n 'justify-center',\n 'rounded-lg',\n 'font-medium',\n 'bg-transparent',\n 'text-text-950',\n 'cursor-pointer',\n 'hover:bg-primary-600',\n 'hover:text-text',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-offset-0',\n 'focus-visible:ring-indicator-info',\n 'disabled:opacity-50',\n 'disabled:cursor-not-allowed',\n 'disabled:pointer-events-none',\n ];\n\n // Classes de tamanho\n const sizeClasses = {\n sm: ['w-6', 'h-6', 'text-sm'],\n md: ['w-10', 'h-10', 'text-base'],\n };\n\n // Classes de estado ativo\n const activeClasses = active\n ? ['!bg-primary-50', '!text-primary-950', 'hover:!bg-primary-100']\n : [];\n\n const allClasses = [\n ...baseClasses,\n ...sizeClasses[size],\n ...activeClasses,\n ].join(' ');\n\n // Garantir acessibilidade com aria-label padrão\n const ariaLabel = props['aria-label'] ?? 'Botão de ação';\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(allClasses, className)}\n disabled={disabled}\n aria-pressed={active}\n aria-label={ariaLabel}\n {...props}\n >\n <span className=\"flex items-center justify-center\">{icon}</span>\n </button>\n );\n }\n);\n\nIconButton.displayName = 'IconButton';\n\nexport default IconButton;\n","import { ButtonHTMLAttributes } from 'react';\nimport { cn } from '../../utils/utils';\nimport Button from '../Button/Button';\n\n/**\n * ToggleSwitch size variants\n */\ntype ToggleSwitchSize = 'small' | 'medium' | 'large';\n\n/**\n * Size configurations using Tailwind classes\n */\nconst SIZE_CLASSES = {\n small: {\n track: 'h-5 w-9',\n thumb: 'h-4 w-4',\n translate: 'translate-x-4',\n },\n medium: {\n track: 'h-6 w-11',\n thumb: 'h-5 w-5',\n translate: 'translate-x-5',\n },\n large: {\n track: 'h-7 w-14',\n thumb: 'h-6 w-6',\n translate: 'translate-x-7',\n },\n} as const;\n\n/**\n * ToggleSwitch component props interface\n */\nexport type ToggleSwitchProps = {\n /** Whether the toggle is checked */\n checked?: boolean;\n /** Callback when toggle state changes */\n onChange?: () => void;\n /** Size variant of the toggle switch */\n size?: ToggleSwitchSize;\n /** Whether the toggle switch is disabled */\n disabled?: boolean;\n /** Additional CSS classes for the track */\n className?: string;\n /** Additional CSS classes for the thumb */\n thumbClassName?: string;\n /** Color when checked (Tailwind class, e.g., 'bg-success-500') */\n checkedColor?: string;\n /** Color when unchecked (Tailwind class, e.g., 'bg-border-300') */\n uncheckedColor?: string;\n} & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'size' | 'type' | 'onChange'>;\n\n/**\n * ToggleSwitch component for Analytica Ensino platforms\n *\n * A toggle switch component with support for different sizes and colors.\n * Uses the Button component with variant=\"raw\" for clean rendering.\n *\n * @example\n * ```tsx\n * // Basic toggle switch\n * <ToggleSwitch checked={isActive} onChange={() => setIsActive(!isActive)} />\n *\n * // Different sizes\n * <ToggleSwitch size=\"small\" checked={value} onChange={toggle} />\n * <ToggleSwitch size=\"large\" checked={value} onChange={toggle} />\n *\n * // Custom colors\n * <ToggleSwitch\n * checked={isEnabled}\n * onChange={toggle}\n * checkedColor=\"bg-primary-950\"\n * uncheckedColor=\"bg-border-400\"\n * />\n * ```\n */\nconst ToggleSwitch = ({\n checked = false,\n onChange,\n size = 'small',\n disabled = false,\n className = '',\n thumbClassName = '',\n checkedColor = 'bg-success-500',\n uncheckedColor = 'bg-border-300',\n ...props\n}: ToggleSwitchProps) => {\n const sizeClasses = SIZE_CLASSES[size];\n\n const trackClasses = cn(\n 'relative inline-flex shrink-0 cursor-pointer rounded-full border-2 border-transparent',\n 'transition-colors duration-200 ease-in-out',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2',\n sizeClasses.track,\n checked ? checkedColor : uncheckedColor,\n disabled && 'cursor-not-allowed opacity-40',\n className\n );\n\n const thumbClasses = cn(\n 'pointer-events-none inline-block transform rounded-full bg-white shadow ring-0',\n 'transition duration-200 ease-in-out',\n sizeClasses.thumb,\n checked ? sizeClasses.translate : 'translate-x-0',\n thumbClassName\n );\n\n return (\n <Button\n variant=\"raw\"\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled}\n className={trackClasses}\n {...props}\n onClick={(e) => {\n props.onClick?.(e);\n if (!e.defaultPrevented) {\n onChange?.();\n }\n }}\n >\n <span className={thumbClasses} />\n </Button>\n );\n};\n\nToggleSwitch.displayName = 'ToggleSwitch';\n\nexport default ToggleSwitch;\n","import type { ReactNode } from 'react';\nimport Text from '../Text/Text';\nimport ToggleSwitch from '../ToggleSwitch/ToggleSwitch';\nimport { cn } from '../../utils/utils';\n\nexport interface AccessibilityToggleRowProps {\n /** Conteúdo principal da linha (texto ou JSX, ex.: trecho em Comic Sans). */\n label: ReactNode;\n /** Aria-label do row quando `label` é JSX e não pode ser usado como texto. */\n ariaLabel?: string;\n /** Texto auxiliar abaixo do label. */\n description?: string;\n checked: boolean;\n onChange: () => void;\n /** Testid no wrapper interativo (toda a linha). */\n rowTestId?: string;\n /** Testid no `<ToggleSwitch>` interno — usado pelos testes que ainda\n * clicam diretamente no botão de switch. */\n switchTestId?: string;\n}\n\n/**\n * Linha clicável \"label à esquerda + ToggleSwitch à direita\" usada nos\n * accordions de Animação, Navegação, Atalho, dislexia e leitor de texto.\n *\n * O wrapper inteiro responde a click/Enter/Space; o `ToggleSwitch` interno\n * é apenas decorativo (`tabIndex={-1}`, `aria-hidden`) pra que toda a área\n * vire alvo de clique sem dupla-ativação do switch.\n */\nexport default function AccessibilityToggleRow({\n label,\n ariaLabel,\n description,\n checked,\n onChange,\n rowTestId,\n switchTestId,\n}: Readonly<AccessibilityToggleRowProps>) {\n const computedAriaLabel =\n ariaLabel ?? (typeof label === 'string' ? label : undefined);\n return (\n <div\n role=\"switch\"\n tabIndex={0}\n aria-checked={checked}\n aria-label={computedAriaLabel}\n data-testid={rowTestId}\n className={cn(\n 'flex items-center justify-between gap-3 rounded-md py-1.5',\n 'cursor-pointer transition-colors duration-150 hover:bg-background-100',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-info-500'\n )}\n onClick={onChange}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onChange();\n }\n }}\n >\n <div className=\"flex flex-col\">\n <Text size=\"sm\" className=\"text-text-900\">\n {label}\n </Text>\n {description && (\n <Text size=\"2xs\" className=\"text-text-600\">\n {description}\n </Text>\n )}\n </div>\n <ToggleSwitch\n checked={checked}\n onChange={() => undefined}\n checkedColor=\"bg-info-600\"\n data-testid={switchTestId}\n tabIndex={-1}\n aria-hidden\n />\n </div>\n );\n}\n","import { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { PauseIcon, PlayIcon, StopIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport Text from '../Text/Text';\nimport Select, {\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n} from '../Select/Select';\nimport AccessibilityToggleRow from './AccessibilityToggleRow';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\nimport { useTTS } from '../../hooks/useTTS';\n\n/**\n * Opções de velocidade da fala. O `preview` mostra o valor numérico\n * dentro do botão (1.0, 1.25, etc.) e o `label` aparece como caption\n * abaixo. Valores escolhidos para casarem com o que o Web Speech API\n * aceita (0.5–2.0) — `0.75` foi mantido para \"Lento\" em vez de algo\n * mais agressivo porque vozes nativas ficam ininteligíveis abaixo disso.\n */\nconst RATE_OPTIONS: {\n value: string;\n label: string;\n preview: ReactNode;\n}[] = [\n { value: '0.75', label: 'Lento', preview: '0.75' },\n { value: '1', label: 'Normal', preview: '1.0' },\n { value: '1.25', label: 'Rápido', preview: '1.25' },\n { value: '2', label: 'Muito rápido', preview: '2.0' },\n];\n\nexport interface TTSSectionProps {\n /** Componente de segmented com preview visual + label abaixo */\n PreviewSegmented: <T extends string | number>(props: {\n value: T;\n options: { value: T; label: string; preview: ReactNode }[];\n ariaLabel: string;\n onChange: (value: T) => void;\n }) => ReactElement;\n}\n\n/**\n * Bloco de controles do leitor de texto dentro do painel.\n * Estrutura segue o design do Figma:\n * 1. \"Texto para voz\" — toggle que ativa/desativa o modo click-to-read\n * 2. \"Voz\" — seletor de voz\n * 3. \"Velocidade\" — PreviewSegmented com valor numérico\n */\nexport default function TTSSection({\n PreviewSegmented,\n}: Readonly<TTSSectionProps>) {\n const ttsMode = useAccessibilityStore((s) => s.ttsMode);\n const ttsStatus = useAccessibilityStore((s) => s.ttsStatus);\n const ttsRate = useAccessibilityStore((s) => s.ttsRate);\n const ttsVoiceId = useAccessibilityStore((s) => s.ttsVoiceId);\n const setTTSMode = useAccessibilityStore((s) => s.setTTSMode);\n const setTTSRate = useAccessibilityStore((s) => s.setTTSRate);\n const setTTSVoiceId = useAccessibilityStore((s) => s.setTTSVoiceId);\n\n const { isSupported, voices, hasPortugueseVoice, pause, resume, stop } =\n useTTS();\n\n // Preferimos vozes em português; demais ficam ao final.\n const sortedVoices = useMemo(() => {\n return [...voices].sort((a, b) => {\n const aPt = a.lang.toLowerCase().startsWith('pt') ? 0 : 1;\n const bPt = b.lang.toLowerCase().startsWith('pt') ? 0 : 1;\n if (aPt !== bPt) return aPt - bPt;\n return a.name.localeCompare(b.name);\n });\n }, [voices]);\n\n if (!isSupported) {\n return (\n <Text size=\"2xs\" className=\"text-text-600\">\n Síntese de voz não está disponível neste navegador.\n </Text>\n );\n }\n\n const isClickToReadOn = ttsMode === 'click-to-read';\n\n return (\n <div className=\"flex flex-col gap-6\">\n {/* Texto para voz: toggle row */}\n <div className=\"flex flex-col gap-2\">\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"uppercase leading-none text-text-600\"\n >\n Texto para voz\n </Text>\n <AccessibilityToggleRow\n label=\"Ative a leitura e clique no texto para ouvir\"\n rowTestId=\"a11y-tts-mode-toggle\"\n checked={isClickToReadOn}\n onChange={() => setTTSMode(isClickToReadOn ? 'off' : 'click-to-read')}\n />\n </div>\n\n {!hasPortugueseVoice && (\n <Text size=\"2xs\" className=\"text-warning-700\">\n Nenhuma voz em português foi encontrada no seu sistema. A leitura será\n feita com a voz padrão disponível.\n </Text>\n )}\n\n {/* Voz: pill select */}\n <div className=\"flex flex-col gap-2\">\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"uppercase leading-none text-text-600\"\n >\n Voz\n </Text>\n <Select\n size=\"small\"\n value={ttsVoiceId ?? ''}\n onValueChange={(value) => setTTSVoiceId(value || null)}\n >\n <SelectTrigger\n variant=\"rounded\"\n aria-label=\"Voz da síntese de fala\"\n data-testid=\"a11y-tts-voice-select\"\n >\n <SelectValue placeholder=\"Padrão do navegador\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">Padrão do navegador</SelectItem>\n {sortedVoices.map((v) => (\n <SelectItem key={v.id} value={v.id}>\n {v.name} — {v.lang}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Velocidade: preview segmented com valor numérico no botão */}\n <div className=\"flex flex-col gap-2\">\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"uppercase leading-none text-text-600\"\n >\n Velocidade\n </Text>\n <PreviewSegmented\n ariaLabel=\"Velocidade da fala\"\n value={String(ttsRate)}\n options={RATE_OPTIONS.map((opt) => ({\n value: opt.value,\n label: opt.label,\n preview: (\n <span className=\"text-base font-bold leading-none\">\n {opt.preview}\n </span>\n ),\n }))}\n onChange={(v) => setTTSRate(Number(v))}\n />\n </div>\n\n {/* Ações: pause/resume/stop conforme status — só visíveis durante a fala */}\n {ttsStatus !== 'idle' && (\n <div className=\"flex items-center gap-2\">\n {ttsStatus === 'speaking' && (\n <Button\n variant=\"outline\"\n action=\"secondary\"\n size=\"small\"\n iconLeft={\n <PauseIcon size={14} weight=\"bold\" aria-hidden=\"true\" />\n }\n onClick={pause}\n data-testid=\"a11y-tts-pause\"\n >\n Pausar\n </Button>\n )}\n\n {ttsStatus === 'paused' && (\n <Button\n variant=\"outline\"\n action=\"secondary\"\n size=\"small\"\n iconLeft={<PlayIcon size={14} weight=\"bold\" aria-hidden=\"true\" />}\n onClick={resume}\n data-testid=\"a11y-tts-resume\"\n >\n Retomar\n </Button>\n )}\n\n <Button\n variant=\"outline\"\n action=\"negative\"\n size=\"small\"\n iconLeft={<StopIcon size={14} weight=\"bold\" aria-hidden=\"true\" />}\n onClick={stop}\n data-testid=\"a11y-tts-stop\"\n className=\"ml-auto\"\n >\n Parar\n </Button>\n </div>\n )}\n </div>\n );\n}\n","import { create, StoreApi, useStore } from 'zustand';\nimport {\n ReactNode,\n useEffect,\n useRef,\n useState,\n useCallback,\n ButtonHTMLAttributes,\n forwardRef,\n HTMLAttributes,\n KeyboardEvent,\n MouseEvent,\n ReactElement,\n isValidElement,\n Children,\n cloneElement,\n useId,\n CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { CaretDown, Check, WarningCircle } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\nconst VARIANT_CLASSES = {\n outlined: 'border-2 rounded-lg focus:border-primary-950',\n underlined: 'border-b-2 focus:border-primary-950',\n rounded: 'border-2 rounded-full focus:border-primary-950',\n} as const;\n\nconst SIZE_CLASSES = {\n small: 'text-sm',\n medium: 'text-md',\n large: 'text-lg',\n 'extra-large': 'text-lg',\n} as const;\n\nconst HEIGHT_CLASSES = {\n small: 'h-8',\n medium: 'h-9',\n large: 'h-10',\n 'extra-large': 'h-12',\n} as const;\n\nconst PADDING_CLASSES = {\n small: 'px-2 py-1',\n medium: 'px-3 py-2',\n large: 'px-4 py-3',\n 'extra-large': 'px-5 py-4',\n} as const;\n\ninterface TriggerRect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\ntype SelectAlign = 'start' | 'center' | 'end';\n\ninterface SelectStore {\n open: boolean;\n setOpen: (open: boolean) => void;\n value: string;\n setValue: (value: string) => void;\n selectedLabel: ReactNode;\n setSelectedLabel: (label: ReactNode) => void;\n onValueChange?: (value: string) => void;\n triggerRect: TriggerRect | null;\n setTriggerRect: (rect: TriggerRect | null) => void;\n}\n\ntype SelectStoreApi = StoreApi<SelectStore>;\n\nexport function createSelectStore(\n onValueChange?: (value: string) => void\n): SelectStoreApi {\n return create<SelectStore>((set) => ({\n open: false,\n setOpen: (open) => set({ open }),\n value: '',\n setValue: (value) => set({ value }),\n selectedLabel: '',\n setSelectedLabel: (label) => set({ selectedLabel: label }),\n onValueChange,\n triggerRect: null,\n setTriggerRect: (rect) => set({ triggerRect: rect }),\n }));\n}\n\nexport const useSelectStore = (externalStore?: SelectStoreApi) => {\n if (!externalStore) {\n throw new Error(\n 'Component must be used within a Select (store is missing)'\n );\n }\n\n return externalStore;\n};\n\nexport function getLabelAsNode(children: ReactNode): ReactNode {\n if (typeof children === 'string' || typeof children === 'number') {\n return children;\n }\n const flattened = Children.toArray(children);\n\n if (flattened.length === 1) return flattened[0];\n\n return <>{flattened}</>;\n}\n\ninterface SelectProps {\n className?: string;\n children: ReactNode;\n defaultValue?: string;\n value?: string;\n onValueChange?: (value: string) => void;\n size?: 'small' | 'medium' | 'large' | 'extra-large';\n label?: string;\n helperText?: string;\n errorMessage?: string;\n id?: string;\n}\n\nconst injectStore = (\n children: ReactNode,\n store: SelectStoreApi,\n size: string,\n selectId: string\n): ReactNode => {\n return Children.map(children, (child) => {\n if (isValidElement(child)) {\n const typedChild = child as ReactElement<{\n store?: SelectStoreApi;\n children?: ReactNode;\n size?: string;\n selectId?: string;\n }>;\n\n const newProps: Partial<{\n store: SelectStoreApi;\n children: ReactNode;\n size: string;\n selectId: string;\n }> = {\n store,\n };\n\n // Pass size to SelectTrigger, selectId to both Trigger and Content\n if (typedChild.type === SelectTrigger) {\n newProps.size = size;\n newProps.selectId = selectId;\n }\n\n if (typedChild.type === SelectContent) {\n newProps.selectId = selectId;\n }\n\n if (typedChild.props.children) {\n newProps.children = injectStore(\n typedChild.props.children,\n store,\n size,\n selectId\n );\n }\n\n return cloneElement(typedChild, newProps);\n }\n return child;\n });\n};\n\nconst Select = ({\n children,\n defaultValue = '',\n className,\n value: propValue,\n onValueChange,\n size = 'small',\n label,\n helperText,\n errorMessage,\n id,\n}: SelectProps) => {\n const storeRef = useRef<SelectStoreApi | null>(null);\n storeRef.current ??= createSelectStore(onValueChange);\n const store = storeRef.current;\n\n const selectRef = useRef<HTMLDivElement>(null);\n const { open, setOpen, setValue, selectedLabel } = useStore(store, (s) => s);\n\n // Generate unique ID if not provided\n const generatedId = useId();\n const selectId = id ?? `select-${generatedId}`;\n\n const findLabelForValue = (\n children: ReactNode,\n targetValue: string\n ): string | null => {\n let found: string | null = null;\n const search = (nodes: ReactNode) => {\n Children.forEach(nodes, (child) => {\n if (!isValidElement(child)) return;\n const typedChild = child as ReactElement<{\n value?: string;\n children?: ReactNode;\n }>;\n if (\n typedChild.type === SelectItem &&\n typedChild.props.value === targetValue\n ) {\n if (typeof typedChild.props.children === 'string')\n found = typedChild.props.children;\n }\n if (typedChild.props.children && !found)\n search(typedChild.props.children);\n });\n };\n search(children);\n return found;\n };\n\n useEffect(() => {\n if (!selectedLabel && defaultValue) {\n const label = findLabelForValue(children, defaultValue);\n if (label) store.setState({ selectedLabel: label });\n }\n }, [children, defaultValue, selectedLabel]);\n\n useEffect(() => {\n const handleClickOutside = (event: globalThis.MouseEvent) => {\n const target = event.target as Node;\n // Check if click is inside the trigger container\n const isInsideTrigger = selectRef.current?.contains(target);\n // Check if click is inside the portaled content (scoped to this Select instance)\n const portaledMenu = document.body.querySelector(\n `[role=\"menu\"][data-select-id=\"${selectId}\"]`\n );\n const isInsidePortaledMenu = portaledMenu?.contains(target);\n\n if (!isInsideTrigger && !isInsidePortaledMenu) {\n setOpen(false);\n }\n };\n\n const handleArrowKeys = (event: globalThis.KeyboardEvent) => {\n // Find the portaled menu in the body (scoped to this Select instance)\n const selectContent = document.body.querySelector(\n `[role=\"menu\"][data-select-id=\"${selectId}\"]`\n );\n if (selectContent) {\n event.preventDefault();\n const items = Array.from(\n selectContent.querySelectorAll(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])'\n )\n ).filter((el): el is HTMLElement => el instanceof HTMLElement);\n\n const focused = document.activeElement as HTMLElement;\n const currentIndex = items.findIndex((item) => item === focused);\n\n let nextIndex: number;\n if (event.key === 'ArrowDown') {\n nextIndex =\n currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;\n } else {\n nextIndex =\n currentIndex === -1\n ? items.length - 1\n : (currentIndex - 1 + items.length) % items.length;\n }\n items[nextIndex]?.focus();\n }\n };\n\n if (open) {\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('keydown', handleArrowKeys);\n }\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n document.removeEventListener('keydown', handleArrowKeys);\n };\n }, [open, selectId, setOpen]);\n\n useEffect(() => {\n // Skip when the consumer isn't using controlled mode\n if (propValue === undefined) return;\n setValue(propValue);\n // Resolve the label for the new value; if it can't be resolved (empty\n // value, or a value with no matching SelectItem), clear the stale label\n // so the placeholder takes over.\n const label = findLabelForValue(children, propValue);\n store.setState({ selectedLabel: label ?? '' });\n }, [propValue, children]);\n\n const sizeClasses = SIZE_CLASSES[size];\n\n return (\n <div className={cn('w-full', className)}>\n {/* Label */}\n {label && (\n <label\n htmlFor={selectId}\n className={cn('block font-bold text-text-900 mb-1.5', sizeClasses)}\n >\n {label}\n </label>\n )}\n\n {/* Select Container */}\n <div className={cn('relative w-full')} ref={selectRef}>\n {injectStore(children, store, size, selectId)}\n </div>\n\n {/* Helper Text or Error Message */}\n {(helperText || errorMessage) && (\n <div className=\"mt-1.5 gap-1.5\">\n {helperText && <p className=\"text-sm text-text-500\">{helperText}</p>}\n {errorMessage && (\n <p className=\"flex gap-1 items-center text-sm text-indicator-error\">\n <WarningCircle size={16} /> {errorMessage}\n </p>\n )}\n </div>\n )}\n </div>\n );\n};\n\nconst SelectValue = ({\n placeholder,\n store: externalStore,\n}: {\n placeholder?: string;\n store?: SelectStoreApi;\n}) => {\n const store = useSelectStore(externalStore);\n\n const selectedLabel = useStore(store, (s) => s.selectedLabel);\n const value = useStore(store, (s) => s.value);\n return (\n <span className=\"text-inherit flex gap-2 items-center\">\n {selectedLabel || placeholder || value}\n </span>\n );\n};\n\ninterface SelectTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n className?: string;\n invalid?: boolean;\n variant?: 'outlined' | 'underlined' | 'rounded';\n store?: SelectStoreApi;\n size?: 'small' | 'medium' | 'large' | 'extra-large';\n selectId?: string;\n}\n\nconst SelectTrigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(\n (\n {\n className,\n invalid = false,\n variant = 'outlined',\n store: externalStore,\n disabled,\n size = 'medium',\n selectId,\n type = 'button',\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const internalRef = useRef<HTMLButtonElement>(null);\n\n const setRefs = useCallback(\n (element: HTMLButtonElement | null) => {\n internalRef.current = element;\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n },\n [ref]\n );\n\n const updateTriggerRect = useCallback(() => {\n if (internalRef.current) {\n const rect = internalRef.current.getBoundingClientRect();\n store.setState({\n triggerRect: {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n },\n });\n }\n }, [store]);\n\n // Update triggerRect on scroll/resize while open\n useEffect(() => {\n if (!open) return;\n\n const handleUpdate = () => {\n updateTriggerRect();\n };\n\n window.addEventListener('scroll', handleUpdate, true);\n window.addEventListener('resize', handleUpdate);\n\n return () => {\n window.removeEventListener('scroll', handleUpdate, true);\n window.removeEventListener('resize', handleUpdate);\n };\n }, [open, updateTriggerRect]);\n\n const toggleOpen = () => {\n const newOpen = !open;\n if (newOpen) {\n updateTriggerRect();\n }\n store.setState({ open: newOpen });\n };\n\n const variantClasses = VARIANT_CLASSES[variant];\n const heightClasses = HEIGHT_CLASSES[size];\n const paddingClasses = PADDING_CLASSES[size];\n\n return (\n <button\n ref={setRefs}\n id={selectId}\n type={type}\n className={cn(\n 'flex w-full items-center justify-between border-border-300',\n heightClasses,\n paddingClasses,\n invalid &&\n `${variant == 'underlined' ? 'border-b-2' : 'border-2'} border-indicator-error text-text-600`,\n disabled\n ? 'cursor-not-allowed text-text-400 pointer-events-none opacity-50'\n : 'cursor-pointer hover:bg-background-50 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground',\n !invalid && !disabled ? 'text-text-700' : '',\n variantClasses,\n className\n )}\n onClick={toggleOpen}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={open ? 'select-content' : undefined}\n {...props}\n >\n {props.children}\n <CaretDown\n className={cn(\n 'h-[1em] w-[1em] opacity-50 transition-transform',\n open ? 'rotate-180' : ''\n )}\n />\n </button>\n );\n }\n);\nSelectTrigger.displayName = 'SelectTrigger';\n\nfunction applyVerticalPosition(\n styles: CSSProperties,\n triggerRect: TriggerRect,\n side: 'top' | 'bottom',\n align: SelectAlign,\n gap: number\n): void {\n styles.top =\n side === 'top'\n ? triggerRect.top - gap\n : triggerRect.top + triggerRect.height + gap;\n styles.transform = side === 'top' ? 'translateY(-100%)' : undefined;\n\n if (align === 'start') {\n styles.left = triggerRect.left;\n } else if (align === 'center') {\n styles.left = triggerRect.left + triggerRect.width / 2;\n styles.transform =\n side === 'top' ? 'translate(-50%, -100%)' : 'translateX(-50%)';\n } else {\n styles.left = triggerRect.left + triggerRect.width;\n styles.transform =\n side === 'top' ? 'translate(-100%, -100%)' : 'translateX(-100%)';\n }\n}\n\nfunction applyHorizontalPosition(\n styles: CSSProperties,\n triggerRect: TriggerRect,\n side: 'left' | 'right',\n align: SelectAlign,\n gap: number\n): void {\n styles.left =\n side === 'left'\n ? triggerRect.left - gap\n : triggerRect.left + triggerRect.width + gap;\n styles.transform = side === 'left' ? 'translateX(-100%)' : undefined;\n\n if (align === 'start') {\n styles.top = triggerRect.top;\n } else if (align === 'center') {\n styles.top = triggerRect.top + triggerRect.height / 2;\n styles.transform =\n side === 'left' ? 'translate(-100%, -50%)' : 'translateY(-50%)';\n } else {\n styles.top = triggerRect.top + triggerRect.height;\n styles.transform =\n side === 'left' ? 'translate(-100%, -100%)' : 'translateY(-100%)';\n }\n}\n\ninterface SelectContentProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n align?: 'start' | 'center' | 'end';\n side?: 'top' | 'right' | 'bottom' | 'left';\n store?: SelectStoreApi;\n selectId?: string;\n}\n\nconst SelectContent = forwardRef<HTMLDivElement, SelectContentProps>(\n (\n {\n children,\n className,\n align = 'start',\n side = 'bottom',\n store: externalStore,\n selectId,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const triggerRect = useStore(store, (s) => s.triggerRect);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n if (!open || !mounted) return null;\n\n // Calculate position based on trigger rect\n const getPositionStyles = (): CSSProperties => {\n if (!triggerRect) {\n return {};\n }\n\n const gap = 4;\n const styles: CSSProperties = {\n position: 'fixed',\n zIndex: 9999,\n };\n\n const isVertical = side === 'top' || side === 'bottom';\n\n if (isVertical) {\n applyVerticalPosition(styles, triggerRect, side, align, gap);\n } else {\n applyHorizontalPosition(styles, triggerRect, side, align, gap);\n }\n\n return styles;\n };\n\n const content = (\n <div\n role=\"menu\"\n ref={ref}\n data-select-id={selectId}\n style={getPositionStyles()}\n className={cn(\n 'bg-secondary min-w-[210px] max-h-[300px] overflow-y-auto overflow-x-hidden rounded-md border p-1 shadow-md border-border-100',\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n\n // Render using portal to escape overflow constraints\n return createPortal(content, document.body);\n }\n);\nSelectContent.displayName = 'SelectContent';\n\ninterface SelectItemProps extends HTMLAttributes<HTMLDivElement> {\n value: string;\n disabled?: boolean;\n store?: SelectStoreApi;\n truncate?: boolean;\n}\n\nconst SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(\n (\n {\n className,\n children,\n value,\n disabled = false,\n store: externalStore,\n truncate = false,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const {\n value: selectedValue,\n setValue,\n setOpen,\n setSelectedLabel,\n onValueChange,\n } = useStore(store, (s) => s);\n\n const handleClick = (\n e: MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>\n ) => {\n const labelNode = getLabelAsNode(children);\n if (!disabled) {\n // Always set the clicked value, even if it's already selected\n setValue(value);\n setSelectedLabel(labelNode);\n setOpen(false);\n onValueChange?.(value);\n }\n props.onClick?.(e as MouseEvent<HTMLDivElement>);\n };\n\n return (\n <div\n role=\"menuitem\"\n aria-disabled={disabled}\n ref={ref}\n className={`\n bg-secondary focus-visible:bg-background-50\n relative flex select-none items-center gap-2 rounded-sm p-3 outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0\n ${className}\n ${\n disabled\n ? 'cursor-not-allowed text-text-400 pointer-events-none opacity-50'\n : 'cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground'\n }\n ${selectedValue === value && 'bg-background-50'}\n `}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') handleClick(e);\n }}\n tabIndex={disabled ? -1 : 0}\n {...props}\n >\n <span className=\"absolute right-2 flex h-3.5 w-3.5 items-center justify-center\">\n {selectedValue === value && <Check className=\"\" />}\n </span>\n {truncate ? (\n <span className=\"truncate block max-w-[200px]\">{children}</span>\n ) : (\n children\n )}\n </div>\n );\n }\n);\n\nSelectItem.displayName = 'SelectItem';\n\nexport default Select;\nexport { SelectTrigger, SelectContent, SelectItem, SelectValue };\n","import { create } from 'zustand';\nimport { devtools, persist } from 'zustand/middleware';\n\n/**\n * Modos de contraste suportados pelo widget de acessibilidade\n */\nexport type ContrastMode = 'normal' | 'high' | 'inverted';\n\n/**\n * Modos de saturação para usuários com sensibilidade visual\n */\nexport type SaturationMode = 'normal' | 'grayscale' | 'low';\n\n/**\n * Auxiliares de leitura mutuamente exclusivos:\n * - `none`: nenhum auxiliar\n * - `ruler`: linha horizontal acompanha o cursor\n * - `mask`: escurece a tela exceto uma faixa central na altura do cursor\n */\nexport type ReadingAid = 'none' | 'ruler' | 'mask';\n\n/**\n * Modo do leitor de texto (TTS):\n * - `off`: leitor desligado\n * - `click-to-read`: usuário clica em um elemento e o widget lê o texto dele\n * - `read-selection`: usuário pressiona \"Ler seleção\" para falar o texto selecionado\n */\nexport type TTSMode = 'off' | 'click-to-read' | 'read-selection';\n\n/** Status runtime da síntese de voz — não persistido. */\nexport type TTSStatus = 'idle' | 'speaking' | 'paused';\n\n/**\n * Modos de daltonismo. Aplicam matrizes `<feColorMatrix>` (SVG) que\n * remapeiam cores para ajudar usuários com cada tipo de discromatopsia.\n *\n * Enum (e não union de strings como os outros modos) porque cada valor\n * é também usado como identificador estável em três outros lugares —\n * classes CSS, IDs de filter SVG e seletores no CSS — então centralizar\n * em um enum + helpers evita strings duplicadas e typos silenciosos.\n */\nexport enum ColorBlindMode {\n None = 'none',\n Protanopia = 'protanopia',\n Deuteranopia = 'deuteranopia',\n Tritanopia = 'tritanopia',\n}\n\n/** Prefixo único para todos os identificadores de daltonismo (classe + SVG id) */\nconst COLOR_BLIND_PREFIX = 'a11y-cb-';\n\n/**\n * Retorna a classe CSS aplicada no `<html>` para o modo informado, ou\n * `null` quando nenhum modo está ativo.\n */\nexport const getColorBlindClass = (mode: ColorBlindMode): string | null =>\n mode === ColorBlindMode.None ? null : `${COLOR_BLIND_PREFIX}${mode}`;\n\n/** Retorna o id do `<filter>` SVG correspondente ao modo. */\nexport const getColorBlindFilterId = (\n mode: Exclude<ColorBlindMode, ColorBlindMode.None>\n): string => `${COLOR_BLIND_PREFIX}${mode}`;\n\n/**\n * Níveis discretos para tamanho de fonte (0 = padrão da página)\n */\nexport type FontSizeLevel = 0 | 1 | 2 | 3;\n\n/**\n * Níveis discretos para espaçamento entre letras e linhas\n */\nexport type SpacingLevel = 0 | 1 | 2 | 3;\n\n/**\n * Conjunto completo de preferências persistidas\n */\nexport interface AccessibilityPreferences {\n contrastMode: ContrastMode;\n saturationMode: SaturationMode;\n fontSize: FontSizeLevel;\n letterSpacing: SpacingLevel;\n lineSpacing: SpacingLevel;\n highlightLinks: boolean;\n pauseAnimations: boolean;\n bigCursor: boolean;\n /** Fonte amigável para dislexia (OpenDyslexic com fallback Comic Sans MS) */\n dyslexiaFont: boolean;\n /** Auxiliar visual de leitura (régua, máscara ou nenhum) */\n readingAid: ReadingAid;\n /** Habilita atalho Alt+A para abrir/fechar o painel */\n keyboardShortcut: boolean;\n /** Modo de daltonismo aplicado (filtro SVG na página inteira) */\n colorBlindMode: ColorBlindMode;\n /** Modo do leitor de texto (TTS) */\n ttsMode: TTSMode;\n /** Velocidade da fala (0.5 a 2.0) */\n ttsRate: number;\n /** Voz selecionada pelo usuário (id retornado pelo provider) ou null para padrão */\n ttsVoiceId: string | null;\n /** Habilita o widget VLibras (gov.br) — carregado lazy ao ativar */\n librasEnabled: boolean;\n}\n\nexport interface AccessibilityState extends AccessibilityPreferences {\n /** Indica se o painel de acessibilidade está aberto */\n isPanelOpen: boolean;\n /** Status runtime da síntese de voz — não persistido */\n ttsStatus: TTSStatus;\n}\n\nexport interface AccessibilityActions {\n setContrastMode: (mode: ContrastMode) => void;\n setSaturationMode: (mode: SaturationMode) => void;\n setFontSize: (level: FontSizeLevel) => void;\n setLetterSpacing: (level: SpacingLevel) => void;\n setLineSpacing: (level: SpacingLevel) => void;\n setHighlightLinks: (value: boolean) => void;\n setPauseAnimations: (value: boolean) => void;\n setBigCursor: (value: boolean) => void;\n setDyslexiaFont: (value: boolean) => void;\n setReadingAid: (mode: ReadingAid) => void;\n setKeyboardShortcut: (value: boolean) => void;\n setColorBlindMode: (mode: ColorBlindMode) => void;\n setTTSMode: (mode: TTSMode) => void;\n setTTSRate: (rate: number) => void;\n setTTSVoiceId: (id: string | null) => void;\n setTTSStatus: (status: TTSStatus) => void;\n setLibrasEnabled: (value: boolean) => void;\n /** Restaura todas as preferências para o estado padrão */\n resetPreferences: () => void;\n openPanel: () => void;\n closePanel: () => void;\n togglePanel: () => void;\n}\n\nexport type AccessibilityStore = AccessibilityState & AccessibilityActions;\n\nexport const DEFAULT_ACCESSIBILITY_PREFERENCES: AccessibilityPreferences = {\n contrastMode: 'normal',\n saturationMode: 'normal',\n fontSize: 0,\n letterSpacing: 0,\n lineSpacing: 0,\n highlightLinks: false,\n pauseAnimations: false,\n bigCursor: false,\n dyslexiaFont: false,\n readingAid: 'none',\n keyboardShortcut: true,\n colorBlindMode: ColorBlindMode.None,\n ttsMode: 'off',\n ttsRate: 1,\n ttsVoiceId: null,\n librasEnabled: false,\n};\n\n/**\n * Store Zustand com persistência em localStorage para preferências\n * de acessibilidade. As preferências são aplicadas ao DOM pelo hook\n * `useA11yPreferences` — este store apenas guarda o estado.\n */\nexport const useAccessibilityStore = create<AccessibilityStore>()(\n devtools(\n persist(\n (set) => ({\n ...DEFAULT_ACCESSIBILITY_PREFERENCES,\n isPanelOpen: false,\n ttsStatus: 'idle',\n\n setContrastMode: (contrastMode) => set({ contrastMode }),\n setSaturationMode: (saturationMode) => set({ saturationMode }),\n setFontSize: (fontSize) => set({ fontSize }),\n setLetterSpacing: (letterSpacing) => set({ letterSpacing }),\n setLineSpacing: (lineSpacing) => set({ lineSpacing }),\n setHighlightLinks: (highlightLinks) => set({ highlightLinks }),\n setPauseAnimations: (pauseAnimations) => set({ pauseAnimations }),\n setBigCursor: (bigCursor) => set({ bigCursor }),\n setDyslexiaFont: (dyslexiaFont) => set({ dyslexiaFont }),\n setReadingAid: (readingAid) => set({ readingAid }),\n setKeyboardShortcut: (keyboardShortcut) => set({ keyboardShortcut }),\n setColorBlindMode: (colorBlindMode) => set({ colorBlindMode }),\n setTTSMode: (ttsMode) => set({ ttsMode }),\n setTTSRate: (ttsRate) => set({ ttsRate }),\n setTTSVoiceId: (ttsVoiceId) => set({ ttsVoiceId }),\n setTTSStatus: (ttsStatus) => set({ ttsStatus }),\n setLibrasEnabled: (librasEnabled) => set({ librasEnabled }),\n\n resetPreferences: () => set({ ...DEFAULT_ACCESSIBILITY_PREFERENCES }),\n\n openPanel: () => set({ isPanelOpen: true }),\n closePanel: () => set({ isPanelOpen: false }),\n togglePanel: () =>\n set((state) => ({ isPanelOpen: !state.isPanelOpen })),\n }),\n {\n name: 'accessibility-store',\n // version 1: removeu `librasEnabled` da persistência (era custoso\n // recarregar VLibras a cada refresh). A migração limpa esse valor\n // de localStorages antigos para evitar auto-load surpresa.\n version: 1,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- migrate works with arbitrary persisted shapes from older versions\n migrate: (persistedState: any) => {\n if (\n persistedState &&\n typeof persistedState === 'object' &&\n 'librasEnabled' in persistedState\n ) {\n delete persistedState.librasEnabled;\n }\n return persistedState;\n },\n partialize: (state) => ({\n contrastMode: state.contrastMode,\n saturationMode: state.saturationMode,\n fontSize: state.fontSize,\n letterSpacing: state.letterSpacing,\n lineSpacing: state.lineSpacing,\n highlightLinks: state.highlightLinks,\n pauseAnimations: state.pauseAnimations,\n bigCursor: state.bigCursor,\n dyslexiaFont: state.dyslexiaFont,\n readingAid: state.readingAid,\n keyboardShortcut: state.keyboardShortcut,\n colorBlindMode: state.colorBlindMode,\n ttsRate: state.ttsRate,\n ttsVoiceId: state.ttsVoiceId,\n // ttsMode e librasEnabled NÃO são persistidos:\n // - ttsMode: o leitor sempre começa desligado ao recarregar\n // a página para evitar comportamento confuso\n // - librasEnabled: o VLibras é pesado (~MBs de assets); auto-\n // carregar a cada refresh seria custoso e surpreendente. O\n // usuário ativa explicitamente quando precisa.\n }),\n }\n ),\n { name: 'accessibility-store' }\n )\n);\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useAccessibilityStore } from '../store/accessibilityStore';\nimport { WebSpeechProvider } from '../components/AccessibilityWidget/tts/WebSpeechProvider';\nimport type {\n TTSProvider,\n TTSVoice,\n} from '../components/AccessibilityWidget/tts/types';\n\nexport interface UseTTSReturn {\n /** Indica se a síntese de voz está disponível neste navegador */\n isSupported: boolean;\n /** Vozes carregadas do provider (resolvidas após o primeiro mount) */\n voices: TTSVoice[];\n /**\n * Indica se há ao menos uma voz pt-BR/pt disponível. Útil para\n * mostrar aviso quando o usuário do SO não tem vozes em português.\n */\n hasPortugueseVoice: boolean;\n /** Lê o texto informado, respeitando rate/voiceId do store. */\n speak: (text: string) => void;\n pause: () => void;\n resume: () => void;\n stop: () => void;\n}\n\n/**\n * Hook que liga o `TTSProvider` (default: `WebSpeechProvider`) ao store\n * de acessibilidade — propaga eventos do provider para `ttsStatus` e\n * fornece comandos `speak/pause/resume/stop` que já aplicam as\n * preferências persistidas (`ttsRate`, `ttsVoiceId`).\n *\n * Aceita um provider custom para casos de TTS via backend (Azure,\n * Google etc.) sem alterar a UI.\n */\nexport const useTTS = (providerOverride?: TTSProvider | null): UseTTSReturn => {\n const setTTSStatus = useAccessibilityStore((s) => s.setTTSStatus);\n const ttsRate = useAccessibilityStore((s) => s.ttsRate);\n const ttsVoiceId = useAccessibilityStore((s) => s.ttsVoiceId);\n\n // Garante uma única instância do provider durante o ciclo de vida do hook.\n const provider = useMemo<TTSProvider>(\n () => providerOverride ?? new WebSpeechProvider(),\n [providerOverride]\n );\n\n const [voices, setVoices] = useState<TTSVoice[]>([]);\n\n // Mantemos as preferências em refs para que callbacks de eventos\n // (onstart/onend) leiam sempre o valor mais recente sem precisar\n // recriar o handler — evita resetar a fala quando o usuário ajusta\n // a velocidade no painel.\n const rateRef = useRef(ttsRate);\n const voiceIdRef = useRef(ttsVoiceId);\n rateRef.current = ttsRate;\n voiceIdRef.current = ttsVoiceId;\n\n /**\n * Marca se ESTA instância de `useTTS` foi quem iniciou a fala atual.\n *\n * Necessário porque vários componentes podem chamar `useTTS()`\n * independentemente (TTSController, TTSSection) e cada um cria seu\n * próprio `WebSpeechProvider`, mas todos compartilham o mesmo\n * `window.speechSynthesis` global. O `cancel()` é global — então,\n * sem essa guarda, o cleanup de uma instância (ex.: TTSSection\n * desmontando ao fechar o painel) cancelaria fala iniciada por outra\n * (ex.: click-to-read em andamento no TTSController).\n */\n const isActiveSpeakerRef = useRef(false);\n\n useEffect(() => {\n let mounted = true;\n if (provider.isSupported()) {\n provider\n .getVoices()\n .then((list) => {\n if (mounted) setVoices(list);\n })\n .catch(() => undefined);\n }\n return () => {\n mounted = false;\n // Só interrompe a síntese se foi essa instância que a iniciou.\n // Caso contrário, deixa o synth global em paz — outras instâncias\n // podem estar usando.\n if (isActiveSpeakerRef.current) {\n provider.stop();\n setTTSStatus('idle');\n isActiveSpeakerRef.current = false;\n }\n };\n }, [provider, setTTSStatus]);\n\n const hasPortugueseVoice = voices.some((v) =>\n v.lang.toLowerCase().startsWith('pt')\n );\n\n /**\n * IMPORTANTE: as funções abaixo são memoizadas com `useCallback` porque\n * são frequentemente passadas para `useEffect` deps em consumidores\n * (TTSController, etc.). Sem memoização, as referências mudam a cada\n * render e disparam cleanups indesejados — incluindo o `stop()` que\n * cancela a fala recém-iniciada (sintoma: ícone de áudio acende na aba\n * do browser mas você não ouve nada).\n */\n const speak = useCallback(\n (text: string) => {\n isActiveSpeakerRef.current = true;\n provider.speak(\n text,\n {\n rate: rateRef.current,\n voiceId: voiceIdRef.current ?? undefined,\n // Não fixamos `lang: 'pt-BR'` aqui — em sistemas sem voz pt-BR\n // instalada, o browser fica tentando achar uma e nunca emite\n // áudio. O provider usa o `voice.lang` quando há voz escolhida.\n },\n {\n onStart: () => setTTSStatus('speaking'),\n onEnd: () => {\n isActiveSpeakerRef.current = false;\n setTTSStatus('idle');\n },\n onPause: () => setTTSStatus('paused'),\n onResume: () => setTTSStatus('speaking'),\n onError: () => {\n isActiveSpeakerRef.current = false;\n setTTSStatus('idle');\n },\n }\n );\n },\n [provider, setTTSStatus]\n );\n\n const pause = useCallback(() => {\n provider.pause();\n setTTSStatus('paused');\n }, [provider, setTTSStatus]);\n\n const resume = useCallback(() => {\n provider.resume();\n setTTSStatus('speaking');\n }, [provider, setTTSStatus]);\n\n const stop = useCallback(() => {\n isActiveSpeakerRef.current = false;\n provider.stop();\n setTTSStatus('idle');\n }, [provider, setTTSStatus]);\n\n return {\n isSupported: provider.isSupported(),\n voices,\n hasPortugueseVoice,\n speak,\n pause,\n resume,\n stop,\n };\n};\n","/* eslint-disable no-undef -- DOM lib types (SpeechSynthesis, SpeechSynthesisVoice)\n são reconhecidas pelo TypeScript mas não pelo `globals.browser` do ESLint. */\nimport type {\n TTSProvider,\n TTSProviderEvents,\n TTSSpeakOptions,\n TTSVoice,\n} from './types';\n\n/**\n * Provider default usando a Web Speech API (`window.speechSynthesis`).\n *\n * Notas de compatibilidade:\n * - Disponível em Chrome, Edge, Firefox, Safari e iOS, mas a qualidade\n * das vozes varia muito por SO.\n * - Em Safari/iOS, `getVoices()` é assíncrono — voltamos vazio na\n * primeira chamada e populamos no evento `voiceschanged`. O método\n * `getVoices()` deste provider já trata isso retornando uma `Promise`.\n * - Em alguns SOs não há voz pt-BR pré-instalada — chamadores devem\n * exibir um aviso quando `getVoices()` não retornar voz nesse idioma.\n */\nexport class WebSpeechProvider implements TTSProvider {\n private readonly synth: SpeechSynthesis | null;\n private currentUtterance: SpeechSynthesisUtterance | null = null;\n\n constructor(synth: SpeechSynthesis | null = getSynth()) {\n this.synth = synth;\n }\n\n isSupported(): boolean {\n return this.synth !== null;\n }\n\n async getVoices(): Promise<TTSVoice[]> {\n if (!this.synth) return [];\n\n const synth = this.synth;\n const collect = (): TTSVoice[] =>\n synth.getVoices().map((v) => ({\n id: v.voiceURI,\n name: v.name,\n lang: v.lang,\n isLocal: v.localService,\n }));\n\n const initial = collect();\n if (initial.length > 0) return initial;\n\n // Safari/iOS: vozes carregam de forma assíncrona via 'voiceschanged'.\n return new Promise<TTSVoice[]>((resolve) => {\n const onChange = () => {\n synth.removeEventListener('voiceschanged', onChange);\n resolve(collect());\n };\n synth.addEventListener('voiceschanged', onChange);\n // Fallback: timeout para ambientes que nunca disparam o evento.\n setTimeout(() => {\n synth.removeEventListener('voiceschanged', onChange);\n resolve(collect());\n }, 1500);\n });\n }\n\n speak(\n text: string,\n options: TTSSpeakOptions = {},\n events: TTSProviderEvents = {}\n ): void {\n if (!this.synth) {\n events.onError?.('Síntese de voz não disponível neste navegador.');\n return;\n }\n const trimmed = text.trim();\n if (!trimmed) return;\n\n // Cancela qualquer fala em andamento (evita fila acumulando).\n this.synth.cancel();\n\n const utterance = new SpeechSynthesisUtterance(trimmed);\n utterance.rate = options.rate ?? 1;\n utterance.pitch = options.pitch ?? 1;\n\n // Resolução de voz/idioma:\n // 1. Se o usuário escolheu uma voz, usa ela e o `lang` dela\n // (para evitar mismatch — voz en-US com utterance.lang='pt-BR'\n // causa silêncio em alguns browsers).\n // 2. Se não escolheu mas pediu um lang específico via options,\n // procuramos uma voz que cubra esse lang. Se não houver,\n // deixamos `lang` em branco para o browser usar a voz default.\n const allVoices = this.synth.getVoices();\n let chosenVoice: SpeechSynthesisVoice | undefined;\n if (options.voiceId) {\n chosenVoice = allVoices.find((v) => v.voiceURI === options.voiceId);\n } else if (options.lang) {\n chosenVoice = allVoices.find((v) =>\n v.lang.toLowerCase().startsWith(options.lang!.toLowerCase())\n );\n }\n if (chosenVoice) {\n utterance.voice = chosenVoice;\n utterance.lang = chosenVoice.lang;\n }\n\n utterance.onstart = () => events.onStart?.();\n utterance.onend = () => {\n this.currentUtterance = null;\n events.onEnd?.();\n };\n utterance.onpause = () => events.onPause?.();\n utterance.onresume = () => events.onResume?.();\n utterance.onerror = (event) => {\n this.currentUtterance = null;\n events.onError?.(event.error || 'Erro desconhecido na síntese.');\n };\n\n this.currentUtterance = utterance;\n this.synth.speak(utterance);\n }\n\n pause(): void {\n this.synth?.pause();\n }\n\n resume(): void {\n this.synth?.resume();\n }\n\n stop(): void {\n this.synth?.cancel();\n this.currentUtterance = null;\n }\n}\n\nconst getSynth = (): SpeechSynthesis | null => {\n if (typeof globalThis === 'undefined') return null;\n const synth = (globalThis as unknown as { speechSynthesis?: SpeechSynthesis })\n .speechSynthesis;\n return synth ?? null;\n};\n","import { HandWavingIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport { Tooltip } from '../Tooltip/Tooltip';\nimport { cn } from '../../utils/utils';\nimport {\n FAB_POSITION_CLASSES,\n FAB_TOOLTIP_POSITION,\n FAB_VERTICAL_ALIGN_CLASSES,\n type FabPosition,\n type FabVerticalAlign,\n} from './fabPositioning';\n\ntype AccessibilityFabPosition = FabPosition;\ntype AccessibilityFabVerticalAlign = FabVerticalAlign;\n\nexport interface LibrasFabProps {\n /** Click handler — alterna o painel do VLibras (abrir/fechar) */\n onClick: () => void;\n /** Lado da viewport onde o botão fica colado */\n position?: AccessibilityFabPosition;\n /** Alinhamento vertical (default: `below-center`) */\n verticalAlign?: AccessibilityFabVerticalAlign;\n /** Classes extras */\n className?: string;\n}\n\n/**\n * Segundo botão flutuante (FAB), posicionado abaixo do `AccessibilityFab`.\n * Aciona o widget oficial do VLibras (gov.br) que faz tradução automática\n * de português para Libras via avatar 3D.\n *\n * O botão tem visual constante — não mantemos um indicador de estado\n * \"ativo\" porque o painel do VLibras já reflete visualmente se está\n * aberto ou fechado. Cada clique alterna o painel.\n *\n * Mesma identidade visual do FAB principal (azul escuro, quadrado com\n * canto interno arredondado, colado na lateral).\n */\nexport default function LibrasFab({\n onClick,\n position = 'right',\n verticalAlign = 'below-center',\n className,\n}: Readonly<LibrasFabProps>) {\n const label = 'Tradução em Libras';\n\n return (\n <Tooltip\n content={label}\n position={FAB_TOOLTIP_POSITION[position]}\n className={cn(\n 'fixed z-40',\n FAB_VERTICAL_ALIGN_CLASSES[verticalAlign],\n FAB_POSITION_CLASSES[position]\n )}\n >\n <Button\n variant=\"raw\"\n onClick={onClick}\n aria-label={label}\n data-testid=\"libras-fab\"\n className={cn(\n 'a11y-widget-shield',\n FAB_POSITION_CLASSES[position],\n 'flex h-10 w-10 cursor-pointer items-center justify-center',\n // `text-text-50` flipa junto com `bg-info-900`: claro no light,\n // escuro no dark. Como o ícone usa `currentColor`, com `text-white`\n // a mãozinha sumiria no tema escuro (branco em fundo azul-claro).\n 'bg-info-900 text-text-50 shadow-lg',\n 'transition-all duration-200 hover:scale-110 hover:bg-info-800',\n 'focus:outline-none focus:ring-4 focus:ring-info-300',\n className\n )}\n >\n <HandWavingIcon size={25} weight=\"fill\" aria-hidden=\"true\" />\n </Button>\n </Tooltip>\n );\n}\n","import { useEffect, useState } from 'react';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\n\nconst RULER_HEIGHT_PX = 32;\nconst MASK_GAP_PX = 80; // metade da altura da janela visível\n\n/**\n * Renderiza auxiliares de leitura (régua ou máscara) que acompanham\n * a posição vertical do mouse. Só monta listener de `mousemove` quando\n * algum auxiliar está ativo, para não pagar custo quando desligado.\n */\nexport default function ReadingAid() {\n const readingAid = useAccessibilityStore((s) => s.readingAid);\n const [mouseY, setMouseY] = useState<number | null>(null);\n\n useEffect(() => {\n if (readingAid === 'none') {\n setMouseY(null);\n return;\n }\n\n let frame = 0;\n const handleMouseMove = (event: MouseEvent) => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => setMouseY(event.clientY));\n };\n\n document.addEventListener('mousemove', handleMouseMove, { passive: true });\n return () => {\n cancelAnimationFrame(frame);\n document.removeEventListener('mousemove', handleMouseMove);\n };\n }, [readingAid]);\n\n if (readingAid === 'none' || mouseY === null) return null;\n\n if (readingAid === 'ruler') {\n return (\n <div\n aria-hidden=\"true\"\n data-testid=\"a11y-reading-ruler\"\n className=\"a11y-widget-shield pointer-events-none fixed left-0 right-0 z-30\"\n style={{\n top: mouseY - RULER_HEIGHT_PX / 2,\n height: RULER_HEIGHT_PX,\n background: 'rgba(255, 235, 59, 0.25)',\n borderTop: '2px solid #facc15',\n borderBottom: '2px solid #facc15',\n }}\n />\n );\n }\n\n // Máscara: dois retângulos escurecendo acima e abaixo do cursor\n return (\n <>\n <div\n aria-hidden=\"true\"\n data-testid=\"a11y-reading-mask-top\"\n className=\"a11y-widget-shield pointer-events-none fixed left-0 right-0 top-0 z-30 bg-black/90\"\n style={{ height: Math.max(0, mouseY - MASK_GAP_PX) }}\n />\n <div\n aria-hidden=\"true\"\n data-testid=\"a11y-reading-mask-bottom\"\n className=\"a11y-widget-shield pointer-events-none fixed left-0 right-0 bottom-0 z-30 bg-black/90\"\n style={{ top: mouseY + MASK_GAP_PX }}\n />\n </>\n );\n}\n","import {\n ColorBlindMode,\n getColorBlindFilterId,\n} from '../../store/accessibilityStore';\n\n/**\n * SVG escondido com matrizes `<feColorMatrix>` que simulam/auxiliam\n * cada tipo de daltonismo. As classes em `accessibility.css` aplicam\n * `filter: url(#a11y-cb-...)` no `<html>` para colorir a página.\n *\n * Os IDs dos filters são derivados do enum `ColorBlindMode` via\n * `getColorBlindFilterId` — mesma fonte da verdade que o hook usa\n * para aplicar a classe.\n *\n * Matrizes baseadas em Brettel/Vienot/Mollon — padrão amplamente\n * utilizado em ferramentas de acessibilidade como AXE, Daltonize e\n * widget oficial do HandTalk.\n */\nexport default function ColorBlindFilters() {\n return (\n <svg\n aria-hidden=\"true\"\n data-testid=\"a11y-colorblind-filters\"\n style={{\n position: 'absolute',\n width: 0,\n height: 0,\n overflow: 'hidden',\n }}\n >\n <defs>\n <filter id={getColorBlindFilterId(ColorBlindMode.Protanopia)}>\n <feColorMatrix\n type=\"matrix\"\n values=\"0.567 0.433 0 0 0\n 0.558 0.442 0 0 0\n 0 0.242 0.758 0 0\n 0 0 0 1 0\"\n />\n </filter>\n <filter id={getColorBlindFilterId(ColorBlindMode.Deuteranopia)}>\n <feColorMatrix\n type=\"matrix\"\n values=\"0.625 0.375 0 0 0\n 0.700 0.300 0 0 0\n 0 0.300 0.7 0 0\n 0 0 0 1 0\"\n />\n </filter>\n <filter id={getColorBlindFilterId(ColorBlindMode.Tritanopia)}>\n <feColorMatrix\n type=\"matrix\"\n values=\"0.95 0.05 0 0 0\n 0 0.433 0.567 0 0\n 0 0.475 0.525 0 0\n 0 0 0 1 0\"\n />\n </filter>\n </defs>\n </svg>\n );\n}\n","import { useEffect } from 'react';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\nimport { useTTS } from '../../hooks/useTTS';\n\nconst HIGHLIGHT_CLASS = 'a11y-tts-target';\n\n/**\n * Controller invisível que conecta o `ttsMode` do store ao DOM:\n * - `click-to-read`: registra um listener global de clique. Ao clicar\n * em qualquer elemento (exceto o próprio widget), lê seu texto.\n * - `read-selection`: lê o texto atualmente selecionado quando o store\n * sinaliza (via `pendingReadSelection`). UI/atalho disparam isso.\n *\n * Aplica a classe `.a11y-tts-target` no elemento sendo lido para\n * destaque visual; remove ao terminar/parar.\n */\nexport default function TTSController() {\n const ttsMode = useAccessibilityStore((s) => s.ttsMode);\n const ttsStatus = useAccessibilityStore((s) => s.ttsStatus);\n const { speak, stop, isSupported } = useTTS();\n\n // Click-to-read: ouve cliques globais enquanto o modo está ativo\n useEffect(() => {\n if (!isSupported || ttsMode !== 'click-to-read') return;\n\n const handler = (event: MouseEvent) => {\n const initialTarget = event.target as HTMLElement | null;\n if (!initialTarget) return;\n\n // Ignora cliques dentro do próprio widget\n if (initialTarget.closest('.a11y-widget-shield')) return;\n\n // Sobe pela árvore até achar o ancestral que tem texto legível.\n // Cobre o caso comum de IconButtons com `<svg>` interno: clicar no\n // SVG não retorna texto, mas o botão pai tem aria-label.\n const found = findReadableAncestor(initialTarget);\n if (!found) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n // Limpa highlight anterior e marca o novo alvo\n document\n .querySelectorAll(`.${HIGHLIGHT_CLASS}`)\n .forEach((el) => el.classList.remove(HIGHLIGHT_CLASS));\n found.node.classList.add(HIGHLIGHT_CLASS);\n\n speak(found.text);\n };\n\n // capture: true → pegamos o clique antes do handler do app/link\n document.addEventListener('click', handler, true);\n return () => {\n document.removeEventListener('click', handler, true);\n document\n .querySelectorAll(`.${HIGHLIGHT_CLASS}`)\n .forEach((el) => el.classList.remove(HIGHLIGHT_CLASS));\n // Interrompe qualquer fala em curso ao sair do modo. Sem isso,\n // desligar o \"Clique para ler\" deixaria a leitura terminando\n // sozinha, contrariando a expectativa do usuário.\n stop();\n };\n }, [isSupported, ttsMode, speak, stop]);\n\n // Quando a fala termina (ttsStatus volta a 'idle'), limpa highlight\n useEffect(() => {\n if (ttsStatus === 'idle') {\n document\n .querySelectorAll(`.${HIGHLIGHT_CLASS}`)\n .forEach((el) => el.classList.remove(HIGHLIGHT_CLASS));\n }\n }, [ttsStatus]);\n\n // Para qualquer fala em curso quando o componente desmonta\n useEffect(() => () => stop(), [stop]);\n\n return null;\n}\n\n/**\n * Extrai texto legível do elemento clicado. Prioriza:\n * 1. `aria-label` se presente\n * 2. `title`\n * 3. `innerText` truncado\n *\n * `innerText` (e não `textContent`) respeita estilos como `display: none`\n * e quebras de linha visuais — leitura mais natural.\n */\nconst extractReadableText = (el: HTMLElement): string => {\n const aria = el.getAttribute('aria-label')?.trim();\n if (aria) return aria;\n const title = el.getAttribute('title')?.trim();\n if (title) return title;\n const text = el.innerText?.trim() ?? '';\n // Limita comprimento para evitar leitura infinita ao clicar em\n // contêineres grandes (ex.: <main>). 600 chars ≈ 1min de fala.\n return text.length > 600 ? `${text.slice(0, 600)}...` : text;\n};\n\n/** Quantos níveis de ancestralidade subimos buscando um nó legível. */\nconst MAX_ANCESTOR_LOOKUP = 8;\n\n/**\n * Caminha pela árvore a partir do elemento clicado até achar o ancestral\n * mais próximo que tenha texto legível (`aria-label`, `title` ou\n * `innerText`). Limita a profundidade e nunca retorna o `<body>` para\n * evitar ler a página inteira ao clicar em ícones soltos.\n */\nconst findReadableAncestor = (\n start: HTMLElement\n): { node: HTMLElement; text: string } | null => {\n let node: HTMLElement | null = start;\n let depth = 0;\n while (node && depth < MAX_ANCESTOR_LOOKUP && node !== document.body) {\n const text = extractReadableText(node);\n if (text) return { node, text };\n node = node.parentElement;\n depth++;\n }\n return null;\n};\n","import { useEffect } from 'react';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\n\nconst SCRIPT_ID = 'a11y-vlibras-script';\nconst SCRIPT_URL = 'https://vlibras.gov.br/app/vlibras-plugin.js';\nconst APP_URL = 'https://vlibras.gov.br/app';\nconst WRAPPER_ID = 'a11y-vlibras-wrapper';\n\ninterface VLibrasGlobal {\n Widget: new (appUrl: string) => unknown;\n}\n\n/**\n * Carrega e ativa o widget oficial de Libras do governo brasileiro\n * (gov.br/vlibras) sob demanda. Estratégia:\n *\n * - Quando `librasEnabled === true`:\n * 1. Injeta o esqueleto DOM esperado pelo VLibras (`<div vw>` etc.)\n * no `<body>` (precisa estar no body, não dentro do React tree).\n * 2. Carrega o script oficial uma única vez (cacheado por id).\n * 3. Instancia `new window.VLibras.Widget(...)` para inicializar o\n * avatar e os assets 3D.\n *\n * - Quando `librasEnabled === false`:\n * - Remove o esqueleto DOM. O script permanece em cache (já carregado)\n * para reativações rápidas.\n *\n * O script é pesado (~MBs de avatares 3D), por isso só carregamos quando\n * o usuário clica para ativar — fluxo opt-in.\n */\nexport default function VLibrasLoader() {\n const enabled = useAccessibilityStore((s) => s.librasEnabled);\n\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n if (!enabled) {\n removeWrapper();\n return;\n }\n\n injectWrapper();\n loadScript()\n .then(() => {\n const VLibras = (globalThis as unknown as { VLibras?: VLibrasGlobal })\n .VLibras;\n if (!VLibras?.Widget) return;\n\n // ATENÇÃO: o construtor do VLibras Widget registra a lógica de\n // inicialização DENTRO de `window.onload = () => {...}`. Como\n // nesta integração o widget é ativado depois da página já ter\n // carregado, o evento `load` já disparou e o handler nunca\n // executa sozinho. Capturamos o handler que o construtor\n // instalou e chamamos manualmente para popular o DOM (avatar,\n // botão de acesso, painel) imediatamente.\n const previousOnload = globalThis.onload;\n // A instanciação importa pelos efeitos colaterais: o construtor\n // do VLibras registra `window.onload` com a lógica de inicialização\n // do widget. O retorno não é usado e é descartado de propósito.\n new VLibras.Widget(APP_URL); // NOSONAR — instantiation by side effect\n const installed = globalThis.onload;\n if (typeof installed === 'function' && installed !== previousOnload) {\n // `onload` espera tipo Window — usamos `as` porque o construtor\n // do VLibras só lê `event.target` (que ignoramos) e não toca em\n // propriedades específicas do Window.\n (installed as (this: Window, ev: Event) => unknown).call(\n globalThis as unknown as Window,\n new Event('load')\n );\n }\n\n // Após popular o DOM, abre direto o painel do VLibras — caso\n // contrário o usuário precisaria clicar duas vezes (nosso FAB\n // + botão de acesso do VLibras). Aguarda um frame para garantir\n // que o handler de click do botão já foi anexado pelo plugin.\n requestAnimationFrame(() => {\n const accessButton =\n document.querySelector<HTMLElement>('[vw-access-button]');\n accessButton?.click();\n });\n })\n .catch(() => {\n // Falha de rede ou bloqueio: desabilita silenciosamente o estado\n // para o usuário não ficar preso achando que vai funcionar.\n useAccessibilityStore.getState().setLibrasEnabled(false);\n });\n\n return () => {\n // Não removemos o wrapper aqui: a remoção real fica no ramo\n // `!enabled` acima. Manter aqui causaria flicker em re-renders.\n };\n }, [enabled]);\n\n return null;\n}\n\nconst injectWrapper = () => {\n if (document.getElementById(WRAPPER_ID)) return;\n\n const wrapper = document.createElement('div');\n wrapper.id = WRAPPER_ID;\n wrapper.setAttribute('vw', '');\n wrapper.className = 'enabled';\n\n const accessButton = document.createElement('div');\n accessButton.setAttribute('vw-access-button', '');\n accessButton.className = 'active';\n wrapper.appendChild(accessButton);\n\n const pluginWrapper = document.createElement('div');\n pluginWrapper.setAttribute('vw-plugin-wrapper', '');\n const top = document.createElement('div');\n top.className = 'vw-plugin-top-wrapper';\n pluginWrapper.appendChild(top);\n wrapper.appendChild(pluginWrapper);\n\n document.body.appendChild(wrapper);\n};\n\nconst removeWrapper = () => {\n document.getElementById(WRAPPER_ID)?.remove();\n};\n\nconst loadScript = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (document.getElementById(SCRIPT_ID)) {\n resolve();\n return;\n }\n\n const script = document.createElement('script');\n script.id = SCRIPT_ID;\n script.src = SCRIPT_URL;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () =>\n reject(new Error('Falha ao carregar o script do VLibras'));\n document.head.appendChild(script);\n });\n};\n\nexport const __testing = {\n SCRIPT_ID,\n SCRIPT_URL,\n WRAPPER_ID,\n injectWrapper,\n removeWrapper,\n};\n","import { useEffect } from 'react';\nimport {\n useAccessibilityStore,\n getColorBlindClass,\n type AccessibilityPreferences,\n type ContrastMode,\n type SaturationMode,\n type SpacingLevel,\n} from '../store/accessibilityStore';\n\n/**\n * Mapeia cada preferência para a classe que deve ser aplicada no\n * `documentElement`. Valores no estado padrão (`normal` ou 0) não\n * adicionam classe alguma — assim a página fica intacta quando o\n * usuário não ativou nada.\n */\ntype ClassMap = Partial<Record<keyof AccessibilityPreferences, string>>;\n\nconst CONTRAST_CLASS: Record<ContrastMode, string | null> = {\n normal: null,\n high: 'a11y-contrast-high',\n inverted: 'a11y-contrast-inverted',\n};\n\nconst SATURATION_CLASS: Record<SaturationMode, string | null> = {\n normal: null,\n grayscale: 'a11y-saturation-grayscale',\n low: 'a11y-saturation-low',\n};\n\nconst LEVEL_PREFIX: Record<\n 'fontSize' | 'letterSpacing' | 'lineSpacing',\n string\n> = {\n fontSize: 'a11y-font-',\n letterSpacing: 'a11y-letter-spacing-',\n lineSpacing: 'a11y-line-spacing-',\n};\n\n/** Lista de todas as classes que o widget pode aplicar — usada para limpar */\nconst ALL_A11Y_CLASSES: readonly string[] = [\n 'a11y-contrast-high',\n 'a11y-contrast-inverted',\n 'a11y-saturation-grayscale',\n 'a11y-saturation-low',\n 'a11y-font-1',\n 'a11y-font-2',\n 'a11y-font-3',\n 'a11y-letter-spacing-1',\n 'a11y-letter-spacing-2',\n 'a11y-letter-spacing-3',\n 'a11y-line-spacing-1',\n 'a11y-line-spacing-2',\n 'a11y-line-spacing-3',\n 'a11y-highlight-links',\n 'a11y-no-animations',\n 'a11y-big-cursor',\n 'a11y-dyslexia-font',\n 'a11y-cb-protanopia',\n 'a11y-cb-deuteranopia',\n 'a11y-cb-tritanopia',\n];\n\nconst buildLevelClass = (\n prefix: string,\n level: SpacingLevel\n): string | null => {\n if (level === 0) return null;\n return `${prefix}${level}`;\n};\n\nconst buildClassMap = (prefs: AccessibilityPreferences): ClassMap => ({\n contrastMode: CONTRAST_CLASS[prefs.contrastMode] ?? undefined,\n saturationMode: SATURATION_CLASS[prefs.saturationMode] ?? undefined,\n fontSize: buildLevelClass(LEVEL_PREFIX.fontSize, prefs.fontSize) ?? undefined,\n letterSpacing:\n buildLevelClass(LEVEL_PREFIX.letterSpacing, prefs.letterSpacing) ??\n undefined,\n lineSpacing:\n buildLevelClass(LEVEL_PREFIX.lineSpacing, prefs.lineSpacing) ?? undefined,\n highlightLinks: prefs.highlightLinks ? 'a11y-highlight-links' : undefined,\n pauseAnimations: prefs.pauseAnimations ? 'a11y-no-animations' : undefined,\n bigCursor: prefs.bigCursor ? 'a11y-big-cursor' : undefined,\n dyslexiaFont: prefs.dyslexiaFont ? 'a11y-dyslexia-font' : undefined,\n colorBlindMode: getColorBlindClass(prefs.colorBlindMode) ?? undefined,\n});\n\n/**\n * Aplica/remove as classes de acessibilidade no `documentElement`\n * conforme as preferências atuais. Limpa tudo antes de aplicar para\n * garantir um estado consistente mesmo se prefs forem alteradas\n * fora do fluxo normal.\n */\nconst syncDom = (prefs: AccessibilityPreferences) => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n\n ALL_A11Y_CLASSES.forEach((cls) => root.classList.remove(cls));\n\n const classMap = buildClassMap(prefs);\n Object.values(classMap).forEach((cls) => {\n if (cls) root.classList.add(cls);\n });\n};\n\n/**\n * Hook que sincroniza o store de acessibilidade com o DOM.\n *\n * Deve ser chamado uma vez no nível raiz da aplicação (o\n * componente `AccessibilityWidget` já faz isso). Cada vez que\n * uma preferência muda, as classes correspondentes são aplicadas\n * no `<html>`, sem `!important` no nível do hook — a especificidade\n * é controlada pelo CSS.\n */\nexport const useA11yPreferences = () => {\n const contrastMode = useAccessibilityStore((s) => s.contrastMode);\n const saturationMode = useAccessibilityStore((s) => s.saturationMode);\n const fontSize = useAccessibilityStore((s) => s.fontSize);\n const letterSpacing = useAccessibilityStore((s) => s.letterSpacing);\n const lineSpacing = useAccessibilityStore((s) => s.lineSpacing);\n const highlightLinks = useAccessibilityStore((s) => s.highlightLinks);\n const pauseAnimations = useAccessibilityStore((s) => s.pauseAnimations);\n const bigCursor = useAccessibilityStore((s) => s.bigCursor);\n const dyslexiaFont = useAccessibilityStore((s) => s.dyslexiaFont);\n const readingAid = useAccessibilityStore((s) => s.readingAid);\n const keyboardShortcut = useAccessibilityStore((s) => s.keyboardShortcut);\n const colorBlindMode = useAccessibilityStore((s) => s.colorBlindMode);\n const ttsMode = useAccessibilityStore((s) => s.ttsMode);\n const ttsRate = useAccessibilityStore((s) => s.ttsRate);\n const ttsVoiceId = useAccessibilityStore((s) => s.ttsVoiceId);\n const librasEnabled = useAccessibilityStore((s) => s.librasEnabled);\n\n useEffect(() => {\n syncDom({\n contrastMode,\n saturationMode,\n fontSize,\n letterSpacing,\n lineSpacing,\n highlightLinks,\n pauseAnimations,\n bigCursor,\n dyslexiaFont,\n readingAid,\n keyboardShortcut,\n colorBlindMode,\n ttsMode,\n ttsRate,\n ttsVoiceId,\n librasEnabled,\n });\n }, [\n contrastMode,\n saturationMode,\n fontSize,\n letterSpacing,\n lineSpacing,\n highlightLinks,\n pauseAnimations,\n bigCursor,\n dyslexiaFont,\n colorBlindMode,\n ]);\n};\n\nexport const __testing = {\n buildClassMap,\n syncDom,\n ALL_A11Y_CLASSES,\n};\n","import { useEffect } from 'react';\nimport { useAccessibilityStore } from '../store/accessibilityStore';\n\n/**\n * Registra o atalho `Alt+A` para abrir/fechar o painel de\n * acessibilidade. Não interfere quando o foco está em campo de\n * texto/edição para evitar conflitos com o usuário digitando.\n *\n * Habilitar/desabilitar é controlado pela preferência\n * `keyboardShortcut` do store.\n */\nexport const useA11yKeyboardShortcut = () => {\n const enabled = useAccessibilityStore((s) => s.keyboardShortcut);\n const togglePanel = useAccessibilityStore((s) => s.togglePanel);\n\n useEffect(() => {\n if (!enabled) return;\n\n const handler = (event: KeyboardEvent) => {\n if (!event.altKey || event.key.toLowerCase() !== 'a') return;\n // Ignora auto-repeat: segurar Alt+A não deve oscilar o painel\n if (event.repeat) return;\n\n // Não ativa se o usuário estiver digitando em algum campo\n const target = event.target as HTMLElement | null;\n if (target) {\n const tag = target.tagName;\n if (\n tag === 'INPUT' ||\n tag === 'TEXTAREA' ||\n tag === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n }\n\n event.preventDefault();\n togglePanel();\n };\n\n globalThis.addEventListener('keydown', handler);\n return () => globalThis.removeEventListener('keydown', handler);\n }, [enabled, togglePanel]);\n};\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_react","import_jsx_runtime","import_jsx_runtime","SIZE_CLASSES","import_jsx_runtime","import_react","import_react","import_react_dom","import_jsx_runtime","SIZE_CLASSES","children","label","import_zustand","import_react","import_jsx_runtime","PreviewSegmented","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_react","import_react","import_react","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/AccessibilityWidget/AccessibilityWidget.tsx","../../src/components/Button/Button.tsx","../../src/utils/utils.ts","../../src/components/Tooltip/Tooltip.tsx","../../src/components/Text/Text.tsx","../../src/components/AccessibilityWidget/fabPositioning.ts","../../src/components/AccessibilityWidget/AccessibilityFab.tsx","../../src/components/AccessibilityWidget/AccessibilityPanel.tsx","../../src/components/IconButton/IconButton.tsx","../../src/components/ToggleSwitch/ToggleSwitch.tsx","../../src/components/AccessibilityWidget/AccessibilityToggleRow.tsx","../../src/components/AccessibilityWidget/TTSSection.tsx","../../src/components/Select/Select.tsx","../../src/store/accessibilityStore.ts","../../src/hooks/useTTS.ts","../../src/components/AccessibilityWidget/tts/WebSpeechProvider.ts","../../src/components/AccessibilityWidget/LibrasFab.tsx","../../src/components/AccessibilityWidget/ReadingAid.tsx","../../src/components/AccessibilityWidget/ColorBlindFilters.tsx","../../src/components/AccessibilityWidget/TTSController.tsx","../../src/components/AccessibilityWidget/VLibrasLoader.tsx","../../src/hooks/useA11yPreferences.ts","../../src/hooks/useA11yKeyboardShortcut.ts"],"sourcesContent":["import AccessibilityFab, {\n type AccessibilityFabPosition,\n} from './AccessibilityFab';\nimport AccessibilityPanel from './AccessibilityPanel';\nimport LibrasFab from './LibrasFab';\nimport ReadingAid from './ReadingAid';\nimport ColorBlindFilters from './ColorBlindFilters';\nimport TTSController from './TTSController';\nimport VLibrasLoader from './VLibrasLoader';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\nimport { useA11yPreferences } from '../../hooks/useA11yPreferences';\nimport { useA11yKeyboardShortcut } from '../../hooks/useA11yKeyboardShortcut';\nimport './accessibility.css';\n\nexport interface AccessibilityWidgetProps {\n /** Lado da viewport onde o botão fica colado (default: 'right') */\n position?: AccessibilityFabPosition;\n /** Classes extras para o botão flutuante */\n fabClassName?: string;\n /** Classes extras para o painel */\n panelClassName?: string;\n /**\n * Mostra o botão de Libras empilhado abaixo do botão de\n * acessibilidade (default: true). Plataformas que já injetam o\n * VLibras de forma própria podem desabilitar passando `false`.\n */\n showLibras?: boolean;\n}\n\n/**\n * Widget de acessibilidade — botão flutuante que abre um painel\n * com controles de contraste, fonte, espaçamento e outras\n * preferências visuais. As preferências são persistidas em\n * localStorage e aplicadas via classes no `<html>`.\n *\n * Renderize uma única instância no shell da aplicação. Tudo fica\n * dentro de `.a11y-widget-root` para que o próprio widget não\n * sofra com os filtros que aplica na página.\n *\n * @example\n * ```tsx\n * <AccessibilityWidget position=\"left\" />\n * ```\n */\nexport default function AccessibilityWidget({\n position = 'right',\n fabClassName,\n panelClassName,\n showLibras = true,\n}: Readonly<AccessibilityWidgetProps>) {\n useA11yPreferences();\n useA11yKeyboardShortcut();\n\n const isPanelOpen = useAccessibilityStore((s) => s.isPanelOpen);\n const togglePanel = useAccessibilityStore((s) => s.togglePanel);\n const closePanel = useAccessibilityStore((s) => s.closePanel);\n const librasEnabled = useAccessibilityStore((s) => s.librasEnabled);\n const setLibrasEnabled = useAccessibilityStore((s) => s.setLibrasEnabled);\n\n // Quando o Libras é exibido, os dois FABs ficam empilhados\n // (acessibilidade acima do meio, libras abaixo). Sem Libras,\n // o de acessibilidade ocupa o centro como antes.\n const accessibilityVerticalAlign = showLibras ? 'above-center' : 'center';\n\n /**\n * Clique no FAB de Libras:\n * - Primeira vez (não ativado): ativa o widget VLibras (que se\n * auto-abre no painel pela primeira vez).\n * - Demais vezes: dispara o click no botão de acesso nativo do\n * VLibras, que abre/fecha o painel sem precisar reinjetar o widget.\n * Isso evita o ciclo de remoção/reinjeção que deixava o VLibras\n * em estado inconsistente após fechá-lo.\n */\n const handleLibrasClick = () => {\n if (!librasEnabled) {\n setLibrasEnabled(true);\n return;\n }\n document.querySelector<HTMLElement>('[vw-access-button]')?.click();\n };\n\n return (\n <>\n {!isPanelOpen && (\n <AccessibilityFab\n onClick={togglePanel}\n isOpen={isPanelOpen}\n position={position}\n verticalAlign={accessibilityVerticalAlign}\n className={fabClassName}\n />\n )}\n {!isPanelOpen && showLibras && (\n <LibrasFab onClick={handleLibrasClick} position={position} />\n )}\n <AccessibilityPanel\n isOpen={isPanelOpen}\n onClose={closePanel}\n position={position}\n className={panelClassName}\n />\n <ReadingAid />\n <ColorBlindFilters />\n <TTSController />\n {showLibras && <VLibrasLoader />}\n </>\n );\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Lookup table for variant and action class combinations\n */\nconst VARIANT_ACTION_CLASSES = {\n solid: {\n primary:\n 'bg-primary-950 text-text border border-primary-950 hover:bg-primary-800 hover:border-primary-800 focus-visible:outline-none focus-visible:bg-primary-950 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-primary-700 active:border-primary-700 disabled:bg-primary-500 disabled:border-primary-500 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-text-950 text-text border border-text-800 hover:bg-text-800 hover:border-text-950 focus-visible:outline-none focus-visible:bg-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-text-700 active:border-text-700 disabled:bg-text-500 disabled:border-text-500 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-success-500 text-text border border-success-500 hover:bg-success-600 hover:border-success-600 focus-visible:outline-none focus-visible:bg-success-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-success-700 active:border-success-700 disabled:bg-success-500 disabled:border-success-500 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-error-500 text-text border border-error-500 hover:bg-error-600 hover:border-error-600 focus-visible:outline-none focus-visible:bg-error-500 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:bg-error-700 active:border-error-700 disabled:bg-error-500 disabled:border-error-500 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n outline: {\n primary:\n 'bg-transparent text-primary-950 border border-primary-950 hover:bg-background-50 hover:text-primary-400 hover:border-primary-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 active:border-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-transparent text-text-950 border border-text-800 hover:bg-background-50 hover:text-text-700 hover:border-text-700 focus-visible:border-0 focus-visible:outline-none focus-visible:text-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-text-700 active:border-text-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 border border-success-300 hover:bg-background-50 hover:text-success-400 hover:border-success-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 active:border-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 border border-error-300 hover:bg-background-50 hover:text-error-400 hover:border-error-400 focus-visible:border-0 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 active:border-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n link: {\n primary:\n 'bg-transparent text-primary-950 hover:text-primary-400 focus-visible:outline-none focus-visible:text-primary-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-primary-700 disabled:opacity-40 disabled:cursor-not-allowed',\n secondary:\n 'bg-transparent text-text-950 hover:text-text-800 focus-visible:outline-none focus-visible:text-text-900 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-text-700 disabled:opacity-40 disabled:cursor-not-allowed',\n positive:\n 'bg-transparent text-success-500 hover:text-success-400 focus-visible:outline-none focus-visible:text-success-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-success-700 disabled:opacity-40 disabled:cursor-not-allowed',\n negative:\n 'bg-transparent text-error-500 hover:text-error-400 focus-visible:outline-none focus-visible:text-error-600 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-indicator-info active:text-error-700 disabled:opacity-40 disabled:cursor-not-allowed',\n },\n} as const;\n\n/**\n * Lookup table for size classes\n */\nconst SIZE_CLASSES = {\n 'extra-small': 'text-xs px-3.5 py-2',\n small: 'text-sm px-4 py-2.5',\n medium: 'text-md px-5 py-2.5',\n large: 'text-lg px-6 py-3',\n 'extra-large': 'text-lg px-7 py-3.5',\n} as const;\n\n/**\n * Button component props interface\n */\ntype ButtonProps = {\n /** Content to be displayed inside the button */\n children: ReactNode;\n /** Ícone à esquerda do texto */\n iconLeft?: ReactNode;\n /** Ícone à direita do texto */\n iconRight?: ReactNode;\n /** Size of the button */\n size?: 'extra-small' | 'small' | 'medium' | 'large' | 'extra-large';\n /** Visual variant of the button. Use 'raw' for no default styling */\n variant?: 'solid' | 'outline' | 'link' | 'raw';\n /** Action type of the button */\n action?: 'primary' | 'secondary' | 'positive' | 'negative';\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * Button component for Analytica Ensino platforms\n *\n * A flexible button component with multiple variants, sizes and actions.\n *\n * @param children - The content to display inside the button\n * @param size - The size variant (extra-small, small, medium, large, extra-large)\n * @param variant - The visual style variant (solid, outline, link)\n * @param action - The action type (primary, secondary, positive, negative)\n * @param className - Additional CSS classes\n * @param props - All other standard button HTML attributes\n * @returns A styled button element\n *\n * @example\n * ```tsx\n * <Button variant=\"solid\" action=\"primary\" size=\"medium\" onClick={() => console.log('clicked')}>\n * Click me\n * </Button>\n * ```\n */\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n children,\n iconLeft,\n iconRight,\n size = 'medium',\n variant = 'solid',\n action = 'primary',\n className = '',\n disabled,\n type = 'button',\n ...props\n },\n ref\n ) => {\n // Raw variant: no default styling, only className\n if (variant === 'raw') {\n return (\n <button\n ref={ref}\n className={className}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && (\n <span className=\"mr-2 flex items-center\">{iconLeft}</span>\n )}\n {children}\n {iconRight && (\n <span className=\"ml-2 flex items-center\">{iconRight}</span>\n )}\n </button>\n );\n }\n\n // Get classes from lookup tables\n const sizeClasses = SIZE_CLASSES[size];\n const variantClasses = VARIANT_ACTION_CLASSES[variant][action];\n\n const baseClasses =\n 'inline-flex items-center justify-center rounded-full cursor-pointer font-medium';\n\n return (\n <button\n ref={ref}\n className={cn(baseClasses, variantClasses, sizeClasses, className)}\n disabled={disabled}\n type={type}\n {...props}\n >\n {iconLeft && <span className=\"mr-2 flex items-center\">{iconLeft}</span>}\n {children}\n {iconRight && (\n <span className=\"ml-2 flex items-center\">{iconRight}</span>\n )}\n </button>\n );\n }\n);\n\nButton.displayName = 'Button';\n\nexport default Button;\n","import { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\nexport { syncDropdownState } from './dropdown';\nexport {\n getSelectedIdsFromCategories,\n toggleArrayItem,\n toggleSingleValue,\n areFiltersEqual,\n} from './activityFilters';\nexport {\n mapQuestionTypeToEnum,\n mapQuestionTypeToEnumRequired,\n} from './questionTypeUtils';\nexport {\n getStatusBadgeConfig,\n formatTimeSpent,\n formatQuestionNumbers,\n formatDateToBrazilian,\n formatActivityDateToBrazilian,\n} from './activityDetailsUtils';\n\n/**\n * Format a number as a rounded percentage string\n * @param value - Number to format (0-100)\n * @returns Formatted string with % suffix (e.g., \"72%\")\n */\nexport function formatPercentageRounded(value: number): string {\n return `${Math.round(value)}%`;\n}\n\nexport { formatScore } from './formatScore';\n\n/**\n * Convert hex color to rgba with opacity for background\n * @param hex - Hex color (e.g., \"#4B0082\")\n * @param opacity - Opacity value (0-1)\n * @returns rgba string\n */\nexport function hexToRgba(hex: string, opacity: number): string {\n const result = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i.exec(hex);\n if (!result) return `rgba(107, 114, 128, ${opacity})`; // fallback gray\n const r = Number.parseInt(result[1], 16);\n const g = Number.parseInt(result[2], 16);\n const b = Number.parseInt(result[3], 16);\n return `rgba(${r}, ${g}, ${b}, ${opacity})`;\n}\n\n/**\n * Maps Tailwind bg-* class to CSS variable\n * @param bgClass - Tailwind background class (e.g., \"bg-error-600\")\n * @returns CSS variable string (e.g., \"var(--color-error-600)\")\n */\nexport function bgClassToCssVar(bgClass: string): string {\n return `var(--color-${bgClass.replace('bg-', '')})`;\n}\n\n/**\n * Converts polar coordinates to Cartesian for SVG arc path calculations.\n * Angles are in degrees, with 0° at the top (12 o'clock position).\n * @param cx - Center X coordinate\n * @param cy - Center Y coordinate\n * @param r - Radius\n * @param angleDeg - Angle in degrees\n * @returns Cartesian coordinates { x, y }\n */\nexport function polarToCartesian(\n cx: number,\n cy: number,\n r: number,\n angleDeg: number\n): { x: number; y: number } {\n const rad = ((angleDeg - 90) * Math.PI) / 180;\n return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };\n}\n\n/**\n * Generates an SVG filled arc (pie slice) path string.\n * @param cx - Center X coordinate\n * @param cy - Center Y coordinate\n * @param r - Radius\n * @param startAngle - Start angle in degrees\n * @param endAngle - End angle in degrees\n * @returns SVG path string for the arc\n */\nexport function describeArc(\n cx: number,\n cy: number,\n r: number,\n startAngle: number,\n endAngle: number\n): string {\n const span = endAngle - startAngle;\n\n // For full circle (or near-full), use two arcs to avoid SVG arc degeneracy\n if (span >= 359.99) {\n const midAngle = startAngle + 180;\n const s = polarToCartesian(cx, cy, r, startAngle);\n const m = polarToCartesian(cx, cy, r, midAngle);\n return `M ${cx} ${cy} L ${s.x} ${s.y} A ${r} ${r} 0 1 1 ${m.x} ${m.y} A ${r} ${r} 0 1 1 ${s.x} ${s.y} Z`;\n }\n\n const s = polarToCartesian(cx, cy, r, endAngle);\n const e = polarToCartesian(cx, cy, r, startAngle);\n const largeArc = span > 180 ? 1 : 0;\n return `M ${cx} ${cy} L ${s.x} ${s.y} A ${r} ${r} 0 ${largeArc} 0 ${e.x} ${e.y} Z`;\n}\n\n/**\n * Retorna a cor hexadecimal com opacidade 0.3 (4d) se não estiver em dark mode.\n * Se estiver em dark mode, retorna a cor original.\n *\n * @param hexColor - Cor hexadecimal (ex: \"#0066b8\" ou \"0066b8\")\n * @param isDark - booleano indicando se está em dark mode\n * @returns string - cor hexadecimal com opacidade se necessário\n */\nexport function getSubjectColorWithOpacity(\n hexColor: string | undefined,\n isDark: boolean\n): string | undefined {\n if (!hexColor) return undefined;\n // Remove o '#' se existir\n let color = hexColor.replace(/^#/, '').toLowerCase();\n\n if (isDark) {\n // Se está em dark mode, sempre remove opacidade se existir\n if (color.length === 8) {\n color = color.slice(0, 6);\n }\n return `#${color}`;\n } else {\n // Se não está em dark mode (light mode)\n let resultColor: string;\n if (color.length === 6) {\n // Adiciona opacidade 0.3 (4D) para cores de 6 dígitos\n resultColor = `#${color}4d`;\n } else if (color.length === 8) {\n // Já tem opacidade, retorna como está\n resultColor = `#${color}`;\n } else {\n // Para outros tamanhos (3, 4, 5 dígitos), retorna como está\n resultColor = `#${color}`;\n }\n return resultColor;\n }\n}\n","import {\n ReactNode,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport Text from '../Text/Text';\nimport { cn } from '../../utils/utils';\n\n/**\n * Tooltip position options\n */\ntype TooltipPosition = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Tooltip component props interface\n */\nexport interface TooltipProps {\n /** Content to display in the tooltip */\n content: ReactNode;\n /** Element that triggers the tooltip */\n children: ReactNode;\n /** Position of the tooltip relative to the trigger */\n position?: TooltipPosition;\n /** Additional className for the tooltip container */\n className?: string;\n /** Additional className for the tooltip content */\n contentClassName?: string;\n /** Whether the tooltip is disabled */\n disabled?: boolean;\n /**\n * Render the tooltip inside a React Portal attached to document.body.\n * Use this when the trigger lives inside an ancestor with `overflow:hidden`\n * (e.g. scroll containers) that would otherwise clip the tooltip.\n */\n usePortal?: boolean;\n}\n\n/**\n * Position classes for tooltip placement (non-portal mode, CSS-only)\n */\nconst POSITION_CLASSES: Record<TooltipPosition, string> = {\n top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',\n bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',\n left: 'right-full top-1/2 -translate-y-1/2 mr-2',\n right: 'left-full top-1/2 -translate-y-1/2 ml-2',\n};\n\nconst TOOLTIP_GAP_PX = 8;\n\n/**\n * Compute fixed-position coordinates relative to the viewport for a given\n * trigger element and desired tooltip position.\n */\nconst computePortalCoords = (\n triggerRect: DOMRect,\n position: TooltipPosition,\n tooltipRect: { width: number; height: number }\n): { top: number; left: number } => {\n switch (position) {\n case 'bottom':\n return {\n top: triggerRect.bottom + TOOLTIP_GAP_PX,\n left: triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2,\n };\n case 'left':\n return {\n top: triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2,\n left: triggerRect.left - tooltipRect.width - TOOLTIP_GAP_PX,\n };\n case 'right':\n return {\n top: triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2,\n left: triggerRect.right + TOOLTIP_GAP_PX,\n };\n case 'top':\n default:\n return {\n top: triggerRect.top - tooltipRect.height - TOOLTIP_GAP_PX,\n left: triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2,\n };\n }\n};\n\nconst TOOLTIP_CONTENT_CLASSES = cn(\n 'whitespace-nowrap',\n 'px-4 py-2 rounded-lg',\n 'bg-background-dark text-white',\n 'text-sm font-medium',\n 'shadow-[0px_3px_10px_0px_rgba(38,38,38,0.2)]',\n 'transition-opacity duration-150'\n);\n\n/**\n * Tooltip component - Displays contextual information on hover/focus\n *\n * By default uses a CSS-only approach with `group-hover` for performance.\n * When `usePortal` is true, the tooltip is rendered in a React Portal so it\n * escapes ancestors with `overflow:hidden`.\n *\n * @example\n * ```tsx\n * <Tooltip content=\"Desempenho baseado nas atividades\">\n * <Info size={18} weight=\"bold\" className=\"text-text-950 cursor-pointer\" />\n * </Tooltip>\n * ```\n */\nexport function Tooltip({\n content,\n children,\n position = 'top',\n className,\n contentClassName,\n disabled = false,\n usePortal = false,\n}: Readonly<TooltipProps>) {\n const triggerRef = useRef<HTMLSpanElement>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const [open, setOpen] = useState(false);\n const [coords, setCoords] = useState<{ top: number; left: number }>({\n top: 0,\n left: 0,\n });\n\n const updatePosition = useCallback(() => {\n if (!triggerRef.current || !tooltipRef.current) return;\n const triggerRect = triggerRef.current.getBoundingClientRect();\n const tooltipRect = {\n width: tooltipRef.current.offsetWidth,\n height: tooltipRef.current.offsetHeight,\n };\n setCoords(computePortalCoords(triggerRect, position, tooltipRect));\n }, [position]);\n\n useLayoutEffect(() => {\n if (!usePortal || !open) return;\n updatePosition();\n const onViewportChange = () => updatePosition();\n window.addEventListener('resize', onViewportChange);\n window.addEventListener('scroll', onViewportChange, true);\n return () => {\n window.removeEventListener('resize', onViewportChange);\n window.removeEventListener('scroll', onViewportChange, true);\n };\n }, [usePortal, open, updatePosition, content]);\n\n /**\n * Attach hover/focus listeners via the DOM API (not JSX props) so the\n * wrapper element stays semantically non-interactive. This satisfies the\n * SonarQube `S6848`/`jsx-a11y/no-static-element-interactions` rule which\n * fires on static elements (`<span>`/`<div>`) that carry interactive JSX\n * handlers (`onMouseEnter`/`onFocus`/etc.).\n */\n useEffect(() => {\n if (!usePortal) return;\n const node = triggerRef.current;\n if (!node) return;\n\n const handleOpen = () => setOpen(true);\n const handleClose = () => setOpen(false);\n\n node.addEventListener('mouseenter', handleOpen);\n node.addEventListener('mouseleave', handleClose);\n node.addEventListener('focusin', handleOpen);\n node.addEventListener('focusout', handleClose);\n\n return () => {\n node.removeEventListener('mouseenter', handleOpen);\n node.removeEventListener('mouseleave', handleClose);\n node.removeEventListener('focusin', handleOpen);\n node.removeEventListener('focusout', handleClose);\n };\n }, [usePortal]);\n\n if (disabled) {\n return <>{children}</>;\n }\n\n if (usePortal) {\n return (\n <Text\n as=\"span\"\n ref={triggerRef}\n className={cn('relative inline-flex', className)}\n aria-describedby={open ? 'tooltip-portal' : undefined}\n >\n {children}\n {open &&\n typeof document !== 'undefined' &&\n createPortal(\n <div\n ref={tooltipRef}\n id=\"tooltip-portal\"\n role=\"tooltip\"\n style={{\n position: 'fixed',\n top: coords.top,\n left: coords.left,\n zIndex: 9999,\n }}\n className={cn(TOOLTIP_CONTENT_CLASSES, contentClassName)}\n >\n {content}\n </div>,\n document.body\n )}\n </Text>\n );\n }\n\n return (\n <div className={cn('relative inline-flex group', className)}>\n {children}\n\n {/* Tooltip content - shown on hover/focus via CSS */}\n <div\n role=\"tooltip\"\n className={cn(\n 'absolute z-50',\n TOOLTIP_CONTENT_CLASSES,\n 'opacity-0 invisible',\n 'group-hover:opacity-100 group-hover:visible',\n 'group-focus-within:opacity-100 group-focus-within:visible',\n POSITION_CLASSES[position],\n contentClassName\n )}\n >\n {content}\n </div>\n </div>\n );\n}\n\nexport default Tooltip;\n","import { ComponentPropsWithRef, ElementType, ReactNode } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * Base text component props\n */\ntype BaseTextProps = {\n /** Content to be displayed */\n children?: ReactNode;\n /** Text size variant */\n size?:\n | '2xs'\n | 'xs'\n | 'sm'\n | 'md'\n | 'lg'\n | 'xl'\n | '2xl'\n | '3xl'\n | '4xl'\n | '5xl'\n | '6xl';\n /** Font weight variant */\n weight?:\n | 'hairline'\n | 'light'\n | 'normal'\n | 'medium'\n | 'semibold'\n | 'bold'\n | 'extrabold'\n | 'black';\n /** Color variant - white for light backgrounds, black for dark backgrounds */\n color?: string;\n /** Additional CSS classes to apply */\n className?: string;\n};\n\n/**\n * Polymorphic text component props that ensures type safety based on the 'as' prop\n */\ntype TextProps<T extends ElementType = 'p'> = BaseTextProps & {\n /** HTML tag to render */\n as?: T;\n} & Omit<ComponentPropsWithRef<T>, keyof BaseTextProps>;\n\n/**\n * Text component for Analytica Ensino platforms\n *\n * A flexible polymorphic text component with multiple sizes, weights, and colors.\n * Automatically adapts to dark and light themes with full type safety.\n *\n * @param children - The content to display\n * @param size - The text size variant (2xs, xs, sm, md, lg, xl, 2xl, 3xl, 4xl, 5xl, 6xl)\n * @param weight - The font weight variant (hairline, light, normal, medium, semibold, bold, extrabold, black)\n * @param color - The color variant - adapts to theme\n * @param as - The HTML tag to render - determines allowed attributes via TypeScript\n * @param className - Additional CSS classes\n * @param props - HTML attributes valid for the chosen tag only\n * @returns A styled text element with type-safe attributes\n *\n * @example\n * ```tsx\n * <Text size=\"lg\" weight=\"bold\" color=\"text-info-800\">\n * This is a large, bold text\n * </Text>\n *\n * <Text as=\"a\" href=\"/link\" target=\"_blank\">\n * Link with type-safe anchor attributes\n * </Text>\n *\n * <Text as=\"button\" onClick={handleClick} disabled>\n * Button with type-safe button attributes\n * </Text>\n * ```\n */\nconst Text = <T extends ElementType = 'p'>({\n children,\n size = 'md',\n weight = 'normal',\n color = 'text-text-950',\n as,\n className = '',\n ...props\n}: TextProps<T>) => {\n let sizeClasses: string;\n let weightClasses: string;\n\n // Text size classes mapping\n const sizeClassMap = {\n '2xs': 'text-2xs',\n xs: 'text-xs',\n sm: 'text-sm',\n md: 'text-md',\n lg: 'text-lg',\n xl: 'text-xl',\n '2xl': 'text-2xl',\n '3xl': 'text-3xl',\n '4xl': 'text-4xl',\n '5xl': 'text-5xl',\n '6xl': 'text-6xl',\n } as const;\n\n sizeClasses = sizeClassMap[size] ?? sizeClassMap.md;\n\n // Font weight classes mapping\n const weightClassMap = {\n hairline: 'font-hairline',\n light: 'font-light',\n normal: 'font-normal',\n medium: 'font-medium',\n semibold: 'font-semibold',\n bold: 'font-bold',\n extrabold: 'font-extrabold',\n black: 'font-black',\n } as const;\n\n weightClasses = weightClassMap[weight] ?? weightClassMap.normal;\n\n const baseClasses = 'font-primary';\n const Component = as ?? ('p' as ElementType);\n\n return (\n <Component\n className={cn(baseClasses, sizeClasses, weightClasses, color, className)}\n {...props}\n >\n {children}\n </Component>\n );\n};\n\nexport default Text;\n","/**\n * Constantes de posicionamento compartilhadas entre os FABs do widget\n * (`AccessibilityFab` e `LibrasFab`). Centralizadas aqui para evitar\n * que mudanças de gap/lateral precisem ser replicadas em cada FAB.\n */\n\nexport type FabPosition = 'right' | 'left';\n\n/**\n * Alinhamento vertical do FAB. Quando o widget renderiza só um botão\n * (sem Libras), `center` deixa-o no meio da viewport. Quando há também\n * o LibrasFab empilhado abaixo, o de acessibilidade fica `above-center`\n * e o Libras fica `below-center` — formando um par grudado no meio.\n */\nexport type FabVerticalAlign = 'center' | 'above-center' | 'below-center';\n\nexport const FAB_POSITION_CLASSES: Record<FabPosition, string> = {\n right: 'right-0 rounded-l-lg',\n left: 'left-0 rounded-r-lg',\n};\n\nexport const FAB_VERTICAL_ALIGN_CLASSES: Record<FabVerticalAlign, string> = {\n center: 'top-1/2 -translate-y-1/2',\n // Pares empilhados (acessibilidade + Libras) com gap mínimo entre si,\n // como no widget do HandTalk. Cada FAB tem 40px (h-10).\n 'above-center': 'top-[calc(50%-1px)] -translate-y-full',\n 'below-center': 'top-[calc(50%+1px)]',\n};\n\n/** Tooltip aparece do lado oposto à borda em que o FAB está colado. */\nexport const FAB_TOOLTIP_POSITION: Record<FabPosition, 'left' | 'right'> = {\n right: 'left',\n left: 'right',\n};\n","import Button from '../Button/Button';\nimport { Tooltip } from '../Tooltip/Tooltip';\nimport { cn } from '../../utils/utils';\nimport accessibilityIcon from '../../assets/img/accessibility.png';\nimport {\n FAB_POSITION_CLASSES,\n FAB_TOOLTIP_POSITION,\n FAB_VERTICAL_ALIGN_CLASSES,\n type FabPosition,\n type FabVerticalAlign,\n} from './fabPositioning';\n\n/** Re-exports mantidos para compatibilidade com a API pública do widget. */\nexport type AccessibilityFabPosition = FabPosition;\nexport type AccessibilityFabVerticalAlign = FabVerticalAlign;\n\nexport interface AccessibilityFabProps {\n /** Click handler — alterna o painel */\n onClick: () => void;\n /** Indica se o painel está aberto (controla aria-expanded) */\n isOpen?: boolean;\n /** Lado da viewport onde o botão fica colado */\n position?: AccessibilityFabPosition;\n /** Alinhamento vertical (default: `center`) */\n verticalAlign?: AccessibilityFabVerticalAlign;\n /** Classes extras */\n className?: string;\n}\n\n/**\n * Botão flutuante (FAB) que abre o painel de acessibilidade.\n * Inspirado no padrão HandTalk: quadrado azul escuro colado na\n * lateral da viewport (direita por padrão), com o ícone universal\n * de acessibilidade. Verticalmente centralizado.\n */\nexport default function AccessibilityFab({\n onClick,\n isOpen = false,\n position = 'right',\n verticalAlign = 'center',\n className,\n}: Readonly<AccessibilityFabProps>) {\n const label = isOpen\n ? 'Fechar opções de acessibilidade'\n : 'Opções de acessibilidade';\n\n return (\n <Tooltip\n content={label}\n position={FAB_TOOLTIP_POSITION[position]}\n className={cn(\n 'fixed z-40',\n FAB_VERTICAL_ALIGN_CLASSES[verticalAlign],\n FAB_POSITION_CLASSES[position]\n )}\n >\n <Button\n variant=\"raw\"\n onClick={onClick}\n aria-label={label}\n aria-expanded={isOpen}\n data-testid=\"accessibility-fab\"\n className={cn(\n 'a11y-widget-shield',\n FAB_POSITION_CLASSES[position],\n 'flex h-10 w-10 cursor-pointer items-center justify-center',\n // `text-text-50` flipa junto com `bg-info-900` (claro no light,\n // escuro no dark). `text-white` deixaria o ícone branco sumindo\n // no tema escuro, onde `bg-info-900` resolve pra azul-claro.\n 'bg-info-900 text-text-50 shadow-lg',\n 'transition-all duration-200 hover:scale-110 hover:bg-info-800',\n 'focus:outline-none focus:ring-4 focus:ring-info-300',\n className\n )}\n >\n <img\n src={accessibilityIcon}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"h-7 w-7\"\n />\n </Button>\n </Tooltip>\n );\n}\n","import { useEffect, useRef, useState, type ReactNode } from 'react';\nimport {\n ArrowCounterClockwiseIcon,\n CaretDownIcon,\n EyeIcon,\n FilmReelIcon,\n KeyboardIcon,\n NavigationArrowIcon,\n SpeakerHighIcon,\n TextTIcon,\n XIcon,\n} from '@phosphor-icons/react';\nimport accessibilityIcon from '../../assets/img/accessibility.png';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\nimport IconButton from '../IconButton/IconButton';\nimport AccessibilityToggleRow from './AccessibilityToggleRow';\nimport TTSSection from './TTSSection';\nimport { cn } from '../../utils/utils';\nimport {\n useAccessibilityStore,\n type ContrastMode,\n type SaturationMode,\n type ReadingAid,\n} from '../../store/accessibilityStore';\nimport type { AccessibilityFabPosition } from './AccessibilityFab';\n\nconst CONTRAST_OPTIONS: { value: ContrastMode; label: string }[] = [\n { value: 'normal', label: 'Normal' },\n { value: 'high', label: 'Alto' },\n { value: 'inverted', label: 'Invertido' },\n];\n\nconst SATURATION_OPTIONS: { value: SaturationMode; label: string }[] = [\n { value: 'normal', label: 'Normal' },\n { value: 'low', label: 'Baixa' },\n { value: 'grayscale', label: 'Tons de cinza' },\n];\n\nconst READING_AID_OPTIONS: { value: ReadingAid; label: string }[] = [\n { value: 'none', label: 'Nenhum' },\n { value: 'ruler', label: 'Régua' },\n { value: 'mask', label: 'Máscara' },\n];\n\nconst LEVEL_LABELS = ['Padrão', 'Pequeno', 'Médio', 'Grande'] as const;\n\n/** Tamanho de fonte do preview \"Aa\" em cada botão (Padrão → Grande). */\nconst FONT_SIZE_PREVIEW_CLASSES = [\n 'text-base', // Padrão\n 'text-lg', // Pequeno (= maior que Padrão, segue a escala incremental do widget)\n 'text-2xl', // Médio\n 'text-4xl', // Grande\n] as const;\n\n/** Letter-spacing do preview \"Aa\" em cada botão. */\nconst LETTER_SPACING_PREVIEW_CLASSES = [\n 'tracking-tighter', // Padrão (junto)\n 'tracking-normal',\n 'tracking-wider',\n 'tracking-widest',\n] as const;\n\n/** Gap vertical entre as 3 barras do preview de espaçamento entre linhas. */\nconst LINE_SPACING_PREVIEW_GAPS = [\n 'gap-0.5', // Padrão (2px) — linhas próximas\n 'gap-1.5', // Pequeno (6px)\n 'gap-2.5', // Médio (10px)\n 'gap-3.5', // Grande (14px)\n] as const;\n\n/**\n * `<dialog open>` aplica `left: 0; right: 0` como user-agent style. Sem\n * resetar o lado oposto para `auto`, o painel acaba grudando no lado\n * errado mesmo com `right-6` ou `left-6` aplicado.\n */\nconst PANEL_EDGE_CLASSES: Record<AccessibilityFabPosition, string> = {\n right: 'right-10 left-auto',\n left: 'left-10 right-auto',\n};\n\ninterface AccordionSectionProps {\n title: string;\n icon: ReactNode;\n defaultExpanded?: boolean;\n testId?: string;\n children: ReactNode;\n}\n\n/**\n * Item de accordion estilo lista (sem cantos arredondados nem card),\n * com ícone + título à esquerda e caret à direita. Cada item separado\n * do próximo por uma linha fina. Mantém a estrutura visual do design\n * do Figma sem usar o `CardAccordation` da lib (que tem visual de card).\n */\nconst AccordionSection = ({\n title,\n icon,\n defaultExpanded = false,\n testId,\n children,\n}: Readonly<AccordionSectionProps>) => {\n const [expanded, setExpanded] = useState(defaultExpanded);\n return (\n <section className=\"border-b border-background-200 last:border-b-0\">\n <Button\n variant=\"raw\"\n aria-expanded={expanded}\n onClick={() => setExpanded((v) => !v)}\n data-testid={testId}\n className={cn(\n 'flex w-full cursor-pointer items-center justify-between gap-2 px-4 py-3',\n 'text-left transition-colors hover:bg-background-50',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-info-500 focus-visible:ring-inset'\n )}\n >\n <span className=\"flex items-center gap-2\">\n <span className=\"text-text-800\" aria-hidden=\"true\">\n {icon}\n </span>\n <Text\n size=\"md\"\n weight=\"semibold\"\n className=\"leading-none text-text-800\"\n >\n {title}\n </Text>\n </span>\n <CaretDownIcon\n size={16}\n weight=\"bold\"\n aria-hidden=\"true\"\n className={cn(\n 'text-text-700 transition-transform duration-200',\n expanded ? 'rotate-180' : 'rotate-0'\n )}\n />\n </Button>\n {expanded && (\n <div className=\"flex flex-col gap-6 px-4 pb-4 pt-2\">{children}</div>\n )}\n </section>\n );\n};\n\ninterface SubControlProps {\n label: string;\n children: ReactNode;\n}\n\n/**\n * Wrapper para uma control (segmented/toggle) com título dentro do\n * accordion. Tipografia do label segue o Figma: Roboto 12px/500/100%\n * uppercase, cor `text-text-600` (#737373).\n */\nconst SubControl = ({ label, children }: Readonly<SubControlProps>) => (\n <div className=\"flex flex-col gap-2\">\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"uppercase leading-none text-text-600\"\n >\n {label}\n </Text>\n {children}\n </div>\n);\n\ninterface SegmentedProps<T extends string | number> {\n value: T;\n options: { value: T; label: string }[];\n onChange: (value: T) => void;\n ariaLabel: string;\n}\n\n/**\n * Estilo de segmented baseado no `SelectionButton` da lib: cada opção é\n * um botão independente com `border` + `bg-background`, e o selecionado\n * ganha `ring-primary-950` (mesma identidade visual do componente da lib,\n * sem o overhead de ícone obrigatório do SelectionButton).\n */\nconst Segmented = <T extends string | number>({\n value,\n options,\n onChange,\n ariaLabel,\n}: Readonly<SegmentedProps<T>>) => (\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n className=\"flex flex-wrap gap-2\"\n >\n {options.map((opt) => {\n const isActive = opt.value === value;\n return (\n <Button\n key={String(opt.value)}\n variant=\"raw\"\n role=\"radio\"\n aria-checked={isActive}\n onClick={() => onChange(opt.value)}\n className={cn(\n 'flex-1 cursor-pointer rounded-xl border border-border-50 bg-background',\n // Texto segue spec do Figma: Roboto 14px/700/100%/0.2px, cor\n // #525252 (text-700). Quando ativo, troca pra primary-950\n // (azul escuro), mas mantém a mesma typography.\n 'px-4 py-4 text-sm font-bold leading-none tracking-[0.2px] text-text-700',\n 'shadow-soft-shadow-1 transition-all duration-150',\n 'hover:bg-background-100',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indicator-info',\n isActive &&\n 'ring-2 ring-primary-950 ring-offset-0 shadow-none text-primary-950'\n )}\n >\n {opt.label}\n </Button>\n );\n })}\n </div>\n);\n\ninterface PreviewSegmentedOption<T extends string | number> {\n value: T;\n label: string;\n /** Conteúdo visual exibido DENTRO do botão (preview do efeito). */\n preview: ReactNode;\n}\n\ninterface PreviewSegmentedProps<T extends string | number> {\n value: T;\n options: PreviewSegmentedOption<T>[];\n onChange: (value: T) => void;\n ariaLabel: string;\n}\n\n/**\n * Variante do Segmented onde o botão mostra um preview visual do efeito\n * (ex.: \"Aa\" em tamanhos diferentes para \"Tamanho da fonte\") e o label\n * textual fica abaixo do botão como legenda.\n */\nconst PreviewSegmented = <T extends string | number>({\n value,\n options,\n onChange,\n ariaLabel,\n}: Readonly<PreviewSegmentedProps<T>>) => (\n <div role=\"radiogroup\" aria-label={ariaLabel} className=\"flex gap-2\">\n {options.map((opt) => {\n const isActive = opt.value === value;\n return (\n <div\n key={String(opt.value)}\n className=\"flex flex-1 flex-col items-center gap-2\"\n >\n <Button\n variant=\"raw\"\n role=\"radio\"\n aria-checked={isActive}\n aria-label={opt.label}\n onClick={() => onChange(opt.value)}\n className={cn(\n 'flex aspect-square w-full items-center justify-center',\n 'cursor-pointer rounded-xl border border-border-50 bg-background',\n 'text-text-700 shadow-soft-shadow-1 transition-all duration-150',\n 'hover:bg-background-100',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indicator-info',\n isActive &&\n 'ring-2 ring-primary-950 ring-offset-0 shadow-none text-primary-950'\n )}\n >\n {opt.preview}\n </Button>\n <Text\n size=\"xs\"\n weight=\"normal\"\n className=\"leading-none text-text-700\"\n >\n {opt.label}\n </Text>\n </div>\n );\n })}\n </div>\n);\n\nexport interface AccessibilityPanelProps {\n isOpen: boolean;\n onClose: () => void;\n /** Lado da viewport onde o painel aparece (default: 'right') */\n position?: AccessibilityFabPosition;\n className?: string;\n}\n\n/**\n * Painel lateral com os controles de acessibilidade organizados em\n * accordions colapsáveis (Visão, Leitura, Leitor de texto, Animação,\n * Navegação, Atalho de teclado).\n *\n * O próprio painel fica dentro de `.a11y-widget-shield`, então\n * filtros aplicados no `<html>` (alto contraste, etc.) não afetam\n * sua legibilidade.\n */\nexport default function AccessibilityPanel({\n isOpen,\n onClose,\n position = 'right',\n className,\n}: Readonly<AccessibilityPanelProps>) {\n const closeButtonRef = useRef<HTMLButtonElement>(null);\n const previouslyFocusedRef = useRef<Element | null>(null);\n\n const {\n contrastMode,\n saturationMode,\n fontSize,\n letterSpacing,\n lineSpacing,\n highlightLinks,\n pauseAnimations,\n bigCursor,\n dyslexiaFont,\n readingAid,\n keyboardShortcut,\n setContrastMode,\n setSaturationMode,\n setFontSize,\n setLetterSpacing,\n setLineSpacing,\n setHighlightLinks,\n setPauseAnimations,\n setBigCursor,\n setDyslexiaFont,\n setReadingAid,\n setKeyboardShortcut,\n resetPreferences,\n } = useAccessibilityStore();\n\n // Foco e Escape: ao abrir, lembra o elemento anteriormente focado,\n // move o foco para o botão de fechar e escuta a tecla Esc. Restaura\n // o foco anterior ao fechar.\n useEffect(() => {\n if (!isOpen) return;\n previouslyFocusedRef.current = document.activeElement;\n closeButtonRef.current?.focus();\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape') {\n event.stopPropagation();\n onClose();\n }\n };\n globalThis.addEventListener('keydown', handleKeyDown);\n\n return () => {\n globalThis.removeEventListener('keydown', handleKeyDown);\n const previous = previouslyFocusedRef.current as HTMLElement | null;\n if (previous && typeof previous.focus === 'function') {\n previous.focus();\n }\n };\n }, [isOpen, onClose]);\n\n if (!isOpen) return null;\n\n const levelOptions = LEVEL_LABELS.map((label, index) => ({\n value: index as 0 | 1 | 2 | 3,\n label,\n }));\n\n return (\n <dialog\n open\n aria-label=\"Opções de acessibilidade\"\n data-testid=\"accessibility-panel\"\n // `<dialog>` não estica automaticamente com `top` + `bottom`, por\n // isso a altura fica explícita: `100vh - 5rem` (80px de respiro do\n // topo). Combinado com `bottom-0` no className, o painel encosta\n // na base da viewport como no Figma.\n style={{ height: 'calc(100vh - 5rem)' }}\n className={cn(\n 'a11y-widget-shield',\n 'fixed bottom-0 z-40 m-0',\n PANEL_EDGE_CLASSES[position],\n 'flex w-[calc(100vw-3rem)] max-w-110 flex-col overflow-hidden rounded-2xl p-0',\n 'border border-background-200 bg-background shadow-2xl',\n className\n )}\n >\n {/* Header — visual do Figma: bg azul-claro (Paraná) / rosa-claro\n (Paraíba) usando `primary-50`. Preferido em vez de `map-attention`\n porque `primary-50` flipa corretamente no dark mode em todos os\n temas (ex.: Paraná dark = #27344a), enquanto `map-attention`\n ficou idêntico em light/dark no theme do Paraná, deixando o\n texto branco invisível sobre o azul-claro no dark. */}\n <header className=\"flex items-center justify-between gap-3 bg-primary-50 px-4 py-3\">\n <div className=\"flex min-w-0 items-center gap-3\">\n <img\n src={accessibilityIcon}\n alt=\"\"\n aria-hidden=\"true\"\n className=\"h-10 w-10 shrink-0\"\n />\n <div className=\"flex min-w-0 flex-col gap-1\">\n <Text\n size=\"md\"\n weight=\"bold\"\n className=\"leading-none tracking-[0.2px] text-text-950\"\n >\n Acessibilidade\n </Text>\n <Text\n size=\"xs\"\n weight=\"normal\"\n className=\"leading-none text-text-800\"\n >\n Personalize a leitura e navegação\n </Text>\n </div>\n </div>\n <IconButton\n ref={closeButtonRef}\n size=\"sm\"\n aria-label=\"Fechar opções de acessibilidade\"\n onClick={onClose}\n icon={<XIcon size={18} />}\n />\n </header>\n\n <div className=\"flex-1 overflow-y-auto\">\n <AccordionSection\n title=\"Visão\"\n icon={<EyeIcon size={18} />}\n testId=\"a11y-section-vision\"\n >\n <SubControl label=\"Contraste\">\n <Segmented\n ariaLabel=\"Modo de contraste\"\n value={contrastMode}\n options={CONTRAST_OPTIONS}\n onChange={setContrastMode}\n />\n </SubControl>\n <SubControl label=\"Saturação\">\n <Segmented\n ariaLabel=\"Modo de saturação\"\n value={saturationMode}\n options={SATURATION_OPTIONS}\n onChange={setSaturationMode}\n />\n </SubControl>\n {/* Daltonismo removido por enquanto — store/store actions\n mantidos para reativação futura sem refactor. */}\n </AccordionSection>\n\n <AccordionSection\n title=\"Leitura\"\n icon={<TextTIcon size={18} />}\n testId=\"a11y-section-reading\"\n >\n <SubControl label=\"Fonte para dislexia\">\n <AccessibilityToggleRow\n ariaLabel=\"Trocar para Comic Sans MS\"\n rowTestId=\"a11y-toggle-dyslexia-font-row\"\n switchTestId=\"a11y-toggle-dyslexia-font\"\n checked={dyslexiaFont}\n onChange={() => setDyslexiaFont(!dyslexiaFont)}\n label={\n <>\n Trocar para{' '}\n <span style={{ fontFamily: '\"Comic Sans MS\", cursive' }}>\n Comic Sans MS\n </span>\n </>\n }\n />\n </SubControl>\n\n <SubControl label=\"Tamanho da fonte\">\n <PreviewSegmented\n ariaLabel=\"Tamanho da fonte\"\n value={fontSize}\n options={levelOptions.map((opt) => ({\n ...opt,\n preview: (\n <span\n className={cn(\n 'font-semibold leading-none',\n FONT_SIZE_PREVIEW_CLASSES[opt.value]\n )}\n >\n Aa\n </span>\n ),\n }))}\n onChange={setFontSize}\n />\n </SubControl>\n\n <SubControl label=\"Espaçamento entre letras\">\n <PreviewSegmented\n ariaLabel=\"Espaçamento entre letras\"\n value={letterSpacing}\n options={levelOptions.map((opt) => ({\n ...opt,\n preview: (\n <span\n className={cn(\n // Mesmo tamanho do \"Aa\" no botão Padrão de \"Tamanho\n // da fonte\" (text-base), pra que essa scale visualize\n // só a variação de letter-spacing.\n 'text-base font-semibold leading-none',\n LETTER_SPACING_PREVIEW_CLASSES[opt.value]\n )}\n >\n Aa\n </span>\n ),\n }))}\n onChange={setLetterSpacing}\n />\n </SubControl>\n\n <SubControl label=\"Espaçamento entre linhas\">\n <PreviewSegmented\n ariaLabel=\"Espaçamento entre linhas\"\n value={lineSpacing}\n options={levelOptions.map((opt) => ({\n ...opt,\n preview: (\n <span\n className={cn(\n 'flex flex-col items-stretch',\n LINE_SPACING_PREVIEW_GAPS[opt.value]\n )}\n >\n <span className=\"h-0.5 w-7 rounded-full bg-current\" />\n <span className=\"h-0.5 w-7 rounded-full bg-current\" />\n <span className=\"h-0.5 w-7 rounded-full bg-current\" />\n </span>\n ),\n }))}\n onChange={setLineSpacing}\n />\n </SubControl>\n\n <SubControl label=\"Auxiliar de leitura\">\n <Segmented\n ariaLabel=\"Auxiliar de leitura\"\n value={readingAid}\n options={READING_AID_OPTIONS}\n onChange={setReadingAid}\n />\n </SubControl>\n </AccordionSection>\n\n <AccordionSection\n title=\"Leitor de texto\"\n icon={<SpeakerHighIcon size={18} />}\n testId=\"a11y-section-tts\"\n >\n <TTSSection PreviewSegmented={PreviewSegmented} />\n </AccordionSection>\n\n <AccordionSection\n title=\"Animação\"\n icon={<FilmReelIcon size={18} />}\n testId=\"a11y-section-animation\"\n >\n <AccessibilityToggleRow\n label=\"Pausar animações para conforto visual\"\n checked={pauseAnimations}\n onChange={() => setPauseAnimations(!pauseAnimations)}\n switchTestId=\"a11y-toggle-pause-animations\"\n />\n </AccordionSection>\n\n <AccordionSection\n title=\"Navegação\"\n icon={<NavigationArrowIcon size={18} />}\n testId=\"a11y-section-navigation\"\n >\n <SubControl label=\"Destacar links e botões\">\n <AccessibilityToggleRow\n label=\"Adicionar contorno em elementos clicáveis\"\n checked={highlightLinks}\n onChange={() => setHighlightLinks(!highlightLinks)}\n switchTestId=\"a11y-toggle-highlight-links\"\n />\n </SubControl>\n <SubControl label=\"Cursor maior\">\n <AccessibilityToggleRow\n label=\"Aumentar e escurecer cursor\"\n checked={bigCursor}\n onChange={() => setBigCursor(!bigCursor)}\n switchTestId=\"a11y-toggle-big-cursor\"\n />\n </SubControl>\n </AccordionSection>\n\n <AccordionSection\n title=\"Atalho de teclado\"\n icon={<KeyboardIcon size={18} />}\n testId=\"a11y-section-shortcut\"\n >\n <AccessibilityToggleRow\n label=\"Alt+A para abrir o painel\"\n checked={keyboardShortcut}\n onChange={() => setKeyboardShortcut(!keyboardShortcut)}\n switchTestId=\"a11y-toggle-keyboard-shortcut\"\n />\n </AccordionSection>\n </div>\n\n <footer className=\"border-t border-background-200 px-4 py-3\">\n <Button\n variant=\"outline\"\n action=\"primary\"\n size=\"medium\"\n iconLeft={\n <ArrowCounterClockwiseIcon\n size={16}\n weight=\"bold\"\n aria-hidden=\"true\"\n />\n }\n onClick={resetPreferences}\n data-testid=\"a11y-reset\"\n className=\"w-full justify-center\"\n >\n Redefinir ajustes\n </Button>\n </footer>\n </dialog>\n );\n}\n","import { ButtonHTMLAttributes, ReactNode, forwardRef } from 'react';\nimport { cn } from '../../utils/utils';\n\n/**\n * IconButton component props interface\n */\nexport type IconButtonProps = {\n /** Ícone a ser exibido no botão */\n icon: ReactNode;\n /** Tamanho do botão */\n size?: 'sm' | 'md';\n /** Estado de seleção/ativo do botão - permanece ativo até ser clicado novamente ou outro botão ser ativado */\n active?: boolean;\n /** Additional CSS classes to apply */\n className?: string;\n} & ButtonHTMLAttributes<HTMLButtonElement>;\n\n/**\n * IconButton component for Analytica Ensino platforms\n *\n * Um botão compacto apenas com ícone, ideal para menus dropdown,\n * barras de ferramentas e ações secundárias.\n * Oferece dois tamanhos com estilo consistente.\n * Estado ativo permanece até ser clicado novamente ou outro botão ser ativado.\n * Suporta forwardRef para acesso programático ao elemento DOM.\n *\n * @param icon - O ícone a ser exibido no botão\n * @param size - Tamanho do botão (sm, md)\n * @param active - Estado ativo/selecionado do botão\n * @param className - Classes CSS adicionais\n * @param props - Todos os outros atributos HTML padrão de button\n * @returns Um elemento button compacto estilizado apenas com ícone\n *\n * @example\n * ```tsx\n * <IconButton\n * icon={<MoreVerticalIcon />}\n * size=\"sm\"\n * onClick={() => openMenu()}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Botão ativo em uma barra de ferramentas - permanece ativo até outro clique\n * <IconButton\n * icon={<BoldIcon />}\n * active={isBold}\n * onClick={toggleBold}\n * />\n * ```\n *\n * @example\n * ```tsx\n * // Usando ref para controle programático\n * const buttonRef = useRef<HTMLButtonElement>(null);\n *\n * <IconButton\n * ref={buttonRef}\n * icon={<EditIcon />}\n * size=\"md\"\n * onClick={() => startEditing()}\n * />\n * ```\n */\nconst IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(\n (\n { icon, size = 'md', active = false, className = '', disabled, ...props },\n ref\n ) => {\n // Classes base para todos os estados\n const baseClasses = [\n 'inline-flex',\n 'items-center',\n 'justify-center',\n 'rounded-lg',\n 'font-medium',\n 'bg-transparent',\n 'text-text-950',\n 'cursor-pointer',\n 'hover:bg-primary-600',\n 'hover:text-text',\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-offset-0',\n 'focus-visible:ring-indicator-info',\n 'disabled:opacity-50',\n 'disabled:cursor-not-allowed',\n 'disabled:pointer-events-none',\n ];\n\n // Classes de tamanho\n const sizeClasses = {\n sm: ['w-6', 'h-6', 'text-sm'],\n md: ['w-10', 'h-10', 'text-base'],\n };\n\n // Classes de estado ativo\n const activeClasses = active\n ? ['!bg-primary-50', '!text-primary-950', 'hover:!bg-primary-100']\n : [];\n\n const allClasses = [\n ...baseClasses,\n ...sizeClasses[size],\n ...activeClasses,\n ].join(' ');\n\n // Garantir acessibilidade com aria-label padrão\n const ariaLabel = props['aria-label'] ?? 'Botão de ação';\n\n return (\n <button\n ref={ref}\n type=\"button\"\n className={cn(allClasses, className)}\n disabled={disabled}\n aria-pressed={active}\n aria-label={ariaLabel}\n {...props}\n >\n <span className=\"flex items-center justify-center\">{icon}</span>\n </button>\n );\n }\n);\n\nIconButton.displayName = 'IconButton';\n\nexport default IconButton;\n","import { ButtonHTMLAttributes } from 'react';\nimport { cn } from '../../utils/utils';\nimport Button from '../Button/Button';\n\n/**\n * ToggleSwitch size variants\n */\ntype ToggleSwitchSize = 'small' | 'medium' | 'large';\n\n/**\n * Size configurations using Tailwind classes\n */\nconst SIZE_CLASSES = {\n small: {\n track: 'h-5 w-9',\n thumb: 'h-4 w-4',\n translate: 'translate-x-4',\n },\n medium: {\n track: 'h-6 w-11',\n thumb: 'h-5 w-5',\n translate: 'translate-x-5',\n },\n large: {\n track: 'h-7 w-14',\n thumb: 'h-6 w-6',\n translate: 'translate-x-7',\n },\n} as const;\n\n/**\n * ToggleSwitch component props interface\n */\nexport type ToggleSwitchProps = {\n /** Whether the toggle is checked */\n checked?: boolean;\n /** Callback when toggle state changes */\n onChange?: () => void;\n /** Size variant of the toggle switch */\n size?: ToggleSwitchSize;\n /** Whether the toggle switch is disabled */\n disabled?: boolean;\n /** Additional CSS classes for the track */\n className?: string;\n /** Additional CSS classes for the thumb */\n thumbClassName?: string;\n /** Color when checked (Tailwind class, e.g., 'bg-success-500') */\n checkedColor?: string;\n /** Color when unchecked (Tailwind class, e.g., 'bg-border-300') */\n uncheckedColor?: string;\n} & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'size' | 'type' | 'onChange'>;\n\n/**\n * ToggleSwitch component for Analytica Ensino platforms\n *\n * A toggle switch component with support for different sizes and colors.\n * Uses the Button component with variant=\"raw\" for clean rendering.\n *\n * @example\n * ```tsx\n * // Basic toggle switch\n * <ToggleSwitch checked={isActive} onChange={() => setIsActive(!isActive)} />\n *\n * // Different sizes\n * <ToggleSwitch size=\"small\" checked={value} onChange={toggle} />\n * <ToggleSwitch size=\"large\" checked={value} onChange={toggle} />\n *\n * // Custom colors\n * <ToggleSwitch\n * checked={isEnabled}\n * onChange={toggle}\n * checkedColor=\"bg-primary-950\"\n * uncheckedColor=\"bg-border-400\"\n * />\n * ```\n */\nconst ToggleSwitch = ({\n checked = false,\n onChange,\n size = 'small',\n disabled = false,\n className = '',\n thumbClassName = '',\n checkedColor = 'bg-success-500',\n uncheckedColor = 'bg-border-300',\n ...props\n}: ToggleSwitchProps) => {\n const sizeClasses = SIZE_CLASSES[size];\n\n const trackClasses = cn(\n 'relative inline-flex shrink-0 cursor-pointer rounded-full border-2 border-transparent',\n 'transition-colors duration-200 ease-in-out',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2',\n sizeClasses.track,\n checked ? checkedColor : uncheckedColor,\n disabled && 'cursor-not-allowed opacity-40',\n className\n );\n\n const thumbClasses = cn(\n 'pointer-events-none inline-block transform rounded-full bg-white shadow ring-0',\n 'transition duration-200 ease-in-out',\n sizeClasses.thumb,\n checked ? sizeClasses.translate : 'translate-x-0',\n thumbClassName\n );\n\n return (\n <Button\n variant=\"raw\"\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled}\n className={trackClasses}\n {...props}\n onClick={(e) => {\n props.onClick?.(e);\n if (!e.defaultPrevented) {\n onChange?.();\n }\n }}\n >\n <span className={thumbClasses} />\n </Button>\n );\n};\n\nToggleSwitch.displayName = 'ToggleSwitch';\n\nexport default ToggleSwitch;\n","import type { ReactNode } from 'react';\nimport Text from '../Text/Text';\nimport ToggleSwitch from '../ToggleSwitch/ToggleSwitch';\nimport { cn } from '../../utils/utils';\n\nexport interface AccessibilityToggleRowProps {\n /** Conteúdo principal da linha (texto ou JSX, ex.: trecho em Comic Sans). */\n label: ReactNode;\n /** Aria-label do row quando `label` é JSX e não pode ser usado como texto. */\n ariaLabel?: string;\n /** Texto auxiliar abaixo do label. */\n description?: string;\n checked: boolean;\n onChange: () => void;\n /** Testid no wrapper interativo (toda a linha). */\n rowTestId?: string;\n /** Testid no `<ToggleSwitch>` interno — usado pelos testes que ainda\n * clicam diretamente no botão de switch. */\n switchTestId?: string;\n}\n\n/**\n * Linha clicável \"label à esquerda + ToggleSwitch à direita\" usada nos\n * accordions de Animação, Navegação, Atalho, dislexia e leitor de texto.\n *\n * O wrapper inteiro responde a click/Enter/Space; o `ToggleSwitch` interno\n * é apenas decorativo (`tabIndex={-1}`, `aria-hidden`) pra que toda a área\n * vire alvo de clique sem dupla-ativação do switch.\n */\nexport default function AccessibilityToggleRow({\n label,\n ariaLabel,\n description,\n checked,\n onChange,\n rowTestId,\n switchTestId,\n}: Readonly<AccessibilityToggleRowProps>) {\n const computedAriaLabel =\n ariaLabel ?? (typeof label === 'string' ? label : undefined);\n return (\n <div\n role=\"switch\"\n tabIndex={0}\n aria-checked={checked}\n aria-label={computedAriaLabel}\n data-testid={rowTestId}\n className={cn(\n 'flex items-center justify-between gap-3 rounded-md py-1.5',\n 'cursor-pointer transition-colors duration-150 hover:bg-background-100',\n 'focus:outline-none focus-visible:ring-2 focus-visible:ring-info-500'\n )}\n onClick={onChange}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n onChange();\n }\n }}\n >\n <div className=\"flex flex-col\">\n <Text size=\"sm\" className=\"text-text-900\">\n {label}\n </Text>\n {description && (\n <Text size=\"2xs\" className=\"text-text-600\">\n {description}\n </Text>\n )}\n </div>\n <ToggleSwitch\n checked={checked}\n onChange={() => undefined}\n checkedColor=\"bg-info-600\"\n data-testid={switchTestId}\n tabIndex={-1}\n aria-hidden\n />\n </div>\n );\n}\n","import { useMemo, type ReactElement, type ReactNode } from 'react';\nimport { PauseIcon, PlayIcon, StopIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport Text from '../Text/Text';\nimport Select, {\n SelectTrigger,\n SelectContent,\n SelectItem,\n SelectValue,\n} from '../Select/Select';\nimport AccessibilityToggleRow from './AccessibilityToggleRow';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\nimport { useTTS } from '../../hooks/useTTS';\n\n/**\n * Opções de velocidade da fala. O `preview` mostra o valor numérico\n * dentro do botão (1.0, 1.25, etc.) e o `label` aparece como caption\n * abaixo. Valores escolhidos para casarem com o que o Web Speech API\n * aceita (0.5–2.0) — `0.75` foi mantido para \"Lento\" em vez de algo\n * mais agressivo porque vozes nativas ficam ininteligíveis abaixo disso.\n */\nconst RATE_OPTIONS: {\n value: string;\n label: string;\n preview: ReactNode;\n}[] = [\n { value: '0.75', label: 'Lento', preview: '0.75' },\n { value: '1', label: 'Normal', preview: '1.0' },\n { value: '1.25', label: 'Rápido', preview: '1.25' },\n { value: '2', label: 'Muito rápido', preview: '2.0' },\n];\n\nexport interface TTSSectionProps {\n /** Componente de segmented com preview visual + label abaixo */\n PreviewSegmented: <T extends string | number>(props: {\n value: T;\n options: { value: T; label: string; preview: ReactNode }[];\n ariaLabel: string;\n onChange: (value: T) => void;\n }) => ReactElement;\n}\n\n/**\n * Bloco de controles do leitor de texto dentro do painel.\n * Estrutura segue o design do Figma:\n * 1. \"Texto para voz\" — toggle que ativa/desativa o modo click-to-read\n * 2. \"Voz\" — seletor de voz\n * 3. \"Velocidade\" — PreviewSegmented com valor numérico\n */\nexport default function TTSSection({\n PreviewSegmented,\n}: Readonly<TTSSectionProps>) {\n const ttsMode = useAccessibilityStore((s) => s.ttsMode);\n const ttsStatus = useAccessibilityStore((s) => s.ttsStatus);\n const ttsRate = useAccessibilityStore((s) => s.ttsRate);\n const ttsVoiceId = useAccessibilityStore((s) => s.ttsVoiceId);\n const setTTSMode = useAccessibilityStore((s) => s.setTTSMode);\n const setTTSRate = useAccessibilityStore((s) => s.setTTSRate);\n const setTTSVoiceId = useAccessibilityStore((s) => s.setTTSVoiceId);\n\n const { isSupported, voices, hasPortugueseVoice, pause, resume, stop } =\n useTTS();\n\n // Preferimos vozes em português; demais ficam ao final.\n const sortedVoices = useMemo(() => {\n return [...voices].sort((a, b) => {\n const aPt = a.lang.toLowerCase().startsWith('pt') ? 0 : 1;\n const bPt = b.lang.toLowerCase().startsWith('pt') ? 0 : 1;\n if (aPt !== bPt) return aPt - bPt;\n return a.name.localeCompare(b.name);\n });\n }, [voices]);\n\n if (!isSupported) {\n return (\n <Text size=\"2xs\" className=\"text-text-600\">\n Síntese de voz não está disponível neste navegador.\n </Text>\n );\n }\n\n const isClickToReadOn = ttsMode === 'click-to-read';\n\n return (\n <div className=\"flex flex-col gap-6\">\n {/* Texto para voz: toggle row */}\n <div className=\"flex flex-col gap-2\">\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"uppercase leading-none text-text-600\"\n >\n Texto para voz\n </Text>\n <AccessibilityToggleRow\n label=\"Ative a leitura e clique no texto para ouvir\"\n rowTestId=\"a11y-tts-mode-toggle\"\n checked={isClickToReadOn}\n onChange={() => setTTSMode(isClickToReadOn ? 'off' : 'click-to-read')}\n />\n </div>\n\n {!hasPortugueseVoice && (\n <Text size=\"2xs\" className=\"text-warning-700\">\n Nenhuma voz em português foi encontrada no seu sistema. A leitura será\n feita com a voz padrão disponível.\n </Text>\n )}\n\n {/* Voz: pill select */}\n <div className=\"flex flex-col gap-2\">\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"uppercase leading-none text-text-600\"\n >\n Voz\n </Text>\n <Select\n size=\"small\"\n value={ttsVoiceId ?? ''}\n onValueChange={(value) => setTTSVoiceId(value || null)}\n >\n <SelectTrigger\n variant=\"rounded\"\n aria-label=\"Voz da síntese de fala\"\n data-testid=\"a11y-tts-voice-select\"\n >\n <SelectValue placeholder=\"Padrão do navegador\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"\">Padrão do navegador</SelectItem>\n {sortedVoices.map((v) => (\n <SelectItem key={v.id} value={v.id}>\n {v.name} — {v.lang}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n {/* Velocidade: preview segmented com valor numérico no botão */}\n <div className=\"flex flex-col gap-2\">\n <Text\n size=\"xs\"\n weight=\"medium\"\n className=\"uppercase leading-none text-text-600\"\n >\n Velocidade\n </Text>\n <PreviewSegmented\n ariaLabel=\"Velocidade da fala\"\n value={String(ttsRate)}\n options={RATE_OPTIONS.map((opt) => ({\n value: opt.value,\n label: opt.label,\n preview: (\n <span className=\"text-base font-bold leading-none\">\n {opt.preview}\n </span>\n ),\n }))}\n onChange={(v) => setTTSRate(Number(v))}\n />\n </div>\n\n {/* Ações: pause/resume/stop conforme status — só visíveis durante a fala */}\n {ttsStatus !== 'idle' && (\n <div className=\"flex items-center gap-2\">\n {ttsStatus === 'speaking' && (\n <Button\n variant=\"outline\"\n action=\"secondary\"\n size=\"small\"\n iconLeft={\n <PauseIcon size={14} weight=\"bold\" aria-hidden=\"true\" />\n }\n onClick={pause}\n data-testid=\"a11y-tts-pause\"\n >\n Pausar\n </Button>\n )}\n\n {ttsStatus === 'paused' && (\n <Button\n variant=\"outline\"\n action=\"secondary\"\n size=\"small\"\n iconLeft={<PlayIcon size={14} weight=\"bold\" aria-hidden=\"true\" />}\n onClick={resume}\n data-testid=\"a11y-tts-resume\"\n >\n Retomar\n </Button>\n )}\n\n <Button\n variant=\"outline\"\n action=\"negative\"\n size=\"small\"\n iconLeft={<StopIcon size={14} weight=\"bold\" aria-hidden=\"true\" />}\n onClick={stop}\n data-testid=\"a11y-tts-stop\"\n className=\"ml-auto\"\n >\n Parar\n </Button>\n </div>\n )}\n </div>\n );\n}\n","import { create, StoreApi, useStore } from 'zustand';\nimport {\n ReactNode,\n useEffect,\n useRef,\n useState,\n useCallback,\n ButtonHTMLAttributes,\n forwardRef,\n HTMLAttributes,\n KeyboardEvent,\n MouseEvent,\n ReactElement,\n isValidElement,\n Children,\n cloneElement,\n useId,\n CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { CaretDown, Check, WarningCircle } from 'phosphor-react';\nimport { cn } from '../../utils/utils';\n\nconst VARIANT_CLASSES = {\n outlined: 'border-2 rounded-lg focus:border-primary-950',\n underlined: 'border-b-2 focus:border-primary-950',\n rounded: 'border-2 rounded-full focus:border-primary-950',\n} as const;\n\nconst SIZE_CLASSES = {\n small: 'text-sm',\n medium: 'text-md',\n large: 'text-lg',\n 'extra-large': 'text-lg',\n} as const;\n\nconst HEIGHT_CLASSES = {\n small: 'h-8',\n medium: 'h-9',\n large: 'h-10',\n 'extra-large': 'h-12',\n} as const;\n\nconst PADDING_CLASSES = {\n small: 'px-2 py-1',\n medium: 'px-3 py-2',\n large: 'px-4 py-3',\n 'extra-large': 'px-5 py-4',\n} as const;\n\ninterface TriggerRect {\n top: number;\n left: number;\n width: number;\n height: number;\n}\n\ntype SelectAlign = 'start' | 'center' | 'end';\n\ninterface SelectStore {\n open: boolean;\n setOpen: (open: boolean) => void;\n value: string;\n setValue: (value: string) => void;\n selectedLabel: ReactNode;\n setSelectedLabel: (label: ReactNode) => void;\n onValueChange?: (value: string) => void;\n triggerRect: TriggerRect | null;\n setTriggerRect: (rect: TriggerRect | null) => void;\n}\n\ntype SelectStoreApi = StoreApi<SelectStore>;\n\nexport function createSelectStore(\n onValueChange?: (value: string) => void\n): SelectStoreApi {\n return create<SelectStore>((set) => ({\n open: false,\n setOpen: (open) => set({ open }),\n value: '',\n setValue: (value) => set({ value }),\n selectedLabel: '',\n setSelectedLabel: (label) => set({ selectedLabel: label }),\n onValueChange,\n triggerRect: null,\n setTriggerRect: (rect) => set({ triggerRect: rect }),\n }));\n}\n\nexport const useSelectStore = (externalStore?: SelectStoreApi) => {\n if (!externalStore) {\n throw new Error(\n 'Component must be used within a Select (store is missing)'\n );\n }\n\n return externalStore;\n};\n\nexport function getLabelAsNode(children: ReactNode): ReactNode {\n if (typeof children === 'string' || typeof children === 'number') {\n return children;\n }\n const flattened = Children.toArray(children);\n\n if (flattened.length === 1) return flattened[0];\n\n return <>{flattened}</>;\n}\n\ninterface SelectProps {\n className?: string;\n children: ReactNode;\n defaultValue?: string;\n value?: string;\n onValueChange?: (value: string) => void;\n size?: 'small' | 'medium' | 'large' | 'extra-large';\n label?: string;\n helperText?: string;\n errorMessage?: string;\n id?: string;\n}\n\nconst injectStore = (\n children: ReactNode,\n store: SelectStoreApi,\n size: string,\n selectId: string\n): ReactNode => {\n return Children.map(children, (child) => {\n if (isValidElement(child)) {\n const typedChild = child as ReactElement<{\n store?: SelectStoreApi;\n children?: ReactNode;\n size?: string;\n selectId?: string;\n }>;\n\n const newProps: Partial<{\n store: SelectStoreApi;\n children: ReactNode;\n size: string;\n selectId: string;\n }> = {\n store,\n };\n\n // Pass size to SelectTrigger, selectId to both Trigger and Content\n if (typedChild.type === SelectTrigger) {\n newProps.size = size;\n newProps.selectId = selectId;\n }\n\n if (typedChild.type === SelectContent) {\n newProps.selectId = selectId;\n }\n\n if (typedChild.props.children) {\n newProps.children = injectStore(\n typedChild.props.children,\n store,\n size,\n selectId\n );\n }\n\n return cloneElement(typedChild, newProps);\n }\n return child;\n });\n};\n\nconst Select = ({\n children,\n defaultValue = '',\n className,\n value: propValue,\n onValueChange,\n size = 'small',\n label,\n helperText,\n errorMessage,\n id,\n}: SelectProps) => {\n const storeRef = useRef<SelectStoreApi | null>(null);\n storeRef.current ??= createSelectStore(onValueChange);\n const store = storeRef.current;\n\n const selectRef = useRef<HTMLDivElement>(null);\n const { open, setOpen, setValue, selectedLabel } = useStore(store, (s) => s);\n\n // Generate unique ID if not provided\n const generatedId = useId();\n const selectId = id ?? `select-${generatedId}`;\n\n const findLabelForValue = (\n children: ReactNode,\n targetValue: string\n ): ReactNode | null => {\n let found: ReactNode | null = null;\n const search = (nodes: ReactNode) => {\n Children.forEach(nodes, (child) => {\n if (!isValidElement(child)) return;\n const typedChild = child as ReactElement<{\n value?: string;\n children?: ReactNode;\n }>;\n if (\n typedChild.type === SelectItem &&\n typedChild.props.value === targetValue\n ) {\n // Use getLabelAsNode to handle both string and ReactNode children\n found = getLabelAsNode(typedChild.props.children);\n }\n if (typedChild.props.children && !found)\n search(typedChild.props.children);\n });\n };\n search(children);\n return found;\n };\n\n useEffect(() => {\n if (!selectedLabel && defaultValue) {\n const label = findLabelForValue(children, defaultValue);\n if (label) store.setState({ selectedLabel: label });\n }\n }, [children, defaultValue, selectedLabel]);\n\n useEffect(() => {\n const handleClickOutside = (event: globalThis.MouseEvent) => {\n const target = event.target as Node;\n // Check if click is inside the trigger container\n const isInsideTrigger = selectRef.current?.contains(target);\n // Check if click is inside the portaled content (scoped to this Select instance)\n const portaledMenu = document.body.querySelector(\n `[role=\"menu\"][data-select-id=\"${selectId}\"]`\n );\n const isInsidePortaledMenu = portaledMenu?.contains(target);\n\n if (!isInsideTrigger && !isInsidePortaledMenu) {\n setOpen(false);\n }\n };\n\n const handleArrowKeys = (event: globalThis.KeyboardEvent) => {\n // Find the portaled menu in the body (scoped to this Select instance)\n const selectContent = document.body.querySelector(\n `[role=\"menu\"][data-select-id=\"${selectId}\"]`\n );\n if (selectContent) {\n event.preventDefault();\n const items = Array.from(\n selectContent.querySelectorAll(\n '[role=\"menuitem\"]:not([aria-disabled=\"true\"])'\n )\n ).filter((el): el is HTMLElement => el instanceof HTMLElement);\n\n const focused = document.activeElement as HTMLElement;\n const currentIndex = items.findIndex((item) => item === focused);\n\n let nextIndex: number;\n if (event.key === 'ArrowDown') {\n nextIndex =\n currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;\n } else {\n nextIndex =\n currentIndex === -1\n ? items.length - 1\n : (currentIndex - 1 + items.length) % items.length;\n }\n items[nextIndex]?.focus();\n }\n };\n\n if (open) {\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('keydown', handleArrowKeys);\n }\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n document.removeEventListener('keydown', handleArrowKeys);\n };\n }, [open, selectId, setOpen]);\n\n useEffect(() => {\n // Skip when the consumer isn't using controlled mode\n if (propValue === undefined) return;\n setValue(propValue);\n // Resolve the label for the new value; if it can't be resolved (empty\n // value, or a value with no matching SelectItem), clear the stale label\n // so the placeholder takes over.\n const label = findLabelForValue(children, propValue);\n store.setState({ selectedLabel: label ?? '' });\n }, [propValue, children]);\n\n const sizeClasses = SIZE_CLASSES[size];\n\n return (\n <div className={cn('w-full', className)}>\n {/* Label */}\n {label && (\n <label\n htmlFor={selectId}\n className={cn('block font-bold text-text-900 mb-1.5', sizeClasses)}\n >\n {label}\n </label>\n )}\n\n {/* Select Container */}\n <div className={cn('relative w-full')} ref={selectRef}>\n {injectStore(children, store, size, selectId)}\n </div>\n\n {/* Helper Text or Error Message */}\n {(helperText || errorMessage) && (\n <div className=\"mt-1.5 gap-1.5\">\n {helperText && <p className=\"text-sm text-text-500\">{helperText}</p>}\n {errorMessage && (\n <p className=\"flex gap-1 items-center text-sm text-indicator-error\">\n <WarningCircle size={16} /> {errorMessage}\n </p>\n )}\n </div>\n )}\n </div>\n );\n};\n\nconst SelectValue = ({\n placeholder,\n store: externalStore,\n}: {\n placeholder?: string;\n store?: SelectStoreApi;\n}) => {\n const store = useSelectStore(externalStore);\n\n const selectedLabel = useStore(store, (s) => s.selectedLabel);\n const value = useStore(store, (s) => s.value);\n return (\n <span className=\"text-inherit flex gap-2 items-center\">\n {selectedLabel || placeholder || value}\n </span>\n );\n};\n\ninterface SelectTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n className?: string;\n invalid?: boolean;\n variant?: 'outlined' | 'underlined' | 'rounded';\n store?: SelectStoreApi;\n size?: 'small' | 'medium' | 'large' | 'extra-large';\n selectId?: string;\n}\n\nconst SelectTrigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(\n (\n {\n className,\n invalid = false,\n variant = 'outlined',\n store: externalStore,\n disabled,\n size = 'medium',\n selectId,\n type = 'button',\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const internalRef = useRef<HTMLButtonElement>(null);\n\n const setRefs = useCallback(\n (element: HTMLButtonElement | null) => {\n internalRef.current = element;\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n },\n [ref]\n );\n\n const updateTriggerRect = useCallback(() => {\n if (internalRef.current) {\n const rect = internalRef.current.getBoundingClientRect();\n store.setState({\n triggerRect: {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n },\n });\n }\n }, [store]);\n\n // Update triggerRect on scroll/resize while open\n useEffect(() => {\n if (!open) return;\n\n const handleUpdate = () => {\n updateTriggerRect();\n };\n\n window.addEventListener('scroll', handleUpdate, true);\n window.addEventListener('resize', handleUpdate);\n\n return () => {\n window.removeEventListener('scroll', handleUpdate, true);\n window.removeEventListener('resize', handleUpdate);\n };\n }, [open, updateTriggerRect]);\n\n const toggleOpen = () => {\n const newOpen = !open;\n if (newOpen) {\n updateTriggerRect();\n }\n store.setState({ open: newOpen });\n };\n\n const variantClasses = VARIANT_CLASSES[variant];\n const heightClasses = HEIGHT_CLASSES[size];\n const paddingClasses = PADDING_CLASSES[size];\n\n return (\n <button\n ref={setRefs}\n id={selectId}\n type={type}\n className={cn(\n 'flex w-full items-center justify-between border-border-300',\n heightClasses,\n paddingClasses,\n invalid &&\n `${variant == 'underlined' ? 'border-b-2' : 'border-2'} border-indicator-error text-text-600`,\n disabled\n ? 'cursor-not-allowed text-text-400 pointer-events-none opacity-50'\n : 'cursor-pointer hover:bg-background-50 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground',\n !invalid && !disabled ? 'text-text-700' : '',\n variantClasses,\n className\n )}\n onClick={toggleOpen}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={open ? 'select-content' : undefined}\n {...props}\n >\n {props.children}\n <CaretDown\n className={cn(\n 'h-[1em] w-[1em] opacity-50 transition-transform',\n open ? 'rotate-180' : ''\n )}\n />\n </button>\n );\n }\n);\nSelectTrigger.displayName = 'SelectTrigger';\n\nfunction applyVerticalPosition(\n styles: CSSProperties,\n triggerRect: TriggerRect,\n side: 'top' | 'bottom',\n align: SelectAlign,\n gap: number\n): void {\n styles.top =\n side === 'top'\n ? triggerRect.top - gap\n : triggerRect.top + triggerRect.height + gap;\n styles.transform = side === 'top' ? 'translateY(-100%)' : undefined;\n\n if (align === 'start') {\n styles.left = triggerRect.left;\n } else if (align === 'center') {\n styles.left = triggerRect.left + triggerRect.width / 2;\n styles.transform =\n side === 'top' ? 'translate(-50%, -100%)' : 'translateX(-50%)';\n } else {\n styles.left = triggerRect.left + triggerRect.width;\n styles.transform =\n side === 'top' ? 'translate(-100%, -100%)' : 'translateX(-100%)';\n }\n}\n\nfunction applyHorizontalPosition(\n styles: CSSProperties,\n triggerRect: TriggerRect,\n side: 'left' | 'right',\n align: SelectAlign,\n gap: number\n): void {\n styles.left =\n side === 'left'\n ? triggerRect.left - gap\n : triggerRect.left + triggerRect.width + gap;\n styles.transform = side === 'left' ? 'translateX(-100%)' : undefined;\n\n if (align === 'start') {\n styles.top = triggerRect.top;\n } else if (align === 'center') {\n styles.top = triggerRect.top + triggerRect.height / 2;\n styles.transform =\n side === 'left' ? 'translate(-100%, -50%)' : 'translateY(-50%)';\n } else {\n styles.top = triggerRect.top + triggerRect.height;\n styles.transform =\n side === 'left' ? 'translate(-100%, -100%)' : 'translateY(-100%)';\n }\n}\n\ninterface SelectContentProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n align?: 'start' | 'center' | 'end';\n side?: 'top' | 'right' | 'bottom' | 'left';\n store?: SelectStoreApi;\n selectId?: string;\n}\n\nconst SelectContent = forwardRef<HTMLDivElement, SelectContentProps>(\n (\n {\n children,\n className,\n align = 'start',\n side = 'bottom',\n store: externalStore,\n selectId,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const open = useStore(store, (s) => s.open);\n const triggerRect = useStore(store, (s) => s.triggerRect);\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n if (!open || !mounted) return null;\n\n // Calculate position based on trigger rect\n const getPositionStyles = (): CSSProperties => {\n if (!triggerRect) {\n return {};\n }\n\n const gap = 4;\n const styles: CSSProperties = {\n position: 'fixed',\n zIndex: 9999,\n };\n\n const isVertical = side === 'top' || side === 'bottom';\n\n if (isVertical) {\n applyVerticalPosition(styles, triggerRect, side, align, gap);\n } else {\n applyHorizontalPosition(styles, triggerRect, side, align, gap);\n }\n\n return styles;\n };\n\n const content = (\n <div\n role=\"menu\"\n ref={ref}\n data-select-id={selectId}\n style={getPositionStyles()}\n className={cn(\n 'bg-secondary min-w-[210px] max-h-[300px] overflow-y-auto overflow-x-hidden rounded-md border p-1 shadow-md border-border-100',\n className\n )}\n {...props}\n >\n {children}\n </div>\n );\n\n // Render using portal to escape overflow constraints\n return createPortal(content, document.body);\n }\n);\nSelectContent.displayName = 'SelectContent';\n\ninterface SelectItemProps extends HTMLAttributes<HTMLDivElement> {\n value: string;\n disabled?: boolean;\n store?: SelectStoreApi;\n truncate?: boolean;\n}\n\nconst SelectItem = forwardRef<HTMLDivElement, SelectItemProps>(\n (\n {\n className,\n children,\n value,\n disabled = false,\n store: externalStore,\n truncate = false,\n ...props\n },\n ref\n ) => {\n const store = useSelectStore(externalStore);\n const {\n value: selectedValue,\n setValue,\n setOpen,\n setSelectedLabel,\n onValueChange,\n } = useStore(store, (s) => s);\n\n const handleClick = (\n e: MouseEvent<HTMLDivElement> | KeyboardEvent<HTMLDivElement>\n ) => {\n const labelNode = getLabelAsNode(children);\n if (!disabled) {\n // Always set the clicked value, even if it's already selected\n setValue(value);\n setSelectedLabel(labelNode);\n setOpen(false);\n onValueChange?.(value);\n }\n props.onClick?.(e as MouseEvent<HTMLDivElement>);\n };\n\n return (\n <div\n role=\"menuitem\"\n aria-disabled={disabled}\n ref={ref}\n className={`\n bg-secondary focus-visible:bg-background-50\n relative flex select-none items-center gap-2 rounded-sm p-3 outline-none transition-colors [&>svg]:size-4 [&>svg]:shrink-0\n ${className}\n ${\n disabled\n ? 'cursor-not-allowed text-text-400 pointer-events-none opacity-50'\n : 'cursor-pointer hover:bg-background-50 text-text-700 focus:bg-accent focus:text-accent-foreground hover:bg-accent hover:text-accent-foreground'\n }\n ${selectedValue === value && 'bg-background-50'}\n `}\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') handleClick(e);\n }}\n tabIndex={disabled ? -1 : 0}\n {...props}\n >\n <span className=\"absolute right-2 flex h-3.5 w-3.5 items-center justify-center\">\n {selectedValue === value && <Check className=\"\" />}\n </span>\n {truncate ? (\n <span className=\"truncate block max-w-[200px]\">{children}</span>\n ) : (\n children\n )}\n </div>\n );\n }\n);\n\nSelectItem.displayName = 'SelectItem';\n\nexport default Select;\nexport { SelectTrigger, SelectContent, SelectItem, SelectValue };\n","import { create } from 'zustand';\nimport { devtools, persist } from 'zustand/middleware';\n\n/**\n * Modos de contraste suportados pelo widget de acessibilidade\n */\nexport type ContrastMode = 'normal' | 'high' | 'inverted';\n\n/**\n * Modos de saturação para usuários com sensibilidade visual\n */\nexport type SaturationMode = 'normal' | 'grayscale' | 'low';\n\n/**\n * Auxiliares de leitura mutuamente exclusivos:\n * - `none`: nenhum auxiliar\n * - `ruler`: linha horizontal acompanha o cursor\n * - `mask`: escurece a tela exceto uma faixa central na altura do cursor\n */\nexport type ReadingAid = 'none' | 'ruler' | 'mask';\n\n/**\n * Modo do leitor de texto (TTS):\n * - `off`: leitor desligado\n * - `click-to-read`: usuário clica em um elemento e o widget lê o texto dele\n * - `read-selection`: usuário pressiona \"Ler seleção\" para falar o texto selecionado\n */\nexport type TTSMode = 'off' | 'click-to-read' | 'read-selection';\n\n/** Status runtime da síntese de voz — não persistido. */\nexport type TTSStatus = 'idle' | 'speaking' | 'paused';\n\n/**\n * Modos de daltonismo. Aplicam matrizes `<feColorMatrix>` (SVG) que\n * remapeiam cores para ajudar usuários com cada tipo de discromatopsia.\n *\n * Enum (e não union de strings como os outros modos) porque cada valor\n * é também usado como identificador estável em três outros lugares —\n * classes CSS, IDs de filter SVG e seletores no CSS — então centralizar\n * em um enum + helpers evita strings duplicadas e typos silenciosos.\n */\nexport enum ColorBlindMode {\n None = 'none',\n Protanopia = 'protanopia',\n Deuteranopia = 'deuteranopia',\n Tritanopia = 'tritanopia',\n}\n\n/** Prefixo único para todos os identificadores de daltonismo (classe + SVG id) */\nconst COLOR_BLIND_PREFIX = 'a11y-cb-';\n\n/**\n * Retorna a classe CSS aplicada no `<html>` para o modo informado, ou\n * `null` quando nenhum modo está ativo.\n */\nexport const getColorBlindClass = (mode: ColorBlindMode): string | null =>\n mode === ColorBlindMode.None ? null : `${COLOR_BLIND_PREFIX}${mode}`;\n\n/** Retorna o id do `<filter>` SVG correspondente ao modo. */\nexport const getColorBlindFilterId = (\n mode: Exclude<ColorBlindMode, ColorBlindMode.None>\n): string => `${COLOR_BLIND_PREFIX}${mode}`;\n\n/**\n * Níveis discretos para tamanho de fonte (0 = padrão da página)\n */\nexport type FontSizeLevel = 0 | 1 | 2 | 3;\n\n/**\n * Níveis discretos para espaçamento entre letras e linhas\n */\nexport type SpacingLevel = 0 | 1 | 2 | 3;\n\n/**\n * Conjunto completo de preferências persistidas\n */\nexport interface AccessibilityPreferences {\n contrastMode: ContrastMode;\n saturationMode: SaturationMode;\n fontSize: FontSizeLevel;\n letterSpacing: SpacingLevel;\n lineSpacing: SpacingLevel;\n highlightLinks: boolean;\n pauseAnimations: boolean;\n bigCursor: boolean;\n /** Fonte amigável para dislexia (OpenDyslexic com fallback Comic Sans MS) */\n dyslexiaFont: boolean;\n /** Auxiliar visual de leitura (régua, máscara ou nenhum) */\n readingAid: ReadingAid;\n /** Habilita atalho Alt+A para abrir/fechar o painel */\n keyboardShortcut: boolean;\n /** Modo de daltonismo aplicado (filtro SVG na página inteira) */\n colorBlindMode: ColorBlindMode;\n /** Modo do leitor de texto (TTS) */\n ttsMode: TTSMode;\n /** Velocidade da fala (0.5 a 2.0) */\n ttsRate: number;\n /** Voz selecionada pelo usuário (id retornado pelo provider) ou null para padrão */\n ttsVoiceId: string | null;\n /** Habilita o widget VLibras (gov.br) — carregado lazy ao ativar */\n librasEnabled: boolean;\n}\n\nexport interface AccessibilityState extends AccessibilityPreferences {\n /** Indica se o painel de acessibilidade está aberto */\n isPanelOpen: boolean;\n /** Status runtime da síntese de voz — não persistido */\n ttsStatus: TTSStatus;\n}\n\nexport interface AccessibilityActions {\n setContrastMode: (mode: ContrastMode) => void;\n setSaturationMode: (mode: SaturationMode) => void;\n setFontSize: (level: FontSizeLevel) => void;\n setLetterSpacing: (level: SpacingLevel) => void;\n setLineSpacing: (level: SpacingLevel) => void;\n setHighlightLinks: (value: boolean) => void;\n setPauseAnimations: (value: boolean) => void;\n setBigCursor: (value: boolean) => void;\n setDyslexiaFont: (value: boolean) => void;\n setReadingAid: (mode: ReadingAid) => void;\n setKeyboardShortcut: (value: boolean) => void;\n setColorBlindMode: (mode: ColorBlindMode) => void;\n setTTSMode: (mode: TTSMode) => void;\n setTTSRate: (rate: number) => void;\n setTTSVoiceId: (id: string | null) => void;\n setTTSStatus: (status: TTSStatus) => void;\n setLibrasEnabled: (value: boolean) => void;\n /** Restaura todas as preferências para o estado padrão */\n resetPreferences: () => void;\n openPanel: () => void;\n closePanel: () => void;\n togglePanel: () => void;\n}\n\nexport type AccessibilityStore = AccessibilityState & AccessibilityActions;\n\nexport const DEFAULT_ACCESSIBILITY_PREFERENCES: AccessibilityPreferences = {\n contrastMode: 'normal',\n saturationMode: 'normal',\n fontSize: 0,\n letterSpacing: 0,\n lineSpacing: 0,\n highlightLinks: false,\n pauseAnimations: false,\n bigCursor: false,\n dyslexiaFont: false,\n readingAid: 'none',\n keyboardShortcut: true,\n colorBlindMode: ColorBlindMode.None,\n ttsMode: 'off',\n ttsRate: 1,\n ttsVoiceId: null,\n librasEnabled: false,\n};\n\n/**\n * Store Zustand com persistência em localStorage para preferências\n * de acessibilidade. As preferências são aplicadas ao DOM pelo hook\n * `useA11yPreferences` — este store apenas guarda o estado.\n */\nexport const useAccessibilityStore = create<AccessibilityStore>()(\n devtools(\n persist(\n (set) => ({\n ...DEFAULT_ACCESSIBILITY_PREFERENCES,\n isPanelOpen: false,\n ttsStatus: 'idle',\n\n setContrastMode: (contrastMode) => set({ contrastMode }),\n setSaturationMode: (saturationMode) => set({ saturationMode }),\n setFontSize: (fontSize) => set({ fontSize }),\n setLetterSpacing: (letterSpacing) => set({ letterSpacing }),\n setLineSpacing: (lineSpacing) => set({ lineSpacing }),\n setHighlightLinks: (highlightLinks) => set({ highlightLinks }),\n setPauseAnimations: (pauseAnimations) => set({ pauseAnimations }),\n setBigCursor: (bigCursor) => set({ bigCursor }),\n setDyslexiaFont: (dyslexiaFont) => set({ dyslexiaFont }),\n setReadingAid: (readingAid) => set({ readingAid }),\n setKeyboardShortcut: (keyboardShortcut) => set({ keyboardShortcut }),\n setColorBlindMode: (colorBlindMode) => set({ colorBlindMode }),\n setTTSMode: (ttsMode) => set({ ttsMode }),\n setTTSRate: (ttsRate) => set({ ttsRate }),\n setTTSVoiceId: (ttsVoiceId) => set({ ttsVoiceId }),\n setTTSStatus: (ttsStatus) => set({ ttsStatus }),\n setLibrasEnabled: (librasEnabled) => set({ librasEnabled }),\n\n resetPreferences: () => set({ ...DEFAULT_ACCESSIBILITY_PREFERENCES }),\n\n openPanel: () => set({ isPanelOpen: true }),\n closePanel: () => set({ isPanelOpen: false }),\n togglePanel: () =>\n set((state) => ({ isPanelOpen: !state.isPanelOpen })),\n }),\n {\n name: 'accessibility-store',\n // version 1: removeu `librasEnabled` da persistência (era custoso\n // recarregar VLibras a cada refresh). A migração limpa esse valor\n // de localStorages antigos para evitar auto-load surpresa.\n version: 1,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- migrate works with arbitrary persisted shapes from older versions\n migrate: (persistedState: any) => {\n if (\n persistedState &&\n typeof persistedState === 'object' &&\n 'librasEnabled' in persistedState\n ) {\n delete persistedState.librasEnabled;\n }\n return persistedState;\n },\n partialize: (state) => ({\n contrastMode: state.contrastMode,\n saturationMode: state.saturationMode,\n fontSize: state.fontSize,\n letterSpacing: state.letterSpacing,\n lineSpacing: state.lineSpacing,\n highlightLinks: state.highlightLinks,\n pauseAnimations: state.pauseAnimations,\n bigCursor: state.bigCursor,\n dyslexiaFont: state.dyslexiaFont,\n readingAid: state.readingAid,\n keyboardShortcut: state.keyboardShortcut,\n colorBlindMode: state.colorBlindMode,\n ttsRate: state.ttsRate,\n ttsVoiceId: state.ttsVoiceId,\n // ttsMode e librasEnabled NÃO são persistidos:\n // - ttsMode: o leitor sempre começa desligado ao recarregar\n // a página para evitar comportamento confuso\n // - librasEnabled: o VLibras é pesado (~MBs de assets); auto-\n // carregar a cada refresh seria custoso e surpreendente. O\n // usuário ativa explicitamente quando precisa.\n }),\n }\n ),\n { name: 'accessibility-store' }\n )\n);\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useAccessibilityStore } from '../store/accessibilityStore';\nimport { WebSpeechProvider } from '../components/AccessibilityWidget/tts/WebSpeechProvider';\nimport type {\n TTSProvider,\n TTSVoice,\n} from '../components/AccessibilityWidget/tts/types';\n\nexport interface UseTTSReturn {\n /** Indica se a síntese de voz está disponível neste navegador */\n isSupported: boolean;\n /** Vozes carregadas do provider (resolvidas após o primeiro mount) */\n voices: TTSVoice[];\n /**\n * Indica se há ao menos uma voz pt-BR/pt disponível. Útil para\n * mostrar aviso quando o usuário do SO não tem vozes em português.\n */\n hasPortugueseVoice: boolean;\n /** Lê o texto informado, respeitando rate/voiceId do store. */\n speak: (text: string) => void;\n pause: () => void;\n resume: () => void;\n stop: () => void;\n}\n\n/**\n * Hook que liga o `TTSProvider` (default: `WebSpeechProvider`) ao store\n * de acessibilidade — propaga eventos do provider para `ttsStatus` e\n * fornece comandos `speak/pause/resume/stop` que já aplicam as\n * preferências persistidas (`ttsRate`, `ttsVoiceId`).\n *\n * Aceita um provider custom para casos de TTS via backend (Azure,\n * Google etc.) sem alterar a UI.\n */\nexport const useTTS = (providerOverride?: TTSProvider | null): UseTTSReturn => {\n const setTTSStatus = useAccessibilityStore((s) => s.setTTSStatus);\n const ttsRate = useAccessibilityStore((s) => s.ttsRate);\n const ttsVoiceId = useAccessibilityStore((s) => s.ttsVoiceId);\n\n // Garante uma única instância do provider durante o ciclo de vida do hook.\n const provider = useMemo<TTSProvider>(\n () => providerOverride ?? new WebSpeechProvider(),\n [providerOverride]\n );\n\n const [voices, setVoices] = useState<TTSVoice[]>([]);\n\n // Mantemos as preferências em refs para que callbacks de eventos\n // (onstart/onend) leiam sempre o valor mais recente sem precisar\n // recriar o handler — evita resetar a fala quando o usuário ajusta\n // a velocidade no painel.\n const rateRef = useRef(ttsRate);\n const voiceIdRef = useRef(ttsVoiceId);\n rateRef.current = ttsRate;\n voiceIdRef.current = ttsVoiceId;\n\n /**\n * Marca se ESTA instância de `useTTS` foi quem iniciou a fala atual.\n *\n * Necessário porque vários componentes podem chamar `useTTS()`\n * independentemente (TTSController, TTSSection) e cada um cria seu\n * próprio `WebSpeechProvider`, mas todos compartilham o mesmo\n * `window.speechSynthesis` global. O `cancel()` é global — então,\n * sem essa guarda, o cleanup de uma instância (ex.: TTSSection\n * desmontando ao fechar o painel) cancelaria fala iniciada por outra\n * (ex.: click-to-read em andamento no TTSController).\n */\n const isActiveSpeakerRef = useRef(false);\n\n useEffect(() => {\n let mounted = true;\n if (provider.isSupported()) {\n provider\n .getVoices()\n .then((list) => {\n if (mounted) setVoices(list);\n })\n .catch(() => undefined);\n }\n return () => {\n mounted = false;\n // Só interrompe a síntese se foi essa instância que a iniciou.\n // Caso contrário, deixa o synth global em paz — outras instâncias\n // podem estar usando.\n if (isActiveSpeakerRef.current) {\n provider.stop();\n setTTSStatus('idle');\n isActiveSpeakerRef.current = false;\n }\n };\n }, [provider, setTTSStatus]);\n\n const hasPortugueseVoice = voices.some((v) =>\n v.lang.toLowerCase().startsWith('pt')\n );\n\n /**\n * IMPORTANTE: as funções abaixo são memoizadas com `useCallback` porque\n * são frequentemente passadas para `useEffect` deps em consumidores\n * (TTSController, etc.). Sem memoização, as referências mudam a cada\n * render e disparam cleanups indesejados — incluindo o `stop()` que\n * cancela a fala recém-iniciada (sintoma: ícone de áudio acende na aba\n * do browser mas você não ouve nada).\n */\n const speak = useCallback(\n (text: string) => {\n isActiveSpeakerRef.current = true;\n provider.speak(\n text,\n {\n rate: rateRef.current,\n voiceId: voiceIdRef.current ?? undefined,\n // Não fixamos `lang: 'pt-BR'` aqui — em sistemas sem voz pt-BR\n // instalada, o browser fica tentando achar uma e nunca emite\n // áudio. O provider usa o `voice.lang` quando há voz escolhida.\n },\n {\n onStart: () => setTTSStatus('speaking'),\n onEnd: () => {\n isActiveSpeakerRef.current = false;\n setTTSStatus('idle');\n },\n onPause: () => setTTSStatus('paused'),\n onResume: () => setTTSStatus('speaking'),\n onError: () => {\n isActiveSpeakerRef.current = false;\n setTTSStatus('idle');\n },\n }\n );\n },\n [provider, setTTSStatus]\n );\n\n const pause = useCallback(() => {\n provider.pause();\n setTTSStatus('paused');\n }, [provider, setTTSStatus]);\n\n const resume = useCallback(() => {\n provider.resume();\n setTTSStatus('speaking');\n }, [provider, setTTSStatus]);\n\n const stop = useCallback(() => {\n isActiveSpeakerRef.current = false;\n provider.stop();\n setTTSStatus('idle');\n }, [provider, setTTSStatus]);\n\n return {\n isSupported: provider.isSupported(),\n voices,\n hasPortugueseVoice,\n speak,\n pause,\n resume,\n stop,\n };\n};\n","/* eslint-disable no-undef -- DOM lib types (SpeechSynthesis, SpeechSynthesisVoice)\n são reconhecidas pelo TypeScript mas não pelo `globals.browser` do ESLint. */\nimport type {\n TTSProvider,\n TTSProviderEvents,\n TTSSpeakOptions,\n TTSVoice,\n} from './types';\n\n/**\n * Provider default usando a Web Speech API (`window.speechSynthesis`).\n *\n * Notas de compatibilidade:\n * - Disponível em Chrome, Edge, Firefox, Safari e iOS, mas a qualidade\n * das vozes varia muito por SO.\n * - Em Safari/iOS, `getVoices()` é assíncrono — voltamos vazio na\n * primeira chamada e populamos no evento `voiceschanged`. O método\n * `getVoices()` deste provider já trata isso retornando uma `Promise`.\n * - Em alguns SOs não há voz pt-BR pré-instalada — chamadores devem\n * exibir um aviso quando `getVoices()` não retornar voz nesse idioma.\n */\nexport class WebSpeechProvider implements TTSProvider {\n private readonly synth: SpeechSynthesis | null;\n private currentUtterance: SpeechSynthesisUtterance | null = null;\n\n constructor(synth: SpeechSynthesis | null = getSynth()) {\n this.synth = synth;\n }\n\n isSupported(): boolean {\n return this.synth !== null;\n }\n\n async getVoices(): Promise<TTSVoice[]> {\n if (!this.synth) return [];\n\n const synth = this.synth;\n const collect = (): TTSVoice[] =>\n synth.getVoices().map((v) => ({\n id: v.voiceURI,\n name: v.name,\n lang: v.lang,\n isLocal: v.localService,\n }));\n\n const initial = collect();\n if (initial.length > 0) return initial;\n\n // Safari/iOS: vozes carregam de forma assíncrona via 'voiceschanged'.\n return new Promise<TTSVoice[]>((resolve) => {\n const onChange = () => {\n synth.removeEventListener('voiceschanged', onChange);\n resolve(collect());\n };\n synth.addEventListener('voiceschanged', onChange);\n // Fallback: timeout para ambientes que nunca disparam o evento.\n setTimeout(() => {\n synth.removeEventListener('voiceschanged', onChange);\n resolve(collect());\n }, 1500);\n });\n }\n\n speak(\n text: string,\n options: TTSSpeakOptions = {},\n events: TTSProviderEvents = {}\n ): void {\n if (!this.synth) {\n events.onError?.('Síntese de voz não disponível neste navegador.');\n return;\n }\n const trimmed = text.trim();\n if (!trimmed) return;\n\n // Cancela qualquer fala em andamento (evita fila acumulando).\n this.synth.cancel();\n\n const utterance = new SpeechSynthesisUtterance(trimmed);\n utterance.rate = options.rate ?? 1;\n utterance.pitch = options.pitch ?? 1;\n\n // Resolução de voz/idioma:\n // 1. Se o usuário escolheu uma voz, usa ela e o `lang` dela\n // (para evitar mismatch — voz en-US com utterance.lang='pt-BR'\n // causa silêncio em alguns browsers).\n // 2. Se não escolheu mas pediu um lang específico via options,\n // procuramos uma voz que cubra esse lang. Se não houver,\n // deixamos `lang` em branco para o browser usar a voz default.\n const allVoices = this.synth.getVoices();\n let chosenVoice: SpeechSynthesisVoice | undefined;\n if (options.voiceId) {\n chosenVoice = allVoices.find((v) => v.voiceURI === options.voiceId);\n } else if (options.lang) {\n chosenVoice = allVoices.find((v) =>\n v.lang.toLowerCase().startsWith(options.lang!.toLowerCase())\n );\n }\n if (chosenVoice) {\n utterance.voice = chosenVoice;\n utterance.lang = chosenVoice.lang;\n }\n\n utterance.onstart = () => events.onStart?.();\n utterance.onend = () => {\n this.currentUtterance = null;\n events.onEnd?.();\n };\n utterance.onpause = () => events.onPause?.();\n utterance.onresume = () => events.onResume?.();\n utterance.onerror = (event) => {\n this.currentUtterance = null;\n events.onError?.(event.error || 'Erro desconhecido na síntese.');\n };\n\n this.currentUtterance = utterance;\n this.synth.speak(utterance);\n }\n\n pause(): void {\n this.synth?.pause();\n }\n\n resume(): void {\n this.synth?.resume();\n }\n\n stop(): void {\n this.synth?.cancel();\n this.currentUtterance = null;\n }\n}\n\nconst getSynth = (): SpeechSynthesis | null => {\n if (typeof globalThis === 'undefined') return null;\n const synth = (globalThis as unknown as { speechSynthesis?: SpeechSynthesis })\n .speechSynthesis;\n return synth ?? null;\n};\n","import { HandWavingIcon } from '@phosphor-icons/react';\nimport Button from '../Button/Button';\nimport { Tooltip } from '../Tooltip/Tooltip';\nimport { cn } from '../../utils/utils';\nimport {\n FAB_POSITION_CLASSES,\n FAB_TOOLTIP_POSITION,\n FAB_VERTICAL_ALIGN_CLASSES,\n type FabPosition,\n type FabVerticalAlign,\n} from './fabPositioning';\n\ntype AccessibilityFabPosition = FabPosition;\ntype AccessibilityFabVerticalAlign = FabVerticalAlign;\n\nexport interface LibrasFabProps {\n /** Click handler — alterna o painel do VLibras (abrir/fechar) */\n onClick: () => void;\n /** Lado da viewport onde o botão fica colado */\n position?: AccessibilityFabPosition;\n /** Alinhamento vertical (default: `below-center`) */\n verticalAlign?: AccessibilityFabVerticalAlign;\n /** Classes extras */\n className?: string;\n}\n\n/**\n * Segundo botão flutuante (FAB), posicionado abaixo do `AccessibilityFab`.\n * Aciona o widget oficial do VLibras (gov.br) que faz tradução automática\n * de português para Libras via avatar 3D.\n *\n * O botão tem visual constante — não mantemos um indicador de estado\n * \"ativo\" porque o painel do VLibras já reflete visualmente se está\n * aberto ou fechado. Cada clique alterna o painel.\n *\n * Mesma identidade visual do FAB principal (azul escuro, quadrado com\n * canto interno arredondado, colado na lateral).\n */\nexport default function LibrasFab({\n onClick,\n position = 'right',\n verticalAlign = 'below-center',\n className,\n}: Readonly<LibrasFabProps>) {\n const label = 'Tradução em Libras';\n\n return (\n <Tooltip\n content={label}\n position={FAB_TOOLTIP_POSITION[position]}\n className={cn(\n 'fixed z-40',\n FAB_VERTICAL_ALIGN_CLASSES[verticalAlign],\n FAB_POSITION_CLASSES[position]\n )}\n >\n <Button\n variant=\"raw\"\n onClick={onClick}\n aria-label={label}\n data-testid=\"libras-fab\"\n className={cn(\n 'a11y-widget-shield',\n FAB_POSITION_CLASSES[position],\n 'flex h-10 w-10 cursor-pointer items-center justify-center',\n // `text-text-50` flipa junto com `bg-info-900`: claro no light,\n // escuro no dark. Como o ícone usa `currentColor`, com `text-white`\n // a mãozinha sumiria no tema escuro (branco em fundo azul-claro).\n 'bg-info-900 text-text-50 shadow-lg',\n 'transition-all duration-200 hover:scale-110 hover:bg-info-800',\n 'focus:outline-none focus:ring-4 focus:ring-info-300',\n className\n )}\n >\n <HandWavingIcon size={25} weight=\"fill\" aria-hidden=\"true\" />\n </Button>\n </Tooltip>\n );\n}\n","import { useEffect, useState } from 'react';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\n\nconst RULER_HEIGHT_PX = 32;\nconst MASK_GAP_PX = 80; // metade da altura da janela visível\n\n/**\n * Renderiza auxiliares de leitura (régua ou máscara) que acompanham\n * a posição vertical do mouse. Só monta listener de `mousemove` quando\n * algum auxiliar está ativo, para não pagar custo quando desligado.\n */\nexport default function ReadingAid() {\n const readingAid = useAccessibilityStore((s) => s.readingAid);\n const [mouseY, setMouseY] = useState<number | null>(null);\n\n useEffect(() => {\n if (readingAid === 'none') {\n setMouseY(null);\n return;\n }\n\n let frame = 0;\n const handleMouseMove = (event: MouseEvent) => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => setMouseY(event.clientY));\n };\n\n document.addEventListener('mousemove', handleMouseMove, { passive: true });\n return () => {\n cancelAnimationFrame(frame);\n document.removeEventListener('mousemove', handleMouseMove);\n };\n }, [readingAid]);\n\n if (readingAid === 'none' || mouseY === null) return null;\n\n if (readingAid === 'ruler') {\n return (\n <div\n aria-hidden=\"true\"\n data-testid=\"a11y-reading-ruler\"\n className=\"a11y-widget-shield pointer-events-none fixed left-0 right-0 z-30\"\n style={{\n top: mouseY - RULER_HEIGHT_PX / 2,\n height: RULER_HEIGHT_PX,\n background: 'rgba(255, 235, 59, 0.25)',\n borderTop: '2px solid #facc15',\n borderBottom: '2px solid #facc15',\n }}\n />\n );\n }\n\n // Máscara: dois retângulos escurecendo acima e abaixo do cursor\n return (\n <>\n <div\n aria-hidden=\"true\"\n data-testid=\"a11y-reading-mask-top\"\n className=\"a11y-widget-shield pointer-events-none fixed left-0 right-0 top-0 z-30 bg-black/90\"\n style={{ height: Math.max(0, mouseY - MASK_GAP_PX) }}\n />\n <div\n aria-hidden=\"true\"\n data-testid=\"a11y-reading-mask-bottom\"\n className=\"a11y-widget-shield pointer-events-none fixed left-0 right-0 bottom-0 z-30 bg-black/90\"\n style={{ top: mouseY + MASK_GAP_PX }}\n />\n </>\n );\n}\n","import {\n ColorBlindMode,\n getColorBlindFilterId,\n} from '../../store/accessibilityStore';\n\n/**\n * SVG escondido com matrizes `<feColorMatrix>` que simulam/auxiliam\n * cada tipo de daltonismo. As classes em `accessibility.css` aplicam\n * `filter: url(#a11y-cb-...)` no `<html>` para colorir a página.\n *\n * Os IDs dos filters são derivados do enum `ColorBlindMode` via\n * `getColorBlindFilterId` — mesma fonte da verdade que o hook usa\n * para aplicar a classe.\n *\n * Matrizes baseadas em Brettel/Vienot/Mollon — padrão amplamente\n * utilizado em ferramentas de acessibilidade como AXE, Daltonize e\n * widget oficial do HandTalk.\n */\nexport default function ColorBlindFilters() {\n return (\n <svg\n aria-hidden=\"true\"\n data-testid=\"a11y-colorblind-filters\"\n style={{\n position: 'absolute',\n width: 0,\n height: 0,\n overflow: 'hidden',\n }}\n >\n <defs>\n <filter id={getColorBlindFilterId(ColorBlindMode.Protanopia)}>\n <feColorMatrix\n type=\"matrix\"\n values=\"0.567 0.433 0 0 0\n 0.558 0.442 0 0 0\n 0 0.242 0.758 0 0\n 0 0 0 1 0\"\n />\n </filter>\n <filter id={getColorBlindFilterId(ColorBlindMode.Deuteranopia)}>\n <feColorMatrix\n type=\"matrix\"\n values=\"0.625 0.375 0 0 0\n 0.700 0.300 0 0 0\n 0 0.300 0.7 0 0\n 0 0 0 1 0\"\n />\n </filter>\n <filter id={getColorBlindFilterId(ColorBlindMode.Tritanopia)}>\n <feColorMatrix\n type=\"matrix\"\n values=\"0.95 0.05 0 0 0\n 0 0.433 0.567 0 0\n 0 0.475 0.525 0 0\n 0 0 0 1 0\"\n />\n </filter>\n </defs>\n </svg>\n );\n}\n","import { useEffect } from 'react';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\nimport { useTTS } from '../../hooks/useTTS';\n\nconst HIGHLIGHT_CLASS = 'a11y-tts-target';\n\n/**\n * Controller invisível que conecta o `ttsMode` do store ao DOM:\n * - `click-to-read`: registra um listener global de clique. Ao clicar\n * em qualquer elemento (exceto o próprio widget), lê seu texto.\n * - `read-selection`: lê o texto atualmente selecionado quando o store\n * sinaliza (via `pendingReadSelection`). UI/atalho disparam isso.\n *\n * Aplica a classe `.a11y-tts-target` no elemento sendo lido para\n * destaque visual; remove ao terminar/parar.\n */\nexport default function TTSController() {\n const ttsMode = useAccessibilityStore((s) => s.ttsMode);\n const ttsStatus = useAccessibilityStore((s) => s.ttsStatus);\n const { speak, stop, isSupported } = useTTS();\n\n // Click-to-read: ouve cliques globais enquanto o modo está ativo\n useEffect(() => {\n if (!isSupported || ttsMode !== 'click-to-read') return;\n\n const handler = (event: MouseEvent) => {\n const initialTarget = event.target as HTMLElement | null;\n if (!initialTarget) return;\n\n // Ignora cliques dentro do próprio widget\n if (initialTarget.closest('.a11y-widget-shield')) return;\n\n // Sobe pela árvore até achar o ancestral que tem texto legível.\n // Cobre o caso comum de IconButtons com `<svg>` interno: clicar no\n // SVG não retorna texto, mas o botão pai tem aria-label.\n const found = findReadableAncestor(initialTarget);\n if (!found) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n // Limpa highlight anterior e marca o novo alvo\n document\n .querySelectorAll(`.${HIGHLIGHT_CLASS}`)\n .forEach((el) => el.classList.remove(HIGHLIGHT_CLASS));\n found.node.classList.add(HIGHLIGHT_CLASS);\n\n speak(found.text);\n };\n\n // capture: true → pegamos o clique antes do handler do app/link\n document.addEventListener('click', handler, true);\n return () => {\n document.removeEventListener('click', handler, true);\n document\n .querySelectorAll(`.${HIGHLIGHT_CLASS}`)\n .forEach((el) => el.classList.remove(HIGHLIGHT_CLASS));\n // Interrompe qualquer fala em curso ao sair do modo. Sem isso,\n // desligar o \"Clique para ler\" deixaria a leitura terminando\n // sozinha, contrariando a expectativa do usuário.\n stop();\n };\n }, [isSupported, ttsMode, speak, stop]);\n\n // Quando a fala termina (ttsStatus volta a 'idle'), limpa highlight\n useEffect(() => {\n if (ttsStatus === 'idle') {\n document\n .querySelectorAll(`.${HIGHLIGHT_CLASS}`)\n .forEach((el) => el.classList.remove(HIGHLIGHT_CLASS));\n }\n }, [ttsStatus]);\n\n // Para qualquer fala em curso quando o componente desmonta\n useEffect(() => () => stop(), [stop]);\n\n return null;\n}\n\n/**\n * Extrai texto legível do elemento clicado. Prioriza:\n * 1. `aria-label` se presente\n * 2. `title`\n * 3. `innerText` truncado\n *\n * `innerText` (e não `textContent`) respeita estilos como `display: none`\n * e quebras de linha visuais — leitura mais natural.\n */\nconst extractReadableText = (el: HTMLElement): string => {\n const aria = el.getAttribute('aria-label')?.trim();\n if (aria) return aria;\n const title = el.getAttribute('title')?.trim();\n if (title) return title;\n const text = el.innerText?.trim() ?? '';\n // Limita comprimento para evitar leitura infinita ao clicar em\n // contêineres grandes (ex.: <main>). 600 chars ≈ 1min de fala.\n return text.length > 600 ? `${text.slice(0, 600)}...` : text;\n};\n\n/** Quantos níveis de ancestralidade subimos buscando um nó legível. */\nconst MAX_ANCESTOR_LOOKUP = 8;\n\n/**\n * Caminha pela árvore a partir do elemento clicado até achar o ancestral\n * mais próximo que tenha texto legível (`aria-label`, `title` ou\n * `innerText`). Limita a profundidade e nunca retorna o `<body>` para\n * evitar ler a página inteira ao clicar em ícones soltos.\n */\nconst findReadableAncestor = (\n start: HTMLElement\n): { node: HTMLElement; text: string } | null => {\n let node: HTMLElement | null = start;\n let depth = 0;\n while (node && depth < MAX_ANCESTOR_LOOKUP && node !== document.body) {\n const text = extractReadableText(node);\n if (text) return { node, text };\n node = node.parentElement;\n depth++;\n }\n return null;\n};\n","import { useEffect } from 'react';\nimport { useAccessibilityStore } from '../../store/accessibilityStore';\n\nconst SCRIPT_ID = 'a11y-vlibras-script';\nconst SCRIPT_URL = 'https://vlibras.gov.br/app/vlibras-plugin.js';\nconst APP_URL = 'https://vlibras.gov.br/app';\nconst WRAPPER_ID = 'a11y-vlibras-wrapper';\n\ninterface VLibrasGlobal {\n Widget: new (appUrl: string) => unknown;\n}\n\n/**\n * Carrega e ativa o widget oficial de Libras do governo brasileiro\n * (gov.br/vlibras) sob demanda. Estratégia:\n *\n * - Quando `librasEnabled === true`:\n * 1. Injeta o esqueleto DOM esperado pelo VLibras (`<div vw>` etc.)\n * no `<body>` (precisa estar no body, não dentro do React tree).\n * 2. Carrega o script oficial uma única vez (cacheado por id).\n * 3. Instancia `new window.VLibras.Widget(...)` para inicializar o\n * avatar e os assets 3D.\n *\n * - Quando `librasEnabled === false`:\n * - Remove o esqueleto DOM. O script permanece em cache (já carregado)\n * para reativações rápidas.\n *\n * O script é pesado (~MBs de avatares 3D), por isso só carregamos quando\n * o usuário clica para ativar — fluxo opt-in.\n */\nexport default function VLibrasLoader() {\n const enabled = useAccessibilityStore((s) => s.librasEnabled);\n\n useEffect(() => {\n if (typeof document === 'undefined') return;\n\n if (!enabled) {\n removeWrapper();\n return;\n }\n\n injectWrapper();\n loadScript()\n .then(() => {\n const VLibras = (globalThis as unknown as { VLibras?: VLibrasGlobal })\n .VLibras;\n if (!VLibras?.Widget) return;\n\n // ATENÇÃO: o construtor do VLibras Widget registra a lógica de\n // inicialização DENTRO de `window.onload = () => {...}`. Como\n // nesta integração o widget é ativado depois da página já ter\n // carregado, o evento `load` já disparou e o handler nunca\n // executa sozinho. Capturamos o handler que o construtor\n // instalou e chamamos manualmente para popular o DOM (avatar,\n // botão de acesso, painel) imediatamente.\n const previousOnload = globalThis.onload;\n // A instanciação importa pelos efeitos colaterais: o construtor\n // do VLibras registra `window.onload` com a lógica de inicialização\n // do widget. O retorno não é usado e é descartado de propósito.\n new VLibras.Widget(APP_URL); // NOSONAR — instantiation by side effect\n const installed = globalThis.onload;\n if (typeof installed === 'function' && installed !== previousOnload) {\n // `onload` espera tipo Window — usamos `as` porque o construtor\n // do VLibras só lê `event.target` (que ignoramos) e não toca em\n // propriedades específicas do Window.\n (installed as (this: Window, ev: Event) => unknown).call(\n globalThis as unknown as Window,\n new Event('load')\n );\n }\n\n // Após popular o DOM, abre direto o painel do VLibras — caso\n // contrário o usuário precisaria clicar duas vezes (nosso FAB\n // + botão de acesso do VLibras). Aguarda um frame para garantir\n // que o handler de click do botão já foi anexado pelo plugin.\n requestAnimationFrame(() => {\n const accessButton =\n document.querySelector<HTMLElement>('[vw-access-button]');\n accessButton?.click();\n });\n })\n .catch(() => {\n // Falha de rede ou bloqueio: desabilita silenciosamente o estado\n // para o usuário não ficar preso achando que vai funcionar.\n useAccessibilityStore.getState().setLibrasEnabled(false);\n });\n\n return () => {\n // Não removemos o wrapper aqui: a remoção real fica no ramo\n // `!enabled` acima. Manter aqui causaria flicker em re-renders.\n };\n }, [enabled]);\n\n return null;\n}\n\nconst injectWrapper = () => {\n if (document.getElementById(WRAPPER_ID)) return;\n\n const wrapper = document.createElement('div');\n wrapper.id = WRAPPER_ID;\n wrapper.setAttribute('vw', '');\n wrapper.className = 'enabled';\n\n const accessButton = document.createElement('div');\n accessButton.setAttribute('vw-access-button', '');\n accessButton.className = 'active';\n wrapper.appendChild(accessButton);\n\n const pluginWrapper = document.createElement('div');\n pluginWrapper.setAttribute('vw-plugin-wrapper', '');\n const top = document.createElement('div');\n top.className = 'vw-plugin-top-wrapper';\n pluginWrapper.appendChild(top);\n wrapper.appendChild(pluginWrapper);\n\n document.body.appendChild(wrapper);\n};\n\nconst removeWrapper = () => {\n document.getElementById(WRAPPER_ID)?.remove();\n};\n\nconst loadScript = (): Promise<void> => {\n return new Promise((resolve, reject) => {\n if (document.getElementById(SCRIPT_ID)) {\n resolve();\n return;\n }\n\n const script = document.createElement('script');\n script.id = SCRIPT_ID;\n script.src = SCRIPT_URL;\n script.async = true;\n script.onload = () => resolve();\n script.onerror = () =>\n reject(new Error('Falha ao carregar o script do VLibras'));\n document.head.appendChild(script);\n });\n};\n\nexport const __testing = {\n SCRIPT_ID,\n SCRIPT_URL,\n WRAPPER_ID,\n injectWrapper,\n removeWrapper,\n};\n","import { useEffect } from 'react';\nimport {\n useAccessibilityStore,\n getColorBlindClass,\n type AccessibilityPreferences,\n type ContrastMode,\n type SaturationMode,\n type SpacingLevel,\n} from '../store/accessibilityStore';\n\n/**\n * Mapeia cada preferência para a classe que deve ser aplicada no\n * `documentElement`. Valores no estado padrão (`normal` ou 0) não\n * adicionam classe alguma — assim a página fica intacta quando o\n * usuário não ativou nada.\n */\ntype ClassMap = Partial<Record<keyof AccessibilityPreferences, string>>;\n\nconst CONTRAST_CLASS: Record<ContrastMode, string | null> = {\n normal: null,\n high: 'a11y-contrast-high',\n inverted: 'a11y-contrast-inverted',\n};\n\nconst SATURATION_CLASS: Record<SaturationMode, string | null> = {\n normal: null,\n grayscale: 'a11y-saturation-grayscale',\n low: 'a11y-saturation-low',\n};\n\nconst LEVEL_PREFIX: Record<\n 'fontSize' | 'letterSpacing' | 'lineSpacing',\n string\n> = {\n fontSize: 'a11y-font-',\n letterSpacing: 'a11y-letter-spacing-',\n lineSpacing: 'a11y-line-spacing-',\n};\n\n/** Lista de todas as classes que o widget pode aplicar — usada para limpar */\nconst ALL_A11Y_CLASSES: readonly string[] = [\n 'a11y-contrast-high',\n 'a11y-contrast-inverted',\n 'a11y-saturation-grayscale',\n 'a11y-saturation-low',\n 'a11y-font-1',\n 'a11y-font-2',\n 'a11y-font-3',\n 'a11y-letter-spacing-1',\n 'a11y-letter-spacing-2',\n 'a11y-letter-spacing-3',\n 'a11y-line-spacing-1',\n 'a11y-line-spacing-2',\n 'a11y-line-spacing-3',\n 'a11y-highlight-links',\n 'a11y-no-animations',\n 'a11y-big-cursor',\n 'a11y-dyslexia-font',\n 'a11y-cb-protanopia',\n 'a11y-cb-deuteranopia',\n 'a11y-cb-tritanopia',\n];\n\nconst buildLevelClass = (\n prefix: string,\n level: SpacingLevel\n): string | null => {\n if (level === 0) return null;\n return `${prefix}${level}`;\n};\n\nconst buildClassMap = (prefs: AccessibilityPreferences): ClassMap => ({\n contrastMode: CONTRAST_CLASS[prefs.contrastMode] ?? undefined,\n saturationMode: SATURATION_CLASS[prefs.saturationMode] ?? undefined,\n fontSize: buildLevelClass(LEVEL_PREFIX.fontSize, prefs.fontSize) ?? undefined,\n letterSpacing:\n buildLevelClass(LEVEL_PREFIX.letterSpacing, prefs.letterSpacing) ??\n undefined,\n lineSpacing:\n buildLevelClass(LEVEL_PREFIX.lineSpacing, prefs.lineSpacing) ?? undefined,\n highlightLinks: prefs.highlightLinks ? 'a11y-highlight-links' : undefined,\n pauseAnimations: prefs.pauseAnimations ? 'a11y-no-animations' : undefined,\n bigCursor: prefs.bigCursor ? 'a11y-big-cursor' : undefined,\n dyslexiaFont: prefs.dyslexiaFont ? 'a11y-dyslexia-font' : undefined,\n colorBlindMode: getColorBlindClass(prefs.colorBlindMode) ?? undefined,\n});\n\n/**\n * Aplica/remove as classes de acessibilidade no `documentElement`\n * conforme as preferências atuais. Limpa tudo antes de aplicar para\n * garantir um estado consistente mesmo se prefs forem alteradas\n * fora do fluxo normal.\n */\nconst syncDom = (prefs: AccessibilityPreferences) => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n\n ALL_A11Y_CLASSES.forEach((cls) => root.classList.remove(cls));\n\n const classMap = buildClassMap(prefs);\n Object.values(classMap).forEach((cls) => {\n if (cls) root.classList.add(cls);\n });\n};\n\n/**\n * Hook que sincroniza o store de acessibilidade com o DOM.\n *\n * Deve ser chamado uma vez no nível raiz da aplicação (o\n * componente `AccessibilityWidget` já faz isso). Cada vez que\n * uma preferência muda, as classes correspondentes são aplicadas\n * no `<html>`, sem `!important` no nível do hook — a especificidade\n * é controlada pelo CSS.\n */\nexport const useA11yPreferences = () => {\n const contrastMode = useAccessibilityStore((s) => s.contrastMode);\n const saturationMode = useAccessibilityStore((s) => s.saturationMode);\n const fontSize = useAccessibilityStore((s) => s.fontSize);\n const letterSpacing = useAccessibilityStore((s) => s.letterSpacing);\n const lineSpacing = useAccessibilityStore((s) => s.lineSpacing);\n const highlightLinks = useAccessibilityStore((s) => s.highlightLinks);\n const pauseAnimations = useAccessibilityStore((s) => s.pauseAnimations);\n const bigCursor = useAccessibilityStore((s) => s.bigCursor);\n const dyslexiaFont = useAccessibilityStore((s) => s.dyslexiaFont);\n const readingAid = useAccessibilityStore((s) => s.readingAid);\n const keyboardShortcut = useAccessibilityStore((s) => s.keyboardShortcut);\n const colorBlindMode = useAccessibilityStore((s) => s.colorBlindMode);\n const ttsMode = useAccessibilityStore((s) => s.ttsMode);\n const ttsRate = useAccessibilityStore((s) => s.ttsRate);\n const ttsVoiceId = useAccessibilityStore((s) => s.ttsVoiceId);\n const librasEnabled = useAccessibilityStore((s) => s.librasEnabled);\n\n useEffect(() => {\n syncDom({\n contrastMode,\n saturationMode,\n fontSize,\n letterSpacing,\n lineSpacing,\n highlightLinks,\n pauseAnimations,\n bigCursor,\n dyslexiaFont,\n readingAid,\n keyboardShortcut,\n colorBlindMode,\n ttsMode,\n ttsRate,\n ttsVoiceId,\n librasEnabled,\n });\n }, [\n contrastMode,\n saturationMode,\n fontSize,\n letterSpacing,\n lineSpacing,\n highlightLinks,\n pauseAnimations,\n bigCursor,\n dyslexiaFont,\n colorBlindMode,\n ]);\n};\n\nexport const __testing = {\n buildClassMap,\n syncDom,\n ALL_A11Y_CLASSES,\n};\n","import { useEffect } from 'react';\nimport { useAccessibilityStore } from '../store/accessibilityStore';\n\n/**\n * Registra o atalho `Alt+A` para abrir/fechar o painel de\n * acessibilidade. Não interfere quando o foco está em campo de\n * texto/edição para evitar conflitos com o usuário digitando.\n *\n * Habilitar/desabilitar é controlado pela preferência\n * `keyboardShortcut` do store.\n */\nexport const useA11yKeyboardShortcut = () => {\n const enabled = useAccessibilityStore((s) => s.keyboardShortcut);\n const togglePanel = useAccessibilityStore((s) => s.togglePanel);\n\n useEffect(() => {\n if (!enabled) return;\n\n const handler = (event: KeyboardEvent) => {\n if (!event.altKey || event.key.toLowerCase() !== 'a') return;\n // Ignora auto-repeat: segurar Alt+A não deve oscilar o painel\n if (event.repeat) return;\n\n // Não ativa se o usuário estiver digitando em algum campo\n const target = event.target as HTMLElement | null;\n if (target) {\n const tag = target.tagName;\n if (\n tag === 'INPUT' ||\n tag === 'TEXTAREA' ||\n tag === 'SELECT' ||\n target.isContentEditable\n ) {\n return;\n }\n }\n\n event.preventDefault();\n togglePanel();\n };\n\n globalThis.addEventListener('keydown', handler);\n return () => globalThis.removeEventListener('keydown', handler);\n }, [enabled, togglePanel]);\n};\n"],"mappings":"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","names":["import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","import_react","import_react","import_jsx_runtime","import_jsx_runtime","SIZE_CLASSES","import_jsx_runtime","import_react","import_react","import_react_dom","import_jsx_runtime","SIZE_CLASSES","children","label","import_zustand","import_react","import_jsx_runtime","PreviewSegmented","import_jsx_runtime","import_react","import_jsx_runtime","import_react","import_jsx_runtime","import_jsx_runtime","import_react","import_react","import_react","import_react","import_jsx_runtime"]}
|