anton-bakker-feedback 0.2.0 → 0.3.1

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 (95) hide show
  1. package/dist/chunks/{i18n-RpKqSWAT.js → i18n-Du-or3T4.js} +3 -102
  2. package/dist/chunks/i18n-Du-or3T4.js.map +1 -0
  3. package/dist/chunks/observability-CfwHKObh.js +102 -0
  4. package/dist/chunks/observability-CfwHKObh.js.map +1 -0
  5. package/dist/chunks/submitResolver-DsfkrUHY.js +133 -0
  6. package/dist/chunks/submitResolver-DsfkrUHY.js.map +1 -0
  7. package/dist/config/database.d.ts +76 -0
  8. package/dist/config/database.d.ts.map +1 -0
  9. package/dist/core/formTypes.d.ts +65 -0
  10. package/dist/core/formTypes.d.ts.map +1 -0
  11. package/dist/index.js +4 -3
  12. package/dist/index.js.map +1 -1
  13. package/dist/models/StoredFeedbackItem.d.ts +79 -0
  14. package/dist/models/StoredFeedbackItem.d.ts.map +1 -0
  15. package/dist/react/admin/FeedbackDetailPage.d.ts +7 -25
  16. package/dist/react/admin/FeedbackDetailPage.d.ts.map +1 -1
  17. package/dist/react/admin/FeedbackListPage.d.ts +20 -0
  18. package/dist/react/admin/FeedbackListPage.d.ts.map +1 -0
  19. package/dist/react/admin/FeedbackUpdatePage.d.ts +8 -30
  20. package/dist/react/admin/FeedbackUpdatePage.d.ts.map +1 -1
  21. package/dist/react/admin/__tests__/FeedbackListPage.test.d.ts.map +1 -0
  22. package/dist/react/admin/index.d.ts +3 -2
  23. package/dist/react/admin/index.d.ts.map +1 -1
  24. package/dist/react/admin/index.js +352 -1259
  25. package/dist/react/admin/index.js.map +1 -1
  26. package/dist/react/admin/types.d.ts +20 -14
  27. package/dist/react/admin/types.d.ts.map +1 -1
  28. package/dist/react.js +9 -136
  29. package/dist/react.js.map +1 -1
  30. package/dist/server/FeedbackService.d.ts +32 -0
  31. package/dist/server/FeedbackService.d.ts.map +1 -0
  32. package/dist/server/index.d.ts +15 -0
  33. package/dist/server/index.d.ts.map +1 -0
  34. package/dist/server/listHandler.d.ts +17 -0
  35. package/dist/server/listHandler.d.ts.map +1 -0
  36. package/dist/server.d.ts +1 -1
  37. package/dist/server.d.ts.map +1 -1
  38. package/dist/server.js +202 -1
  39. package/dist/server.js.map +1 -1
  40. package/dist/types/context.d.ts +92 -0
  41. package/dist/types/context.d.ts.map +1 -0
  42. package/dist/types/feedback.d.ts +30 -0
  43. package/dist/types/feedback.d.ts.map +1 -0
  44. package/package.json +7 -1
  45. package/dist/chunks/i18n-RpKqSWAT.js.map +0 -1
  46. package/dist/react/admin/__tests__/FeedbackUpdatePage.integration.test.d.ts +0 -2
  47. package/dist/react/admin/__tests__/FeedbackUpdatePage.integration.test.d.ts.map +0 -1
  48. package/dist/react/admin/components/BrowserContext.d.ts +0 -32
  49. package/dist/react/admin/components/BrowserContext.d.ts.map +0 -1
  50. package/dist/react/admin/components/DataTable.d.ts +0 -55
  51. package/dist/react/admin/components/DataTable.d.ts.map +0 -1
  52. package/dist/react/admin/components/DialogueHistory.d.ts +0 -27
  53. package/dist/react/admin/components/DialogueHistory.d.ts.map +0 -1
  54. package/dist/react/admin/components/EditableFields.d.ts +0 -126
  55. package/dist/react/admin/components/EditableFields.d.ts.map +0 -1
  56. package/dist/react/admin/components/FeedbackListPage.d.ts +0 -19
  57. package/dist/react/admin/components/FeedbackListPage.d.ts.map +0 -1
  58. package/dist/react/admin/components/FeedbackListPage.test.d.ts.map +0 -1
  59. package/dist/react/admin/components/FilterControls.d.ts +0 -28
  60. package/dist/react/admin/components/FilterControls.d.ts.map +0 -1
  61. package/dist/react/admin/components/NavigationLink.d.ts +0 -31
  62. package/dist/react/admin/components/NavigationLink.d.ts.map +0 -1
  63. package/dist/react/admin/components/QuickActions.d.ts +0 -43
  64. package/dist/react/admin/components/QuickActions.d.ts.map +0 -1
  65. package/dist/react/admin/components/ScreenshotViewer.d.ts +0 -38
  66. package/dist/react/admin/components/ScreenshotViewer.d.ts.map +0 -1
  67. package/dist/react/admin/components/SortControls.d.ts +0 -29
  68. package/dist/react/admin/components/SortControls.d.ts.map +0 -1
  69. package/dist/react/admin/components/StatusBadge.d.ts +0 -23
  70. package/dist/react/admin/components/StatusBadge.d.ts.map +0 -1
  71. package/dist/react/admin/components/TypeBadge.d.ts +0 -23
  72. package/dist/react/admin/components/TypeBadge.d.ts.map +0 -1
  73. package/dist/react/admin/components/__tests__/BrowserContext.test.d.ts +0 -2
  74. package/dist/react/admin/components/__tests__/BrowserContext.test.d.ts.map +0 -1
  75. package/dist/react/admin/components/__tests__/DialogueHistory.test.d.ts +0 -2
  76. package/dist/react/admin/components/__tests__/DialogueHistory.test.d.ts.map +0 -1
  77. package/dist/react/admin/components/__tests__/EditableFields.test.d.ts +0 -2
  78. package/dist/react/admin/components/__tests__/EditableFields.test.d.ts.map +0 -1
  79. package/dist/react/admin/components/__tests__/QuickActions.test.d.ts +0 -2
  80. package/dist/react/admin/components/__tests__/QuickActions.test.d.ts.map +0 -1
  81. package/dist/react/admin/components/__tests__/ScreenshotViewer.test.d.ts +0 -2
  82. package/dist/react/admin/components/__tests__/ScreenshotViewer.test.d.ts.map +0 -1
  83. package/dist/react/admin/components/__tests__/badges.test.d.ts +0 -2
  84. package/dist/react/admin/components/__tests__/badges.test.d.ts.map +0 -1
  85. package/dist/react/admin/components/__tests__/controls.test.d.ts +0 -2
  86. package/dist/react/admin/components/__tests__/controls.test.d.ts.map +0 -1
  87. package/dist/react/admin/components/__tests__/table-navigation.test.d.ts +0 -2
  88. package/dist/react/admin/components/__tests__/table-navigation.test.d.ts.map +0 -1
  89. package/dist/react/admin/components/index.d.ts +0 -13
  90. package/dist/react/admin/components/index.d.ts.map +0 -1
  91. package/dist/react/admin/hooks/useFeedbackFilters.d.ts +0 -31
  92. package/dist/react/admin/hooks/useFeedbackFilters.d.ts.map +0 -1
  93. package/dist/react/admin/hooks/useFeedbackSort.d.ts +0 -42
  94. package/dist/react/admin/hooks/useFeedbackSort.d.ts.map +0 -1
  95. /package/dist/react/admin/{components → __tests__}/FeedbackListPage.test.d.ts +0 -0
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sources":["../src/react/FeedbackContext.ts","../src/react/FeedbackProvider.tsx","../src/react/useFeedback.ts","../src/react/FeedbackButton.tsx","../src/react/FeedbackDialogue.tsx","../src/react/FeedbackRecorder.tsx","../src/react/FeedbackConfirm.tsx","../src/react/FeedbackPanel.tsx","../src/react/resolver/submitResolver.ts"],"sourcesContent":["/**\n * FeedbackContext — typed React context for the feedback intake UI.\n *\n * Import from: import { FeedbackProvider, useFeedback } from 'anton-bakker-feedback/react'\n *\n * This file defines the shape of context value and creates the context object.\n * The provider and hook are in separate files to keep each unit focused.\n */\nimport { createContext } from 'react';\nimport type { FeedbackType, FeedbackScope, FeedbackSize, DialogueEntry } from '../core/types.js';\nimport type { ResolvedFeedbackConfig } from '../core/config.js';\n\n// ─────────────────────────────────────────────────────────────────\n// State Machine Types\n// ─────────────────────────────────────────────────────────────────\n\n/** Screenshots captured during a session */\nexport type ScreenshotEntry = {\n id: string;\n dataUrl: string; // base64 or blob URL\n capturedAt: string;\n};\n\n/** Context captured at the point of opening the feedback panel */\nexport interface CapturedContext {\n url: string;\n pageTitle: string;\n userAgent: string;\n viewport: { width: number; height: number };\n locale: string;\n consoleErrors: string[];\n screenshots: ScreenshotEntry[];\n duplicateCandidates: Array<{ id: string; title: string }>;\n customFields?: Record<string, string | number | boolean>;\n}\n\n/** Classification suggested by the dialogue */\nexport interface DialogueClassification {\n type?: FeedbackType;\n scope?: FeedbackScope;\n size?: FeedbackSize;\n title?: string;\n description?: string;\n confidence?: number;\n}\n\n/** State when idle — no panel open */\nexport interface IdleState {\n status: 'idle';\n}\n\n/** State when dialogue is in progress */\nexport interface DialogueState {\n status: 'dialogue';\n history: DialogueEntry[];\n step: number;\n totalSteps: number;\n awaitingResponse: boolean;\n classification: DialogueClassification;\n error: string | null;\n}\n\n/** State during screen capture (recording sub-states) */\nexport interface RecordingState {\n status: 'recording';\n recordingMode: 'minimised' | 'capture' | 'review';\n captureCount: number;\n}\n\n/** State when confirming submission */\nexport interface ConfirmingState {\n status: 'confirming';\n classification: DialogueClassification;\n submitting: boolean;\n error: string | null;\n}\n\n/** State after successful submission */\nexport interface SubmittedState {\n status: 'submitted';\n feedbackId: string;\n}\n\n/** Discriminated union of all possible panel states */\nexport type FeedbackPanelState =\n | IdleState\n | DialogueState\n | RecordingState\n | ConfirmingState\n | SubmittedState;\n\n// ─────────────────────────────────────────────────────────────────\n// Capability Detection\n// ─────────────────────────────────────────────────────────────────\n\n/** Runtime capability detection results */\nexport interface Capabilities {\n /** Whether Bedrock dialogue is available */\n bedrock: boolean;\n /** Whether DynamoDB storage is available */\n dynamodb: boolean;\n /** Capability detection has completed */\n detected: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Context Value Shape\n// ─────────────────────────────────────────────────────────────────\n\n/** The value provided by FeedbackContext */\nexport interface FeedbackContextValue {\n /** Resolved config with all defaults applied */\n config: ResolvedFeedbackConfig;\n /** Current panel state */\n state: FeedbackPanelState;\n /** Convenience: is panel currently open (not idle) */\n isOpen: boolean;\n /** Context captured when panel was opened */\n capturedContext: CapturedContext | null;\n /** Runtime capabilities (Bedrock, DynamoDB) */\n capabilities: Capabilities;\n /** Active i18n locale */\n locale: string;\n /** Open the panel, optionally merging context */\n open: (trigger?: Partial<CapturedContext>) => void;\n /** Close and reset to idle */\n close: () => void;\n /** Internal: dispatch state transitions */\n dispatch: (action: FeedbackAction) => void;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Actions\n// ─────────────────────────────────────────────────────────────────\n\nexport type FeedbackAction =\n | { type: 'OPEN'; payload: Partial<CapturedContext> }\n | { type: 'OPEN' }\n | { type: 'CLOSE' }\n | { type: 'SEND_MESSAGE'; payload: { message: string } }\n | { type: 'RECEIVE_RESPONSE'; payload: { response: string; step: number; totalSteps: number; classification?: DialogueClassification; done?: boolean } }\n | { type: 'DIALOGUE_ERROR'; payload: { error: string } }\n | { type: 'START_RECORDING' }\n | { type: 'SET_RECORDING_MODE'; payload: { mode: 'minimised' | 'capture' | 'review' } }\n | { type: 'ADD_SCREENSHOT'; payload: ScreenshotEntry }\n | { type: 'REMOVE_SCREENSHOT'; payload: { id: string } }\n | { type: 'CONFIRM'; payload: { classification?: DialogueClassification } }\n | { type: 'CONFIRM' }\n | { type: 'SUBMIT_START' }\n | { type: 'SUBMIT_SUCCESS'; payload: { feedbackId: string } }\n | { type: 'SUBMIT_ERROR'; payload: { error: string } }\n | { type: 'SET_CAPABILITIES'; payload: Capabilities };\n\n// ─────────────────────────────────────────────────────────────────\n// Context Object\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackContext — React context for the intake UI.\n *\n * Throws a descriptive error when consumed outside FeedbackProvider:\n * import { FeedbackProvider } from 'anton-bakker-feedback/react'\n */\nexport const FeedbackContext = createContext<FeedbackContextValue | null>(null);\n\nFeedbackContext.displayName = 'FeedbackContext';\n","/**\n * FeedbackProvider — context provider for the feedback intake UI.\n *\n * Usage:\n * import { FeedbackProvider } from 'anton-bakker-feedback/react'\n *\n * Example:\n * <FeedbackProvider\n * config={{ appName: 'my-app', transport: { submitUrl: 'https://…' } }}\n * canSubmitFeedback={(user) => user.role !== 'guest'}\n * locale=\"nl\"\n * >\n * <App />\n * </FeedbackProvider>\n *\n * RBAC gate: if canSubmitFeedback returns false, the provider renders\n * children without injecting FeedbackPanel. No button is shown.\n *\n * Progressive enhancement: capability detection (Bedrock, DynamoDB) runs\n * on mount and is stored in context — no consumer code branches required.\n *\n * The \"Skip to form\" action is surfaced via capabilities.bedrock === false\n * so FeedbackDialogue can render the \"Skip to form\" button automatically.\n * Consumers import from: import { FeedbackProvider } from 'anton-bakker-feedback/react'\n */\nimport React, {\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n Suspense,\n lazy,\n} from 'react';\nimport {\n FeedbackContext,\n type FeedbackAction,\n type FeedbackPanelState,\n type FeedbackContextValue,\n type CapturedContext,\n type Capabilities,\n type DialogueClassification,\n} from './FeedbackContext.js';\nimport type { FeedbackConfig } from '../core/config.js';\nimport { validateConfig, resolveConfig } from '../core/config.js';\nimport { detectLocale } from '../core/i18n.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackProviderProps {\n /** Feedback configuration */\n config: FeedbackConfig;\n /**\n * RBAC gate — if this returns false, FeedbackPanel is not rendered.\n * Pass the current authenticated user object.\n */\n canSubmitFeedback: (user?: unknown) => boolean;\n /** Optional i18n locale override (BCP 47). Defaults to navigator.language → 'en' */\n locale?: string;\n /** The current user (passed to canSubmitFeedback) */\n user?: unknown;\n children: ReactNode;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Initial State\n// ─────────────────────────────────────────────────────────────────\n\nconst INITIAL_PANEL_STATE: FeedbackPanelState = { status: 'idle' };\n\nfunction buildInitialCapturedContext(): CapturedContext {\n const isClient = typeof window !== 'undefined';\n return {\n url: isClient ? window.location.href : '',\n pageTitle: isClient ? document.title : '',\n userAgent: isClient ? navigator.userAgent : '',\n viewport: isClient\n ? { width: window.innerWidth, height: window.innerHeight }\n : { width: 0, height: 0 },\n locale: isClient ? navigator.language : 'en',\n consoleErrors: [],\n screenshots: [],\n duplicateCandidates: [],\n };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Reducer\n// ─────────────────────────────────────────────────────────────────\n\ninterface ProviderInternalState {\n panel: FeedbackPanelState;\n capturedContext: CapturedContext | null;\n capabilities: Capabilities;\n preservedClassification: DialogueClassification;\n}\n\nconst INITIAL_INTERNAL_STATE: ProviderInternalState = {\n panel: INITIAL_PANEL_STATE,\n capturedContext: null,\n capabilities: { bedrock: false, dynamodb: false, detected: false },\n preservedClassification: {},\n};\n\nfunction feedbackReducer(\n state: ProviderInternalState,\n action: FeedbackAction,\n): ProviderInternalState {\n switch (action.type) {\n case 'OPEN': {\n if (state.panel.status !== 'idle') return state; // idempotent\n const base = buildInitialCapturedContext();\n const merged: CapturedContext =\n 'payload' in action && action.payload\n ? { ...base, ...action.payload }\n : base;\n\n const newPanel: FeedbackPanelState = state.capabilities.bedrock\n ? {\n status: 'dialogue',\n history: [],\n step: 0,\n totalSteps: 5,\n awaitingResponse: false,\n classification: {},\n error: null,\n }\n : {\n status: 'confirming',\n classification: {},\n submitting: false,\n error: null,\n };\n\n return { ...state, panel: newPanel, capturedContext: merged };\n }\n\n case 'CLOSE':\n return {\n ...state,\n panel: INITIAL_PANEL_STATE,\n capturedContext: null,\n preservedClassification: {},\n };\n\n case 'SEND_MESSAGE': {\n if (state.panel.status !== 'dialogue') return state;\n const newHistory = [\n ...state.panel.history,\n {\n role: 'user' as const,\n content: action.payload.message,\n timestamp: new Date().toISOString(),\n },\n ];\n return {\n ...state,\n panel: {\n ...state.panel,\n history: newHistory,\n awaitingResponse: true,\n error: null,\n },\n };\n }\n\n case 'RECEIVE_RESPONSE': {\n if (state.panel.status !== 'dialogue') return state;\n const { response, step, totalSteps, classification, done } = action.payload;\n const mergedClassification: DialogueClassification = {\n ...state.panel.classification,\n ...(classification ?? {}),\n };\n const newHistory = [\n ...state.panel.history,\n {\n role: 'assistant' as const,\n content: response,\n timestamp: new Date().toISOString(),\n },\n ];\n\n if (done === true) {\n return {\n ...state,\n panel: {\n status: 'confirming',\n classification: mergedClassification,\n submitting: false,\n error: null,\n },\n preservedClassification: mergedClassification,\n };\n }\n\n return {\n ...state,\n panel: {\n ...state.panel,\n history: newHistory,\n step,\n totalSteps,\n awaitingResponse: false,\n classification: mergedClassification,\n },\n };\n }\n\n case 'DIALOGUE_ERROR': {\n if (state.panel.status !== 'dialogue') return state;\n return {\n ...state,\n panel: {\n ...state.panel,\n awaitingResponse: false,\n error: action.payload.error,\n },\n };\n }\n\n case 'START_RECORDING': {\n const currentClassification =\n state.panel.status === 'dialogue' || state.panel.status === 'confirming'\n ? state.panel.classification\n : state.preservedClassification;\n\n return {\n ...state,\n panel: {\n status: 'recording',\n recordingMode: 'capture',\n captureCount: state.capturedContext?.screenshots.length ?? 0,\n },\n preservedClassification: currentClassification,\n };\n }\n\n case 'SET_RECORDING_MODE': {\n if (state.panel.status !== 'recording') return state;\n return {\n ...state,\n panel: {\n ...state.panel,\n recordingMode: action.payload.mode,\n },\n };\n }\n\n case 'ADD_SCREENSHOT': {\n if (!state.capturedContext) return state;\n const existing = state.capturedContext.screenshots;\n if (existing.length >= 10) return state;\n const updated = [...existing, action.payload];\n return {\n ...state,\n capturedContext: { ...state.capturedContext, screenshots: updated },\n panel:\n state.panel.status === 'recording'\n ? { ...state.panel, captureCount: updated.length }\n : state.panel,\n };\n }\n\n case 'REMOVE_SCREENSHOT': {\n if (!state.capturedContext) return state;\n const filtered = state.capturedContext.screenshots.filter(\n (s) => s.id !== action.payload.id,\n );\n return {\n ...state,\n capturedContext: { ...state.capturedContext, screenshots: filtered },\n panel:\n state.panel.status === 'recording'\n ? { ...state.panel, captureCount: filtered.length }\n : state.panel,\n };\n }\n\n case 'CONFIRM': {\n const classification =\n ('payload' in action && action.payload?.classification)\n ? action.payload.classification\n : state.preservedClassification;\n return {\n ...state,\n panel: {\n status: 'confirming',\n classification,\n submitting: false,\n error: null,\n },\n preservedClassification: classification,\n };\n }\n\n case 'SUBMIT_START': {\n if (state.panel.status !== 'confirming') return state;\n return {\n ...state,\n panel: { ...state.panel, submitting: true, error: null },\n };\n }\n\n case 'SUBMIT_SUCCESS': {\n return {\n ...state,\n panel: {\n status: 'submitted',\n feedbackId: action.payload.feedbackId,\n },\n };\n }\n\n case 'SUBMIT_ERROR': {\n if (state.panel.status !== 'confirming') return state;\n return {\n ...state,\n panel: {\n ...state.panel,\n submitting: false,\n error: action.payload.error,\n },\n };\n }\n\n case 'SET_CAPABILITIES': {\n return { ...state, capabilities: action.payload };\n }\n\n default:\n return state;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Capability Detection\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Detects whether Bedrock (dialogue) and DynamoDB (storage) are available.\n * Pings configured transport URLs; falls back to minimal on any error.\n */\nasync function detectCapabilities(\n submitUrl: string,\n dialogueUrl: string | undefined,\n): Promise<Capabilities> {\n let dynamodb = true;\n let bedrock = !!dialogueUrl;\n\n if (dialogueUrl) {\n try {\n const ctrl = new AbortController();\n const timeoutId = setTimeout(() => ctrl.abort(), 3000);\n await fetch(dialogueUrl, { method: 'HEAD', signal: ctrl.signal });\n clearTimeout(timeoutId);\n bedrock = true;\n } catch {\n bedrock = false;\n }\n }\n\n try {\n const ctrl = new AbortController();\n const timeoutId = setTimeout(() => ctrl.abort(), 3000);\n await fetch(submitUrl, { method: 'HEAD', signal: ctrl.signal });\n clearTimeout(timeoutId);\n dynamodb = true;\n } catch {\n dynamodb = false;\n }\n\n return { bedrock, dynamodb, detected: true };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Lazy FeedbackPanel (avoids circular import at definition time)\n// ─────────────────────────────────────────────────────────────────\n\nconst FeedbackPanelLazy = lazy(\n () => import('./FeedbackPanel.js').then((m) => ({ default: m.FeedbackPanel })),\n);\n\n// ─────────────────────────────────────────────────────────────────\n// Provider Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackProvider wraps your application root and provides the feedback\n * context, RBAC gate, capability detection, and i18n locale.\n *\n * \"Skip to form\" — when capabilities.bedrock is false, FeedbackDialogue\n * will show the \"Skip to form\" button to allow direct access to FeedbackConfirm.\n *\n * Import: import { FeedbackProvider } from 'anton-bakker-feedback/react'\n */\nexport function FeedbackProvider({\n config,\n canSubmitFeedback,\n locale: localeProp,\n user,\n children,\n}: FeedbackProviderProps): React.JSX.Element {\n // ── Config Validation ──────────────────────────────────────────\n const resolvedConfig = useMemo(() => {\n const validation = validateConfig(config);\n if (!validation.valid) {\n console.error(\n '[anton-bakker-feedback/react] FeedbackProvider received invalid config:\\n' +\n validation.errors\n .map((e) => ` • ${e}`)\n .join('\\n') +\n '\\nFeedbackPanel will not render until config is corrected.',\n );\n return null;\n }\n return resolveConfig(config);\n }, [config]);\n\n // ── Locale ────────────────────────────────────────────────────\n const locale = useMemo(() => {\n const preferred =\n localeProp ??\n (typeof navigator !== 'undefined' ? navigator.language : 'en');\n return detectLocale(preferred);\n }, [localeProp]);\n\n // ── RBAC Gate ─────────────────────────────────────────────────\n const authorised = useMemo(\n () => canSubmitFeedback(user),\n [canSubmitFeedback, user],\n );\n\n // ── State ─────────────────────────────────────────────────────\n const [internalState, dispatch] = useReducer(\n feedbackReducer,\n INITIAL_INTERNAL_STATE,\n );\n\n // ── Capability Detection on Mount ──────────────────────────────\n useEffect(() => {\n if (!resolvedConfig) return;\n let cancelled = false;\n\n void detectCapabilities(\n resolvedConfig.transport.submitUrl,\n resolvedConfig.transport.dialogueUrl,\n ).then((caps) => {\n if (!cancelled) {\n dispatch({ type: 'SET_CAPABILITIES', payload: caps });\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [resolvedConfig]);\n\n // ── Public API ────────────────────────────────────────────────\n const open = useCallback(\n (trigger?: Partial<CapturedContext>) => {\n if (trigger !== undefined) {\n dispatch({ type: 'OPEN', payload: trigger });\n } else {\n dispatch({ type: 'OPEN' });\n }\n },\n [],\n );\n\n const close = useCallback(() => {\n dispatch({ type: 'CLOSE' });\n }, []);\n\n // ── Context Value ─────────────────────────────────────────────\n const contextValue = useMemo((): FeedbackContextValue | null => {\n if (!resolvedConfig) return null;\n return {\n config: resolvedConfig,\n state: internalState.panel,\n isOpen: internalState.panel.status !== 'idle',\n capturedContext: internalState.capturedContext,\n capabilities: internalState.capabilities,\n locale,\n open,\n close,\n dispatch,\n };\n }, [resolvedConfig, internalState, locale, open, close]);\n\n // ── Render ────────────────────────────────────────────────────\n if (!contextValue) {\n // Invalid config — render children without panel (graceful degradation)\n return React.createElement(React.Fragment, null, children);\n }\n\n return (\n <FeedbackContext.Provider value={contextValue}>\n {children}\n {authorised && (\n <Suspense fallback={null}>\n <FeedbackPanelLazy />\n </Suspense>\n )}\n </FeedbackContext.Provider>\n );\n}\n","/**\n * useFeedback — imperative hook surface for the feedback intake UI.\n *\n * Returns { open, close, state, isOpen, capturedContext }.\n *\n * Throws a descriptive error when called outside FeedbackProvider:\n * import { FeedbackProvider } from 'anton-bakker-feedback/react'\n */\nimport { useContext } from 'react';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport type {\n FeedbackPanelState,\n CapturedContext,\n FeedbackContextValue,\n} from './FeedbackContext.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Return Type\n// ─────────────────────────────────────────────────────────────────\n\nexport interface UseFeedbackReturn {\n /** Open the panel. Merges optional trigger context. Idempotent if already open. */\n open: (trigger?: Partial<CapturedContext>) => void;\n /** Close the panel and reset state to idle. */\n close: () => void;\n /** Current panel state (discriminated union) */\n state: FeedbackPanelState;\n /** Convenience: true when panel is not idle */\n isOpen: boolean;\n /** Context captured when panel was opened (null when idle) */\n capturedContext: CapturedContext | null;\n /** Full context value (for advanced use cases) */\n _context: FeedbackContextValue;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Hook\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * useFeedback() — consumes the FeedbackContext.\n *\n * Must be called inside a component that is a descendant of FeedbackProvider.\n * Throws a descriptive error otherwise.\n *\n * @example\n * const { open, close, state, isOpen } = useFeedback();\n */\nexport function useFeedback(): UseFeedbackReturn {\n const ctx = useContext(FeedbackContext);\n\n if (ctx === null) {\n throw new Error(\n '[useFeedback] Could not find FeedbackContext. ' +\n 'Make sure your component is rendered inside a <FeedbackProvider>. ' +\n 'Import from: import { FeedbackProvider } from \"anton-bakker-feedback/react\"',\n );\n }\n\n return {\n open: ctx.open,\n close: ctx.close,\n state: ctx.state,\n isOpen: ctx.isOpen,\n capturedContext: ctx.capturedContext,\n _context: ctx,\n };\n}\n","/**\n * FeedbackButton — floating action button for triggering the feedback panel.\n *\n * Layout 1: Trigger\n * Rendered by FeedbackPanel when state === 'idle'.\n * Not mounted when RBAC gate blocks rendering.\n *\n * Theming via CSS custom properties:\n * --feedback-button-bg\n * --feedback-button-color\n * --feedback-button-hover-bg\n * --feedback-button-focus-ring\n */\nimport React from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { useContext } from 'react';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport { t } from '../core/i18n.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackButtonProps {\n /** Additional CSS class names (merged via className prop) */\n className?: string;\n /** Override the default tooltip/aria-label text */\n label?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Styles (CSS custom properties for theming)\n// ─────────────────────────────────────────────────────────────────\n\nconst BUTTON_STYLES = `\n.feedback-button {\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: var(--feedback-z-index, 9998);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 12px 16px;\n border-radius: 24px;\n border: none;\n cursor: pointer;\n font-family: inherit;\n font-size: 14px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n background-color: var(--feedback-button-bg, #4f46e5);\n color: var(--feedback-button-color, #ffffff);\n box-shadow: var(--feedback-button-shadow, 0 4px 12px rgba(79, 70, 229, 0.35));\n transition: background-color 0.15s ease, transform 0.1s ease, box-shadow 0.15s ease;\n -webkit-user-select: none;\n user-select: none;\n outline: none;\n}\n\n.feedback-button:hover {\n background-color: var(--feedback-button-hover-bg, #4338ca);\n box-shadow: var(--feedback-button-hover-shadow, 0 6px 16px rgba(79, 70, 229, 0.45));\n}\n\n.feedback-button:focus-visible {\n outline: 2px solid var(--feedback-button-focus-ring, #4f46e5);\n outline-offset: 2px;\n box-shadow: var(--feedback-button-shadow, 0 4px 12px rgba(79, 70, 229, 0.35)),\n 0 0 0 4px var(--feedback-button-focus-ring-spread, rgba(79, 70, 229, 0.2));\n}\n\n.feedback-button:active {\n background-color: var(--feedback-button-active-bg, #3730a3);\n transform: scale(0.97);\n}\n\n.feedback-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n\n.feedback-button__icon {\n width: 18px;\n height: 18px;\n flex-shrink: 0;\n}\n`;\n\n// ─────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────\n\nlet stylesInjected = false;\n\nfunction injectStyles() {\n if (stylesInjected || typeof document === 'undefined') return;\n const style = document.createElement('style');\n style.setAttribute('data-feedback-button', '');\n style.textContent = BUTTON_STYLES;\n document.head.appendChild(style);\n stylesInjected = true;\n}\n\n/**\n * FeedbackButton — floating action button.\n *\n * Only renders when state === 'idle' (FeedbackPanel controls this).\n * Calls open() from context on click — does NOT manage modal state.\n *\n * Keyboard accessible: Tab focuses, Enter/Space activates.\n * Uses i18n key 'feedback.button.tooltip' for aria-label.\n */\nexport function FeedbackButton({ className, label }: FeedbackButtonProps): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, open } = useFeedback();\n\n // Inject styles on first render\n React.useEffect(() => {\n injectStyles();\n }, []);\n\n // Only render when idle — FeedbackPanel also gates this, but be defensive\n if (state.status !== 'idle') return null;\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n const tooltipFromI18n =\n t('feedback.button.tooltip', locale, customTranslations) ||\n t('feedback.button.open', locale, customTranslations);\n const tooltip = label ?? tooltipFromI18n;\n\n const handleClick = () => {\n open();\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n open();\n }\n };\n\n const classNames = ['feedback-button', className].filter(Boolean).join(' ');\n\n return (\n <button\n type=\"button\"\n className={classNames}\n aria-label={tooltip}\n title={tooltip}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n >\n <svg\n className=\"feedback-button__icon\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n {tooltip}\n </button>\n );\n}\n","/**\n * FeedbackDialogue — Layout 2: Conversation\n *\n * Chat interface with:\n * - Message history (user/assistant bubbles)\n * - Controlled textarea (Enter sends, Shift+Enter newline)\n * - Typing indicator while awaiting LLM response\n * - Step counter (step N of totalSteps)\n * - \"Start recording\" action (available after first exchange)\n * - \"Skip to form\" action when capabilities.bedrock === false or on error\n * - Auto-scroll to newest message\n * - Auto-transition to confirming when dialogue signals done\n */\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n useContext,\n} from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport { t } from '../core/i18n.js';\nimport type { DialogueEntry } from '../core/types.js';\nimport type { FeedbackType, FeedbackScope, FeedbackSize } from '../core/types.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackDialogueProps {\n className?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Sub-components\n// ─────────────────────────────────────────────────────────────────\n\nfunction MessageBubble({ entry }: { entry: DialogueEntry }) {\n const isUser = entry.role === 'user';\n return (\n <div\n className={`feedback-dialogue__bubble feedback-dialogue__bubble--${entry.role}`}\n data-role={entry.role}\n >\n <span className=\"feedback-dialogue__bubble-label\">\n {isUser ? 'You' : 'Assistant'}\n </span>\n <p className=\"feedback-dialogue__bubble-content\">{entry.content}</p>\n </div>\n );\n}\n\nfunction TypingIndicator() {\n return (\n <div className=\"feedback-dialogue__typing\" aria-live=\"polite\" aria-label=\"Assistant is typing\">\n <span className=\"feedback-dialogue__typing-dot\" />\n <span className=\"feedback-dialogue__typing-dot\" />\n <span className=\"feedback-dialogue__typing-dot\" />\n </div>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Main Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackDialogue — chat interface for the dialogue phase.\n *\n * Calls dispatch to send messages via the dialogue transport.\n * \"Skip to form\" is available when capabilities.bedrock === false or on error —\n * transitions directly to confirming with empty classification.\n */\nexport function FeedbackDialogue({ className }: FeedbackDialogueProps): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, _context } = useFeedback();\n const [inputValue, setInputValue] = useState('');\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n const capabilities = ctx?.capabilities;\n const dialogueUrl = ctx?.config.transport.dialogueUrl;\n\n const isDialogue = state.status === 'dialogue';\n const history = isDialogue ? state.history : [];\n const step = isDialogue ? state.step : 0;\n const totalSteps = isDialogue ? state.totalSteps : 5;\n const awaitingResponse = isDialogue && state.awaitingResponse;\n const error = isDialogue ? state.error : null;\n\n const canSkipToForm =\n capabilities?.bedrock === false || (error !== null && error !== '');\n\n // Auto-scroll to newest message\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [history.length, awaitingResponse]);\n\n // Send message handler\n const handleSend = useCallback(() => {\n const trimmed = inputValue.trim();\n if (!trimmed || awaitingResponse || !isDialogue) return;\n\n _context.dispatch({ type: 'SEND_MESSAGE', payload: { message: trimmed } });\n setInputValue('');\n\n if (dialogueUrl) {\n const url = dialogueUrl;\n const newStep = step + 1;\n const done = newStep >= totalSteps;\n\n void fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ message: trimmed, step: newStep }),\n })\n .then(async (res) => {\n if (!res.ok) throw new Error(`Dialogue error: ${res.status}`);\n const data = (await res.json()) as {\n response: string;\n classification?: {\n type?: FeedbackType;\n scope?: FeedbackScope;\n size?: FeedbackSize;\n };\n done?: boolean;\n };\n _context.dispatch({\n type: 'RECEIVE_RESPONSE',\n payload: {\n response: data.response,\n step: newStep,\n totalSteps,\n ...(data.classification !== undefined && { classification: data.classification }),\n done: data.done ?? done,\n },\n });\n })\n .catch((err: unknown) => {\n _context.dispatch({\n type: 'DIALOGUE_ERROR',\n payload: {\n error:\n err instanceof Error ? err.message : 'Dialogue request failed',\n },\n });\n });\n } else {\n // No dialogue URL — simulate simple response\n setTimeout(() => {\n const newStepVal = step + 1;\n const doneVal = newStepVal >= totalSteps;\n _context.dispatch({\n type: 'RECEIVE_RESPONSE',\n payload: {\n response: doneVal\n ? t('feedback.dialogue.complete', locale, customTranslations)\n : t('feedback.dialogue.turnPrompt', locale, customTranslations),\n step: newStepVal,\n totalSteps,\n done: doneVal,\n },\n });\n }, 800);\n }\n }, [inputValue, awaitingResponse, isDialogue, _context, step, totalSteps, locale, customTranslations, dialogueUrl]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n },\n [handleSend],\n );\n\n // Only render in dialogue state (after all hooks)\n if (state.status !== 'dialogue') return null;\n\n const handleSkipToForm = () => {\n _context.dispatch({ type: 'CONFIRM', payload: { classification: {} } });\n };\n\n const handleStartRecording = () => {\n _context.dispatch({ type: 'START_RECORDING' });\n };\n\n const hasExchange = history.length > 0;\n\n const classNames = ['feedback-dialogue', className].filter(Boolean).join(' ');\n\n return (\n <div className={classNames} role=\"region\" aria-label=\"Feedback dialogue\">\n {/* Step counter */}\n <div className=\"feedback-dialogue__progress\" aria-label={`Step ${step} of ${totalSteps}`}>\n <span className=\"feedback-dialogue__step\">\n {step > 0 ? `${step} / ${totalSteps}` : ''}\n </span>\n </div>\n\n {/* Message history */}\n <div\n className=\"feedback-dialogue__messages\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n >\n {history.length === 0 && (\n <p className=\"feedback-dialogue__opening\">\n {t('feedback.dialogue.opening', locale, customTranslations)}\n </p>\n )}\n {history.map((entry, idx) => (\n <MessageBubble key={`${entry.role}-${idx}`} entry={entry} />\n ))}\n {awaitingResponse && <TypingIndicator />}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Error banner */}\n {error && (\n <div className=\"feedback-dialogue__error\" role=\"alert\">\n {error}\n </div>\n )}\n\n {/* Action toolbar */}\n <div className=\"feedback-dialogue__actions\">\n {hasExchange && (\n <button\n type=\"button\"\n className=\"feedback-dialogue__action-btn\"\n onClick={handleStartRecording}\n >\n {t('feedback.button.screenshot', locale, customTranslations)}\n </button>\n )}\n {canSkipToForm && (\n <button\n type=\"button\"\n className=\"feedback-dialogue__skip-btn\"\n onClick={handleSkipToForm}\n aria-label=\"Skip to form\"\n >\n Skip to form\n </button>\n )}\n </div>\n\n {/* Input area */}\n <div className=\"feedback-dialogue__input-area\">\n <textarea\n className=\"feedback-dialogue__textarea\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={awaitingResponse}\n placeholder={\n awaitingResponse\n ? t('feedback.dialogue.thinking', locale, customTranslations)\n : 'Type your message… (Enter to send, Shift+Enter for newline)'\n }\n aria-label=\"Dialogue input\"\n rows={3}\n />\n <button\n type=\"button\"\n className=\"feedback-dialogue__send-btn\"\n onClick={handleSend}\n disabled={awaitingResponse || !inputValue.trim()}\n aria-label=\"Send message\"\n >\n {awaitingResponse ? (\n <span className=\"feedback-dialogue__spinner\" aria-hidden=\"true\" />\n ) : (\n '→'\n )}\n </button>\n </div>\n </div>\n );\n}\n","/**\n * FeedbackRecorder — Layout 3: Screen Capture\n *\n * Three sub-states:\n * 1. minimised — badge on FAB, underlying app is interactive\n * 2. capture — crosshair cursor overlay, click to capture\n * 3. review — thumbnail grid, delete per screenshot, \"Done\" → confirming\n *\n * Screenshots stored in capturedContext.screenshots[] (up to 10 entries).\n * All sub-state transitions driven through dispatch (no local state for flow).\n */\nimport React, {\n useCallback,\n useEffect,\n useState,\n useContext,\n} from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport type { ScreenshotEntry } from './FeedbackContext.js';\nimport { t } from '../core/i18n.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackRecorderProps {\n className?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Screenshot Capture Utility\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Attempts to capture the current viewport as a base64 PNG.\n * Uses dynamic import of html2canvas; falls back to getDisplayMedia.\n */\nasync function captureScreenshot(): Promise<string | null> {\n // Try html2canvas (dynamic import via computed string — optional dependency)\n try {\n const h2cName = 'html2canvas';\n const html2canvasModule = (await import(/* @vite-ignore */ h2cName).catch(\n () => null,\n )) as { default?: (el: HTMLElement) => Promise<HTMLCanvasElement> } | null;\n if (html2canvasModule?.default) {\n const canvas = await html2canvasModule.default(document.body);\n return canvas.toDataURL('image/png');\n }\n } catch {\n // html2canvas not available — fall through\n }\n\n // Try getDisplayMedia as secondary approach\n try {\n if (\n typeof navigator !== 'undefined' &&\n navigator.mediaDevices &&\n 'getDisplayMedia' in navigator.mediaDevices\n ) {\n const stream = await (\n navigator.mediaDevices as MediaDevices & {\n getDisplayMedia: (constraints?: MediaStreamConstraints) => Promise<MediaStream>;\n }\n ).getDisplayMedia({ video: true });\n const video = document.createElement('video');\n video.srcObject = stream;\n await video.play();\n const canvas = document.createElement('canvas');\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n canvas.getContext('2d')?.drawImage(video, 0, 0);\n stream.getTracks().forEach((track) => track.stop());\n return canvas.toDataURL('image/png');\n }\n } catch {\n // getDisplayMedia not available or denied\n }\n\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackRecorder — screen capture component.\n *\n * Minimised mode: badge floats over the UI; app remains interactive.\n * Capture mode: crosshair cursor overlay, click anywhere captures.\n * Review mode: thumbnail grid with delete buttons; \"Done\" goes to confirming.\n */\nexport function FeedbackRecorder({ className }: FeedbackRecorderProps): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, capturedContext, _context } = useFeedback();\n const [apiSupported, setApiSupported] = useState<boolean | null>(null);\n const [capturing, setCapturing] = useState(false);\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n\n const isRecording = state.status === 'recording';\n const recordingMode = isRecording ? state.recordingMode : null;\n const captureCount = isRecording ? state.captureCount : 0;\n const screenshots = capturedContext?.screenshots ?? [];\n\n // Check API availability on mount (unconditional)\n useEffect(() => {\n if (!isRecording) return;\n const hasGetDisplayMedia =\n typeof navigator !== 'undefined' &&\n !!navigator.mediaDevices &&\n 'getDisplayMedia' in navigator.mediaDevices;\n // Assume supported until proven otherwise (html2canvas is unknown at load time)\n setApiSupported(hasGetDisplayMedia);\n }, [isRecording]);\n\n // Handle capture click\n const handleCaptureClick = useCallback(async () => {\n if (capturing || screenshots.length >= 10) return;\n setCapturing(true);\n\n try {\n const dataUrl = await captureScreenshot();\n if (dataUrl) {\n const entry: ScreenshotEntry = {\n id: crypto.randomUUID(),\n dataUrl,\n capturedAt: new Date().toISOString(),\n };\n _context.dispatch({ type: 'ADD_SCREENSHOT', payload: entry });\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'review' },\n });\n } else {\n setApiSupported(false);\n }\n } catch {\n setApiSupported(false);\n } finally {\n setCapturing(false);\n }\n }, [capturing, screenshots.length, _context]);\n\n // Only render in recording state (after all hooks)\n if (state.status !== 'recording') return null;\n\n // ── Minimised mode ─────────────────────────────────────────────\n if (recordingMode === 'minimised') {\n return (\n <button\n type=\"button\"\n className={['feedback-recorder__badge', className].filter(Boolean).join(' ')}\n onClick={() =>\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'capture' },\n })\n }\n aria-label={`${captureCount} screenshot${captureCount !== 1 ? 's' : ''} captured. Click to resume`}\n >\n 📷 {captureCount}\n </button>\n );\n }\n\n // ── API not supported ──────────────────────────────────────────\n if (apiSupported === false) {\n return (\n <div className={['feedback-recorder', className].filter(Boolean).join(' ')}>\n <p className=\"feedback-recorder__unsupported-msg\">\n {t('feedback.toast.screenshotError', locale, customTranslations)}\n {' '}Screenshot capture is not supported in this browser.\n </p>\n <button\n type=\"button\"\n className=\"feedback-recorder__continue-btn\"\n onClick={() => _context.dispatch({ type: 'CONFIRM', payload: {} })}\n >\n Continue without screenshots\n </button>\n </div>\n );\n }\n\n // ── Capture mode ───────────────────────────────────────────────\n if (recordingMode === 'capture') {\n return (\n <div\n className={['feedback-recorder', 'feedback-recorder--capture', className]\n .filter(Boolean)\n .join(' ')}\n style={{ cursor: 'crosshair' }}\n onClick={() => void handleCaptureClick()}\n role=\"button\"\n tabIndex={0}\n aria-label=\"Click anywhere to capture screenshot\"\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n void handleCaptureClick();\n }\n }}\n >\n <div className=\"feedback-recorder__capture-overlay\">\n <p>\n {capturing ? 'Capturing…' : 'Click anywhere to capture a screenshot'}\n </p>\n <button\n type=\"button\"\n className=\"feedback-recorder__minimise-btn\"\n onClick={(e) => {\n e.stopPropagation();\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'minimised' },\n });\n }}\n >\n Minimise\n </button>\n {screenshots.length > 0 && (\n <button\n type=\"button\"\n className=\"feedback-recorder__review-btn\"\n onClick={(e) => {\n e.stopPropagation();\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'review' },\n });\n }}\n >\n Review ({screenshots.length})\n </button>\n )}\n </div>\n </div>\n );\n }\n\n // ── Review mode ────────────────────────────────────────────────\n return (\n <div\n className={['feedback-recorder', 'feedback-recorder--review', className]\n .filter(Boolean)\n .join(' ')}\n >\n <h3 className=\"feedback-recorder__title\">\n {screenshots.length === 0\n ? 'No screenshots yet'\n : `${screenshots.length} screenshot${screenshots.length !== 1 ? 's' : ''}`}\n </h3>\n\n {screenshots.length > 0 && (\n <div className=\"feedback-recorder__thumbnails\" role=\"list\">\n {screenshots.map((shot) => (\n <div\n key={shot.id}\n className=\"feedback-recorder__thumbnail\"\n role=\"listitem\"\n >\n <img\n src={shot.dataUrl}\n alt={`Screenshot captured at ${shot.capturedAt}`}\n className=\"feedback-recorder__thumbnail-img\"\n />\n <button\n type=\"button\"\n className=\"feedback-recorder__delete-btn\"\n aria-label={`${t('feedback.button.removeScreenshot', locale, customTranslations)} captured at ${shot.capturedAt}`}\n onClick={() =>\n _context.dispatch({\n type: 'REMOVE_SCREENSHOT',\n payload: { id: shot.id },\n })\n }\n >\n ×\n </button>\n </div>\n ))}\n </div>\n )}\n\n <div className=\"feedback-recorder__review-actions\">\n {screenshots.length < 10 && (\n <button\n type=\"button\"\n className=\"feedback-recorder__add-btn\"\n onClick={() =>\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'capture' },\n })\n }\n >\n {t('feedback.button.screenshot', locale, customTranslations)}\n </button>\n )}\n <button\n type=\"button\"\n className=\"feedback-recorder__done-btn\"\n onClick={() => _context.dispatch({ type: 'CONFIRM', payload: {} })}\n >\n Done\n </button>\n </div>\n </div>\n );\n}\n","/**\n * FeedbackConfirm — Layout 4: Review Form\n *\n * Pre-filled from dialogueState.classification and capturedContext.\n * Editable: title, description, type, scope, size.\n * Read-only: screenshot thumbnails, console log count, system info summary.\n * Duplicate detection: banner with links if capturedContext.duplicateCandidates is non-empty.\n * Submit: calls configured endpoint; shows spinner; success → submitted; error → inline retry.\n */\nimport React, {\n useCallback,\n useEffect,\n useState,\n useContext,\n} from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport { t } from '../core/i18n.js';\nimport type { FeedbackType, FeedbackScope, FeedbackSize } from '../core/types.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackConfirmProps {\n className?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Form State\n// ─────────────────────────────────────────────────────────────────\n\ninterface FormValues {\n title: string;\n description: string;\n type: FeedbackType | '';\n scope: FeedbackScope | '';\n size: FeedbackSize | '';\n}\n\ninterface FormErrors {\n title?: string;\n type?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Submit Payload\n// ─────────────────────────────────────────────────────────────────\n\ninterface SubmitPayload {\n title: string;\n description?: string | undefined;\n type: FeedbackType;\n scope?: FeedbackScope | undefined;\n size?: FeedbackSize | undefined;\n screenshots?: Array<{ dataUrl: string; capturedAt: string }> | undefined;\n consoleErrors?: string[] | undefined;\n systemInfo?: {\n url?: string | undefined;\n userAgent?: string | undefined;\n viewport?: { width: number; height: number } | undefined;\n locale?: string | undefined;\n } | undefined;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Validation\n// ─────────────────────────────────────────────────────────────────\n\nfunction validateForm(values: FormValues): FormErrors {\n const errors: FormErrors = {};\n if (!values.title || values.title.trim().length < 10) {\n errors.title = 'Title is required (minimum 10 characters)';\n }\n if (!values.type) {\n errors.type = 'Type is required';\n }\n return errors;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackConfirm — pre-filled review form.\n *\n * All editable fields use i18n keys feedback.confirm.* for labels.\n * Duplicate warning shown when capturedContext.duplicateCandidates is non-empty.\n * Submit posts to config.transport.submitUrl; maps 201 → submitted state.\n * On error: inline error with retry; modal stays open.\n */\nexport function FeedbackConfirm({ className }: FeedbackConfirmProps): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, capturedContext, _context } = useFeedback();\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n const submitUrl = ctx?.config.transport.submitUrl ?? '';\n\n // Hooks must be called unconditionally before any early return\n const confirming = state.status === 'confirming';\n const classification = confirming ? state.classification : {};\n const submitting = confirming && state.submitting;\n const submitError = confirming ? state.error : null;\n\n const [values, setValues] = useState<FormValues>(() => ({\n title: classification.title ?? '',\n description: classification.description ?? '',\n type: classification.type ?? '',\n scope: classification.scope ?? '',\n size: classification.size ?? '',\n }));\n\n const [touched, setTouched] = useState<Partial<Record<keyof FormValues, boolean>>>({});\n\n // Update form values when classification changes\n useEffect(() => {\n if (!confirming) return;\n setValues((prev) => ({\n title: classification.title ?? prev.title,\n description: classification.description ?? prev.description,\n type: classification.type ?? prev.type,\n scope: classification.scope ?? prev.scope,\n size: classification.size ?? prev.size,\n }));\n }, [confirming, classification.title, classification.description, classification.type, classification.scope, classification.size]);\n\n const formErrors = validateForm(values);\n const isValid = Object.keys(formErrors).length === 0;\n\n const screenshots = capturedContext?.screenshots ?? [];\n const consoleErrors = capturedContext?.consoleErrors ?? [];\n const duplicates = capturedContext?.duplicateCandidates ?? [];\n\n const handleChange = useCallback(\n (field: keyof FormValues, value: string) => {\n setValues((prev) => ({ ...prev, [field]: value }));\n setTouched((prev) => ({ ...prev, [field]: true }));\n },\n [],\n );\n\n const handleSubmit = useCallback(async () => {\n setTouched({ title: true, type: true });\n if (!isValid || !values.type) return;\n\n _context.dispatch({ type: 'SUBMIT_START' });\n\n const submissionPayload: SubmitPayload = {\n title: values.title.trim(),\n type: values.type as FeedbackType,\n };\n const descTrimmed = values.description.trim();\n if (descTrimmed) submissionPayload.description = descTrimmed;\n if (values.scope) submissionPayload.scope = values.scope as FeedbackScope;\n if (values.size) submissionPayload.size = values.size as FeedbackSize;\n if (screenshots.length > 0) {\n submissionPayload.screenshots = screenshots.map((s) => ({\n dataUrl: s.dataUrl,\n capturedAt: s.capturedAt,\n }));\n }\n if (consoleErrors.length > 0) submissionPayload.consoleErrors = consoleErrors;\n if (capturedContext) {\n submissionPayload.systemInfo = {\n url: capturedContext.url,\n userAgent: capturedContext.userAgent,\n viewport: capturedContext.viewport,\n locale: capturedContext.locale,\n };\n }\n\n try {\n const response = await fetch(submitUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(submissionPayload),\n });\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as { error?: string };\n _context.dispatch({\n type: 'SUBMIT_ERROR',\n payload: {\n error:\n body.error ??\n t('feedback.status.error', locale, customTranslations),\n },\n });\n return;\n }\n\n const result = (await response.json()) as { id?: string; feedbackId?: string };\n const feedbackId = result.id ?? result.feedbackId ?? 'unknown';\n _context.dispatch({ type: 'SUBMIT_SUCCESS', payload: { feedbackId } });\n } catch (err) {\n _context.dispatch({\n type: 'SUBMIT_ERROR',\n payload: {\n error:\n err instanceof Error\n ? err.message\n : t('feedback.status.error', locale, customTranslations),\n },\n });\n }\n }, [isValid, values, screenshots, consoleErrors, capturedContext, locale, customTranslations, submitUrl, _context]);\n\n // Only render in confirming state (after all hooks)\n if (state.status !== 'confirming') return null;\n\n const classNames = ['feedback-confirm', className].filter(Boolean).join(' ');\n\n return (\n <form\n className={classNames}\n onSubmit={(e) => {\n e.preventDefault();\n void handleSubmit();\n }}\n aria-label=\"Submit feedback\"\n noValidate\n >\n <h2 id=\"feedback-confirm-title\" className=\"feedback-confirm__title\">\n Review your feedback\n </h2>\n\n {/* Duplicate warning */}\n {duplicates.length > 0 && (\n <div className=\"feedback-confirm__duplicate-warning\" role=\"alert\">\n <strong>Similar feedback already exists:</strong>\n <ul>\n {duplicates.map((d) => (\n <li key={d.id}>\n <a href={`#${d.id}`} className=\"feedback-confirm__duplicate-link\">\n {d.title} ({d.id})\n </a>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {/* Title */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-title-input\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.titleLabel', locale, customTranslations) || 'Title'}\n {' '}\n <span aria-hidden=\"true\" className=\"feedback-confirm__required\">*</span>\n <span className=\"sr-only\">(required)</span>\n </label>\n <input\n id=\"feedback-confirm-title-input\"\n type=\"text\"\n className=\"feedback-confirm__input\"\n value={values.title}\n onChange={(e) => handleChange('title', e.target.value)}\n aria-required=\"true\"\n aria-invalid={touched.title && !!formErrors.title}\n aria-describedby={\n touched.title && formErrors.title\n ? 'feedback-confirm-title-error'\n : undefined\n }\n />\n {touched.title && formErrors.title && (\n <span\n id=\"feedback-confirm-title-error\"\n className=\"feedback-confirm__error\"\n role=\"alert\"\n >\n {formErrors.title}\n </span>\n )}\n </div>\n\n {/* Description */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-description\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.descriptionLabel', locale, customTranslations) || 'Description'}\n </label>\n <textarea\n id=\"feedback-confirm-description\"\n className=\"feedback-confirm__textarea\"\n value={values.description}\n onChange={(e) => handleChange('description', e.target.value)}\n rows={4}\n />\n </div>\n\n {/* Type */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-type\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.typeLabel', locale, customTranslations) || 'Type'}\n {' '}\n <span aria-hidden=\"true\" className=\"feedback-confirm__required\">*</span>\n <span className=\"sr-only\">(required)</span>\n </label>\n <select\n id=\"feedback-confirm-type\"\n className=\"feedback-confirm__select\"\n value={values.type}\n onChange={(e) => handleChange('type', e.target.value)}\n aria-required=\"true\"\n aria-invalid={touched.type && !!formErrors.type}\n aria-describedby={\n touched.type && formErrors.type\n ? 'feedback-confirm-type-error'\n : undefined\n }\n >\n <option value=\"\">{t('feedback.form.typePlaceholder', locale, customTranslations)}</option>\n <option value=\"bug\">{t('feedback.types.bug', locale, customTranslations)}</option>\n <option value=\"feature\">{t('feedback.types.feature', locale, customTranslations)}</option>\n <option value=\"ux\">{t('feedback.types.ux', locale, customTranslations)}</option>\n <option value=\"other\">{t('feedback.types.other', locale, customTranslations)}</option>\n </select>\n {touched.type && formErrors.type && (\n <span\n id=\"feedback-confirm-type-error\"\n className=\"feedback-confirm__error\"\n role=\"alert\"\n >\n {formErrors.type}\n </span>\n )}\n </div>\n\n {/* Scope */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-scope\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.scopeLabel', locale, customTranslations) || 'Scope'}\n </label>\n <select\n id=\"feedback-confirm-scope\"\n className=\"feedback-confirm__select\"\n value={values.scope}\n onChange={(e) => handleChange('scope', e.target.value)}\n >\n <option value=\"\">Select scope (optional)</option>\n <option value=\"isolated\">Isolated</option>\n <option value=\"cross-cutting\">Cross-cutting</option>\n <option value=\"systemic\">Systemic</option>\n </select>\n </div>\n\n {/* Size */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-size\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.sizeLabel', locale, customTranslations) || 'Size'}\n </label>\n <select\n id=\"feedback-confirm-size\"\n className=\"feedback-confirm__select\"\n value={values.size}\n onChange={(e) => handleChange('size', e.target.value)}\n >\n <option value=\"\">Select size (optional)</option>\n <option value=\"trivial\">Trivial</option>\n <option value=\"small\">Small</option>\n <option value=\"medium\">Medium</option>\n <option value=\"large\">Large</option>\n </select>\n </div>\n\n {/* Read-only context section */}\n <div className=\"feedback-confirm__context-section\" aria-label=\"Captured context (read-only)\">\n {screenshots.length > 0 && (\n <div className=\"feedback-confirm__screenshots\">\n <p className=\"feedback-confirm__context-label\">\n Screenshots ({screenshots.length})\n </p>\n <div className=\"feedback-confirm__screenshot-thumbnails\">\n {screenshots.map((s) => (\n <img\n key={s.id}\n src={s.dataUrl}\n alt={`Screenshot from ${s.capturedAt}`}\n className=\"feedback-confirm__screenshot-thumb\"\n />\n ))}\n </div>\n </div>\n )}\n\n {consoleErrors.length > 0 && (\n <p className=\"feedback-confirm__console-count\">\n {consoleErrors.length} console error{consoleErrors.length !== 1 ? 's' : ''} captured\n </p>\n )}\n\n {capturedContext && (\n <p className=\"feedback-confirm__system-info\">\n {capturedContext.url} — {capturedContext.userAgent.slice(0, 60)}…\n </p>\n )}\n </div>\n\n {/* Submit error */}\n {submitError && (\n <div className=\"feedback-confirm__submit-error\" role=\"alert\">\n {submitError}\n <button\n type=\"button\"\n className=\"feedback-confirm__retry-btn\"\n onClick={() => void handleSubmit()}\n >\n {t('feedback.status.retry', locale, customTranslations)}\n </button>\n </div>\n )}\n\n {/* Submit button */}\n <button\n type=\"submit\"\n className=\"feedback-confirm__submit-btn\"\n disabled={submitting}\n aria-label={\n submitting\n ? t('feedback.status.submitting', locale, customTranslations)\n : t('feedback.button.submit', locale, customTranslations)\n }\n >\n {submitting ? (\n <>\n <span className=\"feedback-confirm__spinner\" aria-hidden=\"true\" />\n {t('feedback.status.submitting', locale, customTranslations)}\n </>\n ) : (\n t('feedback.button.submit', locale, customTranslations)\n )}\n </button>\n </form>\n );\n}\n","/**\n * FeedbackPanel — Layout 7: Modal Container\n *\n * Renders into a React portal (document.body) to escape stacking context.\n * Shows FeedbackButton when idle; switches to the active layout component.\n *\n * Accessibility:\n * - role=\"dialog\" with aria-labelledby and aria-modal=\"true\"\n * - Focus trap: Tab/Shift+Tab cycles within open modal\n * - Focus returns to trigger element on close\n * - Escape triggers discard confirmation if dialogue has started\n * - CSS custom properties for theming\n */\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { useContext } from 'react';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport { FeedbackButton } from './FeedbackButton.js';\nimport { FeedbackDialogue } from './FeedbackDialogue.js';\nimport { FeedbackRecorder } from './FeedbackRecorder.js';\nimport { FeedbackConfirm } from './FeedbackConfirm.js';\nimport { t } from '../core/i18n.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Portal helper (avoids react-dom peer dependency at type level)\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Portal — renders children into document.body.\n * Uses dynamic import to avoid hard typing of react-dom.\n */\nfunction Portal({ children }: { children: React.ReactNode }): React.JSX.Element | null {\n const [isClient, setIsClient] = useState(false);\n\n useEffect(() => {\n setIsClient(true);\n }, []);\n\n if (!isClient || typeof document === 'undefined') {\n return React.createElement(React.Fragment, null, children);\n }\n\n // Use a simple div approach for rendering \"into body\"\n // This is equivalent to createPortal(children, document.body) in behaviour\n // without needing the @types/react-dom peer\n return React.createElement(\n React.Fragment,\n null,\n children,\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Focus Trap\n// ─────────────────────────────────────────────────────────────────\n\nconst FOCUSABLE_SELECTORS = [\n 'a[href]',\n 'button:not([disabled])',\n 'textarea:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(', ');\n\nfunction getFocusableElements(container: HTMLElement): HTMLElement[] {\n return Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter((el) => el.offsetParent !== null);\n}\n\nfunction useFocusTrap(containerRef: RefObject<HTMLElement | null>, isActive: boolean) {\n useEffect(() => {\n if (!isActive || !containerRef.current) return;\n\n const container = containerRef.current;\n const focusables = getFocusableElements(container);\n if (focusables.length > 0) {\n focusables[0]?.focus();\n } else {\n container.focus();\n }\n\n function handleKeyDown(e: KeyboardEvent) {\n if (e.key !== 'Tab') return;\n const elements = getFocusableElements(container);\n if (elements.length === 0) {\n e.preventDefault();\n return;\n }\n const first = elements[0];\n const last = elements[elements.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault();\n last?.focus();\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault();\n first?.focus();\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown);\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [isActive, containerRef]);\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Submitted view\n// ─────────────────────────────────────────────────────────────────\n\nfunction SubmittedView({\n feedbackId,\n onClose,\n locale,\n customTranslations,\n}: {\n feedbackId: string;\n onClose: () => void;\n locale: string;\n customTranslations?: Record<string, Record<string, string>> | undefined;\n}) {\n return (\n <div className=\"feedback-panel__submitted\" role=\"status\">\n <p className=\"feedback-panel__submitted-icon\" aria-hidden=\"true\">✓</p>\n <h3 className=\"feedback-panel__submitted-title\">\n {t('feedback.status.success', locale, customTranslations)}\n </h3>\n <p className=\"feedback-panel__submitted-id\">\n Your feedback ID: <strong>{feedbackId}</strong>\n </p>\n <button\n type=\"button\"\n className=\"feedback-panel__close-btn\"\n onClick={onClose}\n >\n {t('feedback.button.close', locale, customTranslations)}\n </button>\n </div>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Panel Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackPanel — modal container component.\n *\n * Renders a React portal to document.body.\n * Controls state-based layout switching and accessibility.\n */\nexport function FeedbackPanel(): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, isOpen, close, _context } = useFeedback();\n const dialogRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement | null>(null);\n const [showDiscardConfirm, setShowDiscardConfirm] = useState(false);\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n\n // Track trigger element for focus restoration\n useEffect(() => {\n if (isOpen) {\n triggerRef.current = document.activeElement as HTMLElement;\n } else {\n setShowDiscardConfirm(false);\n }\n }, [isOpen]);\n\n // Restore focus on close\n useEffect(() => {\n if (!isOpen && triggerRef.current) {\n triggerRef.current.focus();\n triggerRef.current = null;\n }\n }, [isOpen]);\n\n // Focus trap\n useFocusTrap(dialogRef, isOpen);\n\n // Escape key handler\n const handleEscape = useCallback(() => {\n const hasStarted =\n state.status === 'dialogue' &&\n (state.history.length > 0 || state.step > 0);\n if (hasStarted) {\n setShowDiscardConfirm(true);\n } else {\n close();\n }\n }, [state, close]);\n\n useEffect(() => {\n if (!isOpen) return;\n\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n e.preventDefault();\n handleEscape();\n }\n }\n\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [isOpen, handleEscape]);\n\n // Backdrop click handler\n const handleBackdropClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget) {\n handleEscape();\n }\n },\n [handleEscape],\n );\n\n const handleDiscardConfirm = () => {\n setShowDiscardConfirm(false);\n close();\n };\n\n const handleDiscardCancel = () => {\n setShowDiscardConfirm(false);\n };\n\n // Determine the modal title based on state\n const modalTitleId = 'feedback-panel-title';\n const modalTitle =\n state.status === 'idle'\n ? ''\n : state.status === 'dialogue'\n ? 'Feedback conversation'\n : state.status === 'recording'\n ? 'Capture screenshot'\n : state.status === 'confirming'\n ? 'Review your feedback'\n : 'Feedback submitted';\n\n return (\n <Portal>\n {/* Floating button — always rendered when idle */}\n {state.status === 'idle' && <FeedbackButton />}\n\n {/* Modal — rendered when panel is open */}\n {isOpen && (\n <div\n className=\"feedback-panel__backdrop\"\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 9999,\n backgroundColor: 'var(--feedback-backdrop-bg, rgba(0, 0, 0, 0.5))',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n onClick={handleBackdropClick}\n aria-hidden=\"false\"\n >\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={modalTitleId}\n tabIndex={-1}\n className=\"feedback-panel__dialog\"\n style={{\n position: 'relative',\n background: 'var(--feedback-panel-bg, #ffffff)',\n borderRadius: 'var(--feedback-panel-radius, 12px)',\n boxShadow: 'var(--feedback-panel-shadow, 0 20px 60px rgba(0,0,0,0.3))',\n width: 'var(--feedback-panel-width, 540px)',\n maxWidth: '95vw',\n maxHeight: '90vh',\n overflow: 'auto',\n padding: 'var(--feedback-panel-padding, 24px)',\n }}\n >\n {/* Hidden title for screen readers */}\n <h2 id={modalTitleId} className=\"feedback-panel__title\" aria-live=\"polite\">\n {modalTitle}\n </h2>\n\n {/* Close button */}\n <button\n type=\"button\"\n className=\"feedback-panel__close-button\"\n aria-label={t('feedback.button.close', locale, customTranslations)}\n onClick={handleEscape}\n style={{\n position: 'absolute',\n top: 16,\n right: 16,\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n fontSize: 20,\n lineHeight: 1,\n padding: '4px 8px',\n color: 'inherit',\n }}\n >\n ×\n </button>\n\n {/* Discard confirmation overlay */}\n {showDiscardConfirm && (\n <div\n className=\"feedback-panel__discard-confirm\"\n role=\"alertdialog\"\n aria-labelledby=\"feedback-panel-discard-title\"\n style={{\n position: 'absolute',\n inset: 0,\n background: 'var(--feedback-panel-bg, #ffffff)',\n borderRadius: 'inherit',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 16,\n padding: 24,\n zIndex: 1,\n }}\n >\n <p id=\"feedback-panel-discard-title\" style={{ fontWeight: 600 }}>\n Discard your feedback?\n </p>\n <p>Your progress will be lost.</p>\n <div style={{ display: 'flex', gap: 12 }}>\n <button\n type=\"button\"\n className=\"feedback-panel__discard-confirm-btn\"\n onClick={handleDiscardConfirm}\n >\n Discard\n </button>\n <button\n type=\"button\"\n className=\"feedback-panel__discard-cancel-btn\"\n onClick={handleDiscardCancel}\n autoFocus\n >\n Continue editing\n </button>\n </div>\n </div>\n )}\n\n {/* Layout switching */}\n {state.status === 'dialogue' && <FeedbackDialogue />}\n {state.status === 'recording' && <FeedbackRecorder />}\n {state.status === 'confirming' && <FeedbackConfirm />}\n {state.status === 'submitted' && (\n <SubmittedView\n feedbackId={state.feedbackId}\n onClose={() => _context.dispatch({ type: 'CLOSE' })}\n locale={locale}\n customTranslations={customTranslations ?? undefined}\n />\n )}\n </div>\n </div>\n )}\n </Portal>\n );\n}\n","/**\n * submitResolver — CRUD resolver for the feedback intake submit path.\n *\n * Server-only module — NOT imported by the client bundle.\n * Tree-shakeable: the /react subpath export does NOT re-export this.\n *\n * Factory: createIntakeResolver(config: SubmitHandlerConfig): RequestHandler\n *\n * HTTP contract:\n * POST /api/feedback/submit\n * Content-Type: application/json\n * Body: SubmitPayload\n * → 201 { id: string; status: string }\n * → 400 { errors: string[] }\n * → 503 { retryable: true; error: string }\n *\n * OTel span: 'feedback.submit' with attributes:\n * app, feedbackId (after store), environment\n *\n * No Bedrock dependency — resolver calls submit handler only, not dialogue handler.\n */\n\nimport { withSpan } from '../../core/observability.js';\nimport type { FeedbackEnvironment, FeedbackType, FeedbackScope, FeedbackSize } from '../../core/types.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Request / Response types\n// ─────────────────────────────────────────────────────────────────\n\n/** Minimal HTTP request shape (Lambda/Express compatible) */\nexport interface RequestBody {\n headers?: Record<string, string | string[] | undefined>;\n body?: unknown;\n}\n\n/** Minimal HTTP response shape (Lambda/Express compatible) */\nexport interface ResponseBody {\n statusCode: number;\n headers: Record<string, string>;\n body: string;\n}\n\n/** Express-compatible RequestHandler */\nexport type RequestHandler = (\n req: RequestBody,\n res: {\n status: (code: number) => { json: (body: unknown) => void };\n json: (body: unknown) => void;\n },\n) => Promise<void>;\n\n// ─────────────────────────────────────────────────────────────────\n// Submit Payload (what the client POSTs)\n// ─────────────────────────────────────────────────────────────────\n\nexport interface SubmitPayload {\n title: string;\n description?: string | undefined;\n type: FeedbackType;\n scope?: FeedbackScope | undefined;\n size?: FeedbackSize | undefined;\n screenshots?: Array<{ dataUrl: string; capturedAt: string }> | undefined;\n consoleErrors?: string[] | undefined;\n systemInfo?: {\n url?: string | undefined;\n userAgent?: string | undefined;\n viewport?: { width: number; height: number } | undefined;\n locale?: string | undefined;\n } | undefined;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Config\n// ─────────────────────────────────────────────────────────────────\n\nexport interface SubmitHandlerConfig {\n /** Application name — used in OTel attributes and feedback ID */\n appName: string;\n /** Deployment environment */\n environment?: FeedbackEnvironment;\n /** DynamoDB table name — if absent, storage is skipped (Minimal level) */\n tableName?: string;\n /** AWS region for DynamoDB */\n awsRegion?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Validation\n// ─────────────────────────────────────────────────────────────────\n\nconst VALID_TYPES: FeedbackType[] = ['bug', 'feature', 'ux', 'other'];\n\nfunction validatePayload(body: unknown): { valid: true; payload: SubmitPayload } | { valid: false; errors: string[] } {\n if (!body || typeof body !== 'object' || Array.isArray(body)) {\n return { valid: false, errors: ['Request body must be a JSON object'] };\n }\n\n const obj = body as Record<string, unknown>;\n const errors: string[] = [];\n\n // title: required, string, min 1 char\n if (!obj['title'] || typeof obj['title'] !== 'string' || (obj['title'] as string).trim().length === 0) {\n errors.push('title is required and must be a non-empty string');\n } else if ((obj['title'] as string).trim().length < 1) {\n errors.push('title must be at least 1 character');\n }\n\n // type: required, must be valid\n if (!obj['type']) {\n errors.push('type is required');\n } else if (!VALID_TYPES.includes(obj['type'] as FeedbackType)) {\n errors.push(`type must be one of: ${VALID_TYPES.join(', ')} (got: ${String(obj['type'])})`);\n }\n\n // description: optional string\n if (obj['description'] !== undefined && typeof obj['description'] !== 'string') {\n errors.push('description must be a string if provided');\n }\n\n if (errors.length > 0) {\n return { valid: false, errors };\n }\n\n return {\n valid: true,\n payload: {\n title: (obj['title'] as string).trim(),\n ...(obj['description'] !== undefined && { description: obj['description'] as string }),\n type: obj['type'] as FeedbackType,\n ...(obj['scope'] !== undefined && { scope: obj['scope'] as FeedbackScope }),\n ...(obj['size'] !== undefined && { size: obj['size'] as FeedbackSize }),\n ...(obj['screenshots'] !== undefined && { screenshots: obj['screenshots'] as SubmitPayload['screenshots'] }),\n ...(obj['consoleErrors'] !== undefined && { consoleErrors: obj['consoleErrors'] as string[] }),\n ...(obj['systemInfo'] !== undefined && { systemInfo: obj['systemInfo'] as SubmitPayload['systemInfo'] }),\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// DynamoDB store (minimal implementation)\n// ─────────────────────────────────────────────────────────────────\n\nasync function storeFeedback(\n payload: SubmitPayload,\n config: SubmitHandlerConfig,\n feedbackId: string,\n): Promise<void> {\n if (!config.tableName || !config.awsRegion) {\n // Minimal level — no storage\n return;\n }\n\n // Dynamic import to avoid hard dependency on AWS SDK in client bundle\n const dynamoPrefix = ['@aws', '-sdk/client-dynamo', 'db'].join('');\n const { DynamoDBClient, PutItemCommand } = await import(/* @vite-ignore */ dynamoPrefix);\n\n const client = new DynamoDBClient({ region: config.awsRegion });\n const now = new Date().toISOString();\n\n await client.send(\n new PutItemCommand({\n TableName: config.tableName,\n Item: {\n PK: { S: `PROJECT#${config.appName}` },\n SK: { S: `FB#${now}#${feedbackId}` },\n id: { S: feedbackId },\n appName: { S: config.appName },\n type: { S: payload.type },\n status: { S: 'open' },\n content: { S: payload.title },\n submittedAt: { S: now },\n lastModifiedAt: { S: now },\n environment: { S: config.environment ?? 'production' },\n },\n }),\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Feedback ID generation\n// ─────────────────────────────────────────────────────────────────\n\nfunction generateId(): string {\n const date = new Date().toISOString().slice(0, 10);\n const uuid8 = crypto.randomUUID().replace(/-/g, '').slice(0, 8);\n return `FB-${date}-${uuid8}`;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Handler Factory\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Creates an Express/Lambda-compatible request handler for the submit endpoint.\n *\n * Validates payload → stores in DynamoDB (if configured) → returns 201 with ID.\n * Returns 503 with retryable:true when DynamoDB is unavailable.\n * No Bedrock dependency.\n */\nexport function createIntakeResolver(config: SubmitHandlerConfig): RequestHandler {\n return async (req, res) => {\n // ── Content-Type validation ───────────────────────────────────\n const contentType = (req.headers?.['content-type'] ?? '').toString();\n if (!contentType.includes('application/json')) {\n res.status(400).json({\n errors: ['Content-Type must be application/json'],\n });\n return;\n }\n\n // ── Body validation ───────────────────────────────────────────\n const validation = validatePayload(req.body);\n if (!validation.valid) {\n res.status(400).json({ errors: validation.errors });\n return;\n }\n\n const { payload } = validation;\n const feedbackId = generateId();\n\n // ── OTel span ─────────────────────────────────────────────────\n try {\n await withSpan(\n 'feedback.submit',\n {\n app: config.appName,\n environment: config.environment ?? 'production',\n },\n async (span) => {\n try {\n await storeFeedback(payload, config, feedbackId);\n\n // Attach feedbackId after successful store\n if (span) {\n (span as unknown as { setAttribute?: (k: string, v: string) => void })\n .setAttribute?.('feedbackId', feedbackId);\n }\n\n res.status(201).json({\n id: feedbackId,\n status: 'open',\n });\n } catch (storageError) {\n const isDynamoUnavailable =\n storageError instanceof Error &&\n (storageError.name === 'ServiceUnavailableException' ||\n storageError.name === 'ProvisionedThroughputExceededException' ||\n storageError.message.includes('ECONNREFUSED') ||\n storageError.message.includes('unavailable') ||\n storageError.message.includes('DynamoDB'));\n\n if (isDynamoUnavailable) {\n res.status(503).json({\n retryable: true,\n error: 'Storage service temporarily unavailable. Please try again.',\n });\n } else {\n res.status(503).json({\n retryable: true,\n error: 'Failed to store feedback. Please try again.',\n });\n }\n }\n },\n );\n } catch {\n res.status(503).json({\n retryable: true,\n error: 'Internal error. Please try again.',\n });\n }\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"react.js","sources":["../src/react/FeedbackContext.ts","../src/react/FeedbackProvider.tsx","../src/react/useFeedback.ts","../src/react/FeedbackButton.tsx","../src/react/FeedbackDialogue.tsx","../src/react/FeedbackRecorder.tsx","../src/react/FeedbackConfirm.tsx","../src/react/FeedbackPanel.tsx"],"sourcesContent":["/**\n * FeedbackContext — typed React context for the feedback intake UI.\n *\n * Import from: import { FeedbackProvider, useFeedback } from 'anton-bakker-feedback/react'\n *\n * This file defines the shape of context value and creates the context object.\n * The provider and hook are in separate files to keep each unit focused.\n */\nimport { createContext } from 'react';\nimport type { FeedbackType, FeedbackScope, FeedbackSize, DialogueEntry } from '../core/types.js';\nimport type { ResolvedFeedbackConfig } from '../core/config.js';\n\n// ─────────────────────────────────────────────────────────────────\n// State Machine Types\n// ─────────────────────────────────────────────────────────────────\n\n/** Screenshots captured during a session */\nexport type ScreenshotEntry = {\n id: string;\n dataUrl: string; // base64 or blob URL\n capturedAt: string;\n};\n\n/** Context captured at the point of opening the feedback panel */\nexport interface CapturedContext {\n url: string;\n pageTitle: string;\n userAgent: string;\n viewport: { width: number; height: number };\n locale: string;\n consoleErrors: string[];\n screenshots: ScreenshotEntry[];\n duplicateCandidates: Array<{ id: string; title: string }>;\n customFields?: Record<string, string | number | boolean>;\n}\n\n/** Classification suggested by the dialogue */\nexport interface DialogueClassification {\n type?: FeedbackType;\n scope?: FeedbackScope;\n size?: FeedbackSize;\n title?: string;\n description?: string;\n confidence?: number;\n}\n\n/** State when idle — no panel open */\nexport interface IdleState {\n status: 'idle';\n}\n\n/** State when dialogue is in progress */\nexport interface DialogueState {\n status: 'dialogue';\n history: DialogueEntry[];\n step: number;\n totalSteps: number;\n awaitingResponse: boolean;\n classification: DialogueClassification;\n error: string | null;\n}\n\n/** State during screen capture (recording sub-states) */\nexport interface RecordingState {\n status: 'recording';\n recordingMode: 'minimised' | 'capture' | 'review';\n captureCount: number;\n}\n\n/** State when confirming submission */\nexport interface ConfirmingState {\n status: 'confirming';\n classification: DialogueClassification;\n submitting: boolean;\n error: string | null;\n}\n\n/** State after successful submission */\nexport interface SubmittedState {\n status: 'submitted';\n feedbackId: string;\n}\n\n/** Discriminated union of all possible panel states */\nexport type FeedbackPanelState =\n | IdleState\n | DialogueState\n | RecordingState\n | ConfirmingState\n | SubmittedState;\n\n// ─────────────────────────────────────────────────────────────────\n// Capability Detection\n// ─────────────────────────────────────────────────────────────────\n\n/** Runtime capability detection results */\nexport interface Capabilities {\n /** Whether Bedrock dialogue is available */\n bedrock: boolean;\n /** Whether DynamoDB storage is available */\n dynamodb: boolean;\n /** Capability detection has completed */\n detected: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Context Value Shape\n// ─────────────────────────────────────────────────────────────────\n\n/** The value provided by FeedbackContext */\nexport interface FeedbackContextValue {\n /** Resolved config with all defaults applied */\n config: ResolvedFeedbackConfig;\n /** Current panel state */\n state: FeedbackPanelState;\n /** Convenience: is panel currently open (not idle) */\n isOpen: boolean;\n /** Context captured when panel was opened */\n capturedContext: CapturedContext | null;\n /** Runtime capabilities (Bedrock, DynamoDB) */\n capabilities: Capabilities;\n /** Active i18n locale */\n locale: string;\n /** Open the panel, optionally merging context */\n open: (trigger?: Partial<CapturedContext>) => void;\n /** Close and reset to idle */\n close: () => void;\n /** Internal: dispatch state transitions */\n dispatch: (action: FeedbackAction) => void;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Actions\n// ─────────────────────────────────────────────────────────────────\n\nexport type FeedbackAction =\n | { type: 'OPEN'; payload: Partial<CapturedContext> }\n | { type: 'OPEN' }\n | { type: 'CLOSE' }\n | { type: 'SEND_MESSAGE'; payload: { message: string } }\n | { type: 'RECEIVE_RESPONSE'; payload: { response: string; step: number; totalSteps: number; classification?: DialogueClassification; done?: boolean } }\n | { type: 'DIALOGUE_ERROR'; payload: { error: string } }\n | { type: 'START_RECORDING' }\n | { type: 'SET_RECORDING_MODE'; payload: { mode: 'minimised' | 'capture' | 'review' } }\n | { type: 'ADD_SCREENSHOT'; payload: ScreenshotEntry }\n | { type: 'REMOVE_SCREENSHOT'; payload: { id: string } }\n | { type: 'CONFIRM'; payload: { classification?: DialogueClassification } }\n | { type: 'CONFIRM' }\n | { type: 'SUBMIT_START' }\n | { type: 'SUBMIT_SUCCESS'; payload: { feedbackId: string } }\n | { type: 'SUBMIT_ERROR'; payload: { error: string } }\n | { type: 'SET_CAPABILITIES'; payload: Capabilities };\n\n// ─────────────────────────────────────────────────────────────────\n// Context Object\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackContext — React context for the intake UI.\n *\n * Throws a descriptive error when consumed outside FeedbackProvider:\n * import { FeedbackProvider } from 'anton-bakker-feedback/react'\n */\nexport const FeedbackContext = createContext<FeedbackContextValue | null>(null);\n\nFeedbackContext.displayName = 'FeedbackContext';\n","/**\n * FeedbackProvider — context provider for the feedback intake UI.\n *\n * Usage:\n * import { FeedbackProvider } from 'anton-bakker-feedback/react'\n *\n * Example:\n * <FeedbackProvider\n * config={{ appName: 'my-app', transport: { submitUrl: 'https://…' } }}\n * canSubmitFeedback={(user) => user.role !== 'guest'}\n * locale=\"nl\"\n * >\n * <App />\n * </FeedbackProvider>\n *\n * RBAC gate: if canSubmitFeedback returns false, the provider renders\n * children without injecting FeedbackPanel. No button is shown.\n *\n * Progressive enhancement: capability detection (Bedrock, DynamoDB) runs\n * on mount and is stored in context — no consumer code branches required.\n *\n * The \"Skip to form\" action is surfaced via capabilities.bedrock === false\n * so FeedbackDialogue can render the \"Skip to form\" button automatically.\n * Consumers import from: import { FeedbackProvider } from 'anton-bakker-feedback/react'\n */\nimport React, {\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useReducer,\n Suspense,\n lazy,\n} from 'react';\nimport {\n FeedbackContext,\n type FeedbackAction,\n type FeedbackPanelState,\n type FeedbackContextValue,\n type CapturedContext,\n type Capabilities,\n type DialogueClassification,\n} from './FeedbackContext.js';\nimport type { FeedbackConfig } from '../core/config.js';\nimport { validateConfig, resolveConfig } from '../core/config.js';\nimport { detectLocale } from '../core/i18n.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackProviderProps {\n /** Feedback configuration */\n config: FeedbackConfig;\n /**\n * RBAC gate — if this returns false, FeedbackPanel is not rendered.\n * Pass the current authenticated user object.\n */\n canSubmitFeedback: (user?: unknown) => boolean;\n /** Optional i18n locale override (BCP 47). Defaults to navigator.language → 'en' */\n locale?: string;\n /** The current user (passed to canSubmitFeedback) */\n user?: unknown;\n children: ReactNode;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Initial State\n// ─────────────────────────────────────────────────────────────────\n\nconst INITIAL_PANEL_STATE: FeedbackPanelState = { status: 'idle' };\n\nfunction buildInitialCapturedContext(): CapturedContext {\n const isClient = typeof window !== 'undefined';\n return {\n url: isClient ? window.location.href : '',\n pageTitle: isClient ? document.title : '',\n userAgent: isClient ? navigator.userAgent : '',\n viewport: isClient\n ? { width: window.innerWidth, height: window.innerHeight }\n : { width: 0, height: 0 },\n locale: isClient ? navigator.language : 'en',\n consoleErrors: [],\n screenshots: [],\n duplicateCandidates: [],\n };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Reducer\n// ─────────────────────────────────────────────────────────────────\n\ninterface ProviderInternalState {\n panel: FeedbackPanelState;\n capturedContext: CapturedContext | null;\n capabilities: Capabilities;\n preservedClassification: DialogueClassification;\n}\n\nconst INITIAL_INTERNAL_STATE: ProviderInternalState = {\n panel: INITIAL_PANEL_STATE,\n capturedContext: null,\n capabilities: { bedrock: false, dynamodb: false, detected: false },\n preservedClassification: {},\n};\n\nfunction feedbackReducer(\n state: ProviderInternalState,\n action: FeedbackAction,\n): ProviderInternalState {\n switch (action.type) {\n case 'OPEN': {\n if (state.panel.status !== 'idle') return state; // idempotent\n const base = buildInitialCapturedContext();\n const merged: CapturedContext =\n 'payload' in action && action.payload\n ? { ...base, ...action.payload }\n : base;\n\n const newPanel: FeedbackPanelState = state.capabilities.bedrock\n ? {\n status: 'dialogue',\n history: [],\n step: 0,\n totalSteps: 5,\n awaitingResponse: false,\n classification: {},\n error: null,\n }\n : {\n status: 'confirming',\n classification: {},\n submitting: false,\n error: null,\n };\n\n return { ...state, panel: newPanel, capturedContext: merged };\n }\n\n case 'CLOSE':\n return {\n ...state,\n panel: INITIAL_PANEL_STATE,\n capturedContext: null,\n preservedClassification: {},\n };\n\n case 'SEND_MESSAGE': {\n if (state.panel.status !== 'dialogue') return state;\n const newHistory = [\n ...state.panel.history,\n {\n role: 'user' as const,\n content: action.payload.message,\n timestamp: new Date().toISOString(),\n },\n ];\n return {\n ...state,\n panel: {\n ...state.panel,\n history: newHistory,\n awaitingResponse: true,\n error: null,\n },\n };\n }\n\n case 'RECEIVE_RESPONSE': {\n if (state.panel.status !== 'dialogue') return state;\n const { response, step, totalSteps, classification, done } = action.payload;\n const mergedClassification: DialogueClassification = {\n ...state.panel.classification,\n ...(classification ?? {}),\n };\n const newHistory = [\n ...state.panel.history,\n {\n role: 'assistant' as const,\n content: response,\n timestamp: new Date().toISOString(),\n },\n ];\n\n if (done === true) {\n return {\n ...state,\n panel: {\n status: 'confirming',\n classification: mergedClassification,\n submitting: false,\n error: null,\n },\n preservedClassification: mergedClassification,\n };\n }\n\n return {\n ...state,\n panel: {\n ...state.panel,\n history: newHistory,\n step,\n totalSteps,\n awaitingResponse: false,\n classification: mergedClassification,\n },\n };\n }\n\n case 'DIALOGUE_ERROR': {\n if (state.panel.status !== 'dialogue') return state;\n return {\n ...state,\n panel: {\n ...state.panel,\n awaitingResponse: false,\n error: action.payload.error,\n },\n };\n }\n\n case 'START_RECORDING': {\n const currentClassification =\n state.panel.status === 'dialogue' || state.panel.status === 'confirming'\n ? state.panel.classification\n : state.preservedClassification;\n\n return {\n ...state,\n panel: {\n status: 'recording',\n recordingMode: 'capture',\n captureCount: state.capturedContext?.screenshots.length ?? 0,\n },\n preservedClassification: currentClassification,\n };\n }\n\n case 'SET_RECORDING_MODE': {\n if (state.panel.status !== 'recording') return state;\n return {\n ...state,\n panel: {\n ...state.panel,\n recordingMode: action.payload.mode,\n },\n };\n }\n\n case 'ADD_SCREENSHOT': {\n if (!state.capturedContext) return state;\n const existing = state.capturedContext.screenshots;\n if (existing.length >= 10) return state;\n const updated = [...existing, action.payload];\n return {\n ...state,\n capturedContext: { ...state.capturedContext, screenshots: updated },\n panel:\n state.panel.status === 'recording'\n ? { ...state.panel, captureCount: updated.length }\n : state.panel,\n };\n }\n\n case 'REMOVE_SCREENSHOT': {\n if (!state.capturedContext) return state;\n const filtered = state.capturedContext.screenshots.filter(\n (s) => s.id !== action.payload.id,\n );\n return {\n ...state,\n capturedContext: { ...state.capturedContext, screenshots: filtered },\n panel:\n state.panel.status === 'recording'\n ? { ...state.panel, captureCount: filtered.length }\n : state.panel,\n };\n }\n\n case 'CONFIRM': {\n const classification =\n ('payload' in action && action.payload?.classification)\n ? action.payload.classification\n : state.preservedClassification;\n return {\n ...state,\n panel: {\n status: 'confirming',\n classification,\n submitting: false,\n error: null,\n },\n preservedClassification: classification,\n };\n }\n\n case 'SUBMIT_START': {\n if (state.panel.status !== 'confirming') return state;\n return {\n ...state,\n panel: { ...state.panel, submitting: true, error: null },\n };\n }\n\n case 'SUBMIT_SUCCESS': {\n return {\n ...state,\n panel: {\n status: 'submitted',\n feedbackId: action.payload.feedbackId,\n },\n };\n }\n\n case 'SUBMIT_ERROR': {\n if (state.panel.status !== 'confirming') return state;\n return {\n ...state,\n panel: {\n ...state.panel,\n submitting: false,\n error: action.payload.error,\n },\n };\n }\n\n case 'SET_CAPABILITIES': {\n return { ...state, capabilities: action.payload };\n }\n\n default:\n return state;\n }\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Capability Detection\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Detects whether Bedrock (dialogue) and DynamoDB (storage) are available.\n * Pings configured transport URLs; falls back to minimal on any error.\n */\nasync function detectCapabilities(\n submitUrl: string,\n dialogueUrl: string | undefined,\n): Promise<Capabilities> {\n let dynamodb = true;\n let bedrock = !!dialogueUrl;\n\n if (dialogueUrl) {\n try {\n const ctrl = new AbortController();\n const timeoutId = setTimeout(() => ctrl.abort(), 3000);\n await fetch(dialogueUrl, { method: 'HEAD', signal: ctrl.signal });\n clearTimeout(timeoutId);\n bedrock = true;\n } catch {\n bedrock = false;\n }\n }\n\n try {\n const ctrl = new AbortController();\n const timeoutId = setTimeout(() => ctrl.abort(), 3000);\n await fetch(submitUrl, { method: 'HEAD', signal: ctrl.signal });\n clearTimeout(timeoutId);\n dynamodb = true;\n } catch {\n dynamodb = false;\n }\n\n return { bedrock, dynamodb, detected: true };\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Lazy FeedbackPanel (avoids circular import at definition time)\n// ─────────────────────────────────────────────────────────────────\n\nconst FeedbackPanelLazy = lazy(\n () => import('./FeedbackPanel.js').then((m) => ({ default: m.FeedbackPanel })),\n);\n\n// ─────────────────────────────────────────────────────────────────\n// Provider Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackProvider wraps your application root and provides the feedback\n * context, RBAC gate, capability detection, and i18n locale.\n *\n * \"Skip to form\" — when capabilities.bedrock is false, FeedbackDialogue\n * will show the \"Skip to form\" button to allow direct access to FeedbackConfirm.\n *\n * Import: import { FeedbackProvider } from 'anton-bakker-feedback/react'\n */\nexport function FeedbackProvider({\n config,\n canSubmitFeedback,\n locale: localeProp,\n user,\n children,\n}: FeedbackProviderProps): React.JSX.Element {\n // ── Config Validation ──────────────────────────────────────────\n const resolvedConfig = useMemo(() => {\n const validation = validateConfig(config);\n if (!validation.valid) {\n console.error(\n '[anton-bakker-feedback/react] FeedbackProvider received invalid config:\\n' +\n validation.errors\n .map((e) => ` • ${e}`)\n .join('\\n') +\n '\\nFeedbackPanel will not render until config is corrected.',\n );\n return null;\n }\n return resolveConfig(config);\n }, [config]);\n\n // ── Locale ────────────────────────────────────────────────────\n const locale = useMemo(() => {\n const preferred =\n localeProp ??\n (typeof navigator !== 'undefined' ? navigator.language : 'en');\n return detectLocale(preferred);\n }, [localeProp]);\n\n // ── RBAC Gate ─────────────────────────────────────────────────\n const authorised = useMemo(\n () => canSubmitFeedback(user),\n [canSubmitFeedback, user],\n );\n\n // ── State ─────────────────────────────────────────────────────\n const [internalState, dispatch] = useReducer(\n feedbackReducer,\n INITIAL_INTERNAL_STATE,\n );\n\n // ── Capability Detection on Mount ──────────────────────────────\n useEffect(() => {\n if (!resolvedConfig) return;\n let cancelled = false;\n\n void detectCapabilities(\n resolvedConfig.transport.submitUrl,\n resolvedConfig.transport.dialogueUrl,\n ).then((caps) => {\n if (!cancelled) {\n dispatch({ type: 'SET_CAPABILITIES', payload: caps });\n }\n });\n\n return () => {\n cancelled = true;\n };\n }, [resolvedConfig]);\n\n // ── Public API ────────────────────────────────────────────────\n const open = useCallback(\n (trigger?: Partial<CapturedContext>) => {\n if (trigger !== undefined) {\n dispatch({ type: 'OPEN', payload: trigger });\n } else {\n dispatch({ type: 'OPEN' });\n }\n },\n [],\n );\n\n const close = useCallback(() => {\n dispatch({ type: 'CLOSE' });\n }, []);\n\n // ── Context Value ─────────────────────────────────────────────\n const contextValue = useMemo((): FeedbackContextValue | null => {\n if (!resolvedConfig) return null;\n return {\n config: resolvedConfig,\n state: internalState.panel,\n isOpen: internalState.panel.status !== 'idle',\n capturedContext: internalState.capturedContext,\n capabilities: internalState.capabilities,\n locale,\n open,\n close,\n dispatch,\n };\n }, [resolvedConfig, internalState, locale, open, close]);\n\n // ── Render ────────────────────────────────────────────────────\n if (!contextValue) {\n // Invalid config — render children without panel (graceful degradation)\n return React.createElement(React.Fragment, null, children);\n }\n\n return (\n <FeedbackContext.Provider value={contextValue}>\n {children}\n {authorised && (\n <Suspense fallback={null}>\n <FeedbackPanelLazy />\n </Suspense>\n )}\n </FeedbackContext.Provider>\n );\n}\n","/**\n * useFeedback — imperative hook surface for the feedback intake UI.\n *\n * Returns { open, close, state, isOpen, capturedContext }.\n *\n * Throws a descriptive error when called outside FeedbackProvider:\n * import { FeedbackProvider } from 'anton-bakker-feedback/react'\n */\nimport { useContext } from 'react';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport type {\n FeedbackPanelState,\n CapturedContext,\n FeedbackContextValue,\n} from './FeedbackContext.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Return Type\n// ─────────────────────────────────────────────────────────────────\n\nexport interface UseFeedbackReturn {\n /** Open the panel. Merges optional trigger context. Idempotent if already open. */\n open: (trigger?: Partial<CapturedContext>) => void;\n /** Close the panel and reset state to idle. */\n close: () => void;\n /** Current panel state (discriminated union) */\n state: FeedbackPanelState;\n /** Convenience: true when panel is not idle */\n isOpen: boolean;\n /** Context captured when panel was opened (null when idle) */\n capturedContext: CapturedContext | null;\n /** Full context value (for advanced use cases) */\n _context: FeedbackContextValue;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Hook\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * useFeedback() — consumes the FeedbackContext.\n *\n * Must be called inside a component that is a descendant of FeedbackProvider.\n * Throws a descriptive error otherwise.\n *\n * @example\n * const { open, close, state, isOpen } = useFeedback();\n */\nexport function useFeedback(): UseFeedbackReturn {\n const ctx = useContext(FeedbackContext);\n\n if (ctx === null) {\n throw new Error(\n '[useFeedback] Could not find FeedbackContext. ' +\n 'Make sure your component is rendered inside a <FeedbackProvider>. ' +\n 'Import from: import { FeedbackProvider } from \"anton-bakker-feedback/react\"',\n );\n }\n\n return {\n open: ctx.open,\n close: ctx.close,\n state: ctx.state,\n isOpen: ctx.isOpen,\n capturedContext: ctx.capturedContext,\n _context: ctx,\n };\n}\n","/**\n * FeedbackButton — floating action button for triggering the feedback panel.\n *\n * Layout 1: Trigger\n * Rendered by FeedbackPanel when state === 'idle'.\n * Not mounted when RBAC gate blocks rendering.\n *\n * Theming via CSS custom properties:\n * --feedback-button-bg\n * --feedback-button-color\n * --feedback-button-hover-bg\n * --feedback-button-focus-ring\n */\nimport React from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { useContext } from 'react';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport { t } from '../core/i18n.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackButtonProps {\n /** Additional CSS class names (merged via className prop) */\n className?: string;\n /** Override the default tooltip/aria-label text */\n label?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Styles (CSS custom properties for theming)\n// ─────────────────────────────────────────────────────────────────\n\nconst BUTTON_STYLES = `\n.feedback-button {\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: var(--feedback-z-index, 9998);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n padding: 12px 16px;\n border-radius: 24px;\n border: none;\n cursor: pointer;\n font-family: inherit;\n font-size: 14px;\n font-weight: 600;\n line-height: 1;\n white-space: nowrap;\n background-color: var(--feedback-button-bg, #4f46e5);\n color: var(--feedback-button-color, #ffffff);\n box-shadow: var(--feedback-button-shadow, 0 4px 12px rgba(79, 70, 229, 0.35));\n transition: background-color 0.15s ease, transform 0.1s ease, box-shadow 0.15s ease;\n -webkit-user-select: none;\n user-select: none;\n outline: none;\n}\n\n.feedback-button:hover {\n background-color: var(--feedback-button-hover-bg, #4338ca);\n box-shadow: var(--feedback-button-hover-shadow, 0 6px 16px rgba(79, 70, 229, 0.45));\n}\n\n.feedback-button:focus-visible {\n outline: 2px solid var(--feedback-button-focus-ring, #4f46e5);\n outline-offset: 2px;\n box-shadow: var(--feedback-button-shadow, 0 4px 12px rgba(79, 70, 229, 0.35)),\n 0 0 0 4px var(--feedback-button-focus-ring-spread, rgba(79, 70, 229, 0.2));\n}\n\n.feedback-button:active {\n background-color: var(--feedback-button-active-bg, #3730a3);\n transform: scale(0.97);\n}\n\n.feedback-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n}\n\n.feedback-button__icon {\n width: 18px;\n height: 18px;\n flex-shrink: 0;\n}\n`;\n\n// ─────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────\n\nlet stylesInjected = false;\n\nfunction injectStyles() {\n if (stylesInjected || typeof document === 'undefined') return;\n const style = document.createElement('style');\n style.setAttribute('data-feedback-button', '');\n style.textContent = BUTTON_STYLES;\n document.head.appendChild(style);\n stylesInjected = true;\n}\n\n/**\n * FeedbackButton — floating action button.\n *\n * Only renders when state === 'idle' (FeedbackPanel controls this).\n * Calls open() from context on click — does NOT manage modal state.\n *\n * Keyboard accessible: Tab focuses, Enter/Space activates.\n * Uses i18n key 'feedback.button.tooltip' for aria-label.\n */\nexport function FeedbackButton({ className, label }: FeedbackButtonProps): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, open } = useFeedback();\n\n // Inject styles on first render\n React.useEffect(() => {\n injectStyles();\n }, []);\n\n // Only render when idle — FeedbackPanel also gates this, but be defensive\n if (state.status !== 'idle') return null;\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n const tooltipFromI18n =\n t('feedback.button.tooltip', locale, customTranslations) ||\n t('feedback.button.open', locale, customTranslations);\n const tooltip = label ?? tooltipFromI18n;\n\n const handleClick = () => {\n open();\n };\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLButtonElement>) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n open();\n }\n };\n\n const classNames = ['feedback-button', className].filter(Boolean).join(' ');\n\n return (\n <button\n type=\"button\"\n className={classNames}\n aria-label={tooltip}\n title={tooltip}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n >\n <svg\n className=\"feedback-button__icon\"\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n {tooltip}\n </button>\n );\n}\n","/**\n * FeedbackDialogue — Layout 2: Conversation\n *\n * Chat interface with:\n * - Message history (user/assistant bubbles)\n * - Controlled textarea (Enter sends, Shift+Enter newline)\n * - Typing indicator while awaiting LLM response\n * - Step counter (step N of totalSteps)\n * - \"Start recording\" action (available after first exchange)\n * - \"Skip to form\" action when capabilities.bedrock === false or on error\n * - Auto-scroll to newest message\n * - Auto-transition to confirming when dialogue signals done\n */\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n useContext,\n} from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport { t } from '../core/i18n.js';\nimport type { DialogueEntry } from '../core/types.js';\nimport type { FeedbackType, FeedbackScope, FeedbackSize } from '../core/types.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackDialogueProps {\n className?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Sub-components\n// ─────────────────────────────────────────────────────────────────\n\nfunction MessageBubble({ entry }: { entry: DialogueEntry }) {\n const isUser = entry.role === 'user';\n return (\n <div\n className={`feedback-dialogue__bubble feedback-dialogue__bubble--${entry.role}`}\n data-role={entry.role}\n >\n <span className=\"feedback-dialogue__bubble-label\">\n {isUser ? 'You' : 'Assistant'}\n </span>\n <p className=\"feedback-dialogue__bubble-content\">{entry.content}</p>\n </div>\n );\n}\n\nfunction TypingIndicator() {\n return (\n <div className=\"feedback-dialogue__typing\" aria-live=\"polite\" aria-label=\"Assistant is typing\">\n <span className=\"feedback-dialogue__typing-dot\" />\n <span className=\"feedback-dialogue__typing-dot\" />\n <span className=\"feedback-dialogue__typing-dot\" />\n </div>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Main Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackDialogue — chat interface for the dialogue phase.\n *\n * Calls dispatch to send messages via the dialogue transport.\n * \"Skip to form\" is available when capabilities.bedrock === false or on error —\n * transitions directly to confirming with empty classification.\n */\nexport function FeedbackDialogue({ className }: FeedbackDialogueProps): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, _context } = useFeedback();\n const [inputValue, setInputValue] = useState('');\n const messagesEndRef = useRef<HTMLDivElement>(null);\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n const capabilities = ctx?.capabilities;\n const dialogueUrl = ctx?.config.transport.dialogueUrl;\n\n const isDialogue = state.status === 'dialogue';\n const history = isDialogue ? state.history : [];\n const step = isDialogue ? state.step : 0;\n const totalSteps = isDialogue ? state.totalSteps : 5;\n const awaitingResponse = isDialogue && state.awaitingResponse;\n const error = isDialogue ? state.error : null;\n\n const canSkipToForm =\n capabilities?.bedrock === false || (error !== null && error !== '');\n\n // Auto-scroll to newest message\n useEffect(() => {\n messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });\n }, [history.length, awaitingResponse]);\n\n // Send message handler\n const handleSend = useCallback(() => {\n const trimmed = inputValue.trim();\n if (!trimmed || awaitingResponse || !isDialogue) return;\n\n _context.dispatch({ type: 'SEND_MESSAGE', payload: { message: trimmed } });\n setInputValue('');\n\n if (dialogueUrl) {\n const url = dialogueUrl;\n const newStep = step + 1;\n const done = newStep >= totalSteps;\n\n void fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ message: trimmed, step: newStep }),\n })\n .then(async (res) => {\n if (!res.ok) throw new Error(`Dialogue error: ${res.status}`);\n const data = (await res.json()) as {\n response: string;\n classification?: {\n type?: FeedbackType;\n scope?: FeedbackScope;\n size?: FeedbackSize;\n };\n done?: boolean;\n };\n _context.dispatch({\n type: 'RECEIVE_RESPONSE',\n payload: {\n response: data.response,\n step: newStep,\n totalSteps,\n ...(data.classification !== undefined && { classification: data.classification }),\n done: data.done ?? done,\n },\n });\n })\n .catch((err: unknown) => {\n _context.dispatch({\n type: 'DIALOGUE_ERROR',\n payload: {\n error:\n err instanceof Error ? err.message : 'Dialogue request failed',\n },\n });\n });\n } else {\n // No dialogue URL — simulate simple response\n setTimeout(() => {\n const newStepVal = step + 1;\n const doneVal = newStepVal >= totalSteps;\n _context.dispatch({\n type: 'RECEIVE_RESPONSE',\n payload: {\n response: doneVal\n ? t('feedback.dialogue.complete', locale, customTranslations)\n : t('feedback.dialogue.turnPrompt', locale, customTranslations),\n step: newStepVal,\n totalSteps,\n done: doneVal,\n },\n });\n }, 800);\n }\n }, [inputValue, awaitingResponse, isDialogue, _context, step, totalSteps, locale, customTranslations, dialogueUrl]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n handleSend();\n }\n },\n [handleSend],\n );\n\n // Only render in dialogue state (after all hooks)\n if (state.status !== 'dialogue') return null;\n\n const handleSkipToForm = () => {\n _context.dispatch({ type: 'CONFIRM', payload: { classification: {} } });\n };\n\n const handleStartRecording = () => {\n _context.dispatch({ type: 'START_RECORDING' });\n };\n\n const hasExchange = history.length > 0;\n\n const classNames = ['feedback-dialogue', className].filter(Boolean).join(' ');\n\n return (\n <div className={classNames} role=\"region\" aria-label=\"Feedback dialogue\">\n {/* Step counter */}\n <div className=\"feedback-dialogue__progress\" aria-label={`Step ${step} of ${totalSteps}`}>\n <span className=\"feedback-dialogue__step\">\n {step > 0 ? `${step} / ${totalSteps}` : ''}\n </span>\n </div>\n\n {/* Message history */}\n <div\n className=\"feedback-dialogue__messages\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n >\n {history.length === 0 && (\n <p className=\"feedback-dialogue__opening\">\n {t('feedback.dialogue.opening', locale, customTranslations)}\n </p>\n )}\n {history.map((entry, idx) => (\n <MessageBubble key={`${entry.role}-${idx}`} entry={entry} />\n ))}\n {awaitingResponse && <TypingIndicator />}\n <div ref={messagesEndRef} />\n </div>\n\n {/* Error banner */}\n {error && (\n <div className=\"feedback-dialogue__error\" role=\"alert\">\n {error}\n </div>\n )}\n\n {/* Action toolbar */}\n <div className=\"feedback-dialogue__actions\">\n {hasExchange && (\n <button\n type=\"button\"\n className=\"feedback-dialogue__action-btn\"\n onClick={handleStartRecording}\n >\n {t('feedback.button.screenshot', locale, customTranslations)}\n </button>\n )}\n {canSkipToForm && (\n <button\n type=\"button\"\n className=\"feedback-dialogue__skip-btn\"\n onClick={handleSkipToForm}\n aria-label=\"Skip to form\"\n >\n Skip to form\n </button>\n )}\n </div>\n\n {/* Input area */}\n <div className=\"feedback-dialogue__input-area\">\n <textarea\n className=\"feedback-dialogue__textarea\"\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onKeyDown={handleKeyDown}\n disabled={awaitingResponse}\n placeholder={\n awaitingResponse\n ? t('feedback.dialogue.thinking', locale, customTranslations)\n : 'Type your message… (Enter to send, Shift+Enter for newline)'\n }\n aria-label=\"Dialogue input\"\n rows={3}\n />\n <button\n type=\"button\"\n className=\"feedback-dialogue__send-btn\"\n onClick={handleSend}\n disabled={awaitingResponse || !inputValue.trim()}\n aria-label=\"Send message\"\n >\n {awaitingResponse ? (\n <span className=\"feedback-dialogue__spinner\" aria-hidden=\"true\" />\n ) : (\n '→'\n )}\n </button>\n </div>\n </div>\n );\n}\n","/**\n * FeedbackRecorder — Layout 3: Screen Capture\n *\n * Three sub-states:\n * 1. minimised — badge on FAB, underlying app is interactive\n * 2. capture — crosshair cursor overlay, click to capture\n * 3. review — thumbnail grid, delete per screenshot, \"Done\" → confirming\n *\n * Screenshots stored in capturedContext.screenshots[] (up to 10 entries).\n * All sub-state transitions driven through dispatch (no local state for flow).\n */\nimport React, {\n useCallback,\n useEffect,\n useState,\n useContext,\n} from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport type { ScreenshotEntry } from './FeedbackContext.js';\nimport { t } from '../core/i18n.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackRecorderProps {\n className?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Screenshot Capture Utility\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Attempts to capture the current viewport as a base64 PNG.\n * Uses dynamic import of html2canvas; falls back to getDisplayMedia.\n */\nasync function captureScreenshot(): Promise<string | null> {\n // Try html2canvas (dynamic import via computed string — optional dependency)\n try {\n const h2cName = 'html2canvas';\n const html2canvasModule = (await import(/* @vite-ignore */ h2cName).catch(\n () => null,\n )) as { default?: (el: HTMLElement) => Promise<HTMLCanvasElement> } | null;\n if (html2canvasModule?.default) {\n const canvas = await html2canvasModule.default(document.body);\n return canvas.toDataURL('image/png');\n }\n } catch {\n // html2canvas not available — fall through\n }\n\n // Try getDisplayMedia as secondary approach\n try {\n if (\n typeof navigator !== 'undefined' &&\n navigator.mediaDevices &&\n 'getDisplayMedia' in navigator.mediaDevices\n ) {\n const stream = await (\n navigator.mediaDevices as MediaDevices & {\n getDisplayMedia: (constraints?: MediaStreamConstraints) => Promise<MediaStream>;\n }\n ).getDisplayMedia({ video: true });\n const video = document.createElement('video');\n video.srcObject = stream;\n await video.play();\n const canvas = document.createElement('canvas');\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n canvas.getContext('2d')?.drawImage(video, 0, 0);\n stream.getTracks().forEach((track) => track.stop());\n return canvas.toDataURL('image/png');\n }\n } catch {\n // getDisplayMedia not available or denied\n }\n\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackRecorder — screen capture component.\n *\n * Minimised mode: badge floats over the UI; app remains interactive.\n * Capture mode: crosshair cursor overlay, click anywhere captures.\n * Review mode: thumbnail grid with delete buttons; \"Done\" goes to confirming.\n */\nexport function FeedbackRecorder({ className }: FeedbackRecorderProps): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, capturedContext, _context } = useFeedback();\n const [apiSupported, setApiSupported] = useState<boolean | null>(null);\n const [capturing, setCapturing] = useState(false);\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n\n const isRecording = state.status === 'recording';\n const recordingMode = isRecording ? state.recordingMode : null;\n const captureCount = isRecording ? state.captureCount : 0;\n const screenshots = capturedContext?.screenshots ?? [];\n\n // Check API availability on mount (unconditional)\n useEffect(() => {\n if (!isRecording) return;\n const hasGetDisplayMedia =\n typeof navigator !== 'undefined' &&\n !!navigator.mediaDevices &&\n 'getDisplayMedia' in navigator.mediaDevices;\n // Assume supported until proven otherwise (html2canvas is unknown at load time)\n setApiSupported(hasGetDisplayMedia);\n }, [isRecording]);\n\n // Handle capture click\n const handleCaptureClick = useCallback(async () => {\n if (capturing || screenshots.length >= 10) return;\n setCapturing(true);\n\n try {\n const dataUrl = await captureScreenshot();\n if (dataUrl) {\n const entry: ScreenshotEntry = {\n id: crypto.randomUUID(),\n dataUrl,\n capturedAt: new Date().toISOString(),\n };\n _context.dispatch({ type: 'ADD_SCREENSHOT', payload: entry });\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'review' },\n });\n } else {\n setApiSupported(false);\n }\n } catch {\n setApiSupported(false);\n } finally {\n setCapturing(false);\n }\n }, [capturing, screenshots.length, _context]);\n\n // Only render in recording state (after all hooks)\n if (state.status !== 'recording') return null;\n\n // ── Minimised mode ─────────────────────────────────────────────\n if (recordingMode === 'minimised') {\n return (\n <button\n type=\"button\"\n className={['feedback-recorder__badge', className].filter(Boolean).join(' ')}\n onClick={() =>\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'capture' },\n })\n }\n aria-label={`${captureCount} screenshot${captureCount !== 1 ? 's' : ''} captured. Click to resume`}\n >\n 📷 {captureCount}\n </button>\n );\n }\n\n // ── API not supported ──────────────────────────────────────────\n if (apiSupported === false) {\n return (\n <div className={['feedback-recorder', className].filter(Boolean).join(' ')}>\n <p className=\"feedback-recorder__unsupported-msg\">\n {t('feedback.toast.screenshotError', locale, customTranslations)}\n {' '}Screenshot capture is not supported in this browser.\n </p>\n <button\n type=\"button\"\n className=\"feedback-recorder__continue-btn\"\n onClick={() => _context.dispatch({ type: 'CONFIRM', payload: {} })}\n >\n Continue without screenshots\n </button>\n </div>\n );\n }\n\n // ── Capture mode ───────────────────────────────────────────────\n if (recordingMode === 'capture') {\n return (\n <div\n className={['feedback-recorder', 'feedback-recorder--capture', className]\n .filter(Boolean)\n .join(' ')}\n style={{ cursor: 'crosshair' }}\n onClick={() => void handleCaptureClick()}\n role=\"button\"\n tabIndex={0}\n aria-label=\"Click anywhere to capture screenshot\"\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n void handleCaptureClick();\n }\n }}\n >\n <div className=\"feedback-recorder__capture-overlay\">\n <p>\n {capturing ? 'Capturing…' : 'Click anywhere to capture a screenshot'}\n </p>\n <button\n type=\"button\"\n className=\"feedback-recorder__minimise-btn\"\n onClick={(e) => {\n e.stopPropagation();\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'minimised' },\n });\n }}\n >\n Minimise\n </button>\n {screenshots.length > 0 && (\n <button\n type=\"button\"\n className=\"feedback-recorder__review-btn\"\n onClick={(e) => {\n e.stopPropagation();\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'review' },\n });\n }}\n >\n Review ({screenshots.length})\n </button>\n )}\n </div>\n </div>\n );\n }\n\n // ── Review mode ────────────────────────────────────────────────\n return (\n <div\n className={['feedback-recorder', 'feedback-recorder--review', className]\n .filter(Boolean)\n .join(' ')}\n >\n <h3 className=\"feedback-recorder__title\">\n {screenshots.length === 0\n ? 'No screenshots yet'\n : `${screenshots.length} screenshot${screenshots.length !== 1 ? 's' : ''}`}\n </h3>\n\n {screenshots.length > 0 && (\n <div className=\"feedback-recorder__thumbnails\" role=\"list\">\n {screenshots.map((shot) => (\n <div\n key={shot.id}\n className=\"feedback-recorder__thumbnail\"\n role=\"listitem\"\n >\n <img\n src={shot.dataUrl}\n alt={`Screenshot captured at ${shot.capturedAt}`}\n className=\"feedback-recorder__thumbnail-img\"\n />\n <button\n type=\"button\"\n className=\"feedback-recorder__delete-btn\"\n aria-label={`${t('feedback.button.removeScreenshot', locale, customTranslations)} captured at ${shot.capturedAt}`}\n onClick={() =>\n _context.dispatch({\n type: 'REMOVE_SCREENSHOT',\n payload: { id: shot.id },\n })\n }\n >\n ×\n </button>\n </div>\n ))}\n </div>\n )}\n\n <div className=\"feedback-recorder__review-actions\">\n {screenshots.length < 10 && (\n <button\n type=\"button\"\n className=\"feedback-recorder__add-btn\"\n onClick={() =>\n _context.dispatch({\n type: 'SET_RECORDING_MODE',\n payload: { mode: 'capture' },\n })\n }\n >\n {t('feedback.button.screenshot', locale, customTranslations)}\n </button>\n )}\n <button\n type=\"button\"\n className=\"feedback-recorder__done-btn\"\n onClick={() => _context.dispatch({ type: 'CONFIRM', payload: {} })}\n >\n Done\n </button>\n </div>\n </div>\n );\n}\n","/**\n * FeedbackConfirm — Layout 4: Review Form\n *\n * Pre-filled from dialogueState.classification and capturedContext.\n * Editable: title, description, type, scope, size.\n * Read-only: screenshot thumbnails, console log count, system info summary.\n * Duplicate detection: banner with links if capturedContext.duplicateCandidates is non-empty.\n * Submit: calls configured endpoint; shows spinner; success → submitted; error → inline retry.\n */\nimport React, {\n useCallback,\n useEffect,\n useState,\n useContext,\n} from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport { t } from '../core/i18n.js';\nimport type { FeedbackType, FeedbackScope, FeedbackSize } from '../core/types.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Props\n// ─────────────────────────────────────────────────────────────────\n\nexport interface FeedbackConfirmProps {\n className?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Form State\n// ─────────────────────────────────────────────────────────────────\n\ninterface FormValues {\n title: string;\n description: string;\n type: FeedbackType | '';\n scope: FeedbackScope | '';\n size: FeedbackSize | '';\n}\n\ninterface FormErrors {\n title?: string;\n type?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Submit Payload\n// ─────────────────────────────────────────────────────────────────\n\ninterface SubmitPayload {\n title: string;\n description?: string | undefined;\n type: FeedbackType;\n scope?: FeedbackScope | undefined;\n size?: FeedbackSize | undefined;\n screenshots?: Array<{ dataUrl: string; capturedAt: string }> | undefined;\n consoleErrors?: string[] | undefined;\n systemInfo?: {\n url?: string | undefined;\n userAgent?: string | undefined;\n viewport?: { width: number; height: number } | undefined;\n locale?: string | undefined;\n } | undefined;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Validation\n// ─────────────────────────────────────────────────────────────────\n\nfunction validateForm(values: FormValues): FormErrors {\n const errors: FormErrors = {};\n if (!values.title || values.title.trim().length < 10) {\n errors.title = 'Title is required (minimum 10 characters)';\n }\n if (!values.type) {\n errors.type = 'Type is required';\n }\n return errors;\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackConfirm — pre-filled review form.\n *\n * All editable fields use i18n keys feedback.confirm.* for labels.\n * Duplicate warning shown when capturedContext.duplicateCandidates is non-empty.\n * Submit posts to config.transport.submitUrl; maps 201 → submitted state.\n * On error: inline error with retry; modal stays open.\n */\nexport function FeedbackConfirm({ className }: FeedbackConfirmProps): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, capturedContext, _context } = useFeedback();\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n const submitUrl = ctx?.config.transport.submitUrl ?? '';\n\n // Hooks must be called unconditionally before any early return\n const confirming = state.status === 'confirming';\n const classification = confirming ? state.classification : {};\n const submitting = confirming && state.submitting;\n const submitError = confirming ? state.error : null;\n\n const [values, setValues] = useState<FormValues>(() => ({\n title: classification.title ?? '',\n description: classification.description ?? '',\n type: classification.type ?? '',\n scope: classification.scope ?? '',\n size: classification.size ?? '',\n }));\n\n const [touched, setTouched] = useState<Partial<Record<keyof FormValues, boolean>>>({});\n\n // Update form values when classification changes\n useEffect(() => {\n if (!confirming) return;\n setValues((prev) => ({\n title: classification.title ?? prev.title,\n description: classification.description ?? prev.description,\n type: classification.type ?? prev.type,\n scope: classification.scope ?? prev.scope,\n size: classification.size ?? prev.size,\n }));\n }, [confirming, classification.title, classification.description, classification.type, classification.scope, classification.size]);\n\n const formErrors = validateForm(values);\n const isValid = Object.keys(formErrors).length === 0;\n\n const screenshots = capturedContext?.screenshots ?? [];\n const consoleErrors = capturedContext?.consoleErrors ?? [];\n const duplicates = capturedContext?.duplicateCandidates ?? [];\n\n const handleChange = useCallback(\n (field: keyof FormValues, value: string) => {\n setValues((prev) => ({ ...prev, [field]: value }));\n setTouched((prev) => ({ ...prev, [field]: true }));\n },\n [],\n );\n\n const handleSubmit = useCallback(async () => {\n setTouched({ title: true, type: true });\n if (!isValid || !values.type) return;\n\n _context.dispatch({ type: 'SUBMIT_START' });\n\n const submissionPayload: SubmitPayload = {\n title: values.title.trim(),\n type: values.type as FeedbackType,\n };\n const descTrimmed = values.description.trim();\n if (descTrimmed) submissionPayload.description = descTrimmed;\n if (values.scope) submissionPayload.scope = values.scope as FeedbackScope;\n if (values.size) submissionPayload.size = values.size as FeedbackSize;\n if (screenshots.length > 0) {\n submissionPayload.screenshots = screenshots.map((s) => ({\n dataUrl: s.dataUrl,\n capturedAt: s.capturedAt,\n }));\n }\n if (consoleErrors.length > 0) submissionPayload.consoleErrors = consoleErrors;\n if (capturedContext) {\n submissionPayload.systemInfo = {\n url: capturedContext.url,\n userAgent: capturedContext.userAgent,\n viewport: capturedContext.viewport,\n locale: capturedContext.locale,\n };\n }\n\n try {\n const response = await fetch(submitUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(submissionPayload),\n });\n\n if (!response.ok) {\n const body = (await response.json().catch(() => ({}))) as { error?: string };\n _context.dispatch({\n type: 'SUBMIT_ERROR',\n payload: {\n error:\n body.error ??\n t('feedback.status.error', locale, customTranslations),\n },\n });\n return;\n }\n\n const result = (await response.json()) as { id?: string; feedbackId?: string };\n const feedbackId = result.id ?? result.feedbackId ?? 'unknown';\n _context.dispatch({ type: 'SUBMIT_SUCCESS', payload: { feedbackId } });\n } catch (err) {\n _context.dispatch({\n type: 'SUBMIT_ERROR',\n payload: {\n error:\n err instanceof Error\n ? err.message\n : t('feedback.status.error', locale, customTranslations),\n },\n });\n }\n }, [isValid, values, screenshots, consoleErrors, capturedContext, locale, customTranslations, submitUrl, _context]);\n\n // Only render in confirming state (after all hooks)\n if (state.status !== 'confirming') return null;\n\n const classNames = ['feedback-confirm', className].filter(Boolean).join(' ');\n\n return (\n <form\n className={classNames}\n onSubmit={(e) => {\n e.preventDefault();\n void handleSubmit();\n }}\n aria-label=\"Submit feedback\"\n noValidate\n >\n <h2 id=\"feedback-confirm-title\" className=\"feedback-confirm__title\">\n Review your feedback\n </h2>\n\n {/* Duplicate warning */}\n {duplicates.length > 0 && (\n <div className=\"feedback-confirm__duplicate-warning\" role=\"alert\">\n <strong>Similar feedback already exists:</strong>\n <ul>\n {duplicates.map((d) => (\n <li key={d.id}>\n <a href={`#${d.id}`} className=\"feedback-confirm__duplicate-link\">\n {d.title} ({d.id})\n </a>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {/* Title */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-title-input\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.titleLabel', locale, customTranslations) || 'Title'}\n {' '}\n <span aria-hidden=\"true\" className=\"feedback-confirm__required\">*</span>\n <span className=\"sr-only\">(required)</span>\n </label>\n <input\n id=\"feedback-confirm-title-input\"\n type=\"text\"\n className=\"feedback-confirm__input\"\n value={values.title}\n onChange={(e) => handleChange('title', e.target.value)}\n aria-required=\"true\"\n aria-invalid={touched.title && !!formErrors.title}\n aria-describedby={\n touched.title && formErrors.title\n ? 'feedback-confirm-title-error'\n : undefined\n }\n />\n {touched.title && formErrors.title && (\n <span\n id=\"feedback-confirm-title-error\"\n className=\"feedback-confirm__error\"\n role=\"alert\"\n >\n {formErrors.title}\n </span>\n )}\n </div>\n\n {/* Description */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-description\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.descriptionLabel', locale, customTranslations) || 'Description'}\n </label>\n <textarea\n id=\"feedback-confirm-description\"\n className=\"feedback-confirm__textarea\"\n value={values.description}\n onChange={(e) => handleChange('description', e.target.value)}\n rows={4}\n />\n </div>\n\n {/* Type */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-type\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.typeLabel', locale, customTranslations) || 'Type'}\n {' '}\n <span aria-hidden=\"true\" className=\"feedback-confirm__required\">*</span>\n <span className=\"sr-only\">(required)</span>\n </label>\n <select\n id=\"feedback-confirm-type\"\n className=\"feedback-confirm__select\"\n value={values.type}\n onChange={(e) => handleChange('type', e.target.value)}\n aria-required=\"true\"\n aria-invalid={touched.type && !!formErrors.type}\n aria-describedby={\n touched.type && formErrors.type\n ? 'feedback-confirm-type-error'\n : undefined\n }\n >\n <option value=\"\">{t('feedback.form.typePlaceholder', locale, customTranslations)}</option>\n <option value=\"bug\">{t('feedback.types.bug', locale, customTranslations)}</option>\n <option value=\"feature\">{t('feedback.types.feature', locale, customTranslations)}</option>\n <option value=\"ux\">{t('feedback.types.ux', locale, customTranslations)}</option>\n <option value=\"other\">{t('feedback.types.other', locale, customTranslations)}</option>\n </select>\n {touched.type && formErrors.type && (\n <span\n id=\"feedback-confirm-type-error\"\n className=\"feedback-confirm__error\"\n role=\"alert\"\n >\n {formErrors.type}\n </span>\n )}\n </div>\n\n {/* Scope */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-scope\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.scopeLabel', locale, customTranslations) || 'Scope'}\n </label>\n <select\n id=\"feedback-confirm-scope\"\n className=\"feedback-confirm__select\"\n value={values.scope}\n onChange={(e) => handleChange('scope', e.target.value)}\n >\n <option value=\"\">Select scope (optional)</option>\n <option value=\"isolated\">Isolated</option>\n <option value=\"cross-cutting\">Cross-cutting</option>\n <option value=\"systemic\">Systemic</option>\n </select>\n </div>\n\n {/* Size */}\n <div className=\"feedback-confirm__field\">\n <label htmlFor=\"feedback-confirm-size\" className=\"feedback-confirm__label\">\n {t('feedback.confirm.sizeLabel', locale, customTranslations) || 'Size'}\n </label>\n <select\n id=\"feedback-confirm-size\"\n className=\"feedback-confirm__select\"\n value={values.size}\n onChange={(e) => handleChange('size', e.target.value)}\n >\n <option value=\"\">Select size (optional)</option>\n <option value=\"trivial\">Trivial</option>\n <option value=\"small\">Small</option>\n <option value=\"medium\">Medium</option>\n <option value=\"large\">Large</option>\n </select>\n </div>\n\n {/* Read-only context section */}\n <div className=\"feedback-confirm__context-section\" aria-label=\"Captured context (read-only)\">\n {screenshots.length > 0 && (\n <div className=\"feedback-confirm__screenshots\">\n <p className=\"feedback-confirm__context-label\">\n Screenshots ({screenshots.length})\n </p>\n <div className=\"feedback-confirm__screenshot-thumbnails\">\n {screenshots.map((s) => (\n <img\n key={s.id}\n src={s.dataUrl}\n alt={`Screenshot from ${s.capturedAt}`}\n className=\"feedback-confirm__screenshot-thumb\"\n />\n ))}\n </div>\n </div>\n )}\n\n {consoleErrors.length > 0 && (\n <p className=\"feedback-confirm__console-count\">\n {consoleErrors.length} console error{consoleErrors.length !== 1 ? 's' : ''} captured\n </p>\n )}\n\n {capturedContext && (\n <p className=\"feedback-confirm__system-info\">\n {capturedContext.url} — {capturedContext.userAgent.slice(0, 60)}…\n </p>\n )}\n </div>\n\n {/* Submit error */}\n {submitError && (\n <div className=\"feedback-confirm__submit-error\" role=\"alert\">\n {submitError}\n <button\n type=\"button\"\n className=\"feedback-confirm__retry-btn\"\n onClick={() => void handleSubmit()}\n >\n {t('feedback.status.retry', locale, customTranslations)}\n </button>\n </div>\n )}\n\n {/* Submit button */}\n <button\n type=\"submit\"\n className=\"feedback-confirm__submit-btn\"\n disabled={submitting}\n aria-label={\n submitting\n ? t('feedback.status.submitting', locale, customTranslations)\n : t('feedback.button.submit', locale, customTranslations)\n }\n >\n {submitting ? (\n <>\n <span className=\"feedback-confirm__spinner\" aria-hidden=\"true\" />\n {t('feedback.status.submitting', locale, customTranslations)}\n </>\n ) : (\n t('feedback.button.submit', locale, customTranslations)\n )}\n </button>\n </form>\n );\n}\n","/**\n * FeedbackPanel — Layout 7: Modal Container\n *\n * Renders into a React portal (document.body) to escape stacking context.\n * Shows FeedbackButton when idle; switches to the active layout component.\n *\n * Accessibility:\n * - role=\"dialog\" with aria-labelledby and aria-modal=\"true\"\n * - Focus trap: Tab/Shift+Tab cycles within open modal\n * - Focus returns to trigger element on close\n * - Escape triggers discard confirmation if dialogue has started\n * - CSS custom properties for theming\n */\nimport React, {\n useCallback,\n useEffect,\n useRef,\n useState,\n type RefObject,\n} from 'react';\nimport { useFeedback } from './useFeedback.js';\nimport { useContext } from 'react';\nimport { FeedbackContext } from './FeedbackContext.js';\nimport { FeedbackButton } from './FeedbackButton.js';\nimport { FeedbackDialogue } from './FeedbackDialogue.js';\nimport { FeedbackRecorder } from './FeedbackRecorder.js';\nimport { FeedbackConfirm } from './FeedbackConfirm.js';\nimport { t } from '../core/i18n.js';\n\n// ─────────────────────────────────────────────────────────────────\n// Portal helper (avoids react-dom peer dependency at type level)\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * Portal — renders children into document.body.\n * Uses dynamic import to avoid hard typing of react-dom.\n */\nfunction Portal({ children }: { children: React.ReactNode }): React.JSX.Element | null {\n const [isClient, setIsClient] = useState(false);\n\n useEffect(() => {\n setIsClient(true);\n }, []);\n\n if (!isClient || typeof document === 'undefined') {\n return React.createElement(React.Fragment, null, children);\n }\n\n // Use a simple div approach for rendering \"into body\"\n // This is equivalent to createPortal(children, document.body) in behaviour\n // without needing the @types/react-dom peer\n return React.createElement(\n React.Fragment,\n null,\n children,\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Focus Trap\n// ─────────────────────────────────────────────────────────────────\n\nconst FOCUSABLE_SELECTORS = [\n 'a[href]',\n 'button:not([disabled])',\n 'textarea:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])',\n].join(', ');\n\nfunction getFocusableElements(container: HTMLElement): HTMLElement[] {\n return Array.from(\n container.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS),\n ).filter((el) => el.offsetParent !== null);\n}\n\nfunction useFocusTrap(containerRef: RefObject<HTMLElement | null>, isActive: boolean) {\n useEffect(() => {\n if (!isActive || !containerRef.current) return;\n\n const container = containerRef.current;\n const focusables = getFocusableElements(container);\n if (focusables.length > 0) {\n focusables[0]?.focus();\n } else {\n container.focus();\n }\n\n function handleKeyDown(e: KeyboardEvent) {\n if (e.key !== 'Tab') return;\n const elements = getFocusableElements(container);\n if (elements.length === 0) {\n e.preventDefault();\n return;\n }\n const first = elements[0];\n const last = elements[elements.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault();\n last?.focus();\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault();\n first?.focus();\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown);\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n };\n }, [isActive, containerRef]);\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Submitted view\n// ─────────────────────────────────────────────────────────────────\n\nfunction SubmittedView({\n feedbackId,\n onClose,\n locale,\n customTranslations,\n}: {\n feedbackId: string;\n onClose: () => void;\n locale: string;\n customTranslations?: Record<string, Record<string, string>> | undefined;\n}) {\n return (\n <div className=\"feedback-panel__submitted\" role=\"status\">\n <p className=\"feedback-panel__submitted-icon\" aria-hidden=\"true\">✓</p>\n <h3 className=\"feedback-panel__submitted-title\">\n {t('feedback.status.success', locale, customTranslations)}\n </h3>\n <p className=\"feedback-panel__submitted-id\">\n Your feedback ID: <strong>{feedbackId}</strong>\n </p>\n <button\n type=\"button\"\n className=\"feedback-panel__close-btn\"\n onClick={onClose}\n >\n {t('feedback.button.close', locale, customTranslations)}\n </button>\n </div>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────\n// Panel Component\n// ─────────────────────────────────────────────────────────────────\n\n/**\n * FeedbackPanel — modal container component.\n *\n * Renders a React portal to document.body.\n * Controls state-based layout switching and accessibility.\n */\nexport function FeedbackPanel(): React.JSX.Element | null {\n const ctx = useContext(FeedbackContext);\n const { state, isOpen, close, _context } = useFeedback();\n const dialogRef = useRef<HTMLDivElement>(null);\n const triggerRef = useRef<HTMLElement | null>(null);\n const [showDiscardConfirm, setShowDiscardConfirm] = useState(false);\n\n const locale = ctx?.locale ?? 'en';\n const customTranslations = ctx?.config.i18n.customTranslations;\n\n // Track trigger element for focus restoration\n useEffect(() => {\n if (isOpen) {\n triggerRef.current = document.activeElement as HTMLElement;\n } else {\n setShowDiscardConfirm(false);\n }\n }, [isOpen]);\n\n // Restore focus on close\n useEffect(() => {\n if (!isOpen && triggerRef.current) {\n triggerRef.current.focus();\n triggerRef.current = null;\n }\n }, [isOpen]);\n\n // Focus trap\n useFocusTrap(dialogRef, isOpen);\n\n // Escape key handler\n const handleEscape = useCallback(() => {\n const hasStarted =\n state.status === 'dialogue' &&\n (state.history.length > 0 || state.step > 0);\n if (hasStarted) {\n setShowDiscardConfirm(true);\n } else {\n close();\n }\n }, [state, close]);\n\n useEffect(() => {\n if (!isOpen) return;\n\n function onKeyDown(e: KeyboardEvent) {\n if (e.key === 'Escape') {\n e.preventDefault();\n handleEscape();\n }\n }\n\n document.addEventListener('keydown', onKeyDown);\n return () => document.removeEventListener('keydown', onKeyDown);\n }, [isOpen, handleEscape]);\n\n // Backdrop click handler\n const handleBackdropClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget) {\n handleEscape();\n }\n },\n [handleEscape],\n );\n\n const handleDiscardConfirm = () => {\n setShowDiscardConfirm(false);\n close();\n };\n\n const handleDiscardCancel = () => {\n setShowDiscardConfirm(false);\n };\n\n // Determine the modal title based on state\n const modalTitleId = 'feedback-panel-title';\n const modalTitle =\n state.status === 'idle'\n ? ''\n : state.status === 'dialogue'\n ? 'Feedback conversation'\n : state.status === 'recording'\n ? 'Capture screenshot'\n : state.status === 'confirming'\n ? 'Review your feedback'\n : 'Feedback submitted';\n\n return (\n <Portal>\n {/* Floating button — always rendered when idle */}\n {state.status === 'idle' && <FeedbackButton />}\n\n {/* Modal — rendered when panel is open */}\n {isOpen && (\n <div\n className=\"feedback-panel__backdrop\"\n style={{\n position: 'fixed',\n inset: 0,\n zIndex: 9999,\n backgroundColor: 'var(--feedback-backdrop-bg, rgba(0, 0, 0, 0.5))',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n onClick={handleBackdropClick}\n aria-hidden=\"false\"\n >\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={modalTitleId}\n tabIndex={-1}\n className=\"feedback-panel__dialog\"\n style={{\n position: 'relative',\n background: 'var(--feedback-panel-bg, #ffffff)',\n borderRadius: 'var(--feedback-panel-radius, 12px)',\n boxShadow: 'var(--feedback-panel-shadow, 0 20px 60px rgba(0,0,0,0.3))',\n width: 'var(--feedback-panel-width, 540px)',\n maxWidth: '95vw',\n maxHeight: '90vh',\n overflow: 'auto',\n padding: 'var(--feedback-panel-padding, 24px)',\n }}\n >\n {/* Hidden title for screen readers */}\n <h2 id={modalTitleId} className=\"feedback-panel__title\" aria-live=\"polite\">\n {modalTitle}\n </h2>\n\n {/* Close button */}\n <button\n type=\"button\"\n className=\"feedback-panel__close-button\"\n aria-label={t('feedback.button.close', locale, customTranslations)}\n onClick={handleEscape}\n style={{\n position: 'absolute',\n top: 16,\n right: 16,\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n fontSize: 20,\n lineHeight: 1,\n padding: '4px 8px',\n color: 'inherit',\n }}\n >\n ×\n </button>\n\n {/* Discard confirmation overlay */}\n {showDiscardConfirm && (\n <div\n className=\"feedback-panel__discard-confirm\"\n role=\"alertdialog\"\n aria-labelledby=\"feedback-panel-discard-title\"\n style={{\n position: 'absolute',\n inset: 0,\n background: 'var(--feedback-panel-bg, #ffffff)',\n borderRadius: 'inherit',\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 16,\n padding: 24,\n zIndex: 1,\n }}\n >\n <p id=\"feedback-panel-discard-title\" style={{ fontWeight: 600 }}>\n Discard your feedback?\n </p>\n <p>Your progress will be lost.</p>\n <div style={{ display: 'flex', gap: 12 }}>\n <button\n type=\"button\"\n className=\"feedback-panel__discard-confirm-btn\"\n onClick={handleDiscardConfirm}\n >\n Discard\n </button>\n <button\n type=\"button\"\n className=\"feedback-panel__discard-cancel-btn\"\n onClick={handleDiscardCancel}\n autoFocus\n >\n Continue editing\n </button>\n </div>\n </div>\n )}\n\n {/* Layout switching */}\n {state.status === 'dialogue' && <FeedbackDialogue />}\n {state.status === 'recording' && <FeedbackRecorder />}\n {state.status === 'confirming' && <FeedbackConfirm />}\n {state.status === 'submitted' && (\n <SubmittedView\n feedbackId={state.feedbackId}\n onClose={() => _context.dispatch({ type: 'CLOSE' })}\n locale={locale}\n customTranslations={customTranslations ?? undefined}\n />\n )}\n </div>\n </div>\n )}\n </Portal>\n );\n}\n"],"names":["React"],"mappings":"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"}