@alfadocs/ui-kit-debug 0.11.0 → 0.13.0

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.
Files changed (139) hide show
  1. package/dist/_chunks/_commonjsHelpers-C6fGbg64.js +7 -0
  2. package/dist/_chunks/_commonjsHelpers-C6fGbg64.js.map +1 -0
  3. package/dist/_chunks/{ai-prompt-input-bAJwYu84.js → ai-prompt-input-DEiQwIMn.js} +22 -21
  4. package/dist/_chunks/ai-prompt-input-DEiQwIMn.js.map +1 -0
  5. package/dist/_chunks/{audio-recorder-BHBonrFf.js → audio-recorder-CRh4uyFL.js} +2 -2
  6. package/dist/_chunks/{audio-recorder-BHBonrFf.js.map → audio-recorder-CRh4uyFL.js.map} +1 -1
  7. package/dist/_chunks/{autocomplete-C34hbfKh.js → autocomplete-mOg7WLOh.js} +2 -2
  8. package/dist/_chunks/{autocomplete-C34hbfKh.js.map → autocomplete-mOg7WLOh.js.map} +1 -1
  9. package/dist/_chunks/{chat-input-Bov-gkwP.js → chat-input-UK-bXU7u.js} +9 -8
  10. package/dist/_chunks/chat-input-UK-bXU7u.js.map +1 -0
  11. package/dist/_chunks/{combobox-BHhnR3qm.js → combobox-D5tWe0t_.js} +2 -2
  12. package/dist/_chunks/{combobox-BHhnR3qm.js.map → combobox-D5tWe0t_.js.map} +1 -1
  13. package/dist/_chunks/{contact-card-VJIUqKB2.js → contact-card-DTQUMetD.js} +22 -38
  14. package/dist/_chunks/contact-card-DTQUMetD.js.map +1 -0
  15. package/dist/_chunks/{date-picker-BD5FYW08.js → date-picker-BlhtBhPo.js} +2 -2
  16. package/dist/_chunks/{date-picker-BD5FYW08.js.map → date-picker-BlhtBhPo.js.map} +1 -1
  17. package/dist/_chunks/{date-range-picker-BZLVgcXE.js → date-range-picker-C2hRu_Ke.js} +2 -2
  18. package/dist/_chunks/{date-range-picker-BZLVgcXE.js.map → date-range-picker-C2hRu_Ke.js.map} +1 -1
  19. package/dist/_chunks/{date-time-picker-CCoRWX7R.js → date-time-picker-B67mPZmP.js} +2 -2
  20. package/dist/_chunks/{date-time-picker-CCoRWX7R.js.map → date-time-picker-B67mPZmP.js.map} +1 -1
  21. package/dist/_chunks/external-link-C6F25E6k.js +16 -0
  22. package/dist/_chunks/external-link-C6F25E6k.js.map +1 -0
  23. package/dist/_chunks/{file-upload-DIecAfC-.js → file-upload-nMh-1jDD.js} +2 -2
  24. package/dist/_chunks/{file-upload-DIecAfC-.js.map → file-upload-nMh-1jDD.js.map} +1 -1
  25. package/dist/_chunks/{index-CeY1nNvd.js → index-CFoBa86t.js} +61 -63
  26. package/dist/_chunks/{index-CeY1nNvd.js.map → index-CFoBa86t.js.map} +1 -1
  27. package/dist/_chunks/{input-surface-u4QB0lxe.js → input-surface-xyERuLU_.js} +9 -3
  28. package/dist/_chunks/input-surface-xyERuLU_.js.map +1 -0
  29. package/dist/_chunks/{leo-sidebar-B054wsZm.js → leo-sidebar-D3TuyH5_.js} +2 -2
  30. package/dist/_chunks/{leo-sidebar-B054wsZm.js.map → leo-sidebar-D3TuyH5_.js.map} +1 -1
  31. package/dist/_chunks/{link-BGpwaFik.js → link-DmM5IevO.js} +16 -26
  32. package/dist/_chunks/link-DmM5IevO.js.map +1 -0
  33. package/dist/_chunks/map-pin-B8STOPMJ.js +21 -0
  34. package/dist/_chunks/map-pin-B8STOPMJ.js.map +1 -0
  35. package/dist/_chunks/map-view-Dd48BxVB.js +1941 -0
  36. package/dist/_chunks/map-view-Dd48BxVB.js.map +1 -0
  37. package/dist/_chunks/{multi-select-Bh-xR8kP.js → multi-select-DooDzQIp.js} +2 -2
  38. package/dist/_chunks/{multi-select-Bh-xR8kP.js.map → multi-select-DooDzQIp.js.map} +1 -1
  39. package/dist/_chunks/{number-input-mpSLk-ld.js → number-input-DH00o0DN.js} +28 -27
  40. package/dist/_chunks/number-input-DH00o0DN.js.map +1 -0
  41. package/dist/_chunks/{otp-input-CI-Zv5q6.js → otp-input-BBXYvLx5.js} +49 -48
  42. package/dist/_chunks/otp-input-BBXYvLx5.js.map +1 -0
  43. package/dist/_chunks/{phone-input-DtBVs5fz.js → phone-input-DKSHX7NQ.js} +23 -22
  44. package/dist/_chunks/phone-input-DKSHX7NQ.js.map +1 -0
  45. package/dist/_chunks/{react-day-picker-C04L_28V.js → react-day-picker-C5F3-TTX.js} +6 -3
  46. package/dist/_chunks/{react-day-picker-C04L_28V.js.map → react-day-picker-C5F3-TTX.js.map} +1 -1
  47. package/dist/_chunks/{search-bar-DmZZ9UvV.js → search-bar-DORSAzNt.js} +2 -2
  48. package/dist/_chunks/{search-bar-DmZZ9UvV.js.map → search-bar-DORSAzNt.js.map} +1 -1
  49. package/dist/_chunks/{search-input-BBtSRH-Q.js → search-input-BtEJAJHa.js} +2 -2
  50. package/dist/_chunks/{search-input-BBtSRH-Q.js.map → search-input-BtEJAJHa.js.map} +1 -1
  51. package/dist/_chunks/{select-i9MwQeQy.js → select-Ca6ibiDL.js} +13 -12
  52. package/dist/_chunks/select-Ca6ibiDL.js.map +1 -0
  53. package/dist/_chunks/{tabs-Cg794H0Q.js → tabs-CRCyPpJo.js} +2 -2
  54. package/dist/_chunks/{tabs-Cg794H0Q.js.map → tabs-CRCyPpJo.js.map} +1 -1
  55. package/dist/_chunks/{text-area-DHtcpcLv.js → text-area-D5GAe8pV.js} +26 -25
  56. package/dist/_chunks/text-area-D5GAe8pV.js.map +1 -0
  57. package/dist/_chunks/{text-input-1oqFRbVI.js → text-input-CakysYnD.js} +26 -25
  58. package/dist/_chunks/text-input-CakysYnD.js.map +1 -0
  59. package/dist/_chunks/{transcript-panel-BpJqPr7I.js → transcript-panel-DUrjx5sa.js} +2 -2
  60. package/dist/_chunks/{transcript-panel-BpJqPr7I.js.map → transcript-panel-DUrjx5sa.js.map} +1 -1
  61. package/dist/_chunks/{use-password-requirements-DbPZMfV9.js → use-password-requirements-BOgFsoIe.js} +2 -2
  62. package/dist/_chunks/{use-password-requirements-DbPZMfV9.js.map → use-password-requirements-BOgFsoIe.js.map} +1 -1
  63. package/dist/_chunks/whatsapp-button-Bj5FIhpC.js +175 -0
  64. package/dist/_chunks/whatsapp-button-Bj5FIhpC.js.map +1 -0
  65. package/dist/agent-catalog.json +53 -1
  66. package/dist/components/_shared/date-picker-variants.d.ts.map +1 -1
  67. package/dist/components/_shared/input-surface.d.ts.map +1 -1
  68. package/dist/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
  69. package/dist/components/ai-prompt-input/index.js +1 -1
  70. package/dist/components/audio-recorder/index.js +1 -1
  71. package/dist/components/autocomplete/index.js +1 -1
  72. package/dist/components/chat-input/chat-input.d.ts.map +1 -1
  73. package/dist/components/chat-input/index.js +1 -1
  74. package/dist/components/combobox/index.js +1 -1
  75. package/dist/components/contact-card/index.js +1 -1
  76. package/dist/components/date-picker/index.js +1 -1
  77. package/dist/components/date-range-picker/index.js +1 -1
  78. package/dist/components/date-time-picker/index.js +1 -1
  79. package/dist/components/file-upload/index.js +1 -1
  80. package/dist/components/index.d.ts +2 -0
  81. package/dist/components/index.d.ts.map +1 -1
  82. package/dist/components/link/index.js +1 -1
  83. package/dist/components/map-view/index.d.ts +3 -0
  84. package/dist/components/map-view/index.d.ts.map +1 -0
  85. package/dist/components/map-view/index.js +5 -0
  86. package/dist/components/map-view/index.js.map +1 -0
  87. package/dist/components/map-view/map-view.agent.d.ts +4 -0
  88. package/dist/components/map-view/map-view.agent.d.ts.map +1 -0
  89. package/dist/components/map-view/map-view.d.ts +78 -0
  90. package/dist/components/map-view/map-view.d.ts.map +1 -0
  91. package/dist/components/multi-select/index.js +1 -1
  92. package/dist/components/number-input/index.js +1 -1
  93. package/dist/components/number-input/number-input.d.ts.map +1 -1
  94. package/dist/components/otp-input/index.js +1 -1
  95. package/dist/components/otp-input/otp-input.d.ts.map +1 -1
  96. package/dist/components/password-input/index.js +1 -1
  97. package/dist/components/phone-input/index.js +1 -1
  98. package/dist/components/phone-input/phone-input.d.ts.map +1 -1
  99. package/dist/components/search-bar/index.js +1 -1
  100. package/dist/components/search-input/index.js +1 -1
  101. package/dist/components/select/index.js +1 -1
  102. package/dist/components/select/select.d.ts.map +1 -1
  103. package/dist/components/tabs/index.js +1 -1
  104. package/dist/components/text-area/index.js +1 -1
  105. package/dist/components/text-area/text-area.d.ts.map +1 -1
  106. package/dist/components/text-input/index.js +1 -1
  107. package/dist/components/text-input/text-input.d.ts.map +1 -1
  108. package/dist/components/transcript-panel/index.js +1 -1
  109. package/dist/components/whatsapp-button/index.d.ts +3 -0
  110. package/dist/components/whatsapp-button/index.d.ts.map +1 -0
  111. package/dist/components/whatsapp-button/index.js +5 -0
  112. package/dist/components/whatsapp-button/index.js.map +1 -0
  113. package/dist/components/whatsapp-button/whatsapp-button.d.ts +32 -0
  114. package/dist/components/whatsapp-button/whatsapp-button.d.ts.map +1 -0
  115. package/dist/i18n/config.js +45 -6
  116. package/dist/i18n/config.js.map +1 -1
  117. package/dist/i18n/resources.d.ts +39 -0
  118. package/dist/i18n/resources.d.ts.map +1 -1
  119. package/dist/index.js +361 -357
  120. package/dist/index.js.map +1 -1
  121. package/dist/locales/de.json +13 -0
  122. package/dist/locales/en.json +13 -0
  123. package/dist/locales/it.json +13 -0
  124. package/dist/patterns/leo-assistant/index.js +1 -1
  125. package/dist/tokens/google-maps-theme.d.ts +15 -0
  126. package/dist/tokens/google-maps-theme.d.ts.map +1 -0
  127. package/dist/tokens.css +1 -1
  128. package/package.json +15 -1
  129. package/dist/_chunks/ai-prompt-input-bAJwYu84.js.map +0 -1
  130. package/dist/_chunks/chat-input-Bov-gkwP.js.map +0 -1
  131. package/dist/_chunks/contact-card-VJIUqKB2.js.map +0 -1
  132. package/dist/_chunks/input-surface-u4QB0lxe.js.map +0 -1
  133. package/dist/_chunks/link-BGpwaFik.js.map +0 -1
  134. package/dist/_chunks/number-input-mpSLk-ld.js.map +0 -1
  135. package/dist/_chunks/otp-input-CI-Zv5q6.js.map +0 -1
  136. package/dist/_chunks/phone-input-DtBVs5fz.js.map +0 -1
  137. package/dist/_chunks/select-i9MwQeQy.js.map +0 -1
  138. package/dist/_chunks/text-area-DHtcpcLv.js.map +0 -1
  139. package/dist/_chunks/text-input-1oqFRbVI.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"transcript-panel-BpJqPr7I.js","sources":["../../src/components/transcript-panel/transcript-panel.agent.ts","../../src/components/transcript-panel/transcript-panel.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — TranscriptPanel. */\n/* */\n/* `data-turn-id` carries the line's stable index (opaque id) — never */\n/* the dictated text. Transcript content frequently contains PHI; the */\n/* DOM hook MUST NOT leak it. See PRS §26 §6.1. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { TranscriptPanelHandle } from './transcript-panel';\n\nexport const transcriptPanelAgent: AgentAdapter<TranscriptPanelHandle> = {\n id: 'transcript-panel',\n capabilities: ['select_single'],\n state: {\n selectedTurn: {\n type: 'string | null',\n descriptionKey: 'ui.agent.transcriptPanel.state.selectedTurn',\n description:\n 'Opaque id (line index) of the currently-active turn, or null when none is active.',\n read: (handle) => handle.getSelectedTurn(),\n },\n },\n actions: {\n focus_turn: {\n safety: 'read',\n argsType: '{ id: string }',\n descriptionKey: 'ui.agent.transcriptPanel.actions.focusTurn',\n description: 'Scroll the given turn into view and focus its seek button.',\n invoke: (handle, args: { id: string }) => {\n handle.focusTurn(args.id);\n },\n },\n copy_turn: {\n safety: 'read',\n argsType: '{ id: string }',\n descriptionKey: 'ui.agent.transcriptPanel.actions.copyTurn',\n description: \"Copy the given turn's text to the clipboard.\",\n invoke: (handle, args: { id: string }) => handle.copyTurn(args.id),\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'transcript-panel',\n description: 'Marks the TranscriptPanel wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-turn-id',\n description:\n \"Stable opaque turn id (line index). Never carries the turn's dictated text.\",\n },\n },\n};\n","import {\n forwardRef,\n Fragment,\n useCallback,\n useId,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { transcriptPanelAgent } from './transcript-panel.agent';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { SearchInput } from '../search-input';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport interface TranscriptLine {\n speaker: string;\n text: string;\n /** Seconds, floating-point — matches HTMLMediaElement.currentTime. */\n start: number;\n end: number;\n /** 0..1 — rendered with a dotted underline when below 0.6. */\n confidence?: number;\n}\n\n/** Curated imperative handle for agent / external automation. */\nexport interface TranscriptPanelHandle {\n /** Opaque id (line index) of the active turn, or null. */\n getSelectedTurn: () => string | null;\n /** Scroll the turn with the given id into view and focus its seek button. */\n focusTurn: (id: string) => void;\n /** Copy the turn's text to the clipboard. */\n copyTurn: (id: string) => Promise<void> | void;\n}\n\nexport interface TranscriptPanelProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof rootVariants> {\n lines: TranscriptLine[];\n /** Current playback time in seconds — drives the active line highlight. */\n currentTime?: number;\n onSeek?: (seconds: number) => void;\n /** Show the search bar. */\n searchable?: boolean;\n}\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:w-full ds:min-h-0',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-border',\n 'ds:bg-background',\n 'ds:focus-within:border-[color:var(--primary)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:text-[length:var(--font-size-sm)]',\n md: 'ds:text-[length:var(--font-size-base)]',\n lg: 'ds:text-[length:var(--font-size-lg)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst VIRTUALIZATION_THRESHOLD = 100;\nconst SPEAKER_SLOT_COUNT = 8;\n\nfunction formatTime(seconds: number, locale: string): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n const fmt = (n: number) =>\n new Intl.NumberFormat(locale, { minimumIntegerDigits: 2 }).format(n);\n return `${fmt(m)}:${fmt(s)}`;\n}\n\n/** ISO-8601 duration (PT#M#S) for <time datetime=\"...\">. */\nfunction secondsToIsoDuration(seconds: number): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `PT${m}M${s}S`;\n}\n\n/** Deterministic speaker slot (0..7) based on name hash. */\nfunction speakerSlot(name: string): number {\n let hash = 0;\n for (let i = 0; i < name.length; i += 1) {\n hash = (hash * 31 + name.charCodeAt(i)) | 0;\n }\n return Math.abs(hash) % SPEAKER_SLOT_COUNT;\n}\n\n/** Per-slot background classes. Indices align with the speaker palette tokens\n * in tokens/index.css. Static strings so Tailwind can pick them up at build. */\nconst SPEAKER_BG_CLASSES = [\n 'ds:bg-[color:var(--transcript-speaker-1)]',\n 'ds:bg-[color:var(--transcript-speaker-2)]',\n 'ds:bg-[color:var(--transcript-speaker-3)]',\n 'ds:bg-[color:var(--transcript-speaker-4)]',\n 'ds:bg-[color:var(--transcript-speaker-5)]',\n 'ds:bg-[color:var(--transcript-speaker-6)]',\n 'ds:bg-[color:var(--transcript-speaker-7)]',\n 'ds:bg-[color:var(--transcript-speaker-8)]',\n] as const;\n\n/** Escape a literal string for safe use inside a RegExp. */\nfunction escapeRegExp(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\ninterface HighlightedTextProps {\n text: string;\n query: string;\n}\n\n/** Renders text with <mark> wrapping around case-insensitive matches.\n * No dangerouslySetInnerHTML — splits text nodes only. */\nfunction HighlightedText({ text, query }: HighlightedTextProps): ReactNode {\n if (!query) return text;\n const escaped = escapeRegExp(query);\n const parts = text.split(new RegExp(`(${escaped})`, 'gi'));\n return parts.map((part, i) =>\n part.toLocaleLowerCase() === query.toLocaleLowerCase() ? (\n <mark\n key={i}\n className=\"ds:bg-[color:var(--warning)]/30 ds:text-[color:var(--foreground)] ds:rounded-[var(--radius-sm)]\"\n >\n {part}\n </mark>\n ) : (\n <Fragment key={i}>{part}</Fragment>\n ),\n );\n}\n\nexport const TranscriptPanel = forwardRef<HTMLDivElement, TranscriptPanelProps>(\n (\n {\n lines,\n currentTime = 0,\n onSeek,\n searchable = true,\n size = 'md',\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const [query, setQuery] = useState('');\n const scrollRef = useRef<HTMLDivElement | null>(null);\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n const searchId = useId();\n\n /* Active-line index */\n const activeIndex = useMemo(() => {\n if (!Number.isFinite(currentTime)) return -1;\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (line.start <= currentTime && currentTime < line.end) return i;\n }\n return -1;\n }, [lines, currentTime]);\n\n /* Match count for search */\n const matchCount = useMemo(() => {\n if (!query) return 0;\n let count = 0;\n const needle = query.toLocaleLowerCase();\n for (const line of lines) {\n const hay = line.text.toLocaleLowerCase();\n let idx = hay.indexOf(needle);\n while (idx !== -1) {\n count += 1;\n idx = hay.indexOf(needle, idx + needle.length);\n }\n }\n return count;\n }, [lines, query]);\n\n /* Ctrl/Cmd+F shortcut — focuses the search field when focus is within. */\n const handleRootKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (\n (e.metaKey || e.ctrlKey) &&\n e.key.toLowerCase() === 'f' &&\n searchable\n ) {\n e.preventDefault();\n searchInputRef.current?.focus();\n }\n },\n [searchable],\n );\n\n /* Virtualisation above 100 lines */\n const virtualized = lines.length > VIRTUALIZATION_THRESHOLD;\n const virtualizer = useVirtualizer({\n count: virtualized ? lines.length : 0,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 48,\n overscan: 8,\n getItemKey: (i) => i,\n });\n\n /* Auto-scroll active line into view when it changes. */\n useLayoutEffect(() => {\n if (activeIndex < 0) return;\n const scroller = scrollRef.current;\n if (!scroller) return;\n const target = scroller.querySelector(\n `[data-line-index=\"${activeIndex}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n const prefersReducedMotion =\n typeof window !== 'undefined' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n target.scrollIntoView({\n block: 'center',\n behavior: prefersReducedMotion ? 'auto' : 'smooth',\n });\n }, [activeIndex]);\n\n const renderLine = useCallback(\n (\n line: TranscriptLine,\n index: number,\n extras?: {\n liRef?: (node: HTMLLIElement | null) => void;\n dataIndex?: number;\n // Style is an inline-style escape hatch — virtualizer offsets\n // cannot be expressed as static Tailwind. See 23-constraints §4.\n style?: CSSProperties;\n absolutePositioning?: boolean;\n },\n ) => {\n const isActive = index === activeIndex;\n const slot = speakerSlot(line.speaker);\n const lowConfidence =\n typeof line.confidence === 'number' && line.confidence < 0.6;\n return (\n <li\n key={index}\n ref={extras?.liRef}\n data-line-index={index}\n data-turn-id={String(index)}\n data-index={extras?.dataIndex}\n aria-current={isActive ? 'true' : undefined}\n // eslint-disable-next-line react/forbid-dom-props -- virtualizer translateY offset, runtime-computed\n style={extras?.style}\n className={[\n 'ds:flex ds:items-start ds:gap-[var(--spacing-sm)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n isActive\n ? 'ds:bg-[color:var(--accent)]/12 ds:border-s-[3px] ds:border-[color:var(--primary)]'\n : 'ds:border-s-[3px] ds:border-transparent',\n extras?.absolutePositioning\n ? 'ds:absolute ds:start-0 ds:end-0 ds:top-0 ds:w-full'\n : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <button\n type=\"button\"\n onClick={() => onSeek?.(line.start)}\n aria-label={t('chat.transcript.seekTo', {\n time: formatTime(line.start, i18n.language),\n })}\n className={[\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:font-[family-name:var(--font-mono)]',\n 'type-meta',\n 'ds:tabular-nums',\n 'ds:text-[color:var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n // WCAG 2.5.5 target size: 44×44 default, 48×48 in accessible\n // themes — matches the DS `--min-target-size` token.\n 'ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)]',\n 'ds:hover:bg-muted/20',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' ')}\n >\n <time dir=\"ltr\" dateTime={secondsToIsoDuration(line.start)}>\n {formatTime(line.start, i18n.language)}\n </time>\n </button>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <span\n aria-hidden=\"true\"\n className={`ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${SPEAKER_BG_CLASSES[slot]}`}\n />\n <strong className=\"type-title-item\">\n {t('chat.transcript.speaker', { name: line.speaker })}\n </strong>\n {lowConfidence ? (\n <span className=\"type-meta ds:italic ds:text-[color:var(--muted-foreground)]\">\n ({t('chat.transcript.lowConfidence')})\n </span>\n ) : null}\n </div>\n <span\n dir=\"auto\"\n className={[\n 'ds:block',\n lowConfidence\n ? 'ds:underline ds:decoration-dotted ds:decoration-[color:var(--muted-foreground)]'\n : '',\n ].join(' ')}\n >\n <HighlightedText text={line.text} query={query} />\n </span>\n </div>\n </li>\n );\n },\n [activeIndex, i18n.language, onSeek, query, t],\n );\n\n const rootRef = useRef<HTMLDivElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement, []);\n\n const agentHandle = useMemo<TranscriptPanelHandle>(\n () => ({\n getSelectedTurn: () => (activeIndex >= 0 ? String(activeIndex) : null),\n focusTurn: (id: string) => {\n const target = scrollRef.current?.querySelector(\n `[data-turn-id=\"${id}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n target.scrollIntoView({ block: 'center', behavior: 'auto' });\n const btn = target.querySelector(\n 'button',\n ) as HTMLButtonElement | null;\n btn?.focus();\n },\n copyTurn: (id: string) => {\n const index = Number.parseInt(id, 10);\n if (!Number.isFinite(index)) return;\n const line = lines[index];\n if (!line) return;\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n return navigator.clipboard.writeText(line.text);\n }\n },\n }),\n [activeIndex, lines],\n );\n useAgentRegistration(\n transcriptPanelAgent,\n agentHandle,\n rest.id as string | undefined,\n );\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- root keyboard shortcuts (j/k navigation, /) bubble up from focused descendants\n <div\n ref={rootRef}\n onKeyDown={handleRootKeyDown}\n data-component=\"transcript-panel\"\n data-component-id={rest.id}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {searchable ? (\n <div className=\"ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-xs)] ds:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:border-b ds:border-border\">\n <SearchInput\n ref={searchInputRef}\n id={searchId}\n placeholder={t('chat.transcript.search')}\n aria-label={t('chat.transcript.search')}\n aria-keyshortcuts=\"Meta+F Control+F\"\n debounceMs={150}\n onChange={(v) => setQuery(v)}\n size=\"sm\"\n />\n {query ? (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)] ds:tabular-nums\"\n >\n {matchCount === 0\n ? t('chat.transcript.noMatches')\n : t('chat.transcript.matchCount', { count: matchCount })}\n </span>\n ) : null}\n </div>\n ) : null}\n\n <div\n ref={scrollRef}\n className=\"ds:flex-1 ds:min-h-0 ds:overflow-y-auto ds:overflow-x-hidden\"\n >\n {virtualized ? (\n <ol\n className=\"ds:relative ds:list-none ds:ps-0 ds:m-0\"\n // eslint-disable-next-line react/forbid-dom-props -- virtualizer total size, runtime-computed\n style={{ blockSize: `${virtualizer.getTotalSize()}px` }}\n >\n {virtualizer.getVirtualItems().map((vi) => {\n const line = lines[vi.index];\n if (!line) return null;\n // Put the measureElement ref on the <li> directly — a <div>\n // child of <ol> is invalid and breaks list semantics for\n // assistive tech.\n return renderLine(line, vi.index, {\n liRef: virtualizer.measureElement,\n dataIndex: vi.index,\n style: { transform: `translateY(${vi.start}px)` },\n absolutePositioning: true,\n });\n })}\n </ol>\n ) : (\n <ol className=\"ds:list-none ds:ps-0 ds:m-0\">\n {lines.map((line, index) => renderLine(line, index))}\n </ol>\n )}\n </div>\n </div>\n );\n },\n);\n\nTranscriptPanel.displayName = 'TranscriptPanel';\n"],"names":["transcriptPanelAgent","handle","args","rootVariants","cva","VIRTUALIZATION_THRESHOLD","SPEAKER_SLOT_COUNT","formatTime","seconds","locale","total","m","s","fmt","n","secondsToIsoDuration","speakerSlot","name","hash","i","SPEAKER_BG_CLASSES","escapeRegExp","input","HighlightedText","text","query","escaped","part","jsx","Fragment","TranscriptPanel","forwardRef","lines","currentTime","onSeek","searchable","size","className","rest","ref","t","i18n","useTranslation","setQuery","useState","scrollRef","useRef","searchInputRef","searchId","useId","activeIndex","useMemo","line","matchCount","count","needle","hay","idx","handleRootKeyDown","useCallback","_a","virtualized","virtualizer","useVirtualizer","useLayoutEffect","scroller","target","prefersReducedMotion","renderLine","index","extras","isActive","slot","lowConfidence","jsxs","rootRef","useImperativeHandle","agentHandle","id","btn","useAgentRegistration","SearchInput","v","vi"],"mappings":"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"}
1
+ {"version":3,"file":"transcript-panel-DUrjx5sa.js","sources":["../../src/components/transcript-panel/transcript-panel.agent.ts","../../src/components/transcript-panel/transcript-panel.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — TranscriptPanel. */\n/* */\n/* `data-turn-id` carries the line's stable index (opaque id) — never */\n/* the dictated text. Transcript content frequently contains PHI; the */\n/* DOM hook MUST NOT leak it. See PRS §26 §6.1. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { TranscriptPanelHandle } from './transcript-panel';\n\nexport const transcriptPanelAgent: AgentAdapter<TranscriptPanelHandle> = {\n id: 'transcript-panel',\n capabilities: ['select_single'],\n state: {\n selectedTurn: {\n type: 'string | null',\n descriptionKey: 'ui.agent.transcriptPanel.state.selectedTurn',\n description:\n 'Opaque id (line index) of the currently-active turn, or null when none is active.',\n read: (handle) => handle.getSelectedTurn(),\n },\n },\n actions: {\n focus_turn: {\n safety: 'read',\n argsType: '{ id: string }',\n descriptionKey: 'ui.agent.transcriptPanel.actions.focusTurn',\n description: 'Scroll the given turn into view and focus its seek button.',\n invoke: (handle, args: { id: string }) => {\n handle.focusTurn(args.id);\n },\n },\n copy_turn: {\n safety: 'read',\n argsType: '{ id: string }',\n descriptionKey: 'ui.agent.transcriptPanel.actions.copyTurn',\n description: \"Copy the given turn's text to the clipboard.\",\n invoke: (handle, args: { id: string }) => handle.copyTurn(args.id),\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'transcript-panel',\n description: 'Marks the TranscriptPanel wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n item: {\n attr: 'data-turn-id',\n description:\n \"Stable opaque turn id (line index). Never carries the turn's dictated text.\",\n },\n },\n};\n","import {\n forwardRef,\n Fragment,\n useCallback,\n useId,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type HTMLAttributes,\n type KeyboardEvent,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { useAgentRegistration } from '../../agent/registry';\nimport { transcriptPanelAgent } from './transcript-panel.agent';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { SearchInput } from '../search-input';\n\n/* ------------------------------------------------------------------ */\n/* Public types */\n/* ------------------------------------------------------------------ */\n\nexport interface TranscriptLine {\n speaker: string;\n text: string;\n /** Seconds, floating-point — matches HTMLMediaElement.currentTime. */\n start: number;\n end: number;\n /** 0..1 — rendered with a dotted underline when below 0.6. */\n confidence?: number;\n}\n\n/** Curated imperative handle for agent / external automation. */\nexport interface TranscriptPanelHandle {\n /** Opaque id (line index) of the active turn, or null. */\n getSelectedTurn: () => string | null;\n /** Scroll the turn with the given id into view and focus its seek button. */\n focusTurn: (id: string) => void;\n /** Copy the turn's text to the clipboard. */\n copyTurn: (id: string) => Promise<void> | void;\n}\n\nexport interface TranscriptPanelProps\n extends\n Omit<HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof rootVariants> {\n lines: TranscriptLine[];\n /** Current playback time in seconds — drives the active line highlight. */\n currentTime?: number;\n onSeek?: (seconds: number) => void;\n /** Show the search bar. */\n searchable?: boolean;\n}\n\nconst rootVariants = cva(\n [\n 'ds:flex ds:flex-col ds:w-full ds:min-h-0',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-border',\n 'ds:bg-background',\n 'ds:focus-within:border-[color:var(--primary)]',\n ].join(' '),\n {\n variants: {\n size: {\n sm: 'ds:text-[length:var(--font-size-sm)]',\n md: 'ds:text-[length:var(--font-size-base)]',\n lg: 'ds:text-[length:var(--font-size-lg)]',\n },\n },\n defaultVariants: { size: 'md' },\n },\n);\n\nconst VIRTUALIZATION_THRESHOLD = 100;\nconst SPEAKER_SLOT_COUNT = 8;\n\nfunction formatTime(seconds: number, locale: string): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n const fmt = (n: number) =>\n new Intl.NumberFormat(locale, { minimumIntegerDigits: 2 }).format(n);\n return `${fmt(m)}:${fmt(s)}`;\n}\n\n/** ISO-8601 duration (PT#M#S) for <time datetime=\"...\">. */\nfunction secondsToIsoDuration(seconds: number): string {\n const total = Math.max(0, Math.floor(seconds));\n const m = Math.floor(total / 60);\n const s = total % 60;\n return `PT${m}M${s}S`;\n}\n\n/** Deterministic speaker slot (0..7) based on name hash. */\nfunction speakerSlot(name: string): number {\n let hash = 0;\n for (let i = 0; i < name.length; i += 1) {\n hash = (hash * 31 + name.charCodeAt(i)) | 0;\n }\n return Math.abs(hash) % SPEAKER_SLOT_COUNT;\n}\n\n/** Per-slot background classes. Indices align with the speaker palette tokens\n * in tokens/index.css. Static strings so Tailwind can pick them up at build. */\nconst SPEAKER_BG_CLASSES = [\n 'ds:bg-[color:var(--transcript-speaker-1)]',\n 'ds:bg-[color:var(--transcript-speaker-2)]',\n 'ds:bg-[color:var(--transcript-speaker-3)]',\n 'ds:bg-[color:var(--transcript-speaker-4)]',\n 'ds:bg-[color:var(--transcript-speaker-5)]',\n 'ds:bg-[color:var(--transcript-speaker-6)]',\n 'ds:bg-[color:var(--transcript-speaker-7)]',\n 'ds:bg-[color:var(--transcript-speaker-8)]',\n] as const;\n\n/** Escape a literal string for safe use inside a RegExp. */\nfunction escapeRegExp(input: string): string {\n return input.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\ninterface HighlightedTextProps {\n text: string;\n query: string;\n}\n\n/** Renders text with <mark> wrapping around case-insensitive matches.\n * No dangerouslySetInnerHTML — splits text nodes only. */\nfunction HighlightedText({ text, query }: HighlightedTextProps): ReactNode {\n if (!query) return text;\n const escaped = escapeRegExp(query);\n const parts = text.split(new RegExp(`(${escaped})`, 'gi'));\n return parts.map((part, i) =>\n part.toLocaleLowerCase() === query.toLocaleLowerCase() ? (\n <mark\n key={i}\n className=\"ds:bg-[color:var(--warning)]/30 ds:text-[color:var(--foreground)] ds:rounded-[var(--radius-sm)]\"\n >\n {part}\n </mark>\n ) : (\n <Fragment key={i}>{part}</Fragment>\n ),\n );\n}\n\nexport const TranscriptPanel = forwardRef<HTMLDivElement, TranscriptPanelProps>(\n (\n {\n lines,\n currentTime = 0,\n onSeek,\n searchable = true,\n size = 'md',\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation();\n const [query, setQuery] = useState('');\n const scrollRef = useRef<HTMLDivElement | null>(null);\n const searchInputRef = useRef<HTMLInputElement | null>(null);\n const searchId = useId();\n\n /* Active-line index */\n const activeIndex = useMemo(() => {\n if (!Number.isFinite(currentTime)) return -1;\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n if (line.start <= currentTime && currentTime < line.end) return i;\n }\n return -1;\n }, [lines, currentTime]);\n\n /* Match count for search */\n const matchCount = useMemo(() => {\n if (!query) return 0;\n let count = 0;\n const needle = query.toLocaleLowerCase();\n for (const line of lines) {\n const hay = line.text.toLocaleLowerCase();\n let idx = hay.indexOf(needle);\n while (idx !== -1) {\n count += 1;\n idx = hay.indexOf(needle, idx + needle.length);\n }\n }\n return count;\n }, [lines, query]);\n\n /* Ctrl/Cmd+F shortcut — focuses the search field when focus is within. */\n const handleRootKeyDown = useCallback(\n (e: KeyboardEvent<HTMLDivElement>) => {\n if (\n (e.metaKey || e.ctrlKey) &&\n e.key.toLowerCase() === 'f' &&\n searchable\n ) {\n e.preventDefault();\n searchInputRef.current?.focus();\n }\n },\n [searchable],\n );\n\n /* Virtualisation above 100 lines */\n const virtualized = lines.length > VIRTUALIZATION_THRESHOLD;\n const virtualizer = useVirtualizer({\n count: virtualized ? lines.length : 0,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 48,\n overscan: 8,\n getItemKey: (i) => i,\n });\n\n /* Auto-scroll active line into view when it changes. */\n useLayoutEffect(() => {\n if (activeIndex < 0) return;\n const scroller = scrollRef.current;\n if (!scroller) return;\n const target = scroller.querySelector(\n `[data-line-index=\"${activeIndex}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n const prefersReducedMotion =\n typeof window !== 'undefined' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n target.scrollIntoView({\n block: 'center',\n behavior: prefersReducedMotion ? 'auto' : 'smooth',\n });\n }, [activeIndex]);\n\n const renderLine = useCallback(\n (\n line: TranscriptLine,\n index: number,\n extras?: {\n liRef?: (node: HTMLLIElement | null) => void;\n dataIndex?: number;\n // Style is an inline-style escape hatch — virtualizer offsets\n // cannot be expressed as static Tailwind. See 23-constraints §4.\n style?: CSSProperties;\n absolutePositioning?: boolean;\n },\n ) => {\n const isActive = index === activeIndex;\n const slot = speakerSlot(line.speaker);\n const lowConfidence =\n typeof line.confidence === 'number' && line.confidence < 0.6;\n return (\n <li\n key={index}\n ref={extras?.liRef}\n data-line-index={index}\n data-turn-id={String(index)}\n data-index={extras?.dataIndex}\n aria-current={isActive ? 'true' : undefined}\n // eslint-disable-next-line react/forbid-dom-props -- virtualizer translateY offset, runtime-computed\n style={extras?.style}\n className={[\n 'ds:flex ds:items-start ds:gap-[var(--spacing-sm)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n isActive\n ? 'ds:bg-[color:var(--accent)]/12 ds:border-s-[3px] ds:border-[color:var(--primary)]'\n : 'ds:border-s-[3px] ds:border-transparent',\n extras?.absolutePositioning\n ? 'ds:absolute ds:start-0 ds:end-0 ds:top-0 ds:w-full'\n : '',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n <button\n type=\"button\"\n onClick={() => onSeek?.(line.start)}\n aria-label={t('chat.transcript.seekTo', {\n time: formatTime(line.start, i18n.language),\n })}\n className={[\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:font-[family-name:var(--font-mono)]',\n 'type-meta',\n 'ds:tabular-nums',\n 'ds:text-[color:var(--muted-foreground)]',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n // WCAG 2.5.5 target size: 44×44 default, 48×48 in accessible\n // themes — matches the DS `--min-target-size` token.\n 'ds:min-h-[var(--min-target-size)] ds:min-w-[var(--min-target-size)]',\n 'ds:hover:bg-muted/20',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-[color:var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n ].join(' ')}\n >\n <time dir=\"ltr\" dateTime={secondsToIsoDuration(line.start)}>\n {formatTime(line.start, i18n.language)}\n </time>\n </button>\n <div className=\"ds:flex-1 ds:min-w-0\">\n <div className=\"ds:flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n <span\n aria-hidden=\"true\"\n className={`ds:inline-block ds:size-2 ds:rounded-[var(--radius-full)] ${SPEAKER_BG_CLASSES[slot]}`}\n />\n <strong className=\"type-title-item\">\n {t('chat.transcript.speaker', { name: line.speaker })}\n </strong>\n {lowConfidence ? (\n <span className=\"type-meta ds:italic ds:text-[color:var(--muted-foreground)]\">\n ({t('chat.transcript.lowConfidence')})\n </span>\n ) : null}\n </div>\n <span\n dir=\"auto\"\n className={[\n 'ds:block',\n lowConfidence\n ? 'ds:underline ds:decoration-dotted ds:decoration-[color:var(--muted-foreground)]'\n : '',\n ].join(' ')}\n >\n <HighlightedText text={line.text} query={query} />\n </span>\n </div>\n </li>\n );\n },\n [activeIndex, i18n.language, onSeek, query, t],\n );\n\n const rootRef = useRef<HTMLDivElement>(null);\n useImperativeHandle(ref, () => rootRef.current as HTMLDivElement, []);\n\n const agentHandle = useMemo<TranscriptPanelHandle>(\n () => ({\n getSelectedTurn: () => (activeIndex >= 0 ? String(activeIndex) : null),\n focusTurn: (id: string) => {\n const target = scrollRef.current?.querySelector(\n `[data-turn-id=\"${id}\"]`,\n ) as HTMLElement | null;\n if (!target) return;\n target.scrollIntoView({ block: 'center', behavior: 'auto' });\n const btn = target.querySelector(\n 'button',\n ) as HTMLButtonElement | null;\n btn?.focus();\n },\n copyTurn: (id: string) => {\n const index = Number.parseInt(id, 10);\n if (!Number.isFinite(index)) return;\n const line = lines[index];\n if (!line) return;\n if (typeof navigator !== 'undefined' && navigator.clipboard) {\n return navigator.clipboard.writeText(line.text);\n }\n },\n }),\n [activeIndex, lines],\n );\n useAgentRegistration(\n transcriptPanelAgent,\n agentHandle,\n rest.id as string | undefined,\n );\n\n return (\n // eslint-disable-next-line jsx-a11y/no-static-element-interactions -- root keyboard shortcuts (j/k navigation, /) bubble up from focused descendants\n <div\n ref={rootRef}\n onKeyDown={handleRootKeyDown}\n data-component=\"transcript-panel\"\n data-component-id={rest.id}\n className={rootVariants({ size, className })}\n {...rest}\n >\n {searchable ? (\n <div className=\"ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-sm)] ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-xs)] ds:flex ds:items-center ds:gap-[var(--spacing-sm)] ds:border-b ds:border-border\">\n <SearchInput\n ref={searchInputRef}\n id={searchId}\n placeholder={t('chat.transcript.search')}\n aria-label={t('chat.transcript.search')}\n aria-keyshortcuts=\"Meta+F Control+F\"\n debounceMs={150}\n onChange={(v) => setQuery(v)}\n size=\"sm\"\n />\n {query ? (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)] ds:tabular-nums\"\n >\n {matchCount === 0\n ? t('chat.transcript.noMatches')\n : t('chat.transcript.matchCount', { count: matchCount })}\n </span>\n ) : null}\n </div>\n ) : null}\n\n <div\n ref={scrollRef}\n className=\"ds:flex-1 ds:min-h-0 ds:overflow-y-auto ds:overflow-x-hidden\"\n >\n {virtualized ? (\n <ol\n className=\"ds:relative ds:list-none ds:ps-0 ds:m-0\"\n // eslint-disable-next-line react/forbid-dom-props -- virtualizer total size, runtime-computed\n style={{ blockSize: `${virtualizer.getTotalSize()}px` }}\n >\n {virtualizer.getVirtualItems().map((vi) => {\n const line = lines[vi.index];\n if (!line) return null;\n // Put the measureElement ref on the <li> directly — a <div>\n // child of <ol> is invalid and breaks list semantics for\n // assistive tech.\n return renderLine(line, vi.index, {\n liRef: virtualizer.measureElement,\n dataIndex: vi.index,\n style: { transform: `translateY(${vi.start}px)` },\n absolutePositioning: true,\n });\n })}\n </ol>\n ) : (\n <ol className=\"ds:list-none ds:ps-0 ds:m-0\">\n {lines.map((line, index) => renderLine(line, index))}\n </ol>\n )}\n </div>\n </div>\n );\n },\n);\n\nTranscriptPanel.displayName = 'TranscriptPanel';\n"],"names":["transcriptPanelAgent","handle","args","rootVariants","cva","VIRTUALIZATION_THRESHOLD","SPEAKER_SLOT_COUNT","formatTime","seconds","locale","total","m","s","fmt","n","secondsToIsoDuration","speakerSlot","name","hash","i","SPEAKER_BG_CLASSES","escapeRegExp","input","HighlightedText","text","query","escaped","part","jsx","Fragment","TranscriptPanel","forwardRef","lines","currentTime","onSeek","searchable","size","className","rest","ref","t","i18n","useTranslation","setQuery","useState","scrollRef","useRef","searchInputRef","searchId","useId","activeIndex","useMemo","line","matchCount","count","needle","hay","idx","handleRootKeyDown","useCallback","_a","virtualized","virtualizer","useVirtualizer","useLayoutEffect","scroller","target","prefersReducedMotion","renderLine","index","extras","isActive","slot","lowConfidence","jsxs","rootRef","useImperativeHandle","agentHandle","id","btn","useAgentRegistration","SearchInput","v","vi"],"mappings":"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"}
@@ -2,7 +2,7 @@ import { jsxs as g, jsx as a } from "react/jsx-runtime";
2
2
  import { forwardRef as ts, useRef as as, useState as C, useEffect as is, useCallback as p, useMemo as V } from "react";
3
3
  import { c as L } from "./index-D2ZczOXr.js";
4
4
  import { useTranslation as W } from "react-i18next";
5
- import { T as rs } from "./text-input-1oqFRbVI.js";
5
+ import { T as rs } from "./text-input-CakysYnD.js";
6
6
  import { u as ns } from "./form-field-context-B3APVHKx.js";
7
7
  import { c as os } from "./compose-refs-C0k0tdqF.js";
8
8
  import { u as ds } from "./registry-C9nwlNyL.js";
@@ -440,4 +440,4 @@ export {
440
440
  Ns as P,
441
441
  Hs as u
442
442
  };
443
- //# sourceMappingURL=use-password-requirements-DbPZMfV9.js.map
443
+ //# sourceMappingURL=use-password-requirements-BOgFsoIe.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-password-requirements-DbPZMfV9.js","sources":["../../node_modules/lucide-react/dist/esm/icons/eye.js","../../src/components/password-input/password-input.agent.ts","../../src/components/password-input/password-input.tsx","../../src/components/password-input/use-password-requirements.ts"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\",\n key: \"1nclc0\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Eye = createLucideIcon(\"eye\", __iconNode);\n\nexport { __iconNode, Eye as default };\n//# sourceMappingURL=eye.js.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — PasswordInput. */\n/* */\n/* See `src/docs/26-agent-readiness.mdx` for the contract. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { PasswordInputHandle } from './password-input';\n\nexport const passwordInputAgent: AgentAdapter<PasswordInputHandle> = {\n id: 'password-input',\n capabilities: ['edit_inline'],\n state: {\n value: {\n type: 'string',\n descriptionKey: 'ui.agent.passwordInput.state.value',\n description:\n 'Current password value. Never log or persist this off-device.',\n read: (handle) => handle.getValue(),\n },\n isEmpty: {\n type: 'boolean',\n descriptionKey: 'ui.agent.passwordInput.state.isEmpty',\n description: 'Whether the input has no value.',\n read: (handle) => handle.getValue() === '',\n },\n isRevealed: {\n type: 'boolean',\n descriptionKey: 'ui.agent.passwordInput.state.isRevealed',\n description: 'Whether the password is currently shown in plain text.',\n read: (handle) => handle.isRevealed(),\n },\n },\n actions: {\n set_value: {\n safety: 'write',\n argsType: '{ value: string }',\n descriptionKey: 'ui.agent.passwordInput.actions.setValue',\n description: 'Replace the password value.',\n invoke: (handle, args: { value: string }) => {\n handle.setValue(args.value);\n },\n },\n clear: {\n safety: 'destructive',\n descriptionKey: 'ui.agent.passwordInput.actions.clear',\n description: 'Empty the input. Loses any typed value.',\n invoke: (handle) => {\n handle.clear();\n },\n },\n focus: {\n safety: 'read',\n descriptionKey: 'ui.agent.passwordInput.actions.focus',\n description: 'Move keyboard focus to the input.',\n invoke: (handle) => {\n handle.focus();\n },\n },\n toggle_visibility: {\n safety: 'read',\n descriptionKey: 'ui.agent.passwordInput.actions.toggleVisibility',\n description: 'Toggle between masked and plain-text display.',\n invoke: (handle) => {\n handle.toggleVisibility();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'password-input',\n description: 'Marks the PasswordInput wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type FocusEvent,\n type FormEvent,\n type KeyboardEvent,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { AlertTriangle, Check, Circle, Eye, EyeOff } from 'lucide-react';\nimport { TextInput, type TextInputProps } from '../text-input';\nimport { useFormField } from '../form-field/form-field-context';\nimport { composeRefs } from '../_shared/compose-refs';\nimport { useAgentRegistration } from '../../agent';\nimport { passwordInputAgent } from './password-input.agent';\n\n/** Agent-readiness curated handle for PasswordInput. */\nexport interface PasswordInputHandle {\n getValue: () => string;\n setValue: (value: string) => void;\n clear: () => void;\n focus: () => void;\n isRevealed: () => boolean;\n toggleVisibility: () => void;\n}\n\ntype Strength = 0 | 1 | 2 | 3;\n\ntype SizeKey = NonNullable<TextInputProps['size']>;\n\nconst iconSizeClassBySize: Record<SizeKey, string> = {\n sm: 'ds:size-4',\n md: 'ds:size-[18px]',\n lg: 'ds:size-5',\n};\n\nconst toggleVariants = cva(\n [\n 'ds:absolute ds:inset-y-0 ds:end-0 ds:ps-2 ds:pe-3',\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:text-muted-foreground ds:hover:text-foreground',\n 'ds:bg-transparent ds:border-0 ds:cursor-pointer',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:min-w-[var(--min-target-size)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:cursor-not-allowed ds:disabled:opacity-50',\n ].join(' '),\n);\n\nconst strengthTrackVariants = cva(\n 'ds:h-1 ds:w-full ds:overflow-hidden ds:rounded-[var(--radius-full)] ds:bg-muted',\n);\n\nconst strengthBarVariants = cva(\n [\n 'ds:h-full ds:rounded-[var(--radius-full)]',\n 'ds:transition-all ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n level: {\n 0: 'ds:w-1/4 ds:bg-destructive',\n 1: 'ds:w-1/2 ds:bg-warning',\n 2: 'ds:w-3/4 ds:bg-success',\n 3: 'ds:w-full ds:bg-success',\n },\n },\n defaultVariants: { level: 0 },\n },\n);\n\nconst strengthLabelByLevel: Record<\n Strength,\n 'weak' | 'fair' | 'good' | 'strong'\n> = {\n 0: 'weak',\n 1: 'fair',\n 2: 'good',\n 3: 'strong',\n};\n\nconst requirementRowVariants = cva(\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] type-meta',\n {\n variants: {\n met: {\n true: 'ds:text-success',\n false: 'ds:text-muted-foreground',\n },\n },\n defaultVariants: { met: false },\n },\n);\n\n/**\n * One row in the live requirements checklist. Consumers either build\n * these inline or pull a pre-built set from `usePasswordRequirements`.\n */\nexport interface PasswordRequirement {\n /** Stable id — keyed render + a11y. */\n id: string;\n /** Already-translated label. The consumer owns the wording. */\n label: string;\n /** Predicate against the current value. Pure, runs on every keystroke. */\n test: (value: string) => boolean;\n}\n\nexport interface PasswordInputProps\n extends\n Omit<TextInputProps, 'type' | 'endAdornment' | 'startAdornment'>,\n VariantProps<typeof strengthBarVariants> {\n /** Callback when reveal state changes (for analytics). */\n onRevealChange?: (revealed: boolean) => void;\n /** Show the strength meter below the input. */\n showStrength?: boolean;\n /** Strength score: 0 = weak, 1 = fair, 2 = good, 3 = strong. */\n strength?: Strength;\n /**\n * Live requirements checklist rendered between the input and the\n * strength meter / caps-lock notice. Each row's predicate runs on\n * every keystroke and the row's `data-state` flips between `\"met\"`\n * and `\"unmet\"`. Backed by `aria-live=\"polite\"` so screen readers\n * announce each transition as a visually-hidden suffix span flips\n * between localised \"met\" / \"not yet met\" copy.\n *\n * Tip: hoist or `useMemo` the array — inlining it in JSX makes the\n * predicate-results memo bust every render. The kit's\n * `usePasswordRequirements` helper already does this.\n */\n requirements?: ReadonlyArray<PasswordRequirement>;\n /**\n * When `true` AND `strength` is not provided, derive a 0–3 score\n * from the proportion of `requirements` satisfied. Lets consumers\n * get the strength bar \"for free\" once they declare requirements.\n */\n deriveStrength?: boolean;\n /**\n * Autocomplete hint. Defaults to 'current-password'.\n * Use 'new-password' for registration/change-password forms.\n */\n autoComplete?: 'current-password' | 'new-password' | (string & {});\n}\n\nexport const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(\n (\n {\n onRevealChange,\n showStrength = false,\n strength,\n requirements,\n deriveStrength = false,\n autoComplete,\n size = 'md',\n disabled,\n name,\n id,\n onKeyDown,\n onKeyUp,\n onBlur,\n onInput,\n defaultValue,\n value,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const ctx = useFormField();\n const effectiveDisabled = ctx.disabled || disabled;\n\n const innerInputRef = useRef<HTMLInputElement | null>(null);\n const composedRef = composeRefs(ref, innerInputRef);\n\n const [revealed, setRevealed] = useState(false);\n const [capsLock, setCapsLock] = useState(false);\n\n // Track the current value internally so the requirements checklist\n // can re-evaluate predicates without forcing consumers to lift the\n // input into a controlled state. The seed is whichever of\n // `value` / `defaultValue` was passed; the `input` listener then\n // keeps it in sync on every keystroke (covers both controlled and\n // uncontrolled callers — for controlled callers we resync to the\n // incoming `value` prop in the effect below).\n const [internalValue, setInternalValue] = useState<string>(() => {\n if (typeof value === 'string') return value;\n if (typeof defaultValue === 'string') return defaultValue;\n return '';\n });\n\n useEffect(() => {\n if (typeof value === 'string') setInternalValue(value);\n }, [value]);\n\n const handleInput = useCallback(\n (event: FormEvent<HTMLInputElement>) => {\n setInternalValue(event.currentTarget.value);\n onInput?.(event);\n },\n [onInput],\n );\n\n const writeValueToInput = useCallback((next: string) => {\n const node = innerInputRef.current;\n if (!node) return;\n const prototype = Object.getPrototypeOf(node) as HTMLInputElement;\n const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;\n setter?.call(node, next);\n node.dispatchEvent(new Event('input', { bubbles: true }));\n node.dispatchEvent(new Event('change', { bubbles: true }));\n }, []);\n\n const handleToggle = useCallback(() => {\n setRevealed((prev) => {\n const next = !prev;\n onRevealChange?.(next);\n return next;\n });\n }, [onRevealChange]);\n\n const detectCaps = useCallback((event: KeyboardEvent<HTMLInputElement>) => {\n if (typeof event.getModifierState === 'function') {\n setCapsLock(event.getModifierState('CapsLock'));\n }\n }, []);\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n detectCaps(event);\n onKeyDown?.(event);\n },\n [detectCaps, onKeyDown],\n );\n\n const handleKeyUp = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n detectCaps(event);\n onKeyUp?.(event);\n },\n [detectCaps, onKeyUp],\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLInputElement>) => {\n setCapsLock(false);\n onBlur?.(event);\n },\n [onBlur],\n );\n\n const agentHandle = useMemo<PasswordInputHandle>(\n () => ({\n getValue: () => innerInputRef.current?.value ?? '',\n setValue: (next) => writeValueToInput(next),\n clear: () => writeValueToInput(''),\n focus: () => innerInputRef.current?.focus(),\n isRevealed: () => revealed,\n toggleVisibility: () => {\n setRevealed((prev) => {\n const next = !prev;\n onRevealChange?.(next);\n return next;\n });\n },\n }),\n [onRevealChange, revealed, writeValueToInput],\n );\n useAgentRegistration(passwordInputAgent, agentHandle, id);\n\n const iconSizeClass = iconSizeClassBySize[size];\n const toggleLabel = t(\n revealed ? 'inputs.password.toggleHide' : 'inputs.password.toggleShow',\n revealed ? 'Hide password' : 'Show password',\n );\n\n // Evaluate each requirement against the current value. Cheap by\n // construction — predicates are pure regex/length checks supplied\n // by the consumer; the kit doesn't memoise per-row because the\n // map is O(n) over a handful of rules and React would re-render\n // the row anyway when its `data-state` flips.\n const requirementResults = useMemo(\n () =>\n requirements?.map((req) => ({\n ...req,\n met: req.test(internalValue),\n })) ?? null,\n [requirements, internalValue],\n );\n\n // Derive a 0–3 score from the satisfied proportion when the\n // consumer hasn't supplied an explicit `strength`. Formula:\n // `floor((satisfied / total) * 4)`, clamped to 3 so a fully-met\n // checklist matches the explicit \"strong\" level. With zero\n // requirements we fall back to 0 — meaningless on its own but\n // never displayed since `showStrength` is what controls the bar.\n const derivedStrength: Strength = useMemo(() => {\n if (!deriveStrength || !requirementResults?.length) return 0;\n const satisfied = requirementResults.filter((r) => r.met).length;\n const total = requirementResults.length;\n return Math.min(3, Math.floor((satisfied / total) * 4)) as Strength;\n }, [deriveStrength, requirementResults]);\n\n const effectiveStrength: Strength =\n strength !== undefined ? strength : deriveStrength ? derivedStrength : 0;\n const strengthKey = strengthLabelByLevel[effectiveStrength];\n const strengthText = t(\n `inputs.password.strength.${strengthKey}`,\n strengthKey.charAt(0).toUpperCase() + strengthKey.slice(1),\n );\n\n return (\n <div\n data-component=\"password-input\"\n data-component-id={id}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\"\n >\n <div className=\"ds:relative\">\n <TextInput\n ref={composedRef}\n id={id}\n type={revealed ? 'text' : 'password'}\n autoComplete={autoComplete ?? 'current-password'}\n name={name ?? 'password'}\n spellCheck={false}\n autoCapitalize=\"none\"\n autoCorrect=\"off\"\n size={size}\n disabled={effectiveDisabled}\n onKeyDown={handleKeyDown}\n onKeyUp={handleKeyUp}\n onBlur={handleBlur}\n onInput={handleInput}\n value={value}\n defaultValue={defaultValue}\n endAdornment={\n <span aria-hidden=\"true\" className=\"ds:block ds:size-4\" />\n }\n className={className}\n {...rest}\n />\n <button\n type=\"button\"\n aria-pressed={revealed}\n aria-label={toggleLabel}\n disabled={effectiveDisabled}\n onClick={handleToggle}\n className={toggleVariants()}\n >\n {revealed ? (\n <EyeOff aria-hidden=\"true\" className={iconSizeClass} />\n ) : (\n <Eye aria-hidden=\"true\" className={iconSizeClass} />\n )}\n </button>\n </div>\n\n {requirementResults && requirementResults.length > 0 ? (\n // `role=\"list\"` / `role=\"listitem\"` are set explicitly even\n // though they're the implicit roles on <ul>/<li> — Safari\n // VoiceOver strips the implicit list role when CSS sets\n // `list-style: none` (which `ds:list-none` does), so the\n // redundancy is load-bearing for AT, not a lint nit.\n //\n // aria-live=\"polite\" announces transitions as the per-row\n // `.ds:sr-only` state suffix flips between \"met\" / \"not yet\n // met\". aria-live observes text-content mutations, not\n // attribute or class changes, so the data-state attribute\n // alone wouldn't trigger an announcement — the sr-only span\n // is the load-bearing piece for AT users.\n // eslint-disable-next-line jsx-a11y/no-redundant-roles\n <ul\n role=\"list\"\n aria-live=\"polite\"\n data-component=\"password-requirements\"\n className=\"ds:list-none ds:m-0 ds:p-0 ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n {requirementResults.map((req) => (\n // eslint-disable-next-line jsx-a11y/no-redundant-roles\n <li\n key={req.id}\n role=\"listitem\"\n data-state={req.met ? 'met' : 'unmet'}\n className={requirementRowVariants({ met: req.met })}\n >\n {req.met ? (\n <Check\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:text-success ds:shrink-0\"\n />\n ) : (\n <Circle\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:shrink-0\"\n />\n )}\n <span>{req.label}</span>\n <span className=\"ds:sr-only\">\n {' — '}\n {req.met\n ? t('inputs.password.requirements.met', 'met')\n : t('inputs.password.requirements.unmet', 'not yet met')}\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n\n {capsLock ? (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] type-meta ds:text-warning\"\n >\n <AlertTriangle aria-hidden=\"true\" className=\"ds:size-3.5\" />\n {t('inputs.password.capsLock', 'Caps Lock is on')}\n </span>\n ) : null}\n\n {showStrength ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <div\n role=\"progressbar\"\n aria-valuenow={effectiveStrength}\n aria-valuemin={0}\n aria-valuemax={3}\n aria-label={t(\n 'inputs.password.strengthLabel',\n 'Password strength',\n )}\n aria-valuetext={strengthText}\n className={strengthTrackVariants()}\n >\n <div\n className={strengthBarVariants({ level: effectiveStrength })}\n />\n </div>\n <span\n aria-hidden=\"true\"\n className=\"type-meta ds:text-muted-foreground\"\n >\n {strengthText}\n </span>\n </div>\n ) : null}\n </div>\n );\n },\n);\n\nPasswordInput.displayName = 'PasswordInput';\n","import { useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport type { PasswordRequirement } from './password-input';\n\nexport interface UsePasswordRequirementsOptions {\n /**\n * Minimum password length. When set, adds a \"length\" requirement.\n * Pass `12` (or whatever the server-side regex enforces) so the\n * inline checklist tells users the same rule they'll bounce off\n * server-side. When omitted, no length row is rendered.\n */\n minLength?: number;\n /** Adds an \"uppercase letter\" requirement when true. */\n uppercase?: boolean;\n /** Adds a \"lowercase letter\" requirement when true. */\n lowercase?: boolean;\n /** Adds a \"digit\" requirement when true. */\n digit?: boolean;\n /** Adds a \"symbol\" requirement when true. Matches anything outside `[A-Za-z0-9\\s]`. */\n symbol?: boolean;\n}\n\n/**\n * Builds the common set of password requirements with i18n-resolved\n * labels. Designed for the AlfaDocs platform's patient-registration\n * server-side regex (`/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{12,}$/`) but\n * generic enough for any consumer.\n *\n * Each opt-in flag adds one row to the returned array, in a stable\n * visual order (length → uppercase → lowercase → digit → symbol).\n * Predicates are pure and run on every keystroke.\n *\n * @example\n * const requirements = usePasswordRequirements({\n * minLength: 12,\n * uppercase: true,\n * lowercase: true,\n * digit: true,\n * });\n * <PasswordInput requirements={requirements} deriveStrength />\n */\nexport function usePasswordRequirements(\n opts: UsePasswordRequirementsOptions = {},\n): PasswordRequirement[] {\n const { t } = useTranslation();\n const { minLength, uppercase, lowercase, digit, symbol } = opts;\n\n return useMemo(() => {\n const list: PasswordRequirement[] = [];\n\n if (typeof minLength === 'number' && minLength > 0) {\n list.push({\n id: 'length',\n label: t('inputs.password.requirements.length', {\n count: minLength,\n defaultValue: `At least ${minLength} characters`,\n }),\n test: (v) => v.length >= minLength,\n });\n }\n\n if (uppercase) {\n list.push({\n id: 'uppercase',\n label: t(\n 'inputs.password.requirements.uppercase',\n 'One uppercase letter',\n ),\n test: (v) => /\\p{Lu}/u.test(v),\n });\n }\n\n if (lowercase) {\n list.push({\n id: 'lowercase',\n label: t(\n 'inputs.password.requirements.lowercase',\n 'One lowercase letter',\n ),\n test: (v) => /\\p{Ll}/u.test(v),\n });\n }\n\n if (digit) {\n list.push({\n id: 'digit',\n label: t('inputs.password.requirements.digit', 'One number'),\n test: (v) => /\\p{Nd}/u.test(v),\n });\n }\n\n if (symbol) {\n list.push({\n id: 'symbol',\n label: t('inputs.password.requirements.symbol', 'One symbol'),\n // Anything that isn't a letter, number, or whitespace counts.\n // `\\p{L}` + `\\p{N}` keep this Unicode-aware so non-Latin\n // alphabets (Arabic, Chinese, …) aren't mis-classified as\n // symbols.\n test: (v) => /[^\\p{L}\\p{N}\\s]/u.test(v),\n });\n }\n\n return list;\n }, [t, minLength, uppercase, lowercase, digit, symbol]);\n}\n"],"names":["__iconNode","Eye","createLucideIcon","passwordInputAgent","handle","args","iconSizeClassBySize","toggleVariants","cva","strengthTrackVariants","strengthBarVariants","strengthLabelByLevel","requirementRowVariants","PasswordInput","forwardRef","onRevealChange","showStrength","strength","requirements","deriveStrength","autoComplete","size","disabled","name","id","onKeyDown","onKeyUp","onBlur","onInput","defaultValue","value","className","rest","ref","t","useTranslation","effectiveDisabled","useFormField","innerInputRef","useRef","composedRef","composeRefs","revealed","setRevealed","useState","capsLock","setCapsLock","internalValue","setInternalValue","useEffect","handleInput","useCallback","event","writeValueToInput","next","node","prototype","setter","_a","handleToggle","prev","detectCaps","handleKeyDown","handleKeyUp","handleBlur","agentHandle","useMemo","useAgentRegistration","iconSizeClass","toggleLabel","requirementResults","req","derivedStrength","satisfied","r","total","effectiveStrength","strengthKey","strengthText","jsxs","jsx","TextInput","EyeOff","Check","Circle","AlertTriangle","usePasswordRequirements","opts","minLength","uppercase","lowercase","digit","symbol","list","v"],"mappings":";;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,MAAMA,KAAa;AAAA,EACjB;AAAA,IACE;AAAA,IACA;AAAA,MACE,GAAG;AAAA,MACH,KAAK;AAAA,IACX;AAAA,EACA;AAAA,EACE,CAAC,UAAU,EAAE,IAAI,MAAM,IAAI,MAAM,GAAG,KAAK,KAAK,SAAQ,CAAE;AAC1D,GACMC,KAAMC,GAAiB,OAAOF,EAAU,GCVjCG,KAAwD;AAAA,EACnE,IAAI;AAAA,EACJ,cAAc,CAAC,aAAa;AAAA,EAC5B,OAAO;AAAA,IACL,OAAO;AAAA,MACL,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aACE;AAAA,MACF,MAAM,CAACC,MAAWA,EAAO,SAAA;AAAA,IAAS;AAAA,IAEpC,SAAS;AAAA,MACP,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,MAAM,CAACA,MAAWA,EAAO,eAAe;AAAA,IAAA;AAAA,IAE1C,YAAY;AAAA,MACV,MAAM;AAAA,MACN,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,MAAM,CAACA,MAAWA,EAAO,WAAA;AAAA,IAAW;AAAA,EACtC;AAAA,EAEF,SAAS;AAAA,IACP,WAAW;AAAA,MACT,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ,CAACA,GAAQC,MAA4B;AAC3C,QAAAD,EAAO,SAASC,EAAK,KAAK;AAAA,MAC5B;AAAA,IAAA;AAAA,IAEF,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ,CAACD,MAAW;AAClB,QAAAA,EAAO,MAAA;AAAA,MACT;AAAA,IAAA;AAAA,IAEF,OAAO;AAAA,MACL,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ,CAACA,MAAW;AAClB,QAAAA,EAAO,MAAA;AAAA,MACT;AAAA,IAAA;AAAA,IAEF,mBAAmB;AAAA,MACjB,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,aAAa;AAAA,MACb,QAAQ,CAACA,MAAW;AAClB,QAAAA,EAAO,iBAAA;AAAA,MACT;AAAA,IAAA;AAAA,EACF;AAAA,EAEF,UAAU;AAAA,IACR,MAAM;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,IAAA;AAAA,IAEf,YAAY;AAAA,MACV,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,aAAa;AAAA,IAAA;AAAA,EACf;AAEJ,GC9CME,KAA+C;AAAA,EACnD,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN,GAEMC,KAAiBC;AAAA,EACrB;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AACZ,GAEMC,KAAwBD;AAAA,EAC5B;AACF,GAEME,KAAsBF;AAAA,EAC1B;AAAA,IACE;AAAA,IACA;AAAA,EAAA,EACA,KAAK,GAAG;AAAA,EACV;AAAA,IACE,UAAU;AAAA,MACR,OAAO;AAAA,QACL,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAG;AAAA,MAAA;AAAA,IACL;AAAA,IAEF,iBAAiB,EAAE,OAAO,EAAA;AAAA,EAAE;AAEhC,GAEMG,KAGF;AAAA,EACF,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL,GAEMC,KAAyBJ;AAAA,EAC7B;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,KAAK;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,MAAA;AAAA,IACT;AAAA,IAEF,iBAAiB,EAAE,KAAK,GAAA;AAAA,EAAM;AAElC,GAmDaK,KAAgBC;AAAA,EAC3B,CACE;AAAA,IACE,gBAAAC;AAAA,IACA,cAAAC,IAAe;AAAA,IACf,UAAAC;AAAA,IACA,cAAAC;AAAA,IACA,gBAAAC,IAAiB;AAAA,IACjB,cAAAC;AAAA,IACA,MAAAC,IAAO;AAAA,IACP,UAAAC;AAAA,IACA,MAAAC;AAAA,IACA,IAAAC;AAAA,IACA,WAAAC;AAAA,IACA,SAAAC;AAAA,IACA,QAAAC;AAAA,IACA,SAAAC;AAAA,IACA,cAAAC;AAAA,IACA,OAAAC;AAAA,IACA,WAAAC;AAAA,IACA,GAAGC;AAAA,EAAA,GAELC,MACG;AACH,UAAM,EAAE,GAAAC,EAAA,IAAMC,EAAA,GAERC,IADMC,GAAA,EACkB,YAAYf,GAEpCgB,IAAgBC,GAAgC,IAAI,GACpDC,IAAcC,GAAYR,GAAKK,CAAa,GAE5C,CAACI,GAAUC,CAAW,IAAIC,EAAS,EAAK,GACxC,CAACC,GAAUC,CAAW,IAAIF,EAAS,EAAK,GASxC,CAACG,GAAeC,CAAgB,IAAIJ,EAAiB,MACrD,OAAOd,KAAU,WAAiBA,IAClC,OAAOD,KAAiB,WAAiBA,IACtC,EACR;AAED,IAAAoB,GAAU,MAAM;AACd,MAAI,OAAOnB,KAAU,YAAUkB,EAAiBlB,CAAK;AAAA,IACvD,GAAG,CAACA,CAAK,CAAC;AAEV,UAAMoB,IAAcC;AAAA,MAClB,CAACC,MAAuC;AACtC,QAAAJ,EAAiBI,EAAM,cAAc,KAAK,GAC1CxB,KAAA,QAAAA,EAAUwB;AAAA,MACZ;AAAA,MACA,CAACxB,CAAO;AAAA,IAAA,GAGJyB,IAAoBF,EAAY,CAACG,MAAiB;;AACtD,YAAMC,IAAOjB,EAAc;AAC3B,UAAI,CAACiB,EAAM;AACX,YAAMC,IAAY,OAAO,eAAeD,CAAI,GACtCE,KAASC,IAAA,OAAO,yBAAyBF,GAAW,OAAO,MAAlD,gBAAAE,EAAqD;AACpE,MAAAD,KAAA,QAAAA,EAAQ,KAAKF,GAAMD,IACnBC,EAAK,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,GAAA,CAAM,CAAC,GACxDA,EAAK,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,GAAA,CAAM,CAAC;AAAA,IAC3D,GAAG,CAAA,CAAE,GAECI,IAAeR,EAAY,MAAM;AACrC,MAAAR,EAAY,CAACiB,MAAS;AACpB,cAAMN,IAAO,CAACM;AACd,eAAA7C,KAAA,QAAAA,EAAiBuC,IACVA;AAAA,MACT,CAAC;AAAA,IACH,GAAG,CAACvC,CAAc,CAAC,GAEb8C,IAAaV,EAAY,CAACC,MAA2C;AACzE,MAAI,OAAOA,EAAM,oBAAqB,cACpCN,EAAYM,EAAM,iBAAiB,UAAU,CAAC;AAAA,IAElD,GAAG,CAAA,CAAE,GAECU,IAAgBX;AAAA,MACpB,CAACC,MAA2C;AAC1C,QAAAS,EAAWT,CAAK,GAChB3B,KAAA,QAAAA,EAAY2B;AAAA,MACd;AAAA,MACA,CAACS,GAAYpC,CAAS;AAAA,IAAA,GAGlBsC,IAAcZ;AAAA,MAClB,CAACC,MAA2C;AAC1C,QAAAS,EAAWT,CAAK,GAChB1B,KAAA,QAAAA,EAAU0B;AAAA,MACZ;AAAA,MACA,CAACS,GAAYnC,CAAO;AAAA,IAAA,GAGhBsC,IAAab;AAAA,MACjB,CAACC,MAAwC;AACvC,QAAAN,EAAY,EAAK,GACjBnB,KAAA,QAAAA,EAASyB;AAAA,MACX;AAAA,MACA,CAACzB,CAAM;AAAA,IAAA,GAGHsC,IAAcC;AAAA,MAClB,OAAO;AAAA,QACL,UAAU,MAAA;;AAAM,mBAAAR,IAAApB,EAAc,YAAd,gBAAAoB,EAAuB,UAAS;AAAA;AAAA,QAChD,UAAU,CAACJ,MAASD,EAAkBC,CAAI;AAAA,QAC1C,OAAO,MAAMD,EAAkB,EAAE;AAAA,QACjC,OAAO,MAAA;;AAAM,kBAAAK,IAAApB,EAAc,YAAd,gBAAAoB,EAAuB;AAAA;AAAA,QACpC,YAAY,MAAMhB;AAAA,QAClB,kBAAkB,MAAM;AACtB,UAAAC,EAAY,CAACiB,MAAS;AACpB,kBAAMN,IAAO,CAACM;AACd,mBAAA7C,KAAA,QAAAA,EAAiBuC,IACVA;AAAA,UACT,CAAC;AAAA,QACH;AAAA,MAAA;AAAA,MAEF,CAACvC,GAAgB2B,GAAUW,CAAiB;AAAA,IAAA;AAE9C,IAAAc,GAAqBhE,IAAoB8D,GAAazC,CAAE;AAExD,UAAM4C,IAAgB9D,GAAoBe,CAAI,GACxCgD,KAAcnC;AAAA,MAClBQ,IAAW,+BAA+B;AAAA,MAC1CA,IAAW,kBAAkB;AAAA,IAAA,GAQzB4B,IAAqBJ;AAAA,MACzB,OACEhD,KAAA,gBAAAA,EAAc,IAAI,CAACqD,OAAS;AAAA,QAC1B,GAAGA;AAAA,QACH,KAAKA,EAAI,KAAKxB,CAAa;AAAA,MAAA,QACtB;AAAA,MACT,CAAC7B,GAAc6B,CAAa;AAAA,IAAA,GASxByB,KAA4BN,EAAQ,MAAM;AAC9C,UAAI,CAAC/C,KAAkB,EAACmD,KAAA,QAAAA,EAAoB,QAAQ,QAAO;AAC3D,YAAMG,IAAYH,EAAmB,OAAO,CAACI,MAAMA,EAAE,GAAG,EAAE,QACpDC,IAAQL,EAAmB;AACjC,aAAO,KAAK,IAAI,GAAG,KAAK,MAAOG,IAAYE,IAAS,CAAC,CAAC;AAAA,IACxD,GAAG,CAACxD,GAAgBmD,CAAkB,CAAC,GAEjCM,IACJ3D,MAAa,SAAYA,IAAWE,IAAiBqD,KAAkB,GACnEK,IAAclE,GAAqBiE,CAAiB,GACpDE,IAAe5C;AAAA,MACnB,4BAA4B2C,CAAW;AAAA,MACvCA,EAAY,OAAO,CAAC,EAAE,gBAAgBA,EAAY,MAAM,CAAC;AAAA,IAAA;AAG3D,WACE,gBAAAE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,kBAAe;AAAA,QACf,qBAAmBvD;AAAA,QACnB,WAAU;AAAA,QAEV,UAAA;AAAA,UAAA,gBAAAuD,EAAC,OAAA,EAAI,WAAU,eACb,UAAA;AAAA,YAAA,gBAAAC;AAAA,cAACC;AAAA,cAAA;AAAA,gBACC,KAAKzC;AAAA,gBACL,IAAAhB;AAAA,gBACA,MAAMkB,IAAW,SAAS;AAAA,gBAC1B,cAActB,KAAgB;AAAA,gBAC9B,MAAMG,KAAQ;AAAA,gBACd,YAAY;AAAA,gBACZ,gBAAe;AAAA,gBACf,aAAY;AAAA,gBACZ,MAAAF;AAAA,gBACA,UAAUe;AAAA,gBACV,WAAW0B;AAAA,gBACX,SAASC;AAAA,gBACT,QAAQC;AAAA,gBACR,SAASd;AAAA,gBACT,OAAApB;AAAA,gBACA,cAAAD;AAAA,gBACA,cACE,gBAAAmD,EAAC,QAAA,EAAK,eAAY,QAAO,WAAU,sBAAqB;AAAA,gBAE1D,WAAAjD;AAAA,gBACC,GAAGC;AAAA,cAAA;AAAA,YAAA;AAAA,YAEN,gBAAAgD;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,gBAActC;AAAA,gBACd,cAAY2B;AAAA,gBACZ,UAAUjC;AAAA,gBACV,SAASuB;AAAA,gBACT,WAAWpD,GAAA;AAAA,gBAEV,UAAAmC,IACC,gBAAAsC,EAACE,IAAA,EAAO,eAAY,QAAO,WAAWd,EAAA,CAAe,IAErD,gBAAAY,EAAC/E,IAAA,EAAI,eAAY,QAAO,WAAWmE,EAAA,CAAe;AAAA,cAAA;AAAA,YAAA;AAAA,UAEtD,GACF;AAAA,UAECE,KAAsBA,EAAmB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAcjD,gBAAAU;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,aAAU;AAAA,gBACV,kBAAe;AAAA,gBACf,WAAU;AAAA,gBAET,UAAAV,EAAmB,IAAI,CAACC;AAAA;AAAA,kBAEvB,gBAAAQ;AAAA,oBAAC;AAAA,oBAAA;AAAA,sBAEC,MAAK;AAAA,sBACL,cAAYR,EAAI,MAAM,QAAQ;AAAA,sBAC9B,WAAW3D,GAAuB,EAAE,KAAK2D,EAAI,KAAK;AAAA,sBAEjD,UAAA;AAAA,wBAAAA,EAAI,MACH,gBAAAS;AAAA,0BAACG;AAAA,0BAAA;AAAA,4BACC,eAAY;AAAA,4BACZ,WAAU;AAAA,0BAAA;AAAA,wBAAA,IAGZ,gBAAAH;AAAA,0BAACI;AAAA,0BAAA;AAAA,4BACC,eAAY;AAAA,4BACZ,WAAU;AAAA,0BAAA;AAAA,wBAAA;AAAA,wBAGd,gBAAAJ,EAAC,QAAA,EAAM,UAAAT,EAAI,MAAA,CAAM;AAAA,wBACjB,gBAAAQ,EAAC,QAAA,EAAK,WAAU,cACb,UAAA;AAAA,0BAAA;AAAA,0BACAR,EAAI,MACDrC,EAAE,oCAAoC,KAAK,IAC3CA,EAAE,sCAAsC,aAAa;AAAA,wBAAA,EAAA,CAC3D;AAAA,sBAAA;AAAA,oBAAA;AAAA,oBAtBKqC,EAAI;AAAA,kBAAA;AAAA,iBAwBZ;AAAA,cAAA;AAAA,YAAA;AAAA,cAED;AAAA,UAEH1B,IACC,gBAAAkC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,MAAK;AAAA,cACL,aAAU;AAAA,cACV,WAAU;AAAA,cAEV,UAAA;AAAA,gBAAA,gBAAAC,EAACK,IAAA,EAAc,eAAY,QAAO,WAAU,eAAc;AAAA,gBACzDnD,EAAE,4BAA4B,iBAAiB;AAAA,cAAA;AAAA,YAAA;AAAA,UAAA,IAEhD;AAAA,UAEHlB,IACC,gBAAA+D,EAAC,OAAA,EAAI,WAAU,kDACb,UAAA;AAAA,YAAA,gBAAAC;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,MAAK;AAAA,gBACL,iBAAeJ;AAAA,gBACf,iBAAe;AAAA,gBACf,iBAAe;AAAA,gBACf,cAAY1C;AAAA,kBACV;AAAA,kBACA;AAAA,gBAAA;AAAA,gBAEF,kBAAgB4C;AAAA,gBAChB,WAAWrE,GAAA;AAAA,gBAEX,UAAA,gBAAAuE;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,WAAWtE,GAAoB,EAAE,OAAOkE,GAAmB;AAAA,kBAAA;AAAA,gBAAA;AAAA,cAC7D;AAAA,YAAA;AAAA,YAEF,gBAAAI;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,eAAY;AAAA,gBACZ,WAAU;AAAA,gBAET,UAAAF;AAAA,cAAA;AAAA,YAAA;AAAA,UACH,EAAA,CACF,IACE;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAGV;AACF;AAEAjE,GAAc,cAAc;AC/ZrB,SAASyE,GACdC,IAAuC,IAChB;AACvB,QAAM,EAAE,GAAArD,EAAA,IAAMC,EAAA,GACR,EAAE,WAAAqD,GAAW,WAAAC,GAAW,WAAAC,GAAW,OAAAC,GAAO,QAAAC,MAAWL;AAE3D,SAAOrB,EAAQ,MAAM;AACnB,UAAM2B,IAA8B,CAAA;AAEpC,WAAI,OAAOL,KAAc,YAAYA,IAAY,KAC/CK,EAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,OAAO3D,EAAE,uCAAuC;AAAA,QAC9C,OAAOsD;AAAA,QACP,cAAc,YAAYA,CAAS;AAAA,MAAA,CACpC;AAAA,MACD,MAAM,CAACM,MAAMA,EAAE,UAAUN;AAAA,IAAA,CAC1B,GAGCC,KACFI,EAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,OAAO3D;AAAA,QACL;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,MAAM,CAAC4D,MAAM,WAAA,WAAA,GAAA,EAAU,KAAKA,CAAC;AAAA,IAAA,CAC9B,GAGCJ,KACFG,EAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,OAAO3D;AAAA,QACL;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,MAAM,CAAC4D,MAAM,WAAA,WAAA,GAAA,EAAU,KAAKA,CAAC;AAAA,IAAA,CAC9B,GAGCH,KACFE,EAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,OAAO3D,EAAE,sCAAsC,YAAY;AAAA,MAC3D,MAAM,CAAC4D,MAAM,WAAA,WAAA,GAAA,EAAU,KAAKA,CAAC;AAAA,IAAA,CAC9B,GAGCF,KACFC,EAAK,KAAK;AAAA,MACR,IAAI;AAAA,MACJ,OAAO3D,EAAE,uCAAuC,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,MAK5D,MAAM,CAAC4D,MAAM,mBAAmB,KAAKA,CAAC;AAAA,IAAA,CACvC,GAGID;AAAA,EACT,GAAG,CAAC3D,GAAGsD,GAAWC,GAAWC,GAAWC,GAAOC,CAAM,CAAC;AACxD;","x_google_ignoreList":[0]}
1
+ {"version":3,"file":"use-password-requirements-BOgFsoIe.js","sources":["../../node_modules/lucide-react/dist/esm/icons/eye.js","../../src/components/password-input/password-input.agent.ts","../../src/components/password-input/password-input.tsx","../../src/components/password-input/use-password-requirements.ts"],"sourcesContent":["/**\n * @license lucide-react v1.8.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.js';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\",\n key: \"1nclc0\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Eye = createLucideIcon(\"eye\", __iconNode);\n\nexport { __iconNode, Eye as default };\n//# sourceMappingURL=eye.js.map\n","/* -------------------------------------------------------------------- */\n/* Agent adapter — PasswordInput. */\n/* */\n/* See `src/docs/26-agent-readiness.mdx` for the contract. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { PasswordInputHandle } from './password-input';\n\nexport const passwordInputAgent: AgentAdapter<PasswordInputHandle> = {\n id: 'password-input',\n capabilities: ['edit_inline'],\n state: {\n value: {\n type: 'string',\n descriptionKey: 'ui.agent.passwordInput.state.value',\n description:\n 'Current password value. Never log or persist this off-device.',\n read: (handle) => handle.getValue(),\n },\n isEmpty: {\n type: 'boolean',\n descriptionKey: 'ui.agent.passwordInput.state.isEmpty',\n description: 'Whether the input has no value.',\n read: (handle) => handle.getValue() === '',\n },\n isRevealed: {\n type: 'boolean',\n descriptionKey: 'ui.agent.passwordInput.state.isRevealed',\n description: 'Whether the password is currently shown in plain text.',\n read: (handle) => handle.isRevealed(),\n },\n },\n actions: {\n set_value: {\n safety: 'write',\n argsType: '{ value: string }',\n descriptionKey: 'ui.agent.passwordInput.actions.setValue',\n description: 'Replace the password value.',\n invoke: (handle, args: { value: string }) => {\n handle.setValue(args.value);\n },\n },\n clear: {\n safety: 'destructive',\n descriptionKey: 'ui.agent.passwordInput.actions.clear',\n description: 'Empty the input. Loses any typed value.',\n invoke: (handle) => {\n handle.clear();\n },\n },\n focus: {\n safety: 'read',\n descriptionKey: 'ui.agent.passwordInput.actions.focus',\n description: 'Move keyboard focus to the input.',\n invoke: (handle) => {\n handle.focus();\n },\n },\n toggle_visibility: {\n safety: 'read',\n descriptionKey: 'ui.agent.passwordInput.actions.toggleVisibility',\n description: 'Toggle between masked and plain-text display.',\n invoke: (handle) => {\n handle.toggleVisibility();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'password-input',\n description: 'Marks the PasswordInput wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type FocusEvent,\n type FormEvent,\n type KeyboardEvent,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { AlertTriangle, Check, Circle, Eye, EyeOff } from 'lucide-react';\nimport { TextInput, type TextInputProps } from '../text-input';\nimport { useFormField } from '../form-field/form-field-context';\nimport { composeRefs } from '../_shared/compose-refs';\nimport { useAgentRegistration } from '../../agent';\nimport { passwordInputAgent } from './password-input.agent';\n\n/** Agent-readiness curated handle for PasswordInput. */\nexport interface PasswordInputHandle {\n getValue: () => string;\n setValue: (value: string) => void;\n clear: () => void;\n focus: () => void;\n isRevealed: () => boolean;\n toggleVisibility: () => void;\n}\n\ntype Strength = 0 | 1 | 2 | 3;\n\ntype SizeKey = NonNullable<TextInputProps['size']>;\n\nconst iconSizeClassBySize: Record<SizeKey, string> = {\n sm: 'ds:size-4',\n md: 'ds:size-[18px]',\n lg: 'ds:size-5',\n};\n\nconst toggleVariants = cva(\n [\n 'ds:absolute ds:inset-y-0 ds:end-0 ds:ps-2 ds:pe-3',\n 'ds:inline-flex ds:items-center ds:justify-center',\n 'ds:text-muted-foreground ds:hover:text-foreground',\n 'ds:bg-transparent ds:border-0 ds:cursor-pointer',\n 'ds:rounded-[var(--radius-sm)]',\n 'ds:min-w-[var(--min-target-size)]',\n 'ds:transition-colors ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n 'ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:cursor-not-allowed ds:disabled:opacity-50',\n ].join(' '),\n);\n\nconst strengthTrackVariants = cva(\n 'ds:h-1 ds:w-full ds:overflow-hidden ds:rounded-[var(--radius-full)] ds:bg-muted',\n);\n\nconst strengthBarVariants = cva(\n [\n 'ds:h-full ds:rounded-[var(--radius-full)]',\n 'ds:transition-all ds:duration-[var(--animation-duration)] ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n level: {\n 0: 'ds:w-1/4 ds:bg-destructive',\n 1: 'ds:w-1/2 ds:bg-warning',\n 2: 'ds:w-3/4 ds:bg-success',\n 3: 'ds:w-full ds:bg-success',\n },\n },\n defaultVariants: { level: 0 },\n },\n);\n\nconst strengthLabelByLevel: Record<\n Strength,\n 'weak' | 'fair' | 'good' | 'strong'\n> = {\n 0: 'weak',\n 1: 'fair',\n 2: 'good',\n 3: 'strong',\n};\n\nconst requirementRowVariants = cva(\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] type-meta',\n {\n variants: {\n met: {\n true: 'ds:text-success',\n false: 'ds:text-muted-foreground',\n },\n },\n defaultVariants: { met: false },\n },\n);\n\n/**\n * One row in the live requirements checklist. Consumers either build\n * these inline or pull a pre-built set from `usePasswordRequirements`.\n */\nexport interface PasswordRequirement {\n /** Stable id — keyed render + a11y. */\n id: string;\n /** Already-translated label. The consumer owns the wording. */\n label: string;\n /** Predicate against the current value. Pure, runs on every keystroke. */\n test: (value: string) => boolean;\n}\n\nexport interface PasswordInputProps\n extends\n Omit<TextInputProps, 'type' | 'endAdornment' | 'startAdornment'>,\n VariantProps<typeof strengthBarVariants> {\n /** Callback when reveal state changes (for analytics). */\n onRevealChange?: (revealed: boolean) => void;\n /** Show the strength meter below the input. */\n showStrength?: boolean;\n /** Strength score: 0 = weak, 1 = fair, 2 = good, 3 = strong. */\n strength?: Strength;\n /**\n * Live requirements checklist rendered between the input and the\n * strength meter / caps-lock notice. Each row's predicate runs on\n * every keystroke and the row's `data-state` flips between `\"met\"`\n * and `\"unmet\"`. Backed by `aria-live=\"polite\"` so screen readers\n * announce each transition as a visually-hidden suffix span flips\n * between localised \"met\" / \"not yet met\" copy.\n *\n * Tip: hoist or `useMemo` the array — inlining it in JSX makes the\n * predicate-results memo bust every render. The kit's\n * `usePasswordRequirements` helper already does this.\n */\n requirements?: ReadonlyArray<PasswordRequirement>;\n /**\n * When `true` AND `strength` is not provided, derive a 0–3 score\n * from the proportion of `requirements` satisfied. Lets consumers\n * get the strength bar \"for free\" once they declare requirements.\n */\n deriveStrength?: boolean;\n /**\n * Autocomplete hint. Defaults to 'current-password'.\n * Use 'new-password' for registration/change-password forms.\n */\n autoComplete?: 'current-password' | 'new-password' | (string & {});\n}\n\nexport const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(\n (\n {\n onRevealChange,\n showStrength = false,\n strength,\n requirements,\n deriveStrength = false,\n autoComplete,\n size = 'md',\n disabled,\n name,\n id,\n onKeyDown,\n onKeyUp,\n onBlur,\n onInput,\n defaultValue,\n value,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const ctx = useFormField();\n const effectiveDisabled = ctx.disabled || disabled;\n\n const innerInputRef = useRef<HTMLInputElement | null>(null);\n const composedRef = composeRefs(ref, innerInputRef);\n\n const [revealed, setRevealed] = useState(false);\n const [capsLock, setCapsLock] = useState(false);\n\n // Track the current value internally so the requirements checklist\n // can re-evaluate predicates without forcing consumers to lift the\n // input into a controlled state. The seed is whichever of\n // `value` / `defaultValue` was passed; the `input` listener then\n // keeps it in sync on every keystroke (covers both controlled and\n // uncontrolled callers — for controlled callers we resync to the\n // incoming `value` prop in the effect below).\n const [internalValue, setInternalValue] = useState<string>(() => {\n if (typeof value === 'string') return value;\n if (typeof defaultValue === 'string') return defaultValue;\n return '';\n });\n\n useEffect(() => {\n if (typeof value === 'string') setInternalValue(value);\n }, [value]);\n\n const handleInput = useCallback(\n (event: FormEvent<HTMLInputElement>) => {\n setInternalValue(event.currentTarget.value);\n onInput?.(event);\n },\n [onInput],\n );\n\n const writeValueToInput = useCallback((next: string) => {\n const node = innerInputRef.current;\n if (!node) return;\n const prototype = Object.getPrototypeOf(node) as HTMLInputElement;\n const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;\n setter?.call(node, next);\n node.dispatchEvent(new Event('input', { bubbles: true }));\n node.dispatchEvent(new Event('change', { bubbles: true }));\n }, []);\n\n const handleToggle = useCallback(() => {\n setRevealed((prev) => {\n const next = !prev;\n onRevealChange?.(next);\n return next;\n });\n }, [onRevealChange]);\n\n const detectCaps = useCallback((event: KeyboardEvent<HTMLInputElement>) => {\n if (typeof event.getModifierState === 'function') {\n setCapsLock(event.getModifierState('CapsLock'));\n }\n }, []);\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n detectCaps(event);\n onKeyDown?.(event);\n },\n [detectCaps, onKeyDown],\n );\n\n const handleKeyUp = useCallback(\n (event: KeyboardEvent<HTMLInputElement>) => {\n detectCaps(event);\n onKeyUp?.(event);\n },\n [detectCaps, onKeyUp],\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLInputElement>) => {\n setCapsLock(false);\n onBlur?.(event);\n },\n [onBlur],\n );\n\n const agentHandle = useMemo<PasswordInputHandle>(\n () => ({\n getValue: () => innerInputRef.current?.value ?? '',\n setValue: (next) => writeValueToInput(next),\n clear: () => writeValueToInput(''),\n focus: () => innerInputRef.current?.focus(),\n isRevealed: () => revealed,\n toggleVisibility: () => {\n setRevealed((prev) => {\n const next = !prev;\n onRevealChange?.(next);\n return next;\n });\n },\n }),\n [onRevealChange, revealed, writeValueToInput],\n );\n useAgentRegistration(passwordInputAgent, agentHandle, id);\n\n const iconSizeClass = iconSizeClassBySize[size];\n const toggleLabel = t(\n revealed ? 'inputs.password.toggleHide' : 'inputs.password.toggleShow',\n revealed ? 'Hide password' : 'Show password',\n );\n\n // Evaluate each requirement against the current value. Cheap by\n // construction — predicates are pure regex/length checks supplied\n // by the consumer; the kit doesn't memoise per-row because the\n // map is O(n) over a handful of rules and React would re-render\n // the row anyway when its `data-state` flips.\n const requirementResults = useMemo(\n () =>\n requirements?.map((req) => ({\n ...req,\n met: req.test(internalValue),\n })) ?? null,\n [requirements, internalValue],\n );\n\n // Derive a 0–3 score from the satisfied proportion when the\n // consumer hasn't supplied an explicit `strength`. Formula:\n // `floor((satisfied / total) * 4)`, clamped to 3 so a fully-met\n // checklist matches the explicit \"strong\" level. With zero\n // requirements we fall back to 0 — meaningless on its own but\n // never displayed since `showStrength` is what controls the bar.\n const derivedStrength: Strength = useMemo(() => {\n if (!deriveStrength || !requirementResults?.length) return 0;\n const satisfied = requirementResults.filter((r) => r.met).length;\n const total = requirementResults.length;\n return Math.min(3, Math.floor((satisfied / total) * 4)) as Strength;\n }, [deriveStrength, requirementResults]);\n\n const effectiveStrength: Strength =\n strength !== undefined ? strength : deriveStrength ? derivedStrength : 0;\n const strengthKey = strengthLabelByLevel[effectiveStrength];\n const strengthText = t(\n `inputs.password.strength.${strengthKey}`,\n strengthKey.charAt(0).toUpperCase() + strengthKey.slice(1),\n );\n\n return (\n <div\n data-component=\"password-input\"\n data-component-id={id}\n className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\"\n >\n <div className=\"ds:relative\">\n <TextInput\n ref={composedRef}\n id={id}\n type={revealed ? 'text' : 'password'}\n autoComplete={autoComplete ?? 'current-password'}\n name={name ?? 'password'}\n spellCheck={false}\n autoCapitalize=\"none\"\n autoCorrect=\"off\"\n size={size}\n disabled={effectiveDisabled}\n onKeyDown={handleKeyDown}\n onKeyUp={handleKeyUp}\n onBlur={handleBlur}\n onInput={handleInput}\n value={value}\n defaultValue={defaultValue}\n endAdornment={\n <span aria-hidden=\"true\" className=\"ds:block ds:size-4\" />\n }\n className={className}\n {...rest}\n />\n <button\n type=\"button\"\n aria-pressed={revealed}\n aria-label={toggleLabel}\n disabled={effectiveDisabled}\n onClick={handleToggle}\n className={toggleVariants()}\n >\n {revealed ? (\n <EyeOff aria-hidden=\"true\" className={iconSizeClass} />\n ) : (\n <Eye aria-hidden=\"true\" className={iconSizeClass} />\n )}\n </button>\n </div>\n\n {requirementResults && requirementResults.length > 0 ? (\n // `role=\"list\"` / `role=\"listitem\"` are set explicitly even\n // though they're the implicit roles on <ul>/<li> — Safari\n // VoiceOver strips the implicit list role when CSS sets\n // `list-style: none` (which `ds:list-none` does), so the\n // redundancy is load-bearing for AT, not a lint nit.\n //\n // aria-live=\"polite\" announces transitions as the per-row\n // `.ds:sr-only` state suffix flips between \"met\" / \"not yet\n // met\". aria-live observes text-content mutations, not\n // attribute or class changes, so the data-state attribute\n // alone wouldn't trigger an announcement — the sr-only span\n // is the load-bearing piece for AT users.\n // eslint-disable-next-line jsx-a11y/no-redundant-roles\n <ul\n role=\"list\"\n aria-live=\"polite\"\n data-component=\"password-requirements\"\n className=\"ds:list-none ds:m-0 ds:p-0 ds:flex ds:flex-col ds:gap-[var(--spacing-2xs)]\"\n >\n {requirementResults.map((req) => (\n // eslint-disable-next-line jsx-a11y/no-redundant-roles\n <li\n key={req.id}\n role=\"listitem\"\n data-state={req.met ? 'met' : 'unmet'}\n className={requirementRowVariants({ met: req.met })}\n >\n {req.met ? (\n <Check\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:text-success ds:shrink-0\"\n />\n ) : (\n <Circle\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:shrink-0\"\n />\n )}\n <span>{req.label}</span>\n <span className=\"ds:sr-only\">\n {' — '}\n {req.met\n ? t('inputs.password.requirements.met', 'met')\n : t('inputs.password.requirements.unmet', 'not yet met')}\n </span>\n </li>\n ))}\n </ul>\n ) : null}\n\n {capsLock ? (\n <span\n role=\"status\"\n aria-live=\"polite\"\n className=\"ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)] type-meta ds:text-warning\"\n >\n <AlertTriangle aria-hidden=\"true\" className=\"ds:size-3.5\" />\n {t('inputs.password.capsLock', 'Caps Lock is on')}\n </span>\n ) : null}\n\n {showStrength ? (\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-xs)]\">\n <div\n role=\"progressbar\"\n aria-valuenow={effectiveStrength}\n aria-valuemin={0}\n aria-valuemax={3}\n aria-label={t(\n 'inputs.password.strengthLabel',\n 'Password strength',\n )}\n aria-valuetext={strengthText}\n className={strengthTrackVariants()}\n >\n <div\n className={strengthBarVariants({ level: effectiveStrength })}\n />\n </div>\n <span\n aria-hidden=\"true\"\n className=\"type-meta ds:text-muted-foreground\"\n >\n {strengthText}\n </span>\n </div>\n ) : null}\n </div>\n );\n },\n);\n\nPasswordInput.displayName = 'PasswordInput';\n","import { useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport type { PasswordRequirement } from './password-input';\n\nexport interface UsePasswordRequirementsOptions {\n /**\n * Minimum password length. When set, adds a \"length\" requirement.\n * Pass `12` (or whatever the server-side regex enforces) so the\n * inline checklist tells users the same rule they'll bounce off\n * server-side. When omitted, no length row is rendered.\n */\n minLength?: number;\n /** Adds an \"uppercase letter\" requirement when true. */\n uppercase?: boolean;\n /** Adds a \"lowercase letter\" requirement when true. */\n lowercase?: boolean;\n /** Adds a \"digit\" requirement when true. */\n digit?: boolean;\n /** Adds a \"symbol\" requirement when true. Matches anything outside `[A-Za-z0-9\\s]`. */\n symbol?: boolean;\n}\n\n/**\n * Builds the common set of password requirements with i18n-resolved\n * labels. Designed for the AlfaDocs platform's patient-registration\n * server-side regex (`/^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).{12,}$/`) but\n * generic enough for any consumer.\n *\n * Each opt-in flag adds one row to the returned array, in a stable\n * visual order (length → uppercase → lowercase → digit → symbol).\n * Predicates are pure and run on every keystroke.\n *\n * @example\n * const requirements = usePasswordRequirements({\n * minLength: 12,\n * uppercase: true,\n * lowercase: true,\n * digit: true,\n * });\n * <PasswordInput requirements={requirements} deriveStrength />\n */\nexport function usePasswordRequirements(\n opts: UsePasswordRequirementsOptions = {},\n): PasswordRequirement[] {\n const { t } = useTranslation();\n const { minLength, uppercase, lowercase, digit, symbol } = opts;\n\n return useMemo(() => {\n const list: PasswordRequirement[] = [];\n\n if (typeof minLength === 'number' && minLength > 0) {\n list.push({\n id: 'length',\n label: t('inputs.password.requirements.length', {\n count: minLength,\n defaultValue: `At least ${minLength} characters`,\n }),\n test: (v) => v.length >= minLength,\n });\n }\n\n if (uppercase) {\n list.push({\n id: 'uppercase',\n label: t(\n 'inputs.password.requirements.uppercase',\n 'One uppercase letter',\n ),\n test: (v) => /\\p{Lu}/u.test(v),\n });\n }\n\n if (lowercase) {\n list.push({\n id: 'lowercase',\n label: t(\n 'inputs.password.requirements.lowercase',\n 'One lowercase letter',\n ),\n test: (v) => /\\p{Ll}/u.test(v),\n });\n }\n\n if (digit) {\n list.push({\n id: 'digit',\n label: t('inputs.password.requirements.digit', 'One number'),\n test: (v) => /\\p{Nd}/u.test(v),\n });\n }\n\n if (symbol) {\n list.push({\n id: 'symbol',\n label: t('inputs.password.requirements.symbol', 'One symbol'),\n // Anything that isn't a letter, number, or whitespace counts.\n // `\\p{L}` + `\\p{N}` keep this Unicode-aware so non-Latin\n // alphabets (Arabic, Chinese, …) aren't mis-classified as\n // symbols.\n test: (v) => /[^\\p{L}\\p{N}\\s]/u.test(v),\n });\n }\n\n return list;\n }, [t, minLength, uppercase, lowercase, digit, symbol]);\n}\n"],"names":["__iconNode","Eye","createLucideIcon","passwordInputAgent","handle","args","iconSizeClassBySize","toggleVariants","cva","strengthTrackVariants","strengthBarVariants","strengthLabelByLevel","requirementRowVariants","PasswordInput","forwardRef","onRevealChange","showStrength","strength","requirements","deriveStrength","autoComplete","size","disabled","name","id","onKeyDown","onKeyUp","onBlur","onInput","defaultValue","value","className","rest","ref","t","useTranslation","effectiveDisabled","useFormField","innerInputRef","useRef","composedRef","composeRefs","revealed","setRevealed","useState","capsLock","setCapsLock","internalValue","setInternalValue","useEffect","handleInput","useCallback","event","writeValueToInput","next","node","prototype","setter","_a","handleToggle","prev","detectCaps","handleKeyDown","handleKeyUp","handleBlur","agentHandle","useMemo","useAgentRegistration","iconSizeClass","toggleLabel","requirementResults","req","derivedStrength","satisfied","r","total","effectiveStrength","strengthKey","strengthText","jsxs","jsx","TextInput","EyeOff","Check","Circle","AlertTriangle","usePasswordRequirements","opts","minLength","uppercase","lowercase","digit","symbol","list","v"],"mappings":"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","x_google_ignoreList":[0]}
@@ -0,0 +1,175 @@
1
+ import { jsxs as c, jsx as i } from "react/jsx-runtime";
2
+ import { forwardRef as y, useMemo as A } from "react";
3
+ import { c as m } from "./index-D2ZczOXr.js";
4
+ import { useTranslation as C } from "react-i18next";
5
+ function u({ className: a }) {
6
+ return /* @__PURE__ */ i(
7
+ "svg",
8
+ {
9
+ "aria-hidden": "true",
10
+ viewBox: "0 0 24 24",
11
+ fill: "currentColor",
12
+ xmlns: "http://www.w3.org/2000/svg",
13
+ className: a,
14
+ children: /* @__PURE__ */ i("path", { d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.149-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.71.306 1.263.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413Z" })
15
+ }
16
+ );
17
+ }
18
+ const _ = m(
19
+ [
20
+ "ds:relative ds:inline-flex ds:items-center ds:justify-center",
21
+ "ds:font-medium ds:whitespace-nowrap",
22
+ "ds:bg-[var(--brand-whatsapp)] ds:text-[var(--brand-whatsapp-foreground)]",
23
+ "ds:hover:bg-[var(--brand-whatsapp-hover)]",
24
+ "ds:active:opacity-90",
25
+ "ds:transition-[background-color,box-shadow,opacity]",
26
+ "ds:duration-[var(--animation-duration)]",
27
+ "ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid",
28
+ // Focus ring colour is `var(--ring)` (= `--primary` in light, ditto
29
+ // through theme cascade) — NOT `--brand-whatsapp`. Painting the
30
+ // ring brand-green on a brand-green surface makes it invisible.
31
+ "ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]",
32
+ // Under Windows High Contrast Mode the bg + shadow are stripped;
33
+ // repaint a solid border + swap the focus ring to CanvasText so
34
+ // the affordance stays identifiable.
35
+ "ds:forced-colors:border ds:forced-colors:border-[ButtonBorder]",
36
+ "ds:forced-colors:focus-visible:outline-[CanvasText]",
37
+ "ds:disabled:opacity-50 ds:disabled:cursor-not-allowed",
38
+ "ds:motion-reduce:transition-none"
39
+ ].join(" "),
40
+ {
41
+ variants: {
42
+ variant: {
43
+ // Pill — inline rounded button. Visible label is the brand
44
+ // affordance text; glyph sits leading.
45
+ pill: "ds:rounded-[var(--radius-full)] ds:gap-[var(--spacing-xs)]",
46
+ // FAB — circular floating action button. Glyph only; the
47
+ // label resolves into `aria-label`.
48
+ fab: [
49
+ "ds:rounded-full ds:shadow-[var(--shadow-lg)]",
50
+ "ds:hover:shadow-[var(--shadow-xl)] ds:active:shadow-[var(--shadow-md)]"
51
+ ].join(" ")
52
+ },
53
+ size: {
54
+ sm: "",
55
+ md: "",
56
+ lg: ""
57
+ }
58
+ },
59
+ compoundVariants: [
60
+ {
61
+ variant: "pill",
62
+ size: "sm",
63
+ class: 'ds:h-9 ds:ps-3 ds:pe-3.5 ds:text-[length:var(--font-size-sm)] ds:[&_svg]:size-4 ds:min-h-[var(--min-target-size)] ds:sm:min-h-0 ds:before:absolute ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:before:content-[""] ds:sm:before:hidden'
64
+ },
65
+ {
66
+ variant: "pill",
67
+ size: "md",
68
+ class: "ds:h-10 ds:ps-4 ds:pe-5 ds:text-[length:var(--font-size-base)] ds:[&_svg]:size-5"
69
+ },
70
+ {
71
+ variant: "pill",
72
+ size: "lg",
73
+ class: "ds:h-12 ds:ps-5 ds:pe-6 ds:text-[length:var(--font-size-lg)] ds:[&_svg]:size-6"
74
+ },
75
+ // FAB — fixed square dimensions; the `sm` case mirrors the
76
+ // FloatingActionButton's pseudo-target expansion below the
77
+ // `sm:` breakpoint so a 40px circle still hits 44/48 px on
78
+ // mobile.
79
+ {
80
+ variant: "fab",
81
+ size: "sm",
82
+ class: 'ds:h-10 ds:w-10 ds:[&_svg]:size-5 ds:before:absolute ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:before:content-[""] ds:sm:before:hidden'
83
+ },
84
+ {
85
+ variant: "fab",
86
+ size: "md",
87
+ class: "ds:h-14 ds:w-14 ds:[&_svg]:size-7"
88
+ },
89
+ {
90
+ variant: "fab",
91
+ size: "lg",
92
+ class: "ds:h-16 ds:w-16 ds:[&_svg]:size-8"
93
+ }
94
+ ],
95
+ defaultVariants: {
96
+ variant: "pill",
97
+ size: "md"
98
+ }
99
+ }
100
+ ), j = m("", {
101
+ variants: {
102
+ position: {
103
+ static: "",
104
+ "bottom-end": "ds:fixed ds:z-[var(--z-fixed)] ds:bottom-[calc(var(--spacing-lg)+env(safe-area-inset-bottom,0px))] ds:end-[var(--spacing-lg)]",
105
+ "bottom-start": "ds:fixed ds:z-[var(--z-fixed)] ds:bottom-[calc(var(--spacing-lg)+env(safe-area-inset-bottom,0px))] ds:start-[var(--spacing-lg)]"
106
+ }
107
+ },
108
+ defaultVariants: { position: "static" }
109
+ });
110
+ function B(a, o) {
111
+ const s = a.replace(/\D/g, "");
112
+ if (s.length === 0)
113
+ return null;
114
+ const r = `https://wa.me/${s}`;
115
+ return o ? `${r}?text=${encodeURIComponent(o)}` : r;
116
+ }
117
+ const N = y(
118
+ ({
119
+ phoneNumber: a,
120
+ message: o,
121
+ variant: s = "pill",
122
+ size: r = "md",
123
+ position: h,
124
+ label: g,
125
+ className: w,
126
+ ...n
127
+ }, z) => {
128
+ const { t: p } = C(), f = A(
129
+ () => B(a, o),
130
+ [a, o]
131
+ ), e = g ?? p(s === "fab" ? "whatsApp.fabLabel" : "whatsApp.label"), x = [
132
+ _({ variant: s, size: r }),
133
+ j({ position: h }),
134
+ w
135
+ ].filter(Boolean).join(" "), d = f === null, v = {
136
+ ref: z,
137
+ ...d ? {} : {
138
+ href: f,
139
+ target: "_blank",
140
+ rel: "noopener noreferrer"
141
+ },
142
+ "aria-disabled": d ? !0 : void 0,
143
+ "data-component": "whatsapp-button",
144
+ "data-variant": s,
145
+ onClick: (l) => {
146
+ var b;
147
+ d && l.preventDefault(), (b = n.onClick) == null || b.call(n, l);
148
+ },
149
+ className: x
150
+ }, t = d ? null : p("link.opensInNewTab", "Opens in a new tab");
151
+ if (s === "fab") {
152
+ const l = typeof e == "string" ? t ? `${e} — ${t}` : e : void 0;
153
+ return /* @__PURE__ */ c("a", { ...n, ...v, "aria-label": l, children: [
154
+ /* @__PURE__ */ i(u, {}),
155
+ typeof e != "string" ? /* @__PURE__ */ c("span", { className: "ds:sr-only", children: [
156
+ e,
157
+ t ? ` — ${t}` : null
158
+ ] }) : null
159
+ ] });
160
+ }
161
+ return /* @__PURE__ */ c("a", { ...n, ...v, children: [
162
+ /* @__PURE__ */ i(u, {}),
163
+ /* @__PURE__ */ i("span", { children: e }),
164
+ t ? /* @__PURE__ */ c("span", { className: "ds:sr-only", children: [
165
+ " — ",
166
+ t
167
+ ] }) : null
168
+ ] });
169
+ }
170
+ );
171
+ N.displayName = "WhatsAppButton";
172
+ export {
173
+ N as W
174
+ };
175
+ //# sourceMappingURL=whatsapp-button-Bj5FIhpC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatsapp-button-Bj5FIhpC.js","sources":["../../src/components/whatsapp-button/whatsapp-button.tsx"],"sourcesContent":["/* ------------------------------------------------------------------ */\n/* WhatsAppButton — kit-tokened Click-to-Chat affordance. */\n/* */\n/* Replaces the booking website's loose green-circle-with-caption */\n/* combo (see HTP screenshot in the rebrand discussion) with a single */\n/* well-anchored affordance. Two variants: */\n/* */\n/* - `pill` — inline rounded button, brand green + white glyph + */\n/* label. Drops anywhere in card / header chrome. */\n/* - `fab` — circular floating action button, glyph only, */\n/* persistently anchored in the page corner. Carries an */\n/* `aria-label` so the SR announcement is \"Open WhatsApp */\n/* chat\" without rendering a loose visible caption. */\n/* */\n/* WhatsApp brand guidance requires the canonical #25D366 surface and */\n/* the official phone-bubble glyph — those live in `--brand-whatsapp` */\n/* tokens (added under `src/tokens/index.css`) so all four themes can */\n/* shift the green for accessible contrast without bleeding the brand */\n/* requirement into component code. */\n/* ------------------------------------------------------------------ */\n\nimport {\n forwardRef,\n useMemo,\n type AnchorHTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\n\n/* ------------------------------------------------------------------ */\n/* WhatsApp glyph */\n/* */\n/* Inlined SVG — no external icon dep. The path matches WhatsApp's */\n/* official brand mark (speech bubble + phone). `currentColor` lets */\n/* the kit's `--brand-whatsapp-foreground` paint the glyph from a */\n/* single CVA class, so accessible-theme shifts apply without code. */\n/* ------------------------------------------------------------------ */\n\nfunction WhatsAppGlyph({ className }: { className?: string }): ReactNode {\n return (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n xmlns=\"http://www.w3.org/2000/svg\"\n className={className}\n >\n <path d=\"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.149-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.71.306 1.263.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 0 1-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 0 1-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 0 1 2.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0 0 12.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 0 0 5.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 0 0-3.48-8.413Z\" />\n </svg>\n );\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rootVariants = cva(\n [\n 'ds:relative ds:inline-flex ds:items-center ds:justify-center',\n 'ds:font-medium ds:whitespace-nowrap',\n 'ds:bg-[var(--brand-whatsapp)] ds:text-[var(--brand-whatsapp-foreground)]',\n 'ds:hover:bg-[var(--brand-whatsapp-hover)]',\n 'ds:active:opacity-90',\n 'ds:transition-[background-color,box-shadow,opacity]',\n 'ds:duration-[var(--animation-duration)]',\n 'ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid',\n // Focus ring colour is `var(--ring)` (= `--primary` in light, ditto\n // through theme cascade) — NOT `--brand-whatsapp`. Painting the\n // ring brand-green on a brand-green surface makes it invisible.\n 'ds:focus-visible:outline-[var(--ring)] ds:focus-visible:outline-offset-[length:var(--focus-ring-offset)]',\n // Under Windows High Contrast Mode the bg + shadow are stripped;\n // repaint a solid border + swap the focus ring to CanvasText so\n // the affordance stays identifiable.\n 'ds:forced-colors:border ds:forced-colors:border-[ButtonBorder]',\n 'ds:forced-colors:focus-visible:outline-[CanvasText]',\n 'ds:disabled:opacity-50 ds:disabled:cursor-not-allowed',\n 'ds:motion-reduce:transition-none',\n ].join(' '),\n {\n variants: {\n variant: {\n // Pill — inline rounded button. Visible label is the brand\n // affordance text; glyph sits leading.\n pill: 'ds:rounded-[var(--radius-full)] ds:gap-[var(--spacing-xs)]',\n // FAB — circular floating action button. Glyph only; the\n // label resolves into `aria-label`.\n fab: [\n 'ds:rounded-full ds:shadow-[var(--shadow-lg)]',\n 'ds:hover:shadow-[var(--shadow-xl)] ds:active:shadow-[var(--shadow-md)]',\n ].join(' '),\n },\n size: {\n sm: '',\n md: '',\n lg: '',\n },\n },\n compoundVariants: [\n {\n variant: 'pill',\n size: 'sm',\n class:\n 'ds:h-9 ds:ps-3 ds:pe-3.5 ds:text-[length:var(--font-size-sm)] ds:[&_svg]:size-4 ds:min-h-[var(--min-target-size)] ds:sm:min-h-0 ds:before:absolute ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:before:content-[\"\"] ds:sm:before:hidden',\n },\n {\n variant: 'pill',\n size: 'md',\n class:\n 'ds:h-10 ds:ps-4 ds:pe-5 ds:text-[length:var(--font-size-base)] ds:[&_svg]:size-5',\n },\n {\n variant: 'pill',\n size: 'lg',\n class:\n 'ds:h-12 ds:ps-5 ds:pe-6 ds:text-[length:var(--font-size-lg)] ds:[&_svg]:size-6',\n },\n // FAB — fixed square dimensions; the `sm` case mirrors the\n // FloatingActionButton's pseudo-target expansion below the\n // `sm:` breakpoint so a 40px circle still hits 44/48 px on\n // mobile.\n {\n variant: 'fab',\n size: 'sm',\n class:\n 'ds:h-10 ds:w-10 ds:[&_svg]:size-5 ds:before:absolute ds:before:inset-x-[calc((var(--min-target-size)-100%)/-2)] ds:before:inset-y-[calc((var(--min-target-size)-100%)/-2)] ds:before:content-[\"\"] ds:sm:before:hidden',\n },\n {\n variant: 'fab',\n size: 'md',\n class: 'ds:h-14 ds:w-14 ds:[&_svg]:size-7',\n },\n {\n variant: 'fab',\n size: 'lg',\n class: 'ds:h-16 ds:w-16 ds:[&_svg]:size-8',\n },\n ],\n defaultVariants: {\n variant: 'pill',\n size: 'md',\n },\n },\n);\n\nconst positionVariants = cva('', {\n variants: {\n position: {\n static: '',\n 'bottom-end':\n 'ds:fixed ds:z-[var(--z-fixed)] ds:bottom-[calc(var(--spacing-lg)+env(safe-area-inset-bottom,0px))] ds:end-[var(--spacing-lg)]',\n 'bottom-start':\n 'ds:fixed ds:z-[var(--z-fixed)] ds:bottom-[calc(var(--spacing-lg)+env(safe-area-inset-bottom,0px))] ds:start-[var(--spacing-lg)]',\n },\n },\n defaultVariants: { position: 'static' },\n});\n\n/* ------------------------------------------------------------------ */\n/* URL builder */\n/* ------------------------------------------------------------------ */\n\n/**\n * Build a `wa.me` deep-link from an E.164 phone number and an\n * optional prefilled message. The number is stripped to digits — the\n * `wa.me` host requires digits only (no `+`, no separators).\n *\n * Returns `null` (not an empty `wa.me/` URL) when the input contains\n * no digits — the caller is then responsible for rendering a disabled\n * affordance rather than shipping a broken link. In dev a `console.warn`\n * fires so the typo is visible during development.\n */\nfunction buildWaMeUrl(phoneNumber: string, message?: string): string | null {\n const digits = phoneNumber.replace(/\\D/g, '');\n if (digits.length === 0) {\n if (\n typeof import.meta !== 'undefined' &&\n typeof import.meta.env !== 'undefined' &&\n import.meta.env.DEV === true\n ) {\n console.warn(\n `[WhatsAppButton] phoneNumber \"${phoneNumber}\" contains no digits — ` +\n `the affordance will render disabled. Pass an E.164 number ` +\n `(e.g. \"+393331234567\") to fix.`,\n );\n }\n return null;\n }\n const base = `https://wa.me/${digits}`;\n if (!message) return base;\n return `${base}?text=${encodeURIComponent(message)}`;\n}\n\n/* ------------------------------------------------------------------ */\n/* Props */\n/* ------------------------------------------------------------------ */\n\nexport interface WhatsAppButtonProps\n extends\n Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'children'>,\n VariantProps<typeof rootVariants>,\n VariantProps<typeof positionVariants> {\n /**\n * Phone number to chat to. E.164 format is recommended\n * (e.g. `+393331234567`). Any non-digit characters are stripped\n * before the URL is built — `wa.me` requires digits only.\n */\n phoneNumber: string;\n /**\n * Optional prefilled message. The receiver sees this in their\n * compose box. Keep it short and human — long pasted text is the\n * single most common reason consumers drop the affordance.\n */\n message?: string;\n /**\n * Override the visible / accessible label. Defaults to\n * `t('whatsApp.label')` for the pill variant and\n * `t('whatsApp.fabLabel')` for the FAB.\n */\n label?: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* WhatsAppButton */\n/* ------------------------------------------------------------------ */\n\nexport const WhatsAppButton = forwardRef<\n HTMLAnchorElement,\n WhatsAppButtonProps\n>(\n (\n {\n phoneNumber,\n message,\n variant = 'pill',\n size = 'md',\n position,\n label,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t } = useTranslation();\n const href = useMemo(\n () => buildWaMeUrl(phoneNumber, message),\n [phoneNumber, message],\n );\n const resolvedLabel =\n label ??\n (variant === 'fab' ? t('whatsApp.fabLabel') : t('whatsApp.label'));\n\n const composedClassName = [\n rootVariants({ variant, size }),\n positionVariants({ position }),\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // When the input has no digits, render an `aria-disabled` anchor\n // with the same chrome but no `href` — assistive tech announces the\n // disabled state and click does nothing. `buildWaMeUrl` already\n // emitted a dev-mode warning above. `target` / `rel` are stripped\n // too because they're meaningless without `href`.\n const isDisabled = href === null;\n const commonAnchorProps = {\n ref,\n ...(isDisabled\n ? {}\n : {\n href: href as string,\n target: '_blank' as const,\n rel: 'noopener noreferrer' as const,\n }),\n 'aria-disabled': isDisabled ? true : undefined,\n 'data-component': 'whatsapp-button' as const,\n 'data-variant': variant,\n onClick: (event: React.MouseEvent<HTMLAnchorElement>) => {\n if (isDisabled) event.preventDefault();\n rest.onClick?.(event);\n },\n className: composedClassName,\n };\n\n // Screen-reader cue for the new-tab behaviour. WCAG 3.2.5 / G201:\n // user-initiated link openings to a new context should be\n // announced. Reused `link.opensInNewTab` from the kit's `ui` ns —\n // single source of truth across every external-link affordance.\n const opensInNewTabSrText = isDisabled\n ? null\n : t('link.opensInNewTab', 'Opens in a new tab');\n\n if (variant === 'fab') {\n // FAB — circular, glyph-only. The accessible name lives on the\n // anchor's `aria-label` (resolved label). No visible caption,\n // which is exactly what the redesign fixes vs. the legacy\n // green-circle-with-loose-text affordance.\n const fabAriaLabel =\n typeof resolvedLabel === 'string'\n ? opensInNewTabSrText\n ? `${resolvedLabel} — ${opensInNewTabSrText}`\n : resolvedLabel\n : undefined;\n return (\n <a {...rest} {...commonAnchorProps} aria-label={fabAriaLabel}>\n <WhatsAppGlyph />\n {typeof resolvedLabel !== 'string' ? (\n <span className=\"ds:sr-only\">\n {resolvedLabel}\n {opensInNewTabSrText ? ` — ${opensInNewTabSrText}` : null}\n </span>\n ) : null}\n </a>\n );\n }\n\n return (\n <a {...rest} {...commonAnchorProps}>\n <WhatsAppGlyph />\n <span>{resolvedLabel}</span>\n {opensInNewTabSrText ? (\n <span className=\"ds:sr-only\"> — {opensInNewTabSrText}</span>\n ) : null}\n </a>\n );\n },\n);\nWhatsAppButton.displayName = 'WhatsAppButton';\n"],"names":["WhatsAppGlyph","className","jsx","rootVariants","cva","positionVariants","buildWaMeUrl","phoneNumber","message","digits","base","WhatsAppButton","forwardRef","variant","size","position","label","rest","ref","t","useTranslation","href","useMemo","resolvedLabel","composedClassName","isDisabled","commonAnchorProps","event","_a","opensInNewTabSrText","fabAriaLabel","jsxs"],"mappings":"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"}