@fivepixels-js/react 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (667) hide show
  1. package/README.md +56 -3
  2. package/dist/components/icons/Icons.d.ts +34 -0
  3. package/dist/components/icons/Icons.d.ts.map +1 -1
  4. package/dist/components/icons/Icons.js +67 -13
  5. package/dist/components/icons/Icons.js.map +1 -1
  6. package/dist/components/icons/ProbeLayoutIcons.d.ts +16 -0
  7. package/dist/components/icons/ProbeLayoutIcons.d.ts.map +1 -0
  8. package/dist/components/icons/ProbeLayoutIcons.js +62 -0
  9. package/dist/components/icons/ProbeLayoutIcons.js.map +1 -0
  10. package/dist/components/overlay/PickTargetCompareChip.d.ts +7 -0
  11. package/dist/components/overlay/PickTargetCompareChip.d.ts.map +1 -0
  12. package/dist/components/overlay/PickTargetCompareChip.js +35 -0
  13. package/dist/components/overlay/PickTargetCompareChip.js.map +1 -0
  14. package/dist/components/overlay/PickTargetCompareSegment.d.ts +12 -0
  15. package/dist/components/overlay/PickTargetCompareSegment.d.ts.map +1 -0
  16. package/dist/components/overlay/PickTargetCompareSegment.js +17 -0
  17. package/dist/components/overlay/PickTargetCompareSegment.js.map +1 -0
  18. package/dist/components/overlay/PickTargetContextMenu.d.ts +8 -0
  19. package/dist/components/overlay/PickTargetContextMenu.d.ts.map +1 -0
  20. package/dist/components/overlay/PickTargetContextMenu.js +30 -0
  21. package/dist/components/overlay/PickTargetContextMenu.js.map +1 -0
  22. package/dist/components/overlay/PickTargetHoverTooltip.d.ts +7 -0
  23. package/dist/components/overlay/PickTargetHoverTooltip.d.ts.map +1 -0
  24. package/dist/components/overlay/PickTargetHoverTooltip.js +69 -0
  25. package/dist/components/overlay/PickTargetHoverTooltip.js.map +1 -0
  26. package/dist/components/overlay/PickTargetProbePanel.d.ts +2 -0
  27. package/dist/components/overlay/PickTargetProbePanel.d.ts.map +1 -0
  28. package/dist/components/overlay/PickTargetProbePanel.js +106 -0
  29. package/dist/components/overlay/PickTargetProbePanel.js.map +1 -0
  30. package/dist/components/overlay/PickTargetSavedBadges.d.ts +2 -0
  31. package/dist/components/overlay/PickTargetSavedBadges.d.ts.map +1 -0
  32. package/dist/components/overlay/PickTargetSavedBadges.js +63 -0
  33. package/dist/components/overlay/PickTargetSavedBadges.js.map +1 -0
  34. package/dist/components/overlay/ProbeLayoutControls.d.ts +11 -0
  35. package/dist/components/overlay/ProbeLayoutControls.d.ts.map +1 -0
  36. package/dist/components/overlay/ProbeLayoutControls.js +82 -0
  37. package/dist/components/overlay/ProbeLayoutControls.js.map +1 -0
  38. package/dist/components/overlay/ReportOverlayLayer.d.ts.map +1 -1
  39. package/dist/components/overlay/ReportOverlayLayer.js +16 -2
  40. package/dist/components/overlay/ReportOverlayLayer.js.map +1 -1
  41. package/dist/components/overlay/TargetHighlights.d.ts +7 -2
  42. package/dist/components/overlay/TargetHighlights.d.ts.map +1 -1
  43. package/dist/components/overlay/TargetHighlights.js +18 -14
  44. package/dist/components/overlay/TargetHighlights.js.map +1 -1
  45. package/dist/components/panel/AppearanceThemePicker.d.ts +15 -0
  46. package/dist/components/panel/AppearanceThemePicker.d.ts.map +1 -0
  47. package/dist/components/panel/AppearanceThemePicker.js +30 -0
  48. package/dist/components/panel/AppearanceThemePicker.js.map +1 -0
  49. package/dist/components/panel/DiscreteScaleDial.d.ts +10 -0
  50. package/dist/components/panel/DiscreteScaleDial.d.ts.map +1 -0
  51. package/dist/components/panel/DiscreteScaleDial.js +14 -0
  52. package/dist/components/panel/DiscreteScaleDial.js.map +1 -0
  53. package/dist/components/panel/DraftProbeSummaryBanner.d.ts +2 -0
  54. package/dist/components/panel/DraftProbeSummaryBanner.d.ts.map +1 -0
  55. package/dist/components/panel/DraftProbeSummaryBanner.js +10 -0
  56. package/dist/components/panel/DraftProbeSummaryBanner.js.map +1 -0
  57. package/dist/components/panel/FeedbackActivityHeatmap.d.ts +2 -0
  58. package/dist/components/panel/FeedbackActivityHeatmap.d.ts.map +1 -0
  59. package/dist/components/panel/FeedbackActivityHeatmap.js +172 -0
  60. package/dist/components/panel/FeedbackActivityHeatmap.js.map +1 -0
  61. package/dist/components/panel/HexColorField.d.ts +9 -0
  62. package/dist/components/panel/HexColorField.d.ts.map +1 -0
  63. package/dist/components/panel/HexColorField.js +23 -0
  64. package/dist/components/panel/HexColorField.js.map +1 -0
  65. package/dist/components/panel/MarkerShapePicker.d.ts +10 -0
  66. package/dist/components/panel/MarkerShapePicker.d.ts.map +1 -0
  67. package/dist/components/panel/MarkerShapePicker.js +26 -0
  68. package/dist/components/panel/MarkerShapePicker.js.map +1 -0
  69. package/dist/components/panel/PanelDropdownMenu.d.ts.map +1 -1
  70. package/dist/components/panel/PanelDropdownMenu.js +25 -14
  71. package/dist/components/panel/PanelDropdownMenu.js.map +1 -1
  72. package/dist/components/panel/PanelKeyGate.d.ts +7 -0
  73. package/dist/components/panel/PanelKeyGate.d.ts.map +1 -0
  74. package/dist/components/panel/PanelKeyGate.js +38 -0
  75. package/dist/components/panel/PanelKeyGate.js.map +1 -0
  76. package/dist/components/panel/PanelOnboarding.d.ts +2 -0
  77. package/dist/components/panel/PanelOnboarding.d.ts.map +1 -0
  78. package/dist/components/panel/PanelOnboarding.js +115 -0
  79. package/dist/components/panel/PanelOnboarding.js.map +1 -0
  80. package/dist/components/panel/PanelPresentationSwitch.d.ts +2 -0
  81. package/dist/components/panel/PanelPresentationSwitch.d.ts.map +1 -0
  82. package/dist/components/panel/PanelPresentationSwitch.js +40 -0
  83. package/dist/components/panel/PanelPresentationSwitch.js.map +1 -0
  84. package/dist/components/panel/PanelProjectFooter.d.ts +2 -0
  85. package/dist/components/panel/PanelProjectFooter.d.ts.map +1 -0
  86. package/dist/components/panel/PanelProjectFooter.js +7 -0
  87. package/dist/components/panel/PanelProjectFooter.js.map +1 -0
  88. package/dist/components/panel/PanelRoleSwitch.d.ts +2 -0
  89. package/dist/components/panel/PanelRoleSwitch.d.ts.map +1 -0
  90. package/dist/components/panel/PanelRoleSwitch.js +21 -0
  91. package/dist/components/panel/PanelRoleSwitch.js.map +1 -0
  92. package/dist/components/panel/PanelSettings.d.ts +21 -0
  93. package/dist/components/panel/PanelSettings.d.ts.map +1 -0
  94. package/dist/components/panel/PanelSettings.js +103 -0
  95. package/dist/components/panel/PanelSettings.js.map +1 -0
  96. package/dist/components/panel/PanelTabSelector.d.ts +13 -0
  97. package/dist/components/panel/PanelTabSelector.d.ts.map +1 -0
  98. package/dist/components/panel/PanelTabSelector.js +44 -0
  99. package/dist/components/panel/PanelTabSelector.js.map +1 -0
  100. package/dist/components/panel/ProbeEditModeBanner.d.ts +2 -0
  101. package/dist/components/panel/ProbeEditModeBanner.d.ts.map +1 -0
  102. package/dist/components/panel/ProbeEditModeBanner.js +22 -0
  103. package/dist/components/panel/ProbeEditModeBanner.js.map +1 -0
  104. package/dist/components/panel/ReportAuthDiagnostics.d.ts +2 -0
  105. package/dist/components/panel/ReportAuthDiagnostics.d.ts.map +1 -0
  106. package/dist/components/panel/ReportAuthDiagnostics.js +67 -0
  107. package/dist/components/panel/ReportAuthDiagnostics.js.map +1 -0
  108. package/dist/components/panel/ReportCommandPanel.js +1 -1
  109. package/dist/components/panel/ReportCommandPanel.js.map +1 -1
  110. package/dist/components/panel/ReportCommandReplaceConfirmDialog.d.ts.map +1 -1
  111. package/dist/components/panel/ReportCommandReplaceConfirmDialog.js +2 -1
  112. package/dist/components/panel/ReportCommandReplaceConfirmDialog.js.map +1 -1
  113. package/dist/components/panel/ReportControlPanel.d.ts.map +1 -1
  114. package/dist/components/panel/ReportControlPanel.js +62 -46
  115. package/dist/components/panel/ReportControlPanel.js.map +1 -1
  116. package/dist/components/panel/ReportDraftForm.d.ts.map +1 -1
  117. package/dist/components/panel/ReportDraftForm.js +41 -16
  118. package/dist/components/panel/ReportDraftForm.js.map +1 -1
  119. package/dist/components/panel/ReportFeedbackList.d.ts.map +1 -1
  120. package/dist/components/panel/ReportFeedbackList.js +46 -98
  121. package/dist/components/panel/ReportFeedbackList.js.map +1 -1
  122. package/dist/components/panel/ReportOverview.d.ts +2 -0
  123. package/dist/components/panel/ReportOverview.d.ts.map +1 -0
  124. package/dist/components/panel/ReportOverview.js +6 -0
  125. package/dist/components/panel/ReportOverview.js.map +1 -0
  126. package/dist/components/panel/ReportPersonalKeyDialog.js +2 -2
  127. package/dist/components/panel/ReportPersonalKeyDialog.js.map +1 -1
  128. package/dist/components/panel/ReportRouteDetails.d.ts.map +1 -1
  129. package/dist/components/panel/ReportRouteDetails.js +23 -11
  130. package/dist/components/panel/ReportRouteDetails.js.map +1 -1
  131. package/dist/components/panel/RouteDetailsTimeline.d.ts +13 -0
  132. package/dist/components/panel/RouteDetailsTimeline.d.ts.map +1 -0
  133. package/dist/components/panel/RouteDetailsTimeline.js +53 -0
  134. package/dist/components/panel/RouteDetailsTimeline.js.map +1 -0
  135. package/dist/components/panel/ThemePreviewSkeleton.d.ts +7 -0
  136. package/dist/components/panel/ThemePreviewSkeleton.d.ts.map +1 -0
  137. package/dist/components/panel/ThemePreviewSkeleton.js +25 -0
  138. package/dist/components/panel/ThemePreviewSkeleton.js.map +1 -0
  139. package/dist/components/panel/experimental/ExperimentalFeedbackRow.d.ts +8 -0
  140. package/dist/components/panel/experimental/ExperimentalFeedbackRow.d.ts.map +1 -0
  141. package/dist/components/panel/experimental/ExperimentalFeedbackRow.js +13 -0
  142. package/dist/components/panel/experimental/ExperimentalFeedbackRow.js.map +1 -0
  143. package/dist/components/panel/experimental/ReportMyTasksPanel.d.ts +2 -0
  144. package/dist/components/panel/experimental/ReportMyTasksPanel.d.ts.map +1 -0
  145. package/dist/components/panel/experimental/ReportMyTasksPanel.js +16 -0
  146. package/dist/components/panel/experimental/ReportMyTasksPanel.js.map +1 -0
  147. package/dist/components/panel/experimental/ReportNeedsAttentionPanel.d.ts +2 -0
  148. package/dist/components/panel/experimental/ReportNeedsAttentionPanel.d.ts.map +1 -0
  149. package/dist/components/panel/experimental/ReportNeedsAttentionPanel.js +16 -0
  150. package/dist/components/panel/experimental/ReportNeedsAttentionPanel.js.map +1 -0
  151. package/dist/components/panel/experimental/ReportPageBriefPanel.d.ts +2 -0
  152. package/dist/components/panel/experimental/ReportPageBriefPanel.d.ts.map +1 -0
  153. package/dist/components/panel/experimental/ReportPageBriefPanel.js +15 -0
  154. package/dist/components/panel/experimental/ReportPageBriefPanel.js.map +1 -0
  155. package/dist/components/panel/experimental/ReportProjectHealthPanel.d.ts +2 -0
  156. package/dist/components/panel/experimental/ReportProjectHealthPanel.d.ts.map +1 -0
  157. package/dist/components/panel/experimental/ReportProjectHealthPanel.js +22 -0
  158. package/dist/components/panel/experimental/ReportProjectHealthPanel.js.map +1 -0
  159. package/dist/components/panel/experimental/ReportTodayDigestPanel.d.ts +2 -0
  160. package/dist/components/panel/experimental/ReportTodayDigestPanel.d.ts.map +1 -0
  161. package/dist/components/panel/experimental/ReportTodayDigestPanel.js +16 -0
  162. package/dist/components/panel/experimental/ReportTodayDigestPanel.js.map +1 -0
  163. package/dist/components/panel/feedback/AssigneeThreadEntry.d.ts +20 -0
  164. package/dist/components/panel/feedback/AssigneeThreadEntry.d.ts.map +1 -0
  165. package/dist/components/panel/feedback/AssigneeThreadEntry.js +23 -0
  166. package/dist/components/panel/feedback/AssigneeThreadEntry.js.map +1 -0
  167. package/dist/components/panel/feedback/CaseAssigneeInfo.d.ts +8 -0
  168. package/dist/components/panel/feedback/CaseAssigneeInfo.d.ts.map +1 -0
  169. package/dist/components/panel/feedback/CaseAssigneeInfo.js +20 -0
  170. package/dist/components/panel/feedback/CaseAssigneeInfo.js.map +1 -0
  171. package/dist/components/panel/feedback/ComposerFooterWarning.d.ts +6 -0
  172. package/dist/components/panel/feedback/ComposerFooterWarning.d.ts.map +1 -0
  173. package/dist/components/panel/feedback/ComposerFooterWarning.js +6 -0
  174. package/dist/components/panel/feedback/ComposerFooterWarning.js.map +1 -0
  175. package/dist/components/panel/feedback/FeedbackCaseEditor.d.ts +15 -0
  176. package/dist/components/panel/feedback/FeedbackCaseEditor.d.ts.map +1 -0
  177. package/dist/components/panel/feedback/FeedbackCaseEditor.js +111 -0
  178. package/dist/components/panel/feedback/FeedbackCaseEditor.js.map +1 -0
  179. package/dist/components/panel/feedback/FeedbackCaseList.d.ts +21 -0
  180. package/dist/components/panel/feedback/FeedbackCaseList.d.ts.map +1 -0
  181. package/dist/components/panel/feedback/FeedbackCaseList.js +48 -0
  182. package/dist/components/panel/feedback/FeedbackCaseList.js.map +1 -0
  183. package/dist/components/panel/feedback/FeedbackCaseTabBar.d.ts +34 -0
  184. package/dist/components/panel/feedback/FeedbackCaseTabBar.d.ts.map +1 -0
  185. package/dist/components/panel/feedback/FeedbackCaseTabBar.js +37 -0
  186. package/dist/components/panel/feedback/FeedbackCaseTabBar.js.map +1 -0
  187. package/dist/components/panel/feedback/FeedbackCategorySelector.d.ts +12 -0
  188. package/dist/components/panel/feedback/FeedbackCategorySelector.d.ts.map +1 -0
  189. package/dist/components/panel/feedback/FeedbackCategorySelector.js +25 -0
  190. package/dist/components/panel/feedback/FeedbackCategorySelector.js.map +1 -0
  191. package/dist/components/panel/feedback/FeedbackComposer.d.ts +20 -4
  192. package/dist/components/panel/feedback/FeedbackComposer.d.ts.map +1 -1
  193. package/dist/components/panel/feedback/FeedbackComposer.js +78 -11
  194. package/dist/components/panel/feedback/FeedbackComposer.js.map +1 -1
  195. package/dist/components/panel/feedback/FeedbackHoverCard.d.ts +4 -5
  196. package/dist/components/panel/feedback/FeedbackHoverCard.d.ts.map +1 -1
  197. package/dist/components/panel/feedback/FeedbackHoverCard.js +22 -11
  198. package/dist/components/panel/feedback/FeedbackHoverCard.js.map +1 -1
  199. package/dist/components/panel/feedback/FeedbackListItem.d.ts +17 -0
  200. package/dist/components/panel/feedback/FeedbackListItem.d.ts.map +1 -0
  201. package/dist/components/panel/feedback/FeedbackListItem.js +70 -0
  202. package/dist/components/panel/feedback/FeedbackListItem.js.map +1 -0
  203. package/dist/components/panel/feedback/FeedbackStatusBadge.d.ts +2 -1
  204. package/dist/components/panel/feedback/FeedbackStatusBadge.d.ts.map +1 -1
  205. package/dist/components/panel/feedback/FeedbackStatusBadge.js +2 -2
  206. package/dist/components/panel/feedback/FeedbackStatusBadge.js.map +1 -1
  207. package/dist/components/panel/feedback/FeedbackThread.d.ts +9 -3
  208. package/dist/components/panel/feedback/FeedbackThread.d.ts.map +1 -1
  209. package/dist/components/panel/feedback/FeedbackThread.js +155 -39
  210. package/dist/components/panel/feedback/FeedbackThread.js.map +1 -1
  211. package/dist/components/panel/feedback/PickTargetSnippet.d.ts +7 -0
  212. package/dist/components/panel/feedback/PickTargetSnippet.d.ts.map +1 -0
  213. package/dist/components/panel/feedback/PickTargetSnippet.js +30 -0
  214. package/dist/components/panel/feedback/PickTargetSnippet.js.map +1 -0
  215. package/dist/components/panel/feedback/QuestionThreadGroup.d.ts +9 -0
  216. package/dist/components/panel/feedback/QuestionThreadGroup.d.ts.map +1 -0
  217. package/dist/components/panel/feedback/QuestionThreadGroup.js +30 -0
  218. package/dist/components/panel/feedback/QuestionThreadGroup.js.map +1 -0
  219. package/dist/components/panel/feedback/ReplyHistoryControls.d.ts +8 -0
  220. package/dist/components/panel/feedback/ReplyHistoryControls.d.ts.map +1 -0
  221. package/dist/components/panel/feedback/ReplyHistoryControls.js +26 -0
  222. package/dist/components/panel/feedback/ReplyHistoryControls.js.map +1 -0
  223. package/dist/components/panel/feedback/ThreadAuthorMeta.d.ts +10 -0
  224. package/dist/components/panel/feedback/ThreadAuthorMeta.d.ts.map +1 -0
  225. package/dist/components/panel/feedback/ThreadAuthorMeta.js +9 -0
  226. package/dist/components/panel/feedback/ThreadAuthorMeta.js.map +1 -0
  227. package/dist/components/panel/feedback/ThreadChildReply.d.ts +8 -0
  228. package/dist/components/panel/feedback/ThreadChildReply.d.ts.map +1 -0
  229. package/dist/components/panel/feedback/ThreadChildReply.js +7 -0
  230. package/dist/components/panel/feedback/ThreadChildReply.js.map +1 -0
  231. package/dist/components/panel/feedback/ThreadTimelineRow.d.ts +11 -0
  232. package/dist/components/panel/feedback/ThreadTimelineRow.d.ts.map +1 -0
  233. package/dist/components/panel/feedback/ThreadTimelineRow.js +7 -0
  234. package/dist/components/panel/feedback/ThreadTimelineRow.js.map +1 -0
  235. package/dist/components/point/MarkerCaseSidebar.d.ts +9 -0
  236. package/dist/components/point/MarkerCaseSidebar.d.ts.map +1 -0
  237. package/dist/components/point/MarkerCaseSidebar.js +27 -0
  238. package/dist/components/point/MarkerCaseSidebar.js.map +1 -0
  239. package/dist/components/point/MarkerFeedbackWindow.d.ts +9 -0
  240. package/dist/components/point/MarkerFeedbackWindow.d.ts.map +1 -0
  241. package/dist/components/point/MarkerFeedbackWindow.js +257 -0
  242. package/dist/components/point/MarkerFeedbackWindow.js.map +1 -0
  243. package/dist/components/point/ReportDraftMarker.d.ts.map +1 -1
  244. package/dist/components/point/ReportDraftMarker.js +8 -17
  245. package/dist/components/point/ReportDraftMarker.js.map +1 -1
  246. package/dist/components/point/ReportMarkersLayer.d.ts.map +1 -1
  247. package/dist/components/point/ReportMarkersLayer.js +74 -87
  248. package/dist/components/point/ReportMarkersLayer.js.map +1 -1
  249. package/dist/components/report/FivePixels.d.ts +7 -28
  250. package/dist/components/report/FivePixels.d.ts.map +1 -1
  251. package/dist/components/report/FivePixels.js +3 -3
  252. package/dist/components/report/FivePixels.js.map +1 -1
  253. package/dist/components/report/ReportView.d.ts.map +1 -1
  254. package/dist/components/report/ReportView.js +5 -3
  255. package/dist/components/report/ReportView.js.map +1 -1
  256. package/dist/components/report/ShadowReportRoot.d.ts +2 -2
  257. package/dist/components/report/ShadowReportRoot.d.ts.map +1 -1
  258. package/dist/components/report/ShadowReportRoot.js +4 -7
  259. package/dist/components/report/ShadowReportRoot.js.map +1 -1
  260. package/dist/components/report/ThemeScope.d.ts +10 -0
  261. package/dist/components/report/ThemeScope.d.ts.map +1 -0
  262. package/dist/components/report/ThemeScope.js +5 -0
  263. package/dist/components/report/ThemeScope.js.map +1 -0
  264. package/dist/components/report/index.d.ts +4 -1
  265. package/dist/components/report/index.d.ts.map +1 -1
  266. package/dist/components/report/index.js +3 -0
  267. package/dist/components/report/index.js.map +1 -1
  268. package/dist/components/ui/CornerResizeGhost.d.ts +8 -0
  269. package/dist/components/ui/CornerResizeGhost.d.ts.map +1 -0
  270. package/dist/components/ui/CornerResizeGhost.js +5 -0
  271. package/dist/components/ui/CornerResizeGhost.js.map +1 -0
  272. package/dist/components/ui/CornerResizeHandle.d.ts +11 -0
  273. package/dist/components/ui/CornerResizeHandle.d.ts.map +1 -0
  274. package/dist/components/ui/CornerResizeHandle.js +32 -0
  275. package/dist/components/ui/CornerResizeHandle.js.map +1 -0
  276. package/dist/components/ui/HoverTooltip.d.ts +4 -2
  277. package/dist/components/ui/HoverTooltip.d.ts.map +1 -1
  278. package/dist/components/ui/HoverTooltip.js +28 -17
  279. package/dist/components/ui/HoverTooltip.js.map +1 -1
  280. package/dist/components/ui/IconTooltipButton.d.ts.map +1 -1
  281. package/dist/components/ui/IconTooltipButton.js +5 -2
  282. package/dist/components/ui/IconTooltipButton.js.map +1 -1
  283. package/dist/components/ui/PointerFollowTooltip.d.ts +14 -0
  284. package/dist/components/ui/PointerFollowTooltip.d.ts.map +1 -0
  285. package/dist/components/ui/PointerFollowTooltip.js +58 -0
  286. package/dist/components/ui/PointerFollowTooltip.js.map +1 -0
  287. package/dist/components/ui/ProcessingDots.d.ts +6 -0
  288. package/dist/components/ui/ProcessingDots.d.ts.map +1 -0
  289. package/dist/components/ui/ProcessingDots.js +19 -0
  290. package/dist/components/ui/ProcessingDots.js.map +1 -0
  291. package/dist/components/ui/Text/index.d.ts +6 -0
  292. package/dist/components/ui/Text/index.d.ts.map +1 -0
  293. package/dist/components/ui/Text/index.js +5 -0
  294. package/dist/components/ui/Text/index.js.map +1 -0
  295. package/dist/components/ui/Text/ui/Shimmer.d.ts +14 -0
  296. package/dist/components/ui/Text/ui/Shimmer.d.ts.map +1 -0
  297. package/dist/components/ui/Text/ui/Shimmer.js +41 -0
  298. package/dist/components/ui/Text/ui/Shimmer.js.map +1 -0
  299. package/dist/constants/appearance.d.ts +10 -0
  300. package/dist/constants/appearance.d.ts.map +1 -0
  301. package/dist/constants/appearance.js +10 -0
  302. package/dist/constants/appearance.js.map +1 -0
  303. package/dist/constants/feedbackCategory.d.ts +5 -0
  304. package/dist/constants/feedbackCategory.d.ts.map +1 -0
  305. package/dist/constants/feedbackCategory.js +5 -0
  306. package/dist/constants/feedbackCategory.js.map +1 -0
  307. package/dist/constants/feedbackStatus.d.ts +1 -1
  308. package/dist/constants/feedbackStatus.d.ts.map +1 -1
  309. package/dist/constants/feedbackStatus.js +20 -8
  310. package/dist/constants/feedbackStatus.js.map +1 -1
  311. package/dist/constants/feedbackStorageEvents.d.ts +3 -0
  312. package/dist/constants/feedbackStorageEvents.d.ts.map +1 -0
  313. package/dist/constants/feedbackStorageEvents.js +8 -0
  314. package/dist/constants/feedbackStorageEvents.js.map +1 -0
  315. package/dist/constants/markerAppearance.d.ts +35 -0
  316. package/dist/constants/markerAppearance.d.ts.map +1 -0
  317. package/dist/constants/markerAppearance.js +57 -0
  318. package/dist/constants/markerAppearance.js.map +1 -0
  319. package/dist/constants/panelRole.d.ts +5 -0
  320. package/dist/constants/panelRole.d.ts.map +1 -0
  321. package/dist/constants/panelRole.js +6 -0
  322. package/dist/constants/panelRole.js.map +1 -0
  323. package/dist/constants/panelTabPresets.d.ts +14 -0
  324. package/dist/constants/panelTabPresets.d.ts.map +1 -0
  325. package/dist/constants/panelTabPresets.js +48 -0
  326. package/dist/constants/panelTabPresets.js.map +1 -0
  327. package/dist/constants/panelTabRegistry.d.ts +23 -0
  328. package/dist/constants/panelTabRegistry.d.ts.map +1 -0
  329. package/dist/constants/panelTabRegistry.js +95 -0
  330. package/dist/constants/panelTabRegistry.js.map +1 -0
  331. package/dist/constants/replyHistory.d.ts +8 -0
  332. package/dist/constants/replyHistory.d.ts.map +1 -0
  333. package/dist/constants/replyHistory.js +15 -0
  334. package/dist/constants/replyHistory.js.map +1 -0
  335. package/dist/constants/report.d.ts +5 -0
  336. package/dist/constants/report.d.ts.map +1 -1
  337. package/dist/constants/report.js +5 -0
  338. package/dist/constants/report.js.map +1 -1
  339. package/dist/constants/storageKeys.d.ts +2 -0
  340. package/dist/constants/storageKeys.d.ts.map +1 -1
  341. package/dist/constants/storageKeys.js +4 -0
  342. package/dist/constants/storageKeys.js.map +1 -1
  343. package/dist/hooks/replyHistoryActions.d.ts +30 -0
  344. package/dist/hooks/replyHistoryActions.d.ts.map +1 -0
  345. package/dist/hooks/replyHistoryActions.js +278 -0
  346. package/dist/hooks/replyHistoryActions.js.map +1 -0
  347. package/dist/hooks/report.api.d.ts +3 -1
  348. package/dist/hooks/report.api.d.ts.map +1 -1
  349. package/dist/hooks/report.api.js +27 -4
  350. package/dist/hooks/report.api.js.map +1 -1
  351. package/dist/hooks/report.query.d.ts +1 -1
  352. package/dist/hooks/report.query.d.ts.map +1 -1
  353. package/dist/hooks/report.query.js +3 -2
  354. package/dist/hooks/report.query.js.map +1 -1
  355. package/dist/hooks/useActivitySummary.d.ts +13 -0
  356. package/dist/hooks/useActivitySummary.d.ts.map +1 -0
  357. package/dist/hooks/useActivitySummary.js +41 -0
  358. package/dist/hooks/useActivitySummary.js.map +1 -0
  359. package/dist/hooks/useAppearancePreference.d.ts +1 -1
  360. package/dist/hooks/useAppearancePreference.d.ts.map +1 -1
  361. package/dist/hooks/useAppearancePreference.js +14 -11
  362. package/dist/hooks/useAppearancePreference.js.map +1 -1
  363. package/dist/hooks/useDraggableWindow.d.ts +17 -0
  364. package/dist/hooks/useDraggableWindow.d.ts.map +1 -0
  365. package/dist/hooks/useDraggableWindow.js +104 -0
  366. package/dist/hooks/useDraggableWindow.js.map +1 -0
  367. package/dist/hooks/useGhostCornerResize.d.ts +36 -0
  368. package/dist/hooks/useGhostCornerResize.d.ts.map +1 -0
  369. package/dist/hooks/useGhostCornerResize.js +161 -0
  370. package/dist/hooks/useGhostCornerResize.js.map +1 -0
  371. package/dist/hooks/useMarkerAppearancePreference.d.ts +10 -0
  372. package/dist/hooks/useMarkerAppearancePreference.d.ts.map +1 -0
  373. package/dist/hooks/useMarkerAppearancePreference.js +94 -0
  374. package/dist/hooks/useMarkerAppearancePreference.js.map +1 -0
  375. package/dist/hooks/useMarkerTargetPreviewPreference.d.ts +6 -0
  376. package/dist/hooks/useMarkerTargetPreviewPreference.d.ts.map +1 -0
  377. package/dist/hooks/useMarkerTargetPreviewPreference.js +41 -0
  378. package/dist/hooks/useMarkerTargetPreviewPreference.js.map +1 -0
  379. package/dist/hooks/usePanelBootstrap.d.ts +16 -0
  380. package/dist/hooks/usePanelBootstrap.d.ts.map +1 -0
  381. package/dist/hooks/usePanelBootstrap.js +46 -0
  382. package/dist/hooks/usePanelBootstrap.js.map +1 -0
  383. package/dist/hooks/usePanelDock.d.ts.map +1 -1
  384. package/dist/hooks/usePanelDock.js +40 -31
  385. package/dist/hooks/usePanelDock.js.map +1 -1
  386. package/dist/hooks/usePanelFeedbackTransfer.d.ts +3 -3
  387. package/dist/hooks/usePanelFeedbackTransfer.d.ts.map +1 -1
  388. package/dist/hooks/usePanelFeedbackTransfer.js +13 -13
  389. package/dist/hooks/usePanelFeedbackTransfer.js.map +1 -1
  390. package/dist/hooks/usePanelResize.d.ts +44 -0
  391. package/dist/hooks/usePanelResize.d.ts.map +1 -0
  392. package/dist/hooks/usePanelResize.js +234 -0
  393. package/dist/hooks/usePanelResize.js.map +1 -0
  394. package/dist/hooks/usePanelRolePreference.d.ts +6 -0
  395. package/dist/hooks/usePanelRolePreference.d.ts.map +1 -0
  396. package/dist/hooks/usePanelRolePreference.js +38 -0
  397. package/dist/hooks/usePanelRolePreference.js.map +1 -0
  398. package/dist/hooks/usePanelTabPreference.d.ts +13 -0
  399. package/dist/hooks/usePanelTabPreference.d.ts.map +1 -0
  400. package/dist/hooks/usePanelTabPreference.js +66 -0
  401. package/dist/hooks/usePanelTabPreference.js.map +1 -0
  402. package/dist/hooks/usePersonalKey.d.ts +18 -2
  403. package/dist/hooks/usePersonalKey.d.ts.map +1 -1
  404. package/dist/hooks/usePersonalKey.js +43 -16
  405. package/dist/hooks/usePersonalKey.js.map +1 -1
  406. package/dist/hooks/useQuestionThreadPreference.d.ts +6 -0
  407. package/dist/hooks/useQuestionThreadPreference.d.ts.map +1 -0
  408. package/dist/hooks/useQuestionThreadPreference.js +40 -0
  409. package/dist/hooks/useQuestionThreadPreference.js.map +1 -0
  410. package/dist/hooks/useReportPersistence.d.ts +26 -16
  411. package/dist/hooks/useReportPersistence.d.ts.map +1 -1
  412. package/dist/hooks/useReportPersistence.js +158 -58
  413. package/dist/hooks/useReportPersistence.js.map +1 -1
  414. package/dist/hooks/useReportShortcuts.d.ts +1 -1
  415. package/dist/hooks/useReportShortcuts.d.ts.map +1 -1
  416. package/dist/hooks/useReportShortcuts.js +8 -1
  417. package/dist/hooks/useReportShortcuts.js.map +1 -1
  418. package/dist/hooks/useReportState.d.ts +184 -31
  419. package/dist/hooks/useReportState.d.ts.map +1 -1
  420. package/dist/hooks/useReportState.js +1555 -162
  421. package/dist/hooks/useReportState.js.map +1 -1
  422. package/dist/hooks/useSelfProfile.d.ts +12 -0
  423. package/dist/hooks/useSelfProfile.d.ts.map +1 -0
  424. package/dist/hooks/useSelfProfile.js +65 -0
  425. package/dist/hooks/useSelfProfile.js.map +1 -0
  426. package/dist/hooks/useTooltipLayout.d.ts +5 -1
  427. package/dist/hooks/useTooltipLayout.d.ts.map +1 -1
  428. package/dist/hooks/useTooltipLayout.js +9 -4
  429. package/dist/hooks/useTooltipLayout.js.map +1 -1
  430. package/dist/hooks/useTooltipResize.d.ts +16 -0
  431. package/dist/hooks/useTooltipResize.d.ts.map +1 -0
  432. package/dist/hooks/useTooltipResize.js +27 -0
  433. package/dist/hooks/useTooltipResize.js.map +1 -0
  434. package/dist/hooks/useTypographyPreference.d.ts +8 -0
  435. package/dist/hooks/useTypographyPreference.d.ts.map +1 -0
  436. package/dist/hooks/useTypographyPreference.js +59 -0
  437. package/dist/hooks/useTypographyPreference.js.map +1 -0
  438. package/dist/i18n/en.d.ts.map +1 -1
  439. package/dist/i18n/en.js +406 -14
  440. package/dist/i18n/en.js.map +1 -1
  441. package/dist/i18n/ko.d.ts.map +1 -1
  442. package/dist/i18n/ko.js +407 -15
  443. package/dist/i18n/ko.js.map +1 -1
  444. package/dist/i18n/types.d.ts +386 -10
  445. package/dist/i18n/types.d.ts.map +1 -1
  446. package/dist/index.d.ts +0 -2
  447. package/dist/index.d.ts.map +1 -1
  448. package/dist/index.js +0 -1
  449. package/dist/index.js.map +1 -1
  450. package/dist/providers/ReportProvider.d.ts +7 -28
  451. package/dist/providers/ReportProvider.d.ts.map +1 -1
  452. package/dist/providers/ReportProvider.js +16 -8
  453. package/dist/providers/ReportProvider.js.map +1 -1
  454. package/dist/providers/reportContext.d.ts +324 -54
  455. package/dist/providers/reportContext.d.ts.map +1 -1
  456. package/dist/storage/local/localStorageAdapter.d.ts.map +1 -1
  457. package/dist/storage/local/localStorageAdapter.js +78 -9
  458. package/dist/storage/local/localStorageAdapter.js.map +1 -1
  459. package/dist/styles/reportStylesheet.d.ts +1 -1
  460. package/dist/styles/reportStylesheet.d.ts.map +1 -1
  461. package/dist/styles/reportStylesheet.js +1 -1
  462. package/dist/styles/reportStylesheet.js.map +1 -1
  463. package/dist/types/report-ui.d.ts +89 -5
  464. package/dist/types/report-ui.d.ts.map +1 -1
  465. package/dist/types/report.d.ts +147 -20
  466. package/dist/types/report.d.ts.map +1 -1
  467. package/dist/types/report.js.map +1 -1
  468. package/dist/utils/coordinates.d.ts +16 -1
  469. package/dist/utils/coordinates.d.ts.map +1 -1
  470. package/dist/utils/coordinates.js +69 -37
  471. package/dist/utils/coordinates.js.map +1 -1
  472. package/dist/utils/cssStepper.d.ts +6 -0
  473. package/dist/utils/cssStepper.d.ts.map +1 -0
  474. package/dist/utils/cssStepper.js +40 -0
  475. package/dist/utils/cssStepper.js.map +1 -0
  476. package/dist/utils/dom.d.ts +6 -0
  477. package/dist/utils/dom.d.ts.map +1 -1
  478. package/dist/utils/dom.js +196 -3
  479. package/dist/utils/dom.js.map +1 -1
  480. package/dist/utils/experimentalPanelTabs.d.ts +29 -0
  481. package/dist/utils/experimentalPanelTabs.d.ts.map +1 -0
  482. package/dist/utils/experimentalPanelTabs.js +113 -0
  483. package/dist/utils/experimentalPanelTabs.js.map +1 -0
  484. package/dist/utils/feedbackCaseId.d.ts +7 -0
  485. package/dist/utils/feedbackCaseId.d.ts.map +1 -0
  486. package/dist/utils/feedbackCaseId.js +44 -0
  487. package/dist/utils/feedbackCaseId.js.map +1 -0
  488. package/dist/utils/feedbackDataTransfer.d.ts +4 -0
  489. package/dist/utils/feedbackDataTransfer.d.ts.map +1 -1
  490. package/dist/utils/feedbackDataTransfer.js +70 -0
  491. package/dist/utils/feedbackDataTransfer.js.map +1 -1
  492. package/dist/utils/feedbackDeepLink.d.ts +11 -0
  493. package/dist/utils/feedbackDeepLink.d.ts.map +1 -0
  494. package/dist/utils/feedbackDeepLink.js +35 -0
  495. package/dist/utils/feedbackDeepLink.js.map +1 -0
  496. package/dist/utils/feedbackListStatus.d.ts +4 -0
  497. package/dist/utils/feedbackListStatus.d.ts.map +1 -0
  498. package/dist/utils/feedbackListStatus.js +19 -0
  499. package/dist/utils/feedbackListStatus.js.map +1 -0
  500. package/dist/utils/feedbackThread.d.ts +61 -2
  501. package/dist/utils/feedbackThread.d.ts.map +1 -1
  502. package/dist/utils/feedbackThread.js +325 -10
  503. package/dist/utils/feedbackThread.js.map +1 -1
  504. package/dist/utils/fields.d.ts +3 -2
  505. package/dist/utils/fields.d.ts.map +1 -1
  506. package/dist/utils/fields.js +9 -4
  507. package/dist/utils/fields.js.map +1 -1
  508. package/dist/utils/format.d.ts +1 -0
  509. package/dist/utils/format.d.ts.map +1 -1
  510. package/dist/utils/format.js +6 -0
  511. package/dist/utils/format.js.map +1 -1
  512. package/dist/utils/githubIntegration.d.ts +1 -0
  513. package/dist/utils/githubIntegration.d.ts.map +1 -1
  514. package/dist/utils/githubIntegration.js +8 -2
  515. package/dist/utils/githubIntegration.js.map +1 -1
  516. package/dist/utils/heatmapActivity.d.ts +68 -0
  517. package/dist/utils/heatmapActivity.d.ts.map +1 -0
  518. package/dist/utils/heatmapActivity.js +280 -0
  519. package/dist/utils/heatmapActivity.js.map +1 -0
  520. package/dist/utils/hexColor.d.ts +7 -0
  521. package/dist/utils/hexColor.d.ts.map +1 -0
  522. package/dist/utils/hexColor.js +25 -0
  523. package/dist/utils/hexColor.js.map +1 -0
  524. package/dist/utils/hourlyCompareSparkline.d.ts +19 -0
  525. package/dist/utils/hourlyCompareSparkline.d.ts.map +1 -0
  526. package/dist/utils/hourlyCompareSparkline.js +65 -0
  527. package/dist/utils/hourlyCompareSparkline.js.map +1 -0
  528. package/dist/utils/locateFeedback.d.ts +3 -3
  529. package/dist/utils/locateFeedback.d.ts.map +1 -1
  530. package/dist/utils/locateFeedback.js +11 -3
  531. package/dist/utils/locateFeedback.js.map +1 -1
  532. package/dist/utils/markerContext.d.ts +30 -0
  533. package/dist/utils/markerContext.d.ts.map +1 -0
  534. package/dist/utils/markerContext.js +78 -0
  535. package/dist/utils/markerContext.js.map +1 -0
  536. package/dist/utils/markerRuntime.d.ts +5 -0
  537. package/dist/utils/markerRuntime.d.ts.map +1 -0
  538. package/dist/utils/markerRuntime.js +13 -0
  539. package/dist/utils/markerRuntime.js.map +1 -0
  540. package/dist/utils/markerShape.d.ts +13 -0
  541. package/dist/utils/markerShape.d.ts.map +1 -0
  542. package/dist/utils/markerShape.js +52 -0
  543. package/dist/utils/markerShape.js.map +1 -0
  544. package/dist/utils/markerTarget.d.ts +3 -0
  545. package/dist/utils/markerTarget.d.ts.map +1 -0
  546. package/dist/utils/markerTarget.js +16 -0
  547. package/dist/utils/markerTarget.js.map +1 -0
  548. package/dist/utils/panelBootstrap.d.ts +27 -0
  549. package/dist/utils/panelBootstrap.d.ts.map +1 -0
  550. package/dist/utils/panelBootstrap.js +95 -0
  551. package/dist/utils/panelBootstrap.js.map +1 -0
  552. package/dist/utils/panelRoleStats.d.ts +18 -0
  553. package/dist/utils/panelRoleStats.d.ts.map +1 -0
  554. package/dist/utils/panelRoleStats.js +77 -0
  555. package/dist/utils/panelRoleStats.js.map +1 -0
  556. package/dist/utils/panelTabPreference.d.ts +19 -0
  557. package/dist/utils/panelTabPreference.d.ts.map +1 -0
  558. package/dist/utils/panelTabPreference.js +76 -0
  559. package/dist/utils/panelTabPreference.js.map +1 -0
  560. package/dist/utils/pathname.d.ts +2 -0
  561. package/dist/utils/pathname.d.ts.map +1 -1
  562. package/dist/utils/pathname.js +10 -0
  563. package/dist/utils/pathname.js.map +1 -1
  564. package/dist/utils/personalKey.d.ts +7 -1
  565. package/dist/utils/personalKey.d.ts.map +1 -1
  566. package/dist/utils/personalKey.js +50 -2
  567. package/dist/utils/personalKey.js.map +1 -1
  568. package/dist/utils/pickProbe.d.ts +21 -0
  569. package/dist/utils/pickProbe.d.ts.map +1 -0
  570. package/dist/utils/pickProbe.js +285 -0
  571. package/dist/utils/pickProbe.js.map +1 -0
  572. package/dist/utils/pickProbeLayout.d.ts +16 -0
  573. package/dist/utils/pickProbeLayout.d.ts.map +1 -0
  574. package/dist/utils/pickProbeLayout.js +52 -0
  575. package/dist/utils/pickProbeLayout.js.map +1 -0
  576. package/dist/utils/pickProbeSession.d.ts +14 -0
  577. package/dist/utils/pickProbeSession.d.ts.map +1 -0
  578. package/dist/utils/pickProbeSession.js +134 -0
  579. package/dist/utils/pickProbeSession.js.map +1 -0
  580. package/dist/utils/pickTargetDeleteAnimation.d.ts +3 -0
  581. package/dist/utils/pickTargetDeleteAnimation.d.ts.map +1 -0
  582. package/dist/utils/pickTargetDeleteAnimation.js +29 -0
  583. package/dist/utils/pickTargetDeleteAnimation.js.map +1 -0
  584. package/dist/utils/pickTargetInspect.d.ts +12 -0
  585. package/dist/utils/pickTargetInspect.d.ts.map +1 -0
  586. package/dist/utils/pickTargetInspect.js +75 -0
  587. package/dist/utils/pickTargetInspect.js.map +1 -0
  588. package/dist/utils/probeColor.d.ts +6 -0
  589. package/dist/utils/probeColor.d.ts.map +1 -0
  590. package/dist/utils/probeColor.js +49 -0
  591. package/dist/utils/probeColor.js.map +1 -0
  592. package/dist/utils/probeLayout.d.ts +27 -0
  593. package/dist/utils/probeLayout.d.ts.map +1 -0
  594. package/dist/utils/probeLayout.js +86 -0
  595. package/dist/utils/probeLayout.js.map +1 -0
  596. package/dist/utils/probeSessionHistory.d.ts +8 -0
  597. package/dist/utils/probeSessionHistory.d.ts.map +1 -0
  598. package/dist/utils/probeSessionHistory.js +102 -0
  599. package/dist/utils/probeSessionHistory.js.map +1 -0
  600. package/dist/utils/replyHistory.d.ts +20 -0
  601. package/dist/utils/replyHistory.d.ts.map +1 -0
  602. package/dist/utils/replyHistory.js +82 -0
  603. package/dist/utils/replyHistory.js.map +1 -0
  604. package/dist/utils/reportCases.d.ts +54 -0
  605. package/dist/utils/reportCases.d.ts.map +1 -0
  606. package/dist/utils/reportCases.js +297 -0
  607. package/dist/utils/reportCases.js.map +1 -0
  608. package/dist/utils/reportPosition.d.ts +9 -0
  609. package/dist/utils/reportPosition.d.ts.map +1 -0
  610. package/dist/utils/reportPosition.js +18 -0
  611. package/dist/utils/reportPosition.js.map +1 -0
  612. package/dist/utils/reportProject.d.ts +1 -7
  613. package/dist/utils/reportProject.d.ts.map +1 -1
  614. package/dist/utils/reportProject.js +4 -4
  615. package/dist/utils/reportProject.js.map +1 -1
  616. package/dist/utils/reportSummary.d.ts +7 -0
  617. package/dist/utils/reportSummary.d.ts.map +1 -0
  618. package/dist/utils/reportSummary.js +49 -0
  619. package/dist/utils/reportSummary.js.map +1 -0
  620. package/dist/utils/reportTeam.d.ts +20 -5
  621. package/dist/utils/reportTeam.d.ts.map +1 -1
  622. package/dist/utils/reportTeam.js +60 -3
  623. package/dist/utils/reportTeam.js.map +1 -1
  624. package/dist/utils/reportUi.d.ts +10 -11
  625. package/dist/utils/reportUi.d.ts.map +1 -1
  626. package/dist/utils/reportUi.js +21 -6
  627. package/dist/utils/reportUi.js.map +1 -1
  628. package/dist/utils/reportVisibility.d.ts +1 -9
  629. package/dist/utils/reportVisibility.d.ts.map +1 -1
  630. package/dist/utils/reportVisibility.js +4 -4
  631. package/dist/utils/reportVisibility.js.map +1 -1
  632. package/dist/utils/reportVisual.d.ts +3 -1
  633. package/dist/utils/reportVisual.d.ts.map +1 -1
  634. package/dist/utils/reportVisual.js +17 -6
  635. package/dist/utils/reportVisual.js.map +1 -1
  636. package/dist/utils/routeDetailStatus.d.ts.map +1 -1
  637. package/dist/utils/routeDetailStatus.js +4 -1
  638. package/dist/utils/routeDetailStatus.js.map +1 -1
  639. package/dist/utils/storage.d.ts +3 -1
  640. package/dist/utils/storage.d.ts.map +1 -1
  641. package/dist/utils/storage.js +6 -2
  642. package/dist/utils/storage.js.map +1 -1
  643. package/dist/utils/targetSelector.d.ts +20 -0
  644. package/dist/utils/targetSelector.d.ts.map +1 -0
  645. package/dist/utils/targetSelector.js +123 -0
  646. package/dist/utils/targetSelector.js.map +1 -0
  647. package/dist/utils/validateFeedbackImport.d.ts +2 -1
  648. package/dist/utils/validateFeedbackImport.d.ts.map +1 -1
  649. package/dist/utils/validateFeedbackImport.js +114 -45
  650. package/dist/utils/validateFeedbackImport.js.map +1 -1
  651. package/package.json +78 -78
  652. package/dist/components/motion/index.d.ts +0 -48
  653. package/dist/components/motion/index.d.ts.map +0 -1
  654. package/dist/components/motion/index.js +0 -910
  655. package/dist/components/motion/index.js.map +0 -1
  656. package/dist/components/panel/PanelMoreMenu.d.ts +0 -20
  657. package/dist/components/panel/PanelMoreMenu.d.ts.map +0 -1
  658. package/dist/components/panel/PanelMoreMenu.js +0 -20
  659. package/dist/components/panel/PanelMoreMenu.js.map +0 -1
  660. package/dist/components/panel/feedback/FeedbackIssueHeader.d.ts +0 -12
  661. package/dist/components/panel/feedback/FeedbackIssueHeader.d.ts.map +0 -1
  662. package/dist/components/panel/feedback/FeedbackIssueHeader.js +0 -10
  663. package/dist/components/panel/feedback/FeedbackIssueHeader.js.map +0 -1
  664. package/dist/components/panel/feedback/FieldTagSelector.d.ts +0 -9
  665. package/dist/components/panel/feedback/FieldTagSelector.d.ts.map +0 -1
  666. package/dist/components/panel/feedback/FieldTagSelector.js +0 -15
  667. package/dist/components/panel/feedback/FieldTagSelector.js.map +0 -1
@@ -1,30 +1,68 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
2
  import { ensureReportLocaleMessages, getReportMessages, setActiveReportMessages } from "../i18n/index.js";
3
3
  import { useReportShortcuts } from "./useReportShortcuts.js";
4
+ import { useMarkerTargetPreviewPreference } from "./useMarkerTargetPreviewPreference.js";
5
+ import { useMarkerAppearancePreference } from "./useMarkerAppearancePreference.js";
6
+ import { useTypographyPreference } from "./useTypographyPreference.js";
4
7
  import { useReportPersistence } from "./useReportPersistence.js";
5
8
  import { useIsMobileViewport } from "./useIsMobileViewport.js";
6
9
  import { useAppearancePreference } from "./useAppearancePreference.js";
10
+ import { PANEL_APPEARANCE_STORAGE_KEY, TOOLTIP_APPEARANCE_STORAGE_KEY } from "../constants/appearance.js";
7
11
  import { useLocalePreference } from "./useLocalePreference.js";
12
+ import { useQuestionThreadPreference } from "./useQuestionThreadPreference.js";
13
+ import { usePanelRolePreference } from "./usePanelRolePreference.js";
14
+ import { usePanelTabPreference } from "./usePanelTabPreference.js";
8
15
  import { usePersonalKey } from "./usePersonalKey.js";
16
+ import { useSelfProfile } from "./useSelfProfile.js";
17
+ import { hasStoredPersonalKey, getAuthorIdFromPrivateKey, getAuthorNameFromPrivateKey, parsePrivateKeyBundle, publicKeysMatch, serializePublicKey } from "../utils/personalKey.js";
18
+ import { usePanelBootstrap } from "./usePanelBootstrap.js";
9
19
  import { useResolvedAppearance } from "./useResolvedAppearance.js";
10
- import { createReplyStatusForSubmit, getFeedbackDisplayStatus, resolveOriginalFeedbackAuthorName } from "../utils/feedbackThread.js";
20
+ import { buildPanelStats } from "../utils/panelBootstrap.js";
21
+ import { buildPanelRoleStats } from "../utils/panelRoleStats.js";
22
+ import { getPanelRoleTabPreset } from "../constants/panelTabPresets.js";
23
+ import { getPanelTabDefinition, isUserSelectablePanelTab } from "../constants/panelTabRegistry.js";
24
+ import { formatVisibleTabSummary, resolveDefaultPanelTab, resolveVisibleTabs, } from "../utils/panelTabPreference.js";
25
+ import { canShowCaseClaimAction, createReplyStatusForSubmit, getLatestBranchRootForCase, getReportReplies, ISSUE_ROOT_PARENT_ID, requiresCaseActorPermissionForComposer, resolveOriginalFeedbackAuthorName, resolveParentReplyIdForCaseQuestion, } from "../utils/feedbackThread.js";
11
26
  import { clampRatio, getMarkerFromReport, resolveTooltipAnchor } from "../utils/coordinates.js";
12
27
  import { getFeedbackTargetElement, isFeedbackTargetVisible, scrollToFeedbackTarget, waitForTargetRevealResync } from "../utils/locateFeedback.js";
28
+ import { clearFeedbackDeepLinkFromUrl, parseFeedbackDeepLink } from "../utils/feedbackDeepLink.js";
13
29
  const MARKER_HOVER_LEAVE_MS = 250;
14
30
  const OVERLAY_HOVER_LEAVE_MS = 100;
15
- import { findTargetByPoint, getSelectableTargets, isSameHoverTarget, resolveFeedbackDocumentAnchor, toSnapshot } from "../utils/dom.js";
31
+ function getInitialDeepLinkFeedbackId() {
32
+ if (typeof window === "undefined") {
33
+ return null;
34
+ }
35
+ return parseFeedbackDeepLink()?.feedbackId ?? null;
36
+ }
37
+ import { findPickTargetByPoint, getSelectableTargets, isSameHoverTarget, resolveFeedbackDocumentAnchor, toFeedbackHoverSnapshot } from "../utils/dom.js";
38
+ import { shouldInspectFontStyle } from "../utils/pickTargetInspect.js";
39
+ import { applyPickProbeCompareMode, applyPickProbeValueDiff, capturePickProbeValues, formatSavedProbeEditsSummary, getProposedChanges } from "../utils/pickProbe.js";
40
+ import { applySavedProbeEditsCompareMode, captureProbeOriginalSnapshot, captureSavedProbeDeletion, createSavedProbeEntry, restoreProbeElementFromSnapshot, findElementByProbeKey, getPickProbeElementKey, restoreProbeElementOriginal, restoreSavedProbeDeletion, } from "../utils/pickProbeSession.js";
41
+ import { playPickTargetDeleteAnimation } from "../utils/pickTargetDeleteAnimation.js";
42
+ import { applyProbeSessionActionBackward, applyProbeSessionActionForward } from "../utils/probeSessionHistory.js";
43
+ import { getPickProbeLayoutMode } from "../utils/probeLayout.js";
44
+ import { markerToTargetSnapshot } from "../utils/markerTarget.js";
16
45
  import { createInitialFieldValues, getFieldError, getFieldTags } from "../utils/fields.js";
46
+ import { canEditReportCases, claimCaseAssignee, createReportCase, buildResolvedCasesUpdate, canActOnCase, getCaseAssigneeName, isValidFocusedCase, resolveDefaultFocusedCaseId, transferCaseAssignee, } from "../utils/reportCases.js";
17
47
  import { createReplyId } from "../utils/format.js";
18
- import { notifyFeedbackCreate, notifyFeedbackDelete, notifyFeedbackReply, notifyFeedbackUpdate, notifyGitHubIssueCreated, } from "../utils/reportCallbacks.js";
19
- import { buildGitHubIssueUpdate, canCreateGitHubIssueFromList, canCreateGitHubIssueOnCreate, isGitIssued, } from "../utils/githubIntegration.js";
20
- function resolveDefaultAuthorName(identify, authors) {
48
+ import { notifyFeedbackCreate, notifyFeedbackDelete, notifyFeedbackReply, notifyFeedbackUpdate, notifyGitHubIssueCreated } from "../utils/reportCallbacks.js";
49
+ import { buildGitHubIssueStatusUpdate, buildGitHubIssueUpdate, canCreateGitHubIssueFromList, canCreateGitHubIssueOnCreate, isGitIssued } from "../utils/githubIntegration.js";
50
+ import { buildPresentationViewers, resolveSessionActor } from "../utils/reportTeam.js";
51
+ function resolveDefaultAuthorName(identify, authors, selfName) {
21
52
  if (identify?.name) {
22
53
  return identify.name;
23
54
  }
24
- return authors[0]?.name ?? "";
55
+ return authors[0]?.name ?? selfName ?? "";
25
56
  }
26
- export function useReportState({ projectId, environment, appVersion, appearance, fields, authors = [], requireReviewerKey = false, shortcut: _shortcut, identify, onList, onListAll, onNavigate, onRevealTarget, onCreate, onUpdate, onDelete, onEvent, onReply, github, routeKey, showFeedbackList, visibleShortcutKeys = false, initialLocale, messageOverrides, }) {
27
- const { appearance: activeAppearance, setAppearance } = useAppearancePreference(appearance);
57
+ export function useReportState({ projectId, environment, appVersion, panelAppearance, tooltipAppearance, questionThreadDefault = "expanded", fields, authors = [], requireReviewerKey = false, shortcut: _shortcut, identify, onList, onListAll, onPanelBootstrap, onActivitySummary, onListReplies, onNavigate, onRevealTarget, onCreate, onCreateReply, onUpdate, onDelete, onEvent, onReply, github, routeKey, showFeedbackList, visibleShortcutKeys = false, initialLocale, messageOverrides, pixelsMode = "default", replyHistory, }) {
58
+ const { appearance: activePanelAppearance, setAppearance: setPanelAppearance } = useAppearancePreference(PANEL_APPEARANCE_STORAGE_KEY, panelAppearance);
59
+ const { appearance: activeTooltipAppearance, setAppearance: setTooltipAppearance } = useAppearancePreference(TOOLTIP_APPEARANCE_STORAGE_KEY, tooltipAppearance);
60
+ const { showMarkerTargetPreview, setShowMarkerTargetPreview, toggleMarkerTargetPreview } = useMarkerTargetPreviewPreference();
61
+ const { markerAppearance, setMarkerAppearance, setMarkerSize, setMarkerShape, setMarkerColors, setMarkerColor } = useMarkerAppearancePreference();
62
+ const { typography, setTypography, setFontSize, setFontFamily } = useTypographyPreference();
63
+ const { questionThreadDisplay, setQuestionThreadDisplay } = useQuestionThreadPreference(questionThreadDefault);
64
+ const { panelRole, setPanelRole } = usePanelRolePreference();
65
+ const { storedPreference, setPanelTabPreference, setVisibleTabs, resetTabsToRoleDefault, applyRoleDefaultTabs } = usePanelTabPreference();
28
66
  const { locale, setLocale } = useLocalePreference(initialLocale);
29
67
  const [localeMessagesReady, setLocaleMessagesReady] = useState(locale !== "ko");
30
68
  const messages = useMemo(() => getReportMessages(locale, messageOverrides), [locale, localeMessagesReady, messageOverrides]);
@@ -53,64 +91,339 @@ export function useReportState({ projectId, environment, appVersion, appearance,
53
91
  const selectedElementRef = useRef(null);
54
92
  const hoverLeaveTimeoutRef = useRef(null);
55
93
  const overlayHoverLeaveTimeoutRef = useRef(null);
56
- const resolvedAppearance = useResolvedAppearance(activeAppearance);
94
+ const [mode, setMode] = useState(() => (getInitialDeepLinkFeedbackId() ? "view" : "idle"));
95
+ const [panelCollapsed, setPanelCollapsed] = useState(false);
96
+ const [panelTab, setPanelTab] = useState(null);
97
+ const panelExpanded = !panelCollapsed && mode !== "report";
98
+ const fetchEnabled = panelExpanded || mode === "view";
99
+ const tabAvailabilityContext = useMemo(() => ({
100
+ showFeedbackList,
101
+ canListAllFeedback: true,
102
+ }), [showFeedbackList]);
103
+ const visiblePanelTabs = useMemo(() => resolveVisibleTabs({ role: panelRole, preference: storedPreference, context: tabAvailabilityContext }), [panelRole, storedPreference, tabAvailabilityContext]);
104
+ const roleTabPreset = useMemo(() => getPanelRoleTabPreset(panelRole), [panelRole]);
105
+ const needsFullReportList = useMemo(() => {
106
+ if (mode === "view") {
107
+ return true;
108
+ }
109
+ if (!fetchEnabled) {
110
+ return false;
111
+ }
112
+ if (!onPanelBootstrap) {
113
+ return true;
114
+ }
115
+ if (panelTab === "feedback-list" || panelTab === "overview") {
116
+ return true;
117
+ }
118
+ if (roleTabPreset.headerStatsScope === "all") {
119
+ return true;
120
+ }
121
+ return visiblePanelTabs.some((tabId) => getPanelTabDefinition(tabId).needsFullReportList);
122
+ }, [fetchEnabled, mode, onPanelBootstrap, panelTab, roleTabPreset.headerStatsScope, visiblePanelTabs]);
123
+ const allReportsFetchEnabled = useMemo(() => {
124
+ if (!fetchEnabled || !needsFullReportList) {
125
+ return false;
126
+ }
127
+ const needsAllScopeTabs = ["overview", "my-tasks", "needs-attention", "project-health", "today-digest"];
128
+ return roleTabPreset.headerStatsScope === "all" || visiblePanelTabs.some((tabId) => needsAllScopeTabs.includes(tabId));
129
+ }, [fetchEnabled, needsFullReportList, roleTabPreset.headerStatsScope, visiblePanelTabs]);
130
+ const listFetchEnabled = fetchEnabled && needsFullReportList;
131
+ const bootstrapEnabled = fetchEnabled && panelExpanded && Boolean(onPanelBootstrap);
132
+ const resolvedPanelAppearance = useResolvedAppearance(activePanelAppearance);
133
+ const resolvedTooltipAppearance = useResolvedAppearance(activeTooltipAppearance);
57
134
  const isMobileViewport = useIsMobileViewport();
58
- const { canTransferFeedback, canListAllFeedback, currentPathname, listScope, setListScope, filters, setFilters, selectedReportId, setSelectedReportId, reports, filteredReports, currentPageFilteredReports, routeDetailsStats, selectedReport, isError, isFetching, hasNextPage, isFetchingNextPage, fetchNextPage, isCreating, isUpdating, isDeleting, queryErrorMessage, refetch, createFeedback, updateFeedback, deleteFeedback, } = useReportPersistence({
135
+ const { canTransferFeedback, canListAllFeedback, currentPathname, listScope, setListScope, filters, setFilters, selectedReportId, setSelectedReportId, reports, currentPageReports, filteredReports, currentPageFilteredReports, allPageReports, allPageFilteredReports, routeDetailsStats, selectedReport, isError, isReportsLoading, isFetching, hasNextPage, isFetchingNextPage, fetchNextPage, isCreating, isUpdating, isDeleting, queryErrorMessage, refetch, createFeedback, updateFeedback, deleteFeedback, loadRepliesIfNeeded, createReply, usesCreateReply, replyHistoryByReportId, loadOlderReplies, goToOlderPaginationPage, goToNewerPaginationPage, } = useReportPersistence({
59
136
  projectId,
60
137
  environment,
61
138
  appVersion,
62
139
  fields,
63
140
  onList,
64
141
  onListAll,
142
+ onListReplies,
65
143
  onCreate,
144
+ onCreateReply,
66
145
  onUpdate,
67
146
  onDelete,
68
147
  routeKey,
148
+ fetchEnabled,
149
+ listFetchEnabled,
150
+ allReportsFetchEnabled,
151
+ replyHistory,
152
+ });
153
+ const bootstrapParams = useMemo(() => ({ pathname: currentPathname }), [currentPathname]);
154
+ const { bootstrap: panelBootstrap } = usePanelBootstrap({
155
+ enabled: bootstrapEnabled,
156
+ params: bootstrapParams,
157
+ fields,
158
+ reports: currentPageReports,
159
+ pathname: currentPathname,
160
+ onPanelBootstrap,
69
161
  });
162
+ const resolvedRouteDetailsStats = useMemo(() => panelBootstrap?.routeDetails ?? routeDetailsStats, [panelBootstrap, routeDetailsStats]);
70
163
  const eventCallbacks = useMemo(() => ({
71
164
  onEvent,
72
165
  onReply,
73
166
  }), [onEvent, onReply]);
74
- const { personalKey, publicKey, personalKeyRequired, personalKeyPendingRegistration, personalKeyCandidates, authorizedAuthors, issuePersonalKey, rotatePersonalKey, insertPersonalKey, signPayload, } = usePersonalKey({
75
- enabled: requireReviewerKey || authors.some((author) => Boolean(author.publicKey)),
167
+ const { selfProfile, saveSelfProfile, markOnboardingComplete } = useSelfProfile(projectId, environment);
168
+ const requiresReviewerKey = requireReviewerKey || authors.some((author) => Boolean(author.publicKey));
169
+ const isPresentationMode = pixelsMode === "presentation";
170
+ const { personalKey, publicKey, personalKeyRequired, personalKeyPendingRegistration, personalKeyCandidates, authorizedAuthors, issuePersonalKey, issueSelfKey, rotatePersonalKey, insertPersonalKey, clearPersonalKey, signPayload, } = usePersonalKey({
171
+ enabled: isPresentationMode || !requiresReviewerKey || hasStoredPersonalKey(projectId, environment),
172
+ requireKey: requiresReviewerKey,
76
173
  projectId,
77
174
  environment,
78
175
  identify,
79
176
  authors,
80
177
  });
81
- const activeIdentify = authorizedAuthors[0] ?? (personalKeyRequired ? undefined : identify);
178
+ const activeIdentify = authorizedAuthors[0] ?? (selfProfile?.authorId && selfProfile.name ? { id: selfProfile.authorId, name: selfProfile.name } : undefined) ?? (personalKeyRequired ? undefined : identify);
179
+ const presentationViewers = useMemo(() => buildPresentationViewers(identify, authors), [authors, identify]);
180
+ const [presentationViewerId, setPresentationViewerId] = useState(null);
181
+ const resolvedPresentationViewerId = useMemo(() => {
182
+ if (!isPresentationMode || presentationViewers.length === 0) {
183
+ return null;
184
+ }
185
+ if (presentationViewerId && presentationViewers.some((viewer) => viewer.id === presentationViewerId)) {
186
+ return presentationViewerId;
187
+ }
188
+ return presentationViewers[0]?.id ?? null;
189
+ }, [isPresentationMode, presentationViewerId, presentationViewers]);
190
+ const authorSelectionLocked = Boolean(personalKey);
191
+ const hasPersistedPersonalKey = hasStoredPersonalKey(projectId, environment);
192
+ const isSelfAuthenticated = hasPersistedPersonalKey;
193
+ const authDiagnostics = useMemo(() => {
194
+ const parsedBundle = personalKey ? parsePrivateKeyBundle(personalKey) : null;
195
+ const teamReviewer = parsedBundle ? authors.find((author) => author.id === parsedBundle.authorId) : null;
196
+ const localAuthorName = (parsedBundle?.authorName ?? selfProfile?.name ?? "").trim();
197
+ const expected = {
198
+ projectId,
199
+ environment: environment ?? "",
200
+ authorId: teamReviewer?.id ?? null,
201
+ authorName: teamReviewer?.name?.trim() || null,
202
+ publicKey: teamReviewer?.publicKey?.trim() || null,
203
+ };
204
+ const actual = {
205
+ projectId: parsedBundle?.projectId ?? null,
206
+ environment: parsedBundle?.environment ?? "",
207
+ authorId: parsedBundle?.authorId ?? null,
208
+ authorName: localAuthorName || null,
209
+ publicKey: parsedBundle ? serializePublicKey(parsedBundle.publicKey) : null,
210
+ };
211
+ const items = [
212
+ { field: "projectId", expected: expected.projectId, actual: actual.projectId, matched: expected.projectId === actual.projectId },
213
+ { field: "environment", expected: expected.environment, actual: actual.environment, matched: (expected.environment ?? "") === (actual.environment ?? "") },
214
+ { field: "authorId", expected: expected.authorId, actual: actual.authorId, matched: expected.authorId === actual.authorId },
215
+ {
216
+ field: "authorName",
217
+ expected: expected.authorName,
218
+ actual: actual.authorName,
219
+ matched: Boolean(expected.authorName && actual.authorName && expected.authorName === actual.authorName),
220
+ },
221
+ {
222
+ field: "publicKey",
223
+ expected: expected.publicKey,
224
+ actual: actual.publicKey,
225
+ matched: Boolean(expected.publicKey && actual.publicKey && publicKeysMatch(expected.publicKey, actual.publicKey)),
226
+ },
227
+ ];
228
+ if (!requiresReviewerKey) {
229
+ return { status: "disabled", reason: "reviewer-key-not-enforced", items, expected, actual };
230
+ }
231
+ if (!personalKey) {
232
+ return { status: "failed", reason: "missing-personal-key", items, expected, actual };
233
+ }
234
+ if (!parsedBundle) {
235
+ return { status: "failed", reason: "invalid-personal-key-format", items, expected, actual };
236
+ }
237
+ if (parsedBundle.projectId !== projectId) {
238
+ return { status: "failed", reason: "project-mismatch", items, expected, actual };
239
+ }
240
+ if ((parsedBundle.environment ?? "") !== (environment ?? "")) {
241
+ return { status: "failed", reason: "environment-mismatch", items, expected, actual };
242
+ }
243
+ if (!teamReviewer) {
244
+ return { status: "failed", reason: "missing-team-author", items, expected, actual };
245
+ }
246
+ if (expected.authorName && actual.authorName && expected.authorName !== actual.authorName) {
247
+ return { status: "failed", reason: "author-name-mismatch", items, expected, actual };
248
+ }
249
+ if (!teamReviewer.publicKey?.trim()) {
250
+ return { status: "failed", reason: "missing-team-public-key", items, expected, actual };
251
+ }
252
+ if (!actual.publicKey || !publicKeysMatch(teamReviewer.publicKey, actual.publicKey)) {
253
+ return { status: "failed", reason: "public-key-mismatch", items, expected, actual };
254
+ }
255
+ return { status: "matched", reason: "matched", items, expected, actual };
256
+ }, [authors, environment, personalKey, projectId, requiresReviewerKey, selfProfile?.name]);
257
+ const panelView = useMemo(() => {
258
+ if (isPresentationMode || !requiresReviewerKey) {
259
+ return "ready";
260
+ }
261
+ if (!hasPersistedPersonalKey) {
262
+ return "onboarding";
263
+ }
264
+ if (selfProfile && !selfProfile.completed) {
265
+ return "setup-complete";
266
+ }
267
+ const parsedBundle = personalKey ? parsePrivateKeyBundle(personalKey) : null;
268
+ const teamReviewer = parsedBundle ? authors.find((author) => author.id === parsedBundle.authorId) : null;
269
+ if (!teamReviewer) {
270
+ return "setup-complete";
271
+ }
272
+ if (authDiagnostics.status === "matched") {
273
+ return "ready";
274
+ }
275
+ return "key-issue";
276
+ }, [authDiagnostics.status, authors, hasPersistedPersonalKey, isPresentationMode, personalKey, requiresReviewerKey, selfProfile]);
277
+ useEffect(() => {
278
+ if (selfProfile && !selfProfile.completed && hasPersistedPersonalKey && authorizedAuthors.length > 0) {
279
+ markOnboardingComplete();
280
+ }
281
+ }, [authorizedAuthors.length, hasPersistedPersonalKey, markOnboardingComplete, selfProfile]);
282
+ const completeOnboarding = useCallback(async ({ name }) => {
283
+ const trimmedName = name.trim();
284
+ const authorId = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `self-${Date.now()}-${Math.random().toString(16).slice(2)}`;
285
+ const issued = await issueSelfKey(authorId, trimmedName);
286
+ saveSelfProfile({ name: trimmedName, authorId, completed: false });
287
+ return { ...issued, authorId };
288
+ }, [issueSelfKey, saveSelfProfile]);
289
+ const restoreFromBackup = useCallback(async (backupKey) => {
290
+ const result = await insertPersonalKey(backupKey);
291
+ if (!result.saved) {
292
+ return { restored: false, reason: result.reason };
293
+ }
294
+ if (!requiresReviewerKey && !result.authorized) {
295
+ return { restored: false, reason: "unauthorized" };
296
+ }
297
+ const authorId = getAuthorIdFromPrivateKey(backupKey);
298
+ const restoredName = getAuthorNameFromPrivateKey(backupKey);
299
+ saveSelfProfile({
300
+ name: restoredName ?? "",
301
+ authorId: authorId ?? "",
302
+ completed: result.authorized || !requiresReviewerKey,
303
+ });
304
+ return { restored: true, name: restoredName, authorized: result.authorized };
305
+ }, [insertPersonalKey, requiresReviewerKey, saveSelfProfile]);
306
+ const skipOnboarding = useCallback(() => {
307
+ markOnboardingComplete();
308
+ }, [markOnboardingComplete]);
309
+ const sessionActor = useMemo(() => resolveSessionActor({
310
+ isPresentationMode,
311
+ presentationViewers,
312
+ presentationViewerId: resolvedPresentationViewerId,
313
+ activeIdentify,
314
+ }), [activeIdentify, isPresentationMode, presentationViewers, resolvedPresentationViewerId]);
82
315
  const signCreatePayload = async (payload) => {
83
316
  const auth = await signPayload("feedback:create", payload);
84
317
  return auth ? { ...payload, auth } : payload;
85
318
  };
86
319
  const signUpdatePayload = async (payload) => {
87
- if (personalKeyRequired) {
88
- throw new Error(messages.errors.personalKeyRequired);
89
- }
90
320
  const auth = await signPayload("feedback:update", payload);
91
321
  return auth ? { ...payload, auth } : payload;
92
322
  };
93
- const [mode, setMode] = useState("idle");
323
+ const signReplyPayload = async (payload) => {
324
+ const auth = await signPayload("reply:create", payload);
325
+ return auth ? { ...payload, auth } : payload;
326
+ };
327
+ const applyGitHubIssueUpdate = async (report, result) => {
328
+ if (usesCreateReply) {
329
+ const updatedFeedback = await updateFeedback(report.id, await signUpdatePayload(buildGitHubIssueStatusUpdate(report, result)));
330
+ await createReply(report.id, await signReplyPayload({
331
+ message: messages.resolution.gitIssuedMessage,
332
+ status: "suggested",
333
+ author_type: "system",
334
+ }));
335
+ return updatedFeedback;
336
+ }
337
+ return updateFeedback(report.id, await signUpdatePayload(buildGitHubIssueUpdate(report, result, messages.resolution.gitIssuedMessage)));
338
+ };
94
339
  const [showTargetPreview, setShowTargetPreview] = useState(false);
95
340
  const [selectableTargets, setSelectableTargets] = useState([]);
96
341
  const [errorMessage, setErrorMessage] = useState("");
97
342
  const [draft, setDraft] = useState(null);
98
343
  const [hoveredTarget, setHoveredTarget] = useState(null);
344
+ const [hoverPointer, setHoverPointer] = useState(null);
99
345
  const [selectedTarget, setSelectedTarget] = useState(null);
346
+ const [pickProbeOpen, setPickProbeOpen] = useState(false);
347
+ const [pickProbeBaseline, setPickProbeBaseline] = useState(null);
348
+ const [pickProbeValues, setPickProbeValues] = useState(null);
349
+ const [pickProbeSupportsTextFields, setPickProbeSupportsTextFields] = useState(false);
350
+ const [pickProbeLayoutMode, setPickProbeLayoutMode] = useState(null);
351
+ const [pickProbeCompareMode, setPickProbeCompareModeState] = useState("after");
352
+ const [pickTargetContextMenu, setPickTargetContextMenu] = useState(null);
353
+ const [contextMenuElementKey, setContextMenuElementKey] = useState(null);
354
+ const [savedProbeEdits, setSavedProbeEdits] = useState({});
355
+ const [savedProbeDeletions, setSavedProbeDeletions] = useState([]);
356
+ const savedProbeDeletionsRef = useRef(savedProbeDeletions);
357
+ savedProbeDeletionsRef.current = savedProbeDeletions;
358
+ const [probeSessionHistoryState, setProbeSessionHistoryState] = useState({ actions: [], index: -1 });
359
+ const [savedProbeCompareMode, setSavedProbeCompareModeState] = useState("after");
360
+ const pickProbeRestoreRef = useRef(null);
361
+ const pickProbeOriginalSnapshotRef = useRef(null);
362
+ const pickProbeElementKeyRef = useRef(null);
363
+ const draftElementRef = useRef(null);
364
+ const contextMenuElementRef = useRef(null);
100
365
  const [markers, setMarkers] = useState([]);
101
366
  const [hoveredMarkerId, setHoveredMarkerId] = useState(null);
102
367
  const [activeReplyReportId, setActiveReplyReportId] = useState(null);
103
368
  const [replyDraft, setReplyDraft] = useState("");
104
- const [draftAuthorName, setDraftAuthorName] = useState(() => resolveDefaultAuthorName(activeIdentify, authorizedAuthors));
105
- const [replyAuthorName, setReplyAuthorName] = useState(() => resolveDefaultAuthorName(activeIdentify, authorizedAuthors));
369
+ const [replySubmitAsQuestion, setReplySubmitAsQuestion] = useState(false);
370
+ const [draftAuthorName, setDraftAuthorName] = useState(() => resolveDefaultAuthorName(activeIdentify, authorizedAuthors, selfProfile?.name));
371
+ const [replyAuthorName, setReplyAuthorName] = useState(() => resolveDefaultAuthorName(activeIdentify, authorizedAuthors, selfProfile?.name));
372
+ const applyPresentationViewer = useCallback(async (viewerId) => {
373
+ if (!isPresentationMode) {
374
+ return;
375
+ }
376
+ const viewer = presentationViewers.find((item) => item.id === viewerId) ?? presentationViewers[0];
377
+ if (!viewer) {
378
+ return;
379
+ }
380
+ setPresentationViewerId(viewer.id);
381
+ saveSelfProfile({
382
+ name: viewer.name,
383
+ authorId: viewer.id,
384
+ completed: true,
385
+ });
386
+ if (viewer.privateKey) {
387
+ await insertPersonalKey(viewer.privateKey);
388
+ return;
389
+ }
390
+ clearPersonalKey();
391
+ }, [clearPersonalKey, insertPersonalKey, isPresentationMode, presentationViewers, saveSelfProfile]);
392
+ useEffect(() => {
393
+ if (!sessionActor?.name) {
394
+ return;
395
+ }
396
+ setReplyAuthorName(sessionActor.name);
397
+ setDraftAuthorName(sessionActor.name);
398
+ }, [sessionActor?.id, sessionActor?.name]);
399
+ const setDraftAuthorNameSafe = useCallback((name) => {
400
+ if (authorSelectionLocked && sessionActor?.name) {
401
+ setDraftAuthorName(sessionActor.name);
402
+ return;
403
+ }
404
+ setDraftAuthorName(name);
405
+ }, [authorSelectionLocked, sessionActor?.name]);
406
+ const setReplyAuthorNameSafe = useCallback((name) => {
407
+ if (authorSelectionLocked && sessionActor?.name) {
408
+ setReplyAuthorName(sessionActor.name);
409
+ return;
410
+ }
411
+ setReplyAuthorName(name);
412
+ }, [authorSelectionLocked, sessionActor?.name]);
413
+ const [isSubmittingReply, setIsSubmittingReply] = useState(false);
414
+ const [isClaimingAssignee, setIsClaimingAssignee] = useState(false);
106
415
  const [pendingComposer, setPendingComposer] = useState(null);
107
416
  const [confirmAuthorName, setConfirmAuthorName] = useState("");
108
417
  const [showConfirmAuthorSelect, setShowConfirmAuthorSelect] = useState(false);
109
418
  const pendingLocateReportIdRef = useRef(null);
419
+ const pendingDeepLinkFeedbackIdRef = useRef(getInitialDeepLinkFeedbackId());
420
+ const deepLinkHandledRef = useRef(false);
110
421
  const [editingReportId, setEditingReportId] = useState(null);
111
422
  const [editableDraft, setEditableDraft] = useState(null);
112
- const [panelTab, setPanelTab] = useState(null);
113
423
  const [creatingGitHubIssueId, setCreatingGitHubIssueId] = useState(null);
424
+ const [caseEditReportId, setCaseEditReportId] = useState(null);
425
+ const [caseEditDraft, setCaseEditDraft] = useState(null);
426
+ const [focusedCaseId, setFocusedCaseId] = useState(null);
114
427
  const canCreateGitHubIssueFromListValue = useMemo(() => canCreateGitHubIssueFromList(github), [github]);
115
428
  const canCreateGitHubIssueOnCreateValue = useMemo(() => canCreateGitHubIssueOnCreate(github), [github]);
116
429
  const activeReplyAnchor = useMemo(() => resolveTooltipAnchor(markers, activeReplyReportId), [activeReplyReportId, markers]);
@@ -127,45 +440,87 @@ export function useReportState({ projectId, environment, appVersion, appearance,
127
440
  }, [activeReplyAnchor, activeReplyReportId, hoveredMarkerId, markers]);
128
441
  const tooltipReport = tooltipAnchor?.report ?? null;
129
442
  const tooltipFieldTags = useMemo(() => (tooltipReport ? getFieldTags(fields, tooltipReport.field_values) : []), [fields, tooltipReport]);
443
+ const roleStatsReports = useMemo(() => {
444
+ if (roleTabPreset.headerStatsScope !== "all") {
445
+ return currentPageFilteredReports;
446
+ }
447
+ if (listScope === "all") {
448
+ return filteredReports;
449
+ }
450
+ if (allPageFilteredReports.length > 0) {
451
+ return allPageFilteredReports;
452
+ }
453
+ return currentPageFilteredReports;
454
+ }, [allPageFilteredReports, currentPageFilteredReports, filteredReports, listScope, roleTabPreset.headerStatsScope]);
130
455
  const targetStats = useMemo(() => {
131
- let resolved = 0;
132
- let inProgress = 0;
133
- for (const report of currentPageFilteredReports) {
134
- const status = getFeedbackDisplayStatus(report, true);
135
- if (status === "resolved") {
136
- resolved += 1;
137
- }
138
- else if (status === "wait_for_reply" || status === "suggested" || status === "found_error" || status === "recheck_requested") {
139
- inProgress += 1;
140
- }
456
+ if (panelBootstrap?.stats) {
457
+ return panelBootstrap.stats;
458
+ }
459
+ return buildPanelStats(currentPageFilteredReports);
460
+ }, [currentPageFilteredReports, panelBootstrap]);
461
+ const roleStatItems = useMemo(() => buildPanelRoleStats({
462
+ role: panelRole,
463
+ reports: roleStatsReports,
464
+ actorName: sessionActor?.name ?? null,
465
+ fallbackStats: targetStats,
466
+ messages,
467
+ }), [panelRole, roleStatsReports, sessionActor?.name, targetStats, messages]);
468
+ const listScopeInitializedRef = useRef(false);
469
+ useEffect(() => {
470
+ if (listScopeInitializedRef.current) {
471
+ return;
141
472
  }
142
- const foundCount = currentPageFilteredReports.length;
143
- return {
144
- found: foundCount,
145
- resolved,
146
- inProgress,
147
- };
148
- }, [currentPageFilteredReports]);
149
- const statusText = useMemo(() => {
150
- if (mode === "report") {
151
- const focusTarget = selectedTarget ?? hoveredTarget;
152
- if (!focusTarget) {
153
- return "";
154
- }
155
- const typeLabel = focusTarget.type === "item" ? messages.statusText.selectedItem : messages.statusText.selectedGroup;
156
- return `${typeLabel}\n${focusTarget.id}`;
473
+ if (storedPreference?.customized) {
474
+ listScopeInitializedRef.current = true;
475
+ return;
157
476
  }
158
- if (mode === "view") {
159
- return isFetching ? messages.statusText.loadingFeedback : messages.statusText.ready;
477
+ setListScope(roleTabPreset.defaultListScope);
478
+ listScopeInitializedRef.current = true;
479
+ }, [roleTabPreset.defaultListScope, setListScope, storedPreference?.customized]);
480
+ useEffect(() => {
481
+ if (!panelTab) {
482
+ return;
160
483
  }
161
- if (showTargetPreview) {
162
- return messages.statusText.showingSelectableTargets(selectableTargets.length);
484
+ if (panelTab === "settings" || panelTab === "command") {
485
+ return;
163
486
  }
164
- if (selectableTargets.length === 0) {
165
- return messages.statusText.noSelectableTargets;
487
+ if (!isUserSelectablePanelTab(panelTab)) {
488
+ return;
166
489
  }
167
- return messages.statusText.ready;
168
- }, [filteredReports.length, isFetching, hoveredTarget, messages.statusText, mode, selectableTargets.length, selectedTarget, showTargetPreview]);
490
+ if (visiblePanelTabs.includes(panelTab)) {
491
+ return;
492
+ }
493
+ setPanelTab(resolveDefaultPanelTab(panelRole, visiblePanelTabs));
494
+ }, [panelRole, panelTab, visiblePanelTabs]);
495
+ const resolvedTabAvailabilityContext = useMemo(() => ({
496
+ showFeedbackList,
497
+ canListAllFeedback,
498
+ }), [canListAllFeedback, showFeedbackList]);
499
+ const visiblePanelTabLabels = useMemo(() => {
500
+ const labels = {};
501
+ for (const tabId of visiblePanelTabs) {
502
+ labels[tabId] = messages.panel[getPanelTabDefinition(tabId).labelKey];
503
+ }
504
+ return labels;
505
+ }, [messages.panel, visiblePanelTabs]);
506
+ const visiblePanelTabsSummary = useMemo(() => formatVisibleTabSummary(visiblePanelTabs, visiblePanelTabLabels), [visiblePanelTabLabels, visiblePanelTabs]);
507
+ const setVisiblePanelTabs = useCallback((nextTabs) => {
508
+ setVisibleTabs(nextTabs, resolvedTabAvailabilityContext, true);
509
+ }, [resolvedTabAvailabilityContext, setVisibleTabs]);
510
+ const resetVisibleTabsToRoleDefault = useCallback(() => {
511
+ resetTabsToRoleDefault(panelRole, resolvedTabAvailabilityContext);
512
+ setListScope(getPanelRoleTabPreset(panelRole).defaultListScope);
513
+ }, [panelRole, resetTabsToRoleDefault, resolvedTabAvailabilityContext, setListScope]);
514
+ const applyRoleDefaultTabsForOnboarding = useCallback((role) => {
515
+ applyRoleDefaultTabs(role, resolvedTabAvailabilityContext);
516
+ }, [applyRoleDefaultTabs, resolvedTabAvailabilityContext]);
517
+ const savePanelTabPreference = useCallback((preference) => {
518
+ setPanelTabPreference(preference);
519
+ setListScope(getPanelRoleTabPreset(panelRole).defaultListScope);
520
+ }, [panelRole, setListScope, setPanelTabPreference]);
521
+ const authorizedAuthorId = authorizedAuthors[0]?.id ?? null;
522
+ const activeIdentifyId = activeIdentify?.id ?? null;
523
+ const activeIdentifyName = activeIdentify?.name ?? null;
169
524
  useEffect(() => {
170
525
  setDraft(null);
171
526
  setErrorMessage("");
@@ -177,8 +532,10 @@ export function useReportState({ projectId, environment, appVersion, appearance,
177
532
  setPendingComposer(null);
178
533
  setShowConfirmAuthorSelect(false);
179
534
  setConfirmAuthorName("");
180
- setDraftAuthorName(resolveDefaultAuthorName(activeIdentify, authorizedAuthors));
181
- setReplyAuthorName(resolveDefaultAuthorName(activeIdentify, authorizedAuthors));
535
+ setDraftAuthorName(sessionActor?.name ?? resolveDefaultAuthorName(activeIdentify, authorizedAuthors, selfProfile?.name));
536
+ if (!isPresentationMode) {
537
+ setReplyAuthorName(sessionActor?.name ?? resolveDefaultAuthorName(activeIdentify, authorizedAuthors, selfProfile?.name));
538
+ }
182
539
  setEditingReportId(null);
183
540
  setEditableDraft(null);
184
541
  if (mode !== "idle") {
@@ -194,7 +551,13 @@ export function useReportState({ projectId, environment, appVersion, appearance,
194
551
  window.clearTimeout(overlayHoverLeaveTimeoutRef.current);
195
552
  overlayHoverLeaveTimeoutRef.current = null;
196
553
  }
197
- }, [currentPathname, mode]);
554
+ }, [currentPathname, mode, activeIdentifyId, activeIdentifyName, authorizedAuthorId, isPresentationMode, sessionActor?.name, selfProfile?.authorId, selfProfile?.name]);
555
+ useEffect(() => {
556
+ if (!isPresentationMode || presentationViewers.length === 0 || !resolvedPresentationViewerId) {
557
+ return;
558
+ }
559
+ void applyPresentationViewer(resolvedPresentationViewerId);
560
+ }, [applyPresentationViewer, isPresentationMode, presentationViewers.length, resolvedPresentationViewerId]);
198
561
  useEffect(() => {
199
562
  setShowTargetPreview(false);
200
563
  }, [currentPathname]);
@@ -236,9 +599,85 @@ export function useReportState({ projectId, environment, appVersion, appearance,
236
599
  }, [showTargetPreview]);
237
600
  const syncMarkers = useCallback(() => {
238
601
  setMarkers(currentPageFilteredReports.map((report) => getMarkerFromReport(report, window.scrollY)));
239
- }, [currentPageFilteredReports]);
602
+ }, [currentPageFilteredReports, markerAppearance.size]);
603
+ const activeMarkerReportId = useMemo(() => {
604
+ if (activeReplyReportId) {
605
+ return activeReplyReportId;
606
+ }
607
+ if (hoveredMarkerId) {
608
+ return hoveredMarkerId;
609
+ }
610
+ return null;
611
+ }, [activeReplyReportId, hoveredMarkerId]);
612
+ const activeMarkerTarget = useMemo(() => {
613
+ if (mode === "report") {
614
+ return null;
615
+ }
616
+ if (!activeMarkerReportId) {
617
+ return null;
618
+ }
619
+ const marker = markers.find((item) => item.report.id === activeMarkerReportId);
620
+ if (!marker) {
621
+ return null;
622
+ }
623
+ return markerToTargetSnapshot(marker);
624
+ }, [activeMarkerReportId, markers, mode]);
625
+ const markerPreviewTargets = useMemo(() => {
626
+ if (!showMarkerTargetPreview) {
627
+ return [];
628
+ }
629
+ return markers.flatMap((marker) => {
630
+ const snapshot = markerToTargetSnapshot(marker);
631
+ if (!snapshot) {
632
+ return [];
633
+ }
634
+ if (activeMarkerTarget && snapshot.id === activeMarkerTarget.id) {
635
+ return [];
636
+ }
637
+ return [snapshot];
638
+ });
639
+ }, [activeMarkerTarget, markers, showMarkerTargetPreview]);
640
+ const statusText = useMemo(() => {
641
+ if (mode === "report") {
642
+ const focusTarget = selectedTarget ?? hoveredTarget;
643
+ if (!focusTarget) {
644
+ return messages.statusText.reportReady;
645
+ }
646
+ if (focusTarget.isTagged) {
647
+ const typeLabel = focusTarget.type === "item" ? messages.statusText.selectedItem : messages.statusText.selectedGroup;
648
+ return `${typeLabel}\n${focusTarget.id}`;
649
+ }
650
+ return `${messages.statusText.selectedUntaggedTarget}\n${focusTarget.suggestedReportId ?? focusTarget.id}`;
651
+ }
652
+ if (mode === "view") {
653
+ return isFetching ? messages.statusText.loadingFeedback : messages.statusText.ready;
654
+ }
655
+ if (showTargetPreview) {
656
+ return messages.statusText.showingSelectableTargets(selectableTargets.length);
657
+ }
658
+ if (showMarkerTargetPreview) {
659
+ return messages.statusText.showingMarkerTargets(markerPreviewTargets.length + (activeMarkerTarget ? 1 : 0));
660
+ }
661
+ if (selectableTargets.length === 0) {
662
+ return messages.statusText.noSelectableTargets;
663
+ }
664
+ return messages.statusText.ready;
665
+ }, [
666
+ activeMarkerTarget,
667
+ filteredReports.length,
668
+ hoveredTarget,
669
+ isFetching,
670
+ markerPreviewTargets.length,
671
+ messages.statusText,
672
+ mode,
673
+ selectableTargets.length,
674
+ selectedTarget,
675
+ showMarkerTargetPreview,
676
+ showTargetPreview,
677
+ ]);
240
678
  useEffect(() => {
241
- if (mode !== "view") {
679
+ const shouldSyncMarkers = mode === "view" || showMarkerTargetPreview;
680
+ if (!shouldSyncMarkers) {
242
681
  setMarkers([]);
243
682
  return;
244
683
  }
@@ -261,13 +700,14 @@ export function useReportState({ projectId, environment, appVersion, appearance,
261
700
  }, 50);
262
701
  };
263
702
  const mutationObserver = new MutationObserver((mutations) => {
264
- if (mutations.some((mutation) => mutation.type === "attributes")) {
703
+ if (mutations.some((mutation) => mutation.type === "attributes" || mutation.type === "childList")) {
265
704
  scheduleMutationSync();
266
705
  }
267
706
  });
268
707
  mutationObserver.observe(document.body, {
269
708
  attributes: true,
270
709
  attributeFilter: ["class", "style", "aria-hidden"],
710
+ childList: true,
271
711
  subtree: true,
272
712
  });
273
713
  window.addEventListener("scroll", syncMarkers, { passive: true, capture: true });
@@ -281,7 +721,21 @@ export function useReportState({ projectId, environment, appVersion, appearance,
281
721
  window.removeEventListener("scroll", syncMarkers, { capture: true });
282
722
  window.removeEventListener("resize", syncMarkers);
283
723
  };
284
- }, [currentPathname, mode, syncMarkers]);
724
+ }, [currentPathname, mode, showMarkerTargetPreview, syncMarkers]);
725
+ useEffect(() => {
726
+ if (!showMarkerTargetPreview) {
727
+ return;
728
+ }
729
+ const syncPreviewRects = () => {
730
+ syncMarkers();
731
+ };
732
+ window.addEventListener("scroll", syncPreviewRects, { passive: true, capture: true });
733
+ window.addEventListener("resize", syncPreviewRects);
734
+ return () => {
735
+ window.removeEventListener("scroll", syncPreviewRects, { capture: true });
736
+ window.removeEventListener("resize", syncPreviewRects);
737
+ };
738
+ }, [showMarkerTargetPreview, syncMarkers]);
285
739
  const prepareFeedbackLocation = useCallback(async (report) => {
286
740
  const targetElement = getFeedbackTargetElement(report);
287
741
  if (targetElement && isFeedbackTargetVisible(targetElement)) {
@@ -308,8 +762,8 @@ export function useReportState({ projectId, environment, appVersion, appearance,
308
762
  return;
309
763
  }
310
764
  const syncTargetRects = () => {
311
- setHoveredTarget(toSnapshot(hoveredElementRef.current));
312
- setSelectedTarget(toSnapshot(selectedElementRef.current));
765
+ setHoveredTarget(toFeedbackHoverSnapshot(hoveredElementRef.current));
766
+ setSelectedTarget(toFeedbackHoverSnapshot(selectedElementRef.current));
313
767
  };
314
768
  window.addEventListener("scroll", syncTargetRects, { passive: true, capture: true });
315
769
  window.addEventListener("resize", syncTargetRects);
@@ -350,14 +804,499 @@ export function useReportState({ projectId, environment, appVersion, appearance,
350
804
  overlayHoverLeaveTimeoutRef.current = window.setTimeout(() => {
351
805
  if (!hoveredElementRef.current) {
352
806
  setHoveredTarget(null);
807
+ setHoverPointer(null);
353
808
  }
354
809
  overlayHoverLeaveTimeoutRef.current = null;
355
810
  }, OVERLAY_HOVER_LEAVE_MS);
356
811
  };
812
+ const refreshSelectedTargetSnapshot = useCallback(() => {
813
+ const element = selectedElementRef.current;
814
+ if (!element) {
815
+ return;
816
+ }
817
+ const snapshot = toFeedbackHoverSnapshot(element);
818
+ if (!snapshot) {
819
+ return;
820
+ }
821
+ setSelectedTarget(snapshot);
822
+ setHoveredTarget(snapshot);
823
+ }, []);
824
+ const closePickProbePanelOnly = useCallback(() => {
825
+ setPickProbeOpen(false);
826
+ setPickProbeBaseline(null);
827
+ setPickProbeValues(null);
828
+ setPickProbeSupportsTextFields(false);
829
+ setPickProbeLayoutMode(null);
830
+ setPickProbeCompareModeState("after");
831
+ pickProbeElementKeyRef.current = null;
832
+ }, []);
833
+ const resetPickProbeState = useCallback(() => {
834
+ closePickProbePanelOnly();
835
+ }, [closePickProbePanelOnly]);
836
+ const revertSavedProbeEdit = useCallback((elementKey) => {
837
+ setSavedProbeEdits((current) => {
838
+ const entry = current[elementKey];
839
+ if (!entry) {
840
+ return current;
841
+ }
842
+ const element = findElementByProbeKey(elementKey);
843
+ if (element) {
844
+ restoreProbeElementOriginal(element, entry);
845
+ }
846
+ const next = { ...current };
847
+ delete next[elementKey];
848
+ return next;
849
+ });
850
+ if (pickProbeElementKeyRef.current === elementKey) {
851
+ pickProbeRestoreRef.current = null;
852
+ pickProbeOriginalSnapshotRef.current = null;
853
+ closePickProbePanelOnly();
854
+ }
855
+ }, [closePickProbePanelOnly]);
856
+ const pushProbeSessionAction = useCallback((action) => {
857
+ setProbeSessionHistoryState((current) => {
858
+ const actions = current.actions.slice(0, current.index + 1);
859
+ actions.push(action);
860
+ return {
861
+ actions,
862
+ index: actions.length - 1,
863
+ };
864
+ });
865
+ }, []);
866
+ const undoProbeSessionAction = useCallback(() => {
867
+ setProbeSessionHistoryState((current) => {
868
+ if (current.index < 0) {
869
+ return current;
870
+ }
871
+ const action = current.actions[current.index];
872
+ setSavedProbeEdits((edits) => {
873
+ const next = applyProbeSessionActionBackward(action, {
874
+ edits,
875
+ deletions: savedProbeDeletionsRef.current,
876
+ });
877
+ setSavedProbeDeletions(next.deletions);
878
+ return next.edits;
879
+ });
880
+ return {
881
+ actions: current.actions,
882
+ index: current.index - 1,
883
+ };
884
+ });
885
+ }, []);
886
+ const redoProbeSessionAction = useCallback(() => {
887
+ setProbeSessionHistoryState((current) => {
888
+ if (current.index >= current.actions.length - 1) {
889
+ return current;
890
+ }
891
+ const action = current.actions[current.index + 1];
892
+ setSavedProbeEdits((edits) => {
893
+ const next = applyProbeSessionActionForward(action, {
894
+ edits,
895
+ deletions: savedProbeDeletionsRef.current,
896
+ });
897
+ setSavedProbeDeletions(next.deletions);
898
+ return next.edits;
899
+ });
900
+ return {
901
+ actions: current.actions,
902
+ index: current.index + 1,
903
+ };
904
+ });
905
+ }, []);
906
+ const revertAllSavedProbeEdits = useCallback(() => {
907
+ setProbeSessionHistoryState({ actions: [], index: -1 });
908
+ setSavedProbeDeletions((deletions) => {
909
+ for (const entry of [...deletions].reverse()) {
910
+ restoreSavedProbeDeletion(entry);
911
+ }
912
+ return [];
913
+ });
914
+ setSavedProbeEdits((current) => {
915
+ for (const entry of Object.values(current)) {
916
+ const element = findElementByProbeKey(entry.elementKey);
917
+ if (element) {
918
+ restoreProbeElementOriginal(element, entry);
919
+ }
920
+ }
921
+ return {};
922
+ });
923
+ setSavedProbeCompareModeState("after");
924
+ }, []);
925
+ const canUndoProbeSession = probeSessionHistoryState.index >= 0;
926
+ const canRedoProbeSession = probeSessionHistoryState.index < probeSessionHistoryState.actions.length - 1;
927
+ const hasProbeSessionChanges = useMemo(() => probeSessionHistoryState.index >= 0, [probeSessionHistoryState.index]);
928
+ const setSavedProbeCompareMode = useCallback((compareMode) => {
929
+ setSavedProbeCompareModeState(compareMode);
930
+ setSavedProbeEdits((current) => {
931
+ applySavedProbeEditsCompareMode(current, compareMode);
932
+ return current;
933
+ });
934
+ }, []);
935
+ const pickProbeChanges = useMemo(() => {
936
+ if (!pickProbeBaseline || !pickProbeValues) {
937
+ return [];
938
+ }
939
+ return getProposedChanges(pickProbeBaseline, pickProbeValues, pickProbeSupportsTextFields, pickProbeLayoutMode);
940
+ }, [pickProbeBaseline, pickProbeLayoutMode, pickProbeSupportsTextFields, pickProbeValues]);
941
+ const pickProbeHasEdits = pickProbeChanges.length > 0;
942
+ const persistPickProbeEdits = useCallback((options) => {
943
+ const element = selectedElementRef.current;
944
+ const values = options?.values ?? pickProbeValues;
945
+ if (!element || !pickProbeBaseline || !values) {
946
+ if (options?.closePanel) {
947
+ closePickProbePanelOnly();
948
+ }
949
+ return;
950
+ }
951
+ const elementKey = getPickProbeElementKey(element);
952
+ const changes = getProposedChanges(pickProbeBaseline, values, pickProbeSupportsTextFields, pickProbeLayoutMode);
953
+ const existing = savedProbeEdits[elementKey];
954
+ if (changes.length === 0) {
955
+ if (existing) {
956
+ pushProbeSessionAction({
957
+ kind: "style-revert",
958
+ elementKey,
959
+ revertedEntry: existing,
960
+ });
961
+ setSavedProbeEdits((current) => {
962
+ const next = { ...current };
963
+ delete next[elementKey];
964
+ return next;
965
+ });
966
+ }
967
+ if (options?.closePanel) {
968
+ pickProbeRestoreRef.current = null;
969
+ pickProbeOriginalSnapshotRef.current = null;
970
+ closePickProbePanelOnly();
971
+ }
972
+ refreshSelectedTargetSnapshot();
973
+ return;
974
+ }
975
+ const previousEntry = existing ?? null;
976
+ const originalSnapshot = pickProbeOriginalSnapshotRef.current;
977
+ applyPickProbeCompareMode(element, savedProbeCompareMode, pickProbeBaseline, values);
978
+ const nextEntry = createSavedProbeEntry(elementKey, pickProbeBaseline, values, originalSnapshot?.style ?? pickProbeRestoreRef.current?.style ?? null, originalSnapshot?.textContent ?? pickProbeRestoreRef.current?.textContent ?? pickProbeBaseline.textContent, existing, originalSnapshot?.innerHTML ?? null, originalSnapshot?.inputValue ?? null);
979
+ const appliedChanged = !existing || getProposedChanges(existing.applied, values, pickProbeSupportsTextFields, pickProbeLayoutMode).length > 0;
980
+ setSavedProbeEdits((current) => ({
981
+ ...current,
982
+ [elementKey]: nextEntry,
983
+ }));
984
+ if (appliedChanged) {
985
+ pushProbeSessionAction({
986
+ kind: "style-apply",
987
+ elementKey,
988
+ previousEntry,
989
+ nextEntry,
990
+ });
991
+ }
992
+ if (options?.closePanel) {
993
+ pickProbeRestoreRef.current = null;
994
+ pickProbeOriginalSnapshotRef.current = null;
995
+ closePickProbePanelOnly();
996
+ }
997
+ refreshSelectedTargetSnapshot();
998
+ }, [
999
+ closePickProbePanelOnly,
1000
+ pickProbeBaseline,
1001
+ pickProbeLayoutMode,
1002
+ pickProbeSupportsTextFields,
1003
+ pickProbeValues,
1004
+ pushProbeSessionAction,
1005
+ refreshSelectedTargetSnapshot,
1006
+ savedProbeCompareMode,
1007
+ savedProbeEdits,
1008
+ ]);
1009
+ const commitPickProbeEdits = useCallback(() => {
1010
+ persistPickProbeEdits({ closePanel: true });
1011
+ }, [persistPickProbeEdits]);
1012
+ const openPickProbe = useCallback(() => {
1013
+ const element = selectedElementRef.current;
1014
+ if (!element) {
1015
+ return;
1016
+ }
1017
+ if (pickProbeOpen) {
1018
+ closePickProbePanelOnly();
1019
+ }
1020
+ const elementKey = getPickProbeElementKey(element);
1021
+ const saved = savedProbeEdits[elementKey];
1022
+ pickProbeElementKeyRef.current = elementKey;
1023
+ const supportsTextFields = shouldInspectFontStyle(element);
1024
+ const layoutMode = getPickProbeLayoutMode(element);
1025
+ setPickProbeSupportsTextFields(supportsTextFields);
1026
+ setPickProbeLayoutMode(layoutMode);
1027
+ const freshBaseline = capturePickProbeValues(element);
1028
+ const sessionSnapshot = captureProbeOriginalSnapshot(element);
1029
+ pickProbeOriginalSnapshotRef.current = sessionSnapshot;
1030
+ pickProbeRestoreRef.current = {
1031
+ style: sessionSnapshot.style,
1032
+ textContent: sessionSnapshot.textContent,
1033
+ inputValue: sessionSnapshot.inputValue,
1034
+ };
1035
+ if (saved) {
1036
+ applyPickProbeCompareMode(element, savedProbeCompareMode, saved.baseline, saved.applied);
1037
+ setPickProbeBaseline({ ...freshBaseline, ...saved.baseline });
1038
+ setPickProbeValues({ ...freshBaseline, ...saved.applied });
1039
+ }
1040
+ else {
1041
+ const baseline = capturePickProbeValues(element);
1042
+ setPickProbeBaseline(baseline);
1043
+ setPickProbeValues(baseline);
1044
+ }
1045
+ setPickProbeCompareModeState("after");
1046
+ setPickProbeOpen(true);
1047
+ refreshSelectedTargetSnapshot();
1048
+ }, [closePickProbePanelOnly, pickProbeOpen, refreshSelectedTargetSnapshot, savedProbeCompareMode, savedProbeEdits]);
1049
+ const closePickProbe = useCallback(() => {
1050
+ resetPickProbeState();
1051
+ }, [resetPickProbeState]);
1052
+ const closePickTargetContextMenu = useCallback(() => {
1053
+ setPickTargetContextMenu(null);
1054
+ setContextMenuElementKey(null);
1055
+ }, []);
1056
+ const handlePickTargetRevert = useCallback(() => {
1057
+ const elementKey = contextMenuElementKey;
1058
+ closePickTargetContextMenu();
1059
+ if (!elementKey) {
1060
+ return;
1061
+ }
1062
+ const entry = savedProbeEdits[elementKey];
1063
+ if (!entry) {
1064
+ return;
1065
+ }
1066
+ revertSavedProbeEdit(elementKey);
1067
+ pushProbeSessionAction({
1068
+ kind: "style-revert",
1069
+ elementKey,
1070
+ revertedEntry: entry,
1071
+ });
1072
+ }, [closePickTargetContextMenu, contextMenuElementKey, pushProbeSessionAction, revertSavedProbeEdit, savedProbeEdits]);
1073
+ const handlePickTargetEdit = useCallback(() => {
1074
+ const element = contextMenuElementRef.current;
1075
+ closePickTargetContextMenu();
1076
+ if (!element) {
1077
+ return;
1078
+ }
1079
+ if (pickProbeOpen) {
1080
+ resetPickProbeState();
1081
+ }
1082
+ selectedElementRef.current = element;
1083
+ hoveredElementRef.current = element;
1084
+ const snapshot = toFeedbackHoverSnapshot(element);
1085
+ if (snapshot) {
1086
+ setSelectedTarget(snapshot);
1087
+ }
1088
+ openPickProbe();
1089
+ }, [closePickTargetContextMenu, openPickProbe, pickProbeOpen, resetPickProbeState]);
1090
+ const handlePickTargetDelete = useCallback(() => {
1091
+ const element = contextMenuElementRef.current;
1092
+ const elementKey = element ? getPickProbeElementKey(element) : null;
1093
+ closePickTargetContextMenu();
1094
+ resetPickProbeState();
1095
+ if (!element) {
1096
+ return;
1097
+ }
1098
+ const shouldClearDraft = draftElementRef.current === element;
1099
+ const rect = element.getBoundingClientRect();
1100
+ const deletion = elementKey ? captureSavedProbeDeletion(element, elementKey) : null;
1101
+ const previousStyleEntry = elementKey ? (savedProbeEdits[elementKey] ?? null) : null;
1102
+ contextMenuElementRef.current = null;
1103
+ if (selectedElementRef.current === element) {
1104
+ selectedElementRef.current = null;
1105
+ hoveredElementRef.current = null;
1106
+ setSelectedTarget(null);
1107
+ setHoveredTarget(null);
1108
+ setHoverPointer(null);
1109
+ }
1110
+ void playPickTargetDeleteAnimation(rect).then(() => {
1111
+ if (!element.isConnected) {
1112
+ return;
1113
+ }
1114
+ element.remove();
1115
+ if (deletion) {
1116
+ setSavedProbeDeletions((current) => [...current, deletion]);
1117
+ if (previousStyleEntry) {
1118
+ setSavedProbeEdits((current) => {
1119
+ const next = { ...current };
1120
+ delete next[deletion.elementKey];
1121
+ return next;
1122
+ });
1123
+ }
1124
+ pushProbeSessionAction({
1125
+ kind: "delete",
1126
+ deletion,
1127
+ previousStyleEntry,
1128
+ });
1129
+ }
1130
+ if (shouldClearDraft) {
1131
+ draftElementRef.current = null;
1132
+ setDraft(null);
1133
+ }
1134
+ });
1135
+ }, [closePickTargetContextMenu, pushProbeSessionAction, resetPickProbeState, savedProbeEdits]);
1136
+ useEffect(() => {
1137
+ if (mode !== "report") {
1138
+ resetPickProbeState();
1139
+ closePickTargetContextMenu();
1140
+ }
1141
+ }, [closePickTargetContextMenu, mode, resetPickProbeState]);
1142
+ const setPickProbeCompareMode = useCallback((compareMode) => {
1143
+ const element = selectedElementRef.current;
1144
+ if (!element || !pickProbeBaseline || !pickProbeValues) {
1145
+ return;
1146
+ }
1147
+ applyPickProbeCompareMode(element, compareMode, pickProbeBaseline, pickProbeValues);
1148
+ setPickProbeCompareModeState(compareMode);
1149
+ refreshSelectedTargetSnapshot();
1150
+ if (compareMode === "after") {
1151
+ persistPickProbeEdits();
1152
+ }
1153
+ }, [persistPickProbeEdits, pickProbeBaseline, pickProbeValues, refreshSelectedTargetSnapshot]);
1154
+ const updatePickProbeValue = useCallback((key, value) => {
1155
+ const element = selectedElementRef.current;
1156
+ if (!element || !pickProbeBaseline || !pickProbeValues) {
1157
+ return;
1158
+ }
1159
+ const nextValues = {
1160
+ ...pickProbeValues,
1161
+ [key]: value,
1162
+ };
1163
+ setPickProbeValues(nextValues);
1164
+ if (pickProbeCompareMode === "after") {
1165
+ applyPickProbeValueDiff(element, pickProbeBaseline, nextValues, "after");
1166
+ refreshSelectedTargetSnapshot();
1167
+ persistPickProbeEdits({ values: nextValues });
1168
+ }
1169
+ }, [pickProbeBaseline, pickProbeCompareMode, pickProbeValues, persistPickProbeEdits, refreshSelectedTargetSnapshot]);
1170
+ const resetPickProbeValues = useCallback(() => {
1171
+ const element = selectedElementRef.current;
1172
+ if (!element || !pickProbeBaseline || !pickProbeValues) {
1173
+ return;
1174
+ }
1175
+ setPickProbeValues(pickProbeBaseline);
1176
+ const snapshot = pickProbeOriginalSnapshotRef.current;
1177
+ if (snapshot) {
1178
+ restoreProbeElementFromSnapshot(element, snapshot);
1179
+ }
1180
+ else if (pickProbeHasEdits) {
1181
+ applyPickProbeCompareMode(element, pickProbeCompareMode, pickProbeBaseline, pickProbeBaseline);
1182
+ }
1183
+ persistPickProbeEdits({ values: pickProbeBaseline });
1184
+ refreshSelectedTargetSnapshot();
1185
+ }, [pickProbeBaseline, pickProbeCompareMode, pickProbeHasEdits, pickProbeValues, persistPickProbeEdits, refreshSelectedTargetSnapshot]);
357
1186
  const stopEditing = () => {
358
1187
  setEditingReportId(null);
359
1188
  setEditableDraft(null);
360
1189
  };
1190
+ const cancelCaseEdit = useCallback(() => {
1191
+ setCaseEditReportId(null);
1192
+ setCaseEditDraft(null);
1193
+ }, []);
1194
+ useEffect(() => {
1195
+ if (!activeReplyReport) {
1196
+ return;
1197
+ }
1198
+ setFocusedCaseId((current) => {
1199
+ if (current && isValidFocusedCase(activeReplyReport, current)) {
1200
+ return current;
1201
+ }
1202
+ return resolveDefaultFocusedCaseId(activeReplyReport);
1203
+ });
1204
+ }, [activeReplyReport]);
1205
+ const clearFocusedCase = useCallback(() => {
1206
+ setFocusedCaseId(null);
1207
+ }, []);
1208
+ const selectCase = useCallback((caseId) => {
1209
+ setFocusedCaseId(caseId);
1210
+ setPendingComposer(null);
1211
+ setReplyDraft("");
1212
+ setReplySubmitAsQuestion(false);
1213
+ setErrorMessage("");
1214
+ }, []);
1215
+ const ensureFocusedCase = useCallback((report) => {
1216
+ if (isValidFocusedCase(report, focusedCaseId)) {
1217
+ return true;
1218
+ }
1219
+ setErrorMessage(messages.errors.selectCaseFirst);
1220
+ return false;
1221
+ }, [focusedCaseId, messages.errors.selectCaseFirst]);
1222
+ const ensureCanActOnFocusedCase = useCallback((report) => {
1223
+ if (!ensureFocusedCase(report) || !focusedCaseId) {
1224
+ return false;
1225
+ }
1226
+ const actorName = sessionActor?.name?.trim() ?? "";
1227
+ if (actorName && canActOnCase(report, focusedCaseId, actorName)) {
1228
+ return true;
1229
+ }
1230
+ setErrorMessage(messages.errors.caseAssigneeOnly);
1231
+ return false;
1232
+ }, [ensureFocusedCase, focusedCaseId, messages.errors.caseAssigneeOnly, sessionActor?.name]);
1233
+ const beginCaseEdit = useCallback((report) => {
1234
+ if (!canEditReportCases(report)) {
1235
+ setErrorMessage(messages.errors.archivedReadOnly);
1236
+ return;
1237
+ }
1238
+ setCaseEditReportId(report.id);
1239
+ setCaseEditDraft(report.cases.map((item) => ({ ...item })));
1240
+ setErrorMessage("");
1241
+ }, [messages.errors.archivedReadOnly]);
1242
+ const updateCaseEditDraftCase = useCallback((caseId, text) => {
1243
+ setCaseEditDraft((current) => {
1244
+ if (!current) {
1245
+ return current;
1246
+ }
1247
+ return current.map((item) => (item.id === caseId ? { ...item, text } : item));
1248
+ });
1249
+ }, []);
1250
+ const addCaseEditDraftCase = useCallback(() => {
1251
+ setCaseEditDraft((current) => (current ? [...current, createReportCase("")] : current));
1252
+ }, []);
1253
+ const removeCaseEditDraftCase = useCallback((caseId) => {
1254
+ setCaseEditDraft((current) => {
1255
+ if (!current || current.length <= 1) {
1256
+ return current;
1257
+ }
1258
+ return current.filter((item) => item.id !== caseId);
1259
+ });
1260
+ }, []);
1261
+ const handleCaseEditSave = async () => {
1262
+ if (!caseEditReportId || !caseEditDraft) {
1263
+ return;
1264
+ }
1265
+ const report = reports.find((item) => item.id === caseEditReportId) ?? activeReplyReport;
1266
+ if (!report) {
1267
+ return;
1268
+ }
1269
+ if (!canEditReportCases(report)) {
1270
+ setErrorMessage(messages.errors.archivedNotEditable);
1271
+ return;
1272
+ }
1273
+ const nextError = getFieldError(caseEditDraft, report.field_values, fields, messages.errors);
1274
+ if (nextError) {
1275
+ setErrorMessage(nextError);
1276
+ return;
1277
+ }
1278
+ try {
1279
+ const cases = caseEditDraft.map((item) => ({
1280
+ ...item,
1281
+ text: item.text.trim(),
1282
+ updated_at: new Date().toISOString(),
1283
+ }));
1284
+ const updatedFeedback = await updateFeedback(report.id, await signUpdatePayload({ cases }));
1285
+ await notifyFeedbackUpdate(eventCallbacks, updatedFeedback);
1286
+ cancelCaseEdit();
1287
+ setErrorMessage("");
1288
+ }
1289
+ catch (nextError) {
1290
+ setErrorMessage(nextError instanceof Error ? nextError.message : messages.errors.updateFeedbackFailed);
1291
+ }
1292
+ };
1293
+ const isCaseEditing = Boolean(caseEditReportId && caseEditDraft);
1294
+ const caseEditCases = caseEditReportId === activeReplyReport?.id ? caseEditDraft : null;
1295
+ useEffect(() => {
1296
+ if (caseEditReportId && activeReplyReportId && caseEditReportId !== activeReplyReportId) {
1297
+ cancelCaseEdit();
1298
+ }
1299
+ }, [activeReplyReportId, cancelCaseEdit, caseEditReportId]);
361
1300
  const selectReport = (reportId) => {
362
1301
  setSelectedReportId(reportId);
363
1302
  if (editingReportId && editingReportId !== reportId) {
@@ -367,8 +1306,11 @@ export function useReportState({ projectId, environment, appVersion, appearance,
367
1306
  const closeReplyComposer = () => {
368
1307
  setActiveReplyReportId(null);
369
1308
  setReplyDraft("");
1309
+ setReplySubmitAsQuestion(false);
370
1310
  setPendingComposer(null);
371
1311
  setShowConfirmAuthorSelect(false);
1312
+ cancelCaseEdit();
1313
+ clearFocusedCase();
372
1314
  };
373
1315
  const showFeedbackTooltip = useCallback(async (report) => {
374
1316
  await prepareFeedbackLocation(report);
@@ -434,46 +1376,99 @@ export function useReportState({ projectId, environment, appVersion, appearance,
434
1376
  selectReport(report.id);
435
1377
  setActiveReplyReportId(report.id);
436
1378
  setReplyDraft("");
1379
+ setReplySubmitAsQuestion(false);
437
1380
  setPendingComposer(null);
438
- setReplyAuthorName(resolveDefaultAuthorName(activeIdentify, authorizedAuthors));
1381
+ setReplyAuthorName(sessionActor?.name ?? resolveDefaultAuthorName(activeIdentify, authorizedAuthors, selfProfile?.name));
439
1382
  setConfirmAuthorName(resolveOriginalFeedbackAuthorName(report));
440
1383
  setShowConfirmAuthorSelect(false);
441
- clearHoverLeaveTimeout();
1384
+ setFocusedCaseId(resolveDefaultFocusedCaseId(report));
442
1385
  };
443
1386
  const activateFeedbackMarker = useCallback(async (report) => {
444
- await prepareFeedbackLocation(report);
445
- openReplyComposer(report);
446
- }, [openReplyComposer, prepareFeedbackLocation]);
1387
+ const enrichedReport = await loadRepliesIfNeeded(report);
1388
+ await prepareFeedbackLocation(enrichedReport);
1389
+ openReplyComposer(enrichedReport);
1390
+ }, [loadRepliesIfNeeded, openReplyComposer, prepareFeedbackLocation]);
1391
+ useEffect(() => {
1392
+ const feedbackId = pendingDeepLinkFeedbackIdRef.current;
1393
+ if (!feedbackId || deepLinkHandledRef.current || isReportsLoading || isFetching) {
1394
+ return;
1395
+ }
1396
+ const report = reports.find((item) => item.id === feedbackId);
1397
+ if (!report) {
1398
+ pendingDeepLinkFeedbackIdRef.current = null;
1399
+ clearFeedbackDeepLinkFromUrl();
1400
+ return;
1401
+ }
1402
+ deepLinkHandledRef.current = true;
1403
+ pendingDeepLinkFeedbackIdRef.current = null;
1404
+ void activateFeedbackMarker(report).finally(() => {
1405
+ clearFeedbackDeepLinkFromUrl();
1406
+ });
1407
+ }, [activateFeedbackMarker, isFetching, isReportsLoading, reports]);
447
1408
  const toggleConfirmAuthorSelect = () => {
448
1409
  setShowConfirmAuthorSelect((current) => !current);
449
1410
  };
450
- const startDenyReview = () => {
451
- if (!activeReplyReport) {
1411
+ const startDenyReview = (targetReplyId) => {
1412
+ if (!activeReplyReport || !focusedCaseId) {
452
1413
  return;
453
1414
  }
454
- const latest = activeReplyReport.replies[activeReplyReport.replies.length - 1];
455
- if (!latest) {
1415
+ if (!ensureCanActOnFocusedCase(activeReplyReport)) {
1416
+ return;
1417
+ }
1418
+ const latestRoot = getLatestBranchRootForCase(activeReplyReport, focusedCaseId);
1419
+ if (!latestRoot) {
1420
+ setPendingComposer({
1421
+ type: "deny",
1422
+ targetReplyId: targetReplyId ?? ISSUE_ROOT_PARENT_ID,
1423
+ });
1424
+ setReplyDraft("");
1425
+ setReplySubmitAsQuestion(false);
456
1426
  return;
457
1427
  }
458
1428
  setPendingComposer({
459
- type: latest.status === "found_error" ? "recheck" : "deny",
460
- targetReplyId: latest.id,
1429
+ type: latestRoot.status === "found_error" ? "recheck" : "deny",
1430
+ targetReplyId: latestRoot.id,
461
1431
  });
462
1432
  setReplyDraft("");
1433
+ setReplySubmitAsQuestion(false);
463
1434
  };
464
1435
  const startCheckoutReview = (replyId) => {
1436
+ if (!activeReplyReport || !focusedCaseId || !ensureCanActOnFocusedCase(activeReplyReport)) {
1437
+ return;
1438
+ }
465
1439
  setPendingComposer({ type: "checkout", targetReplyId: replyId });
466
1440
  setReplyDraft("");
1441
+ setReplySubmitAsQuestion(false);
1442
+ };
1443
+ const startAskQuestion = () => {
1444
+ if (!activeReplyReport || !focusedCaseId || !ensureFocusedCase(activeReplyReport)) {
1445
+ return;
1446
+ }
1447
+ const latestRoot = getLatestBranchRootForCase(activeReplyReport, focusedCaseId);
1448
+ setErrorMessage("");
1449
+ setReplyDraft("");
1450
+ if (!latestRoot) {
1451
+ setPendingComposer({
1452
+ type: "question",
1453
+ targetReplyId: ISSUE_ROOT_PARENT_ID,
1454
+ });
1455
+ setReplySubmitAsQuestion(true);
1456
+ return;
1457
+ }
1458
+ if (latestRoot.status === "suggested" || latestRoot.status === "found_error" || latestRoot.status === "recheck_requested") {
1459
+ setPendingComposer({
1460
+ type: "question",
1461
+ targetReplyId: latestRoot.id,
1462
+ });
1463
+ setReplySubmitAsQuestion(true);
1464
+ }
467
1465
  };
468
1466
  const cancelPendingComposer = () => {
469
1467
  setPendingComposer(null);
470
1468
  setReplyDraft("");
1469
+ setReplySubmitAsQuestion(false);
471
1470
  };
472
1471
  const toggleReportMode = () => {
473
- if (personalKeyRequired) {
474
- setErrorMessage(messages.errors.personalKeyRequired);
475
- return;
476
- }
477
1472
  setShowTargetPreview(false);
478
1473
  setMode((current) => (current === "report" ? "idle" : "report"));
479
1474
  };
@@ -503,7 +1498,7 @@ export function useReportState({ projectId, environment, appVersion, appearance,
503
1498
  closeReplyComposer();
504
1499
  setMode((current) => (current === "view" ? "idle" : "view"));
505
1500
  stopEditing();
506
- setSelectedReportId(filteredReports[0]?.id ?? null);
1501
+ setSelectedReportId(null);
507
1502
  };
508
1503
  const toggleTargetPreview = () => {
509
1504
  setShowTargetPreview((current) => {
@@ -514,37 +1509,74 @@ export function useReportState({ projectId, environment, appVersion, appearance,
514
1509
  return next;
515
1510
  });
516
1511
  };
517
- // overlay (target pick, create draft, edit)
518
1512
  const handleOverlayMove = (event) => {
519
1513
  if (mode !== "report" || draft) {
520
1514
  return;
521
1515
  }
522
- const targetElement = findTargetByPoint(overlayRef.current, event.clientX, event.clientY);
1516
+ const targetElement = findPickTargetByPoint(overlayRef.current, event.clientX, event.clientY);
523
1517
  hoveredElementRef.current = targetElement;
524
1518
  if (!targetElement) {
525
1519
  scheduleOverlayHoverLeave();
526
1520
  return;
527
1521
  }
528
1522
  clearOverlayHoverLeaveTimeout();
529
- const snapshot = toSnapshot(targetElement);
1523
+ setHoverPointer({ clientX: event.clientX, clientY: event.clientY });
1524
+ const snapshot = toFeedbackHoverSnapshot(targetElement);
530
1525
  setHoveredTarget((previous) => (isSameHoverTarget(previous, snapshot) ? previous : snapshot));
531
1526
  };
1527
+ const handleOverlayContextMenu = (event) => {
1528
+ if (mode !== "report") {
1529
+ return;
1530
+ }
1531
+ event.preventDefault();
1532
+ const targetElement = findPickTargetByPoint(overlayRef.current, event.clientX, event.clientY);
1533
+ if (!targetElement) {
1534
+ closePickTargetContextMenu();
1535
+ return;
1536
+ }
1537
+ const snapshot = toFeedbackHoverSnapshot(targetElement);
1538
+ if (!snapshot) {
1539
+ closePickTargetContextMenu();
1540
+ return;
1541
+ }
1542
+ const elementKey = getPickProbeElementKey(targetElement);
1543
+ contextMenuElementRef.current = targetElement;
1544
+ setContextMenuElementKey(elementKey);
1545
+ if (!draft) {
1546
+ selectedElementRef.current = targetElement;
1547
+ hoveredElementRef.current = targetElement;
1548
+ setSelectedTarget(snapshot);
1549
+ }
1550
+ setPickTargetContextMenu({
1551
+ clientX: event.clientX,
1552
+ clientY: event.clientY,
1553
+ });
1554
+ };
532
1555
  const handleOverlayClick = (event) => {
533
1556
  if (mode !== "report") {
534
1557
  return;
535
1558
  }
536
- const targetElement = findTargetByPoint(overlayRef.current, event.clientX, event.clientY);
537
- const snapshot = toSnapshot(targetElement);
538
- if (!targetElement || !snapshot) {
539
- setErrorMessage(messages.errors.clickSelectableArea);
1559
+ closePickTargetContextMenu();
1560
+ resetPickProbeState();
1561
+ const targetElement = findPickTargetByPoint(overlayRef.current, event.clientX, event.clientY);
1562
+ if (!targetElement) {
1563
+ setErrorMessage(messages.errors.clickPickTarget);
1564
+ return;
1565
+ }
1566
+ const anchorSnapshot = resolveFeedbackDocumentAnchor(targetElement);
1567
+ const snapshot = toFeedbackHoverSnapshot(targetElement);
1568
+ const isTagged = snapshot?.isTagged ?? false;
1569
+ if (!snapshot) {
1570
+ setErrorMessage(messages.errors.clickPickTarget);
540
1571
  return;
541
1572
  }
542
1573
  hoveredElementRef.current = targetElement;
543
1574
  selectedElementRef.current = targetElement;
544
- setHoveredTarget(snapshot);
1575
+ draftElementRef.current = targetElement;
1576
+ setHoverPointer(null);
1577
+ setHoveredTarget(null);
545
1578
  setSelectedTarget(snapshot);
546
1579
  setErrorMessage("");
547
- const anchorSnapshot = resolveFeedbackDocumentAnchor(targetElement);
548
1580
  setDraft({
549
1581
  clientX: event.clientX,
550
1582
  clientY: event.clientY,
@@ -554,26 +1586,101 @@ export function useReportState({ projectId, environment, appVersion, appearance,
554
1586
  elementYRatio: clampRatio((event.clientY - snapshot.rect.top) / Math.max(snapshot.rect.height, 1)),
555
1587
  anchorReportId: anchorSnapshot?.id ?? null,
556
1588
  anchorReportType: anchorSnapshot?.type ?? null,
557
- anchorXRatio: anchorSnapshot
558
- ? clampRatio((event.clientX - anchorSnapshot.rect.left) / Math.max(anchorSnapshot.rect.width, 1))
559
- : null,
560
- anchorYRatio: anchorSnapshot
561
- ? clampRatio((event.clientY - anchorSnapshot.rect.top) / Math.max(anchorSnapshot.rect.height, 1))
562
- : null,
1589
+ anchorXRatio: anchorSnapshot ? clampRatio((event.clientX - anchorSnapshot.rect.left) / Math.max(anchorSnapshot.rect.width, 1)) : null,
1590
+ anchorYRatio: anchorSnapshot ? clampRatio((event.clientY - anchorSnapshot.rect.top) / Math.max(anchorSnapshot.rect.height, 1)) : null,
563
1591
  scrollY: window.scrollY,
564
1592
  documentY: Math.round(window.scrollY + event.clientY),
565
1593
  reportId: snapshot.id,
566
1594
  reportType: snapshot.type,
567
- message: "",
1595
+ targetSelector: isTagged ? null : (snapshot.targetSelector ?? null),
1596
+ suggestedReportId: isTagged ? null : (snapshot.suggestedReportId ?? snapshot.id),
1597
+ cases: [createReportCase("")],
1598
+ category: null,
568
1599
  fieldValues: createInitialFieldValues(fields),
569
1600
  });
570
1601
  };
571
1602
  const cancelDraft = () => {
1603
+ resetPickProbeState();
1604
+ draftElementRef.current = null;
1605
+ contextMenuElementRef.current = null;
572
1606
  setDraft(null);
573
1607
  setSelectedTarget(null);
1608
+ setHoverPointer(null);
1609
+ };
1610
+ const updateDraftCase = (caseId, text) => {
1611
+ setDraft((current) => {
1612
+ if (!current) {
1613
+ return current;
1614
+ }
1615
+ return {
1616
+ ...current,
1617
+ cases: current.cases.map((item) => (item.id === caseId ? { ...item, text } : item)),
1618
+ };
1619
+ });
1620
+ setErrorMessage("");
1621
+ };
1622
+ const appendSavedProbeSummaryAsNewDraftCase = useCallback(() => {
1623
+ if (!draft || Object.keys(savedProbeEdits).length === 0) {
1624
+ return;
1625
+ }
1626
+ const summary = formatSavedProbeEditsSummary(savedProbeEdits, messages);
1627
+ if (!summary) {
1628
+ return;
1629
+ }
1630
+ setDraft((current) => {
1631
+ if (!current) {
1632
+ return current;
1633
+ }
1634
+ const cases = current.cases.map((item) => ({ ...item }));
1635
+ const emptyIndex = cases.findIndex((item) => !item.text.trim());
1636
+ const targetIndex = emptyIndex >= 0 ? emptyIndex : 0;
1637
+ const target = cases[targetIndex];
1638
+ if (!target) {
1639
+ return {
1640
+ ...current,
1641
+ cases: [createReportCase(summary)],
1642
+ };
1643
+ }
1644
+ cases[targetIndex] = {
1645
+ ...target,
1646
+ text: target.text.trim() ? `${target.text.trim()}\n\n${summary}` : summary,
1647
+ updated_at: new Date().toISOString(),
1648
+ };
1649
+ return {
1650
+ ...current,
1651
+ cases,
1652
+ };
1653
+ });
1654
+ }, [draft, messages, savedProbeEdits]);
1655
+ const updateDraftCategory = (category) => {
1656
+ setDraft((current) => current
1657
+ ? {
1658
+ ...current,
1659
+ category,
1660
+ }
1661
+ : current);
1662
+ if (category) {
1663
+ setErrorMessage("");
1664
+ }
1665
+ };
1666
+ const addDraftCase = () => {
1667
+ setDraft((current) => current
1668
+ ? {
1669
+ ...current,
1670
+ cases: [...current.cases, createReportCase("")],
1671
+ }
1672
+ : current);
574
1673
  };
575
- const updateDraftMessage = (nextMessage) => {
576
- setDraft((current) => (current ? { ...current, message: nextMessage } : current));
1674
+ const removeDraftCase = (caseId) => {
1675
+ setDraft((current) => {
1676
+ if (!current || current.cases.length <= 1) {
1677
+ return current;
1678
+ }
1679
+ return {
1680
+ ...current,
1681
+ cases: current.cases.filter((item) => item.id !== caseId),
1682
+ };
1683
+ });
577
1684
  };
578
1685
  const updateDraftField = (key, nextValue) => {
579
1686
  setDraft((current) => current
@@ -590,50 +1697,72 @@ export function useReportState({ projectId, environment, appVersion, appearance,
590
1697
  if (!draft) {
591
1698
  return null;
592
1699
  }
593
- if (personalKeyRequired) {
594
- setErrorMessage(messages.errors.personalKeyRequired);
595
- return null;
596
- }
597
- const nextError = getFieldError(draft.message, draft.fieldValues, fields, messages.errors);
1700
+ const nextError = getFieldError(draft.cases, draft.fieldValues, fields, messages.errors);
598
1701
  if (nextError) {
599
1702
  setErrorMessage(nextError);
600
1703
  return null;
601
1704
  }
1705
+ if (!draft.category) {
1706
+ setErrorMessage(messages.errors.categoryRequired);
1707
+ return null;
1708
+ }
1709
+ if (!sessionActor) {
1710
+ setErrorMessage(messages.errors.authorRequired);
1711
+ return null;
1712
+ }
1713
+ const cases = draft.cases.map((item) => ({
1714
+ ...item,
1715
+ text: item.text.trim(),
1716
+ updated_at: new Date().toISOString(),
1717
+ }));
602
1718
  return {
603
1719
  pathname: currentPathname,
604
1720
  report_id: draft.reportId,
605
1721
  report_type: draft.reportType,
606
- message: draft.message.trim(),
1722
+ ...(draft.targetSelector ? { target_selector: draft.targetSelector } : {}),
1723
+ cases,
607
1724
  status: "open",
1725
+ category: draft.category,
608
1726
  field_values: draft.fieldValues,
609
- x_ratio: draft.xRatio,
610
- y_ratio: draft.yRatio,
611
- element_x_ratio: draft.elementXRatio,
612
- element_y_ratio: draft.elementYRatio,
613
- anchor_report_id: draft.anchorReportId,
614
- anchor_report_type: draft.anchorReportType,
615
- anchor_x_ratio: draft.anchorXRatio,
616
- anchor_y_ratio: draft.anchorYRatio,
617
- scroll_y: draft.scrollY,
618
- document_y: draft.documentY,
619
- viewport_width: window.innerWidth,
620
- viewport_height: window.innerHeight,
621
- design_width: window.innerWidth,
622
- design_height: window.innerHeight,
1727
+ position: {
1728
+ target: {
1729
+ x: draft.elementXRatio,
1730
+ y: draft.elementYRatio,
1731
+ },
1732
+ viewport: {
1733
+ x: draft.xRatio,
1734
+ y: draft.yRatio,
1735
+ width: window.innerWidth,
1736
+ height: window.innerHeight,
1737
+ },
1738
+ scrollY: draft.scrollY,
1739
+ anchor: draft.anchorReportId && draft.anchorReportType && draft.anchorXRatio !== null && draft.anchorYRatio !== null
1740
+ ? {
1741
+ reportId: draft.anchorReportId,
1742
+ reportType: draft.anchorReportType,
1743
+ x: draft.anchorXRatio,
1744
+ y: draft.anchorYRatio,
1745
+ }
1746
+ : null,
1747
+ },
623
1748
  ...(environment ? { environment } : {}),
624
1749
  ...(appVersion ? { app_version: appVersion } : {}),
625
- ...(activeIdentify || draftAuthorName.trim()
1750
+ ...(sessionActor
626
1751
  ? {
627
- ...(activeIdentify ? { author_id: activeIdentify.id } : {}),
628
- author_name: draftAuthorName.trim() || activeIdentify?.name,
1752
+ author_id: sessionActor.id,
1753
+ author_name: sessionActor.name,
629
1754
  }
630
1755
  : {}),
631
1756
  };
632
1757
  };
633
1758
  const finalizeDraftCreate = () => {
1759
+ resetPickProbeState();
1760
+ draftElementRef.current = null;
1761
+ contextMenuElementRef.current = null;
634
1762
  setDraft(null);
635
1763
  setSelectedTarget(null);
636
1764
  setHoveredTarget(null);
1765
+ setHoverPointer(null);
637
1766
  setErrorMessage("");
638
1767
  setMode("view");
639
1768
  };
@@ -665,7 +1794,7 @@ export function useReportState({ projectId, environment, appVersion, appearance,
665
1794
  const savedFeedback = await createFeedback(await signCreatePayload(payload));
666
1795
  await notifyFeedbackCreate(eventCallbacks, savedFeedback);
667
1796
  const result = await github.onCreate(savedFeedback);
668
- const updatedFeedback = await updateFeedback(savedFeedback.id, await signUpdatePayload(buildGitHubIssueUpdate(savedFeedback, result, messages.resolution.gitIssuedMessage)));
1797
+ const updatedFeedback = await applyGitHubIssueUpdate(savedFeedback, result);
669
1798
  await notifyGitHubIssueCreated(eventCallbacks, {
670
1799
  feedback: updatedFeedback,
671
1800
  issueUrl: result.issueUrl,
@@ -687,7 +1816,7 @@ export function useReportState({ projectId, environment, appVersion, appearance,
687
1816
  }
688
1817
  setEditingReportId(report.id);
689
1818
  setEditableDraft({
690
- message: report.message,
1819
+ cases: report.cases.map((item) => ({ ...item })),
691
1820
  status: report.status,
692
1821
  fieldValues: createInitialFieldValues(fields, report.field_values),
693
1822
  });
@@ -701,14 +1830,19 @@ export function useReportState({ projectId, environment, appVersion, appearance,
701
1830
  setErrorMessage(messages.errors.archivedNotEditable);
702
1831
  return;
703
1832
  }
704
- const nextError = getFieldError(editableDraft.message, editableDraft.fieldValues, fields, messages.errors);
1833
+ const nextError = getFieldError(editableDraft.cases, editableDraft.fieldValues, fields, messages.errors);
705
1834
  if (nextError) {
706
1835
  setErrorMessage(nextError);
707
1836
  return;
708
1837
  }
709
1838
  try {
1839
+ const cases = editableDraft.cases.map((item) => ({
1840
+ ...item,
1841
+ text: item.text.trim(),
1842
+ updated_at: new Date().toISOString(),
1843
+ }));
710
1844
  const updatedFeedback = await updateFeedback(selectedReport.id, await signUpdatePayload({
711
- message: editableDraft.message.trim(),
1845
+ cases,
712
1846
  status: editableDraft.status,
713
1847
  field_values: editableDraft.fieldValues,
714
1848
  }));
@@ -721,10 +1855,22 @@ export function useReportState({ projectId, environment, appVersion, appearance,
721
1855
  }
722
1856
  };
723
1857
  const appendReply = async (report, reply) => {
724
- const payload = await signUpdatePayload({
725
- replies: [...report.replies, reply],
726
- });
727
- await updateFeedback(report.id, payload);
1858
+ if (usesCreateReply) {
1859
+ await createReply(report.id, await signReplyPayload({
1860
+ message: reply.message,
1861
+ status: reply.status,
1862
+ case_ids: reply.case_ids,
1863
+ parent_reply_id: reply.parent_reply_id,
1864
+ author_type: reply.author_type ?? "manager",
1865
+ author_name: reply.author_name,
1866
+ }));
1867
+ }
1868
+ else {
1869
+ const payload = await signUpdatePayload({
1870
+ replies: [...getReportReplies(report), reply],
1871
+ });
1872
+ await updateFeedback(report.id, payload);
1873
+ }
728
1874
  await notifyFeedbackReply(eventCallbacks, {
729
1875
  feedbackId: report.id,
730
1876
  message: reply.message,
@@ -734,61 +1880,203 @@ export function useReportState({ projectId, environment, appVersion, appearance,
734
1880
  if (!activeReplyReport) {
735
1881
  return;
736
1882
  }
737
- if (personalKeyRequired) {
738
- setErrorMessage(messages.errors.personalKeyRequired);
739
- return;
740
- }
741
1883
  if (!replyDraft.trim()) {
742
1884
  setErrorMessage(messages.errors.replyContentRequired);
743
1885
  return;
744
1886
  }
745
- if (!replyAuthorName.trim()) {
1887
+ if (!ensureFocusedCase(activeReplyReport) || !focusedCaseId) {
1888
+ return;
1889
+ }
1890
+ const actorName = sessionActor?.name?.trim() ?? "";
1891
+ if (!actorName) {
746
1892
  setErrorMessage(messages.errors.authorRequired);
747
1893
  return;
748
1894
  }
749
- const replyMessage = replyDraft.trim();
750
1895
  const pendingType = pendingComposer?.type ?? null;
1896
+ const isCreatorSubmit = pendingType === "deny" || pendingType === "recheck" || pendingType === "question";
1897
+ const isQuestionSubmit = pendingType === "question";
1898
+ if (requiresCaseActorPermissionForComposer(pendingType) && !canActOnCase(activeReplyReport, focusedCaseId, actorName)) {
1899
+ setErrorMessage(messages.errors.caseAssigneeOnly);
1900
+ return;
1901
+ }
1902
+ const replyMessage = replyDraft.trim();
1903
+ const replyStatus = createReplyStatusForSubmit(pendingType, isQuestionSubmit);
1904
+ const parentReplyId = replyStatus === "additional_question" ? resolveParentReplyIdForCaseQuestion(activeReplyReport, focusedCaseId, pendingComposer) : null;
751
1905
  const reply = {
752
1906
  id: createReplyId(),
753
1907
  message: replyMessage,
754
1908
  created_at: new Date().toISOString(),
755
- status: createReplyStatusForSubmit(pendingType),
756
- author_type: "manager",
757
- author_name: replyAuthorName.trim(),
1909
+ status: replyStatus,
1910
+ case_ids: [focusedCaseId],
1911
+ ...(parentReplyId ? { parent_reply_id: parentReplyId } : {}),
1912
+ author_type: isCreatorSubmit ? "user" : "manager",
1913
+ author_name: actorName,
758
1914
  };
759
1915
  try {
1916
+ setIsSubmittingReply(true);
760
1917
  await appendReply(activeReplyReport, reply);
761
1918
  setErrorMessage("");
762
1919
  setReplyDraft("");
763
- setPendingComposer(null);
1920
+ if (replyStatus === "additional_question") {
1921
+ setReplySubmitAsQuestion(true);
1922
+ if (pendingType === "question" && pendingComposer) {
1923
+ setPendingComposer({
1924
+ type: "question",
1925
+ targetReplyId: pendingComposer.targetReplyId,
1926
+ });
1927
+ }
1928
+ else {
1929
+ setPendingComposer(null);
1930
+ }
1931
+ }
1932
+ else {
1933
+ setReplySubmitAsQuestion(false);
1934
+ setPendingComposer(null);
1935
+ }
764
1936
  }
765
1937
  catch (nextError) {
766
1938
  setErrorMessage(nextError instanceof Error ? nextError.message : messages.errors.saveReplyFailed);
767
1939
  }
1940
+ finally {
1941
+ setIsSubmittingReply(false);
1942
+ }
768
1943
  };
769
- const handleConfirmResolution = async () => {
1944
+ const handleClaimAssignee = async () => {
770
1945
  if (!activeReplyReport) {
771
1946
  return;
772
1947
  }
773
- const resolverName = confirmAuthorName.trim() || resolveOriginalFeedbackAuthorName(activeReplyReport);
774
- if (!resolverName) {
775
- setErrorMessage(messages.errors.reviewerRequired);
1948
+ if (!ensureFocusedCase(activeReplyReport) || !focusedCaseId) {
1949
+ return;
1950
+ }
1951
+ const assigneeName = sessionActor?.name?.trim() ?? "";
1952
+ if (!assigneeName) {
1953
+ setErrorMessage(messages.errors.authorRequired);
1954
+ return;
1955
+ }
1956
+ if (!canShowCaseClaimAction(activeReplyReport, focusedCaseId, assigneeName)) {
1957
+ setErrorMessage(messages.errors.caseAssigneeOnly);
776
1958
  return;
777
1959
  }
778
1960
  const reply = {
779
1961
  id: createReplyId(),
780
- message: messages.resolution.issueResolvedMessage,
1962
+ message: messages.thread.assigneeAssigned,
781
1963
  created_at: new Date().toISOString(),
782
- status: "resolved",
783
- author_type: "user",
784
- author_name: resolverName,
1964
+ status: "assignee_assigned",
1965
+ case_ids: [focusedCaseId],
1966
+ author_type: "manager",
1967
+ author_name: assigneeName,
785
1968
  };
786
1969
  try {
787
- const updatedFeedback = await updateFeedback(activeReplyReport.id, await signUpdatePayload({
788
- status: "resolved",
789
- replies: [...activeReplyReport.replies, reply],
1970
+ setIsClaimingAssignee(true);
1971
+ await appendReply(activeReplyReport, reply);
1972
+ const nextCases = claimCaseAssignee(activeReplyReport.cases, focusedCaseId, assigneeName);
1973
+ await updateFeedback(activeReplyReport.id, await signUpdatePayload({
1974
+ cases: nextCases,
790
1975
  }));
791
- await notifyFeedbackUpdate(eventCallbacks, updatedFeedback);
1976
+ setErrorMessage("");
1977
+ }
1978
+ catch (nextError) {
1979
+ setErrorMessage(nextError instanceof Error ? nextError.message : messages.errors.saveReplyFailed);
1980
+ }
1981
+ finally {
1982
+ setIsClaimingAssignee(false);
1983
+ }
1984
+ };
1985
+ const handleTransferAssignee = async () => {
1986
+ if (!activeReplyReport) {
1987
+ return;
1988
+ }
1989
+ if (!ensureFocusedCase(activeReplyReport) || !focusedCaseId) {
1990
+ return;
1991
+ }
1992
+ const assigneeName = sessionActor?.name?.trim() ?? "";
1993
+ if (!assigneeName) {
1994
+ setErrorMessage(messages.errors.authorRequired);
1995
+ return;
1996
+ }
1997
+ const currentAssignee = getCaseAssigneeName(activeReplyReport, focusedCaseId);
1998
+ if (!currentAssignee || currentAssignee === assigneeName) {
1999
+ setErrorMessage(messages.errors.caseAssigneeOnly);
2000
+ return;
2001
+ }
2002
+ const authorName = resolveOriginalFeedbackAuthorName(activeReplyReport);
2003
+ if (authorName && assigneeName === authorName) {
2004
+ setErrorMessage(messages.errors.caseAssigneeOnly);
2005
+ return;
2006
+ }
2007
+ const reply = {
2008
+ id: createReplyId(),
2009
+ message: messages.thread.assigneeTransferred,
2010
+ created_at: new Date().toISOString(),
2011
+ status: "assignee_transferred",
2012
+ case_ids: [focusedCaseId],
2013
+ author_type: "manager",
2014
+ author_name: assigneeName,
2015
+ };
2016
+ try {
2017
+ setIsClaimingAssignee(true);
2018
+ await appendReply(activeReplyReport, reply);
2019
+ const nextCases = transferCaseAssignee(activeReplyReport.cases, focusedCaseId, assigneeName);
2020
+ await updateFeedback(activeReplyReport.id, await signUpdatePayload({
2021
+ cases: nextCases,
2022
+ }));
2023
+ setErrorMessage("");
2024
+ }
2025
+ catch (nextError) {
2026
+ setErrorMessage(nextError instanceof Error ? nextError.message : messages.errors.saveReplyFailed);
2027
+ }
2028
+ finally {
2029
+ setIsClaimingAssignee(false);
2030
+ }
2031
+ };
2032
+ const handleConfirmResolution = async () => {
2033
+ if (!activeReplyReport) {
2034
+ return;
2035
+ }
2036
+ if (!ensureFocusedCase(activeReplyReport) || !focusedCaseId) {
2037
+ return;
2038
+ }
2039
+ const resolverName = sessionActor?.name?.trim() ?? "";
2040
+ if (!resolverName) {
2041
+ setErrorMessage(messages.errors.reviewerRequired);
2042
+ return;
2043
+ }
2044
+ if (!canActOnCase(activeReplyReport, focusedCaseId, resolverName)) {
2045
+ setErrorMessage(messages.errors.caseAssigneeOnly);
2046
+ return;
2047
+ }
2048
+ const nextCases = buildResolvedCasesUpdate(activeReplyReport, [focusedCaseId]);
2049
+ try {
2050
+ if (usesCreateReply) {
2051
+ await createReply(activeReplyReport.id, await signReplyPayload({
2052
+ message: messages.resolution.issueResolvedMessage,
2053
+ status: "resolved",
2054
+ case_ids: [focusedCaseId],
2055
+ author_type: "user",
2056
+ author_name: resolverName,
2057
+ }));
2058
+ const updatedFeedback = await updateFeedback(activeReplyReport.id, await signUpdatePayload({
2059
+ cases: nextCases,
2060
+ }));
2061
+ await notifyFeedbackUpdate(eventCallbacks, updatedFeedback);
2062
+ }
2063
+ else {
2064
+ const reply = {
2065
+ id: createReplyId(),
2066
+ message: messages.resolution.issueResolvedMessage,
2067
+ created_at: new Date().toISOString(),
2068
+ status: "resolved",
2069
+ case_ids: [focusedCaseId],
2070
+ author_type: "user",
2071
+ author_name: resolverName,
2072
+ };
2073
+ const updatedFeedback = await updateFeedback(activeReplyReport.id, await signUpdatePayload({
2074
+ cases: nextCases,
2075
+ replies: [...getReportReplies(activeReplyReport), reply],
2076
+ }));
2077
+ await notifyFeedbackUpdate(eventCallbacks, updatedFeedback);
2078
+ }
2079
+ setFocusedCaseId(resolveDefaultFocusedCaseId({ ...activeReplyReport, cases: nextCases }));
792
2080
  setErrorMessage("");
793
2081
  setPendingComposer(null);
794
2082
  setReplyDraft("");
@@ -809,7 +2097,7 @@ export function useReportState({ projectId, environment, appVersion, appearance,
809
2097
  setErrorMessage("");
810
2098
  try {
811
2099
  const result = await github.onCreate(report);
812
- const updatedFeedback = await updateFeedback(report.id, await signUpdatePayload(buildGitHubIssueUpdate(report, result, messages.resolution.gitIssuedMessage)));
2100
+ const updatedFeedback = await applyGitHubIssueUpdate(report, result);
813
2101
  await notifyGitHubIssueCreated(eventCallbacks, {
814
2102
  feedback: updatedFeedback,
815
2103
  issueUrl: result.issueUrl,
@@ -824,10 +2112,6 @@ export function useReportState({ projectId, environment, appVersion, appearance,
824
2112
  }
825
2113
  };
826
2114
  const handleDelete = async (id) => {
827
- if (personalKeyRequired) {
828
- setErrorMessage(messages.errors.personalKeyRequired);
829
- return;
830
- }
831
2115
  try {
832
2116
  await deleteFeedback(id);
833
2117
  await notifyFeedbackDelete(eventCallbacks, id);
@@ -854,11 +2138,13 @@ export function useReportState({ projectId, environment, appVersion, appearance,
854
2138
  showTargetPreview,
855
2139
  activeReplyReportId,
856
2140
  pendingComposer,
2141
+ pickProbeOpen,
857
2142
  toggleReportMode,
858
2143
  toggleTargetPreview,
859
2144
  toggleIssueMode,
860
2145
  cancelDraft,
861
2146
  cancelPendingComposer,
2147
+ closePickProbe,
862
2148
  closeReplyComposer,
863
2149
  handleCreateSubmit,
864
2150
  stopEditing,
@@ -867,8 +2153,12 @@ export function useReportState({ projectId, environment, appVersion, appearance,
867
2153
  selectAdjacentReport,
868
2154
  });
869
2155
  return {
870
- appearance: activeAppearance,
871
- setAppearance,
2156
+ panelAppearance: activePanelAppearance,
2157
+ setPanelAppearance,
2158
+ tooltipAppearance: activeTooltipAppearance,
2159
+ setTooltipAppearance,
2160
+ questionThreadDisplay,
2161
+ setQuestionThreadDisplay,
872
2162
  locale,
873
2163
  setLocale,
874
2164
  messages,
@@ -880,29 +2170,57 @@ export function useReportState({ projectId, environment, appVersion, appearance,
880
2170
  currentPathname,
881
2171
  showFeedbackList,
882
2172
  panelTab,
883
- routeDetailsStats,
2173
+ routeDetailsStats: resolvedRouteDetailsStats,
2174
+ panelCollapsed,
2175
+ setPanelCollapsed,
2176
+ onPanelBootstrap,
884
2177
  canTransferFeedback,
885
2178
  personalKey,
886
2179
  publicKey,
887
2180
  personalKeyRequired,
888
- personalKeyPendingRegistration,
889
2181
  personalKeyCandidates,
2182
+ authDiagnostics,
2183
+ authorSelectionLocked,
2184
+ panelView,
2185
+ completeOnboarding,
2186
+ restoreFromBackup,
2187
+ skipOnboarding,
2188
+ selfProfile,
890
2189
  issuePersonalKey,
891
2190
  rotatePersonalKey,
892
2191
  insertPersonalKey,
893
2192
  canListAllFeedback,
2193
+ onActivitySummary,
894
2194
  visibleShortcutKeys,
895
2195
  searchInputRef,
896
- resolvedAppearance,
2196
+ resolvedPanelAppearance,
2197
+ resolvedTooltipAppearance,
897
2198
  isMobileViewport,
898
2199
  mode,
899
2200
  showTargetPreview,
2201
+ showMarkerTargetPreview,
2202
+ setShowMarkerTargetPreview,
2203
+ toggleMarkerTargetPreview,
2204
+ markerAppearance,
2205
+ setMarkerAppearance,
2206
+ setMarkerSize,
2207
+ setMarkerShape,
2208
+ setMarkerColors,
2209
+ setMarkerColor,
2210
+ typography,
2211
+ setTypography,
2212
+ setFontSize,
2213
+ setFontFamily,
2214
+ activeMarkerTarget,
2215
+ markerPreviewTargets,
900
2216
  selectableTargets,
901
2217
  filters,
902
2218
  setFilters,
903
2219
  listScope,
904
2220
  setListScope,
905
2221
  reports,
2222
+ currentPageReports,
2223
+ allPageReports,
906
2224
  filteredReports,
907
2225
  isError,
908
2226
  isFetching,
@@ -911,14 +2229,52 @@ export function useReportState({ projectId, environment, appVersion, appearance,
911
2229
  fetchNextPage,
912
2230
  isCreating,
913
2231
  isUpdating,
2232
+ isSubmittingReply,
2233
+ isClaimingAssignee,
914
2234
  isDeleting,
915
2235
  queryErrorMessage,
916
2236
  refetch,
2237
+ replyHistory,
2238
+ replyHistoryByReportId,
2239
+ loadRepliesIfNeeded,
2240
+ loadOlderReplies,
2241
+ goToOlderPaginationPage,
2242
+ goToNewerPaginationPage,
917
2243
  errorMessage,
918
2244
  setErrorMessage,
919
2245
  draft,
920
2246
  hoveredTarget,
2247
+ hoverPointer,
921
2248
  selectedTarget,
2249
+ pickProbeOpen,
2250
+ pickProbeSupportsTextFields,
2251
+ pickProbeLayoutMode,
2252
+ pickProbeValues,
2253
+ pickProbeCompareMode,
2254
+ pickProbeHasEdits,
2255
+ pickTargetContextMenu,
2256
+ contextMenuElementKey,
2257
+ savedProbeEdits,
2258
+ savedProbeDeletions,
2259
+ hasProbeSessionChanges,
2260
+ canUndoProbeSession,
2261
+ canRedoProbeSession,
2262
+ undoProbeSessionAction,
2263
+ redoProbeSessionAction,
2264
+ savedProbeCompareMode,
2265
+ closePickProbe,
2266
+ closePickTargetContextMenu,
2267
+ handlePickTargetEdit,
2268
+ handlePickTargetDelete,
2269
+ handlePickTargetRevert,
2270
+ commitPickProbeEdits,
2271
+ revertSavedProbeEdit,
2272
+ revertAllSavedProbeEdits,
2273
+ setSavedProbeCompareMode,
2274
+ setPickProbeCompareMode,
2275
+ updatePickProbeValue,
2276
+ resetPickProbeValues,
2277
+ appendSavedProbeSummaryAsNewDraftCase,
922
2278
  markers,
923
2279
  selectedReport,
924
2280
  editingReportId,
@@ -932,20 +2288,53 @@ export function useReportState({ projectId, environment, appVersion, appearance,
932
2288
  tooltipFieldTags,
933
2289
  replyDraft,
934
2290
  setReplyDraft,
2291
+ replySubmitAsQuestion,
2292
+ setReplySubmitAsQuestion,
935
2293
  draftAuthorName,
936
- setDraftAuthorName,
2294
+ setDraftAuthorName: setDraftAuthorNameSafe,
937
2295
  replyAuthorName,
938
- setReplyAuthorName,
2296
+ setReplyAuthorName: setReplyAuthorNameSafe,
2297
+ isPresentationMode,
2298
+ sessionActor,
2299
+ presentationViewers,
2300
+ presentationViewerId: resolvedPresentationViewerId,
2301
+ setPresentationViewerId: applyPresentationViewer,
939
2302
  pendingComposer,
940
2303
  startDenyReview,
941
2304
  startCheckoutReview,
2305
+ startAskQuestion,
2306
+ handleClaimAssignee,
2307
+ handleTransferAssignee,
942
2308
  cancelPendingComposer,
943
2309
  confirmAuthorName,
944
2310
  setConfirmAuthorName,
945
2311
  showConfirmAuthorSelect,
946
2312
  toggleConfirmAuthorSelect,
947
2313
  handleConfirmResolution,
2314
+ beginCaseEdit,
2315
+ cancelCaseEdit,
2316
+ handleCaseEditSave,
2317
+ updateCaseEditDraftCase,
2318
+ addCaseEditDraftCase,
2319
+ removeCaseEditDraftCase,
2320
+ focusedCaseId,
2321
+ selectCase,
2322
+ clearFocusedCase,
2323
+ isCaseEditing,
2324
+ caseEditReportId,
2325
+ caseEditCases,
948
2326
  targetStats,
2327
+ roleStatItems,
2328
+ panelRole,
2329
+ setPanelRole,
2330
+ visiblePanelTabs,
2331
+ visiblePanelTabsSummary,
2332
+ resolvedTabAvailabilityContext,
2333
+ setVisiblePanelTabs,
2334
+ resetVisibleTabsToRoleDefault,
2335
+ applyRoleDefaultTabsForOnboarding,
2336
+ savePanelTabPreference,
2337
+ storedPanelTabPreference: storedPreference,
949
2338
  statusText,
950
2339
  toggleReportMode,
951
2340
  toggleTargetPreview,
@@ -963,10 +2352,14 @@ export function useReportState({ projectId, environment, appVersion, appearance,
963
2352
  scheduleHoverLeave,
964
2353
  setHoveredMarkerId,
965
2354
  handleOverlayMove,
2355
+ handleOverlayContextMenu,
966
2356
  handleOverlayClick,
967
2357
  cancelDraft,
968
- updateDraftMessage,
2358
+ updateDraftCase,
2359
+ addDraftCase,
2360
+ removeDraftCase,
969
2361
  updateDraftField,
2362
+ updateDraftCategory,
970
2363
  handleCreateSubmit,
971
2364
  startEditing,
972
2365
  stopEditing,