@fivepixels-js/react 0.1.7 → 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 -105
  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 +1 -5
  196. package/dist/components/panel/feedback/FeedbackHoverCard.d.ts.map +1 -1
  197. package/dist/components/panel/feedback/FeedbackHoverCard.js +21 -14
  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 +41 -95
  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 +1553 -161
  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 +403 -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 +404 -15
  443. package/dist/i18n/ko.js.map +1 -1
  444. package/dist/i18n/types.d.ts +383 -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 +87 -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 +66 -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 +139 -4
  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 +3 -3
  533. package/dist/utils/markerContext.d.ts.map +1 -1
  534. package/dist/utils/markerContext.js +6 -5
  535. package/dist/utils/markerContext.js.map +1 -1
  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
  }
@@ -282,7 +721,21 @@ export function useReportState({ projectId, environment, appVersion, appearance,
282
721
  window.removeEventListener("scroll", syncMarkers, { capture: true });
283
722
  window.removeEventListener("resize", syncMarkers);
284
723
  };
285
- }, [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]);
286
739
  const prepareFeedbackLocation = useCallback(async (report) => {
287
740
  const targetElement = getFeedbackTargetElement(report);
288
741
  if (targetElement && isFeedbackTargetVisible(targetElement)) {
@@ -309,8 +762,8 @@ export function useReportState({ projectId, environment, appVersion, appearance,
309
762
  return;
310
763
  }
311
764
  const syncTargetRects = () => {
312
- setHoveredTarget(toSnapshot(hoveredElementRef.current));
313
- setSelectedTarget(toSnapshot(selectedElementRef.current));
765
+ setHoveredTarget(toFeedbackHoverSnapshot(hoveredElementRef.current));
766
+ setSelectedTarget(toFeedbackHoverSnapshot(selectedElementRef.current));
314
767
  };
315
768
  window.addEventListener("scroll", syncTargetRects, { passive: true, capture: true });
316
769
  window.addEventListener("resize", syncTargetRects);
@@ -351,14 +804,499 @@ export function useReportState({ projectId, environment, appVersion, appearance,
351
804
  overlayHoverLeaveTimeoutRef.current = window.setTimeout(() => {
352
805
  if (!hoveredElementRef.current) {
353
806
  setHoveredTarget(null);
807
+ setHoverPointer(null);
354
808
  }
355
809
  overlayHoverLeaveTimeoutRef.current = null;
356
810
  }, OVERLAY_HOVER_LEAVE_MS);
357
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]);
358
1186
  const stopEditing = () => {
359
1187
  setEditingReportId(null);
360
1188
  setEditableDraft(null);
361
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]);
362
1300
  const selectReport = (reportId) => {
363
1301
  setSelectedReportId(reportId);
364
1302
  if (editingReportId && editingReportId !== reportId) {
@@ -368,8 +1306,11 @@ export function useReportState({ projectId, environment, appVersion, appearance,
368
1306
  const closeReplyComposer = () => {
369
1307
  setActiveReplyReportId(null);
370
1308
  setReplyDraft("");
1309
+ setReplySubmitAsQuestion(false);
371
1310
  setPendingComposer(null);
372
1311
  setShowConfirmAuthorSelect(false);
1312
+ cancelCaseEdit();
1313
+ clearFocusedCase();
373
1314
  };
374
1315
  const showFeedbackTooltip = useCallback(async (report) => {
375
1316
  await prepareFeedbackLocation(report);
@@ -435,46 +1376,99 @@ export function useReportState({ projectId, environment, appVersion, appearance,
435
1376
  selectReport(report.id);
436
1377
  setActiveReplyReportId(report.id);
437
1378
  setReplyDraft("");
1379
+ setReplySubmitAsQuestion(false);
438
1380
  setPendingComposer(null);
439
- setReplyAuthorName(resolveDefaultAuthorName(activeIdentify, authorizedAuthors));
1381
+ setReplyAuthorName(sessionActor?.name ?? resolveDefaultAuthorName(activeIdentify, authorizedAuthors, selfProfile?.name));
440
1382
  setConfirmAuthorName(resolveOriginalFeedbackAuthorName(report));
441
1383
  setShowConfirmAuthorSelect(false);
442
- clearHoverLeaveTimeout();
1384
+ setFocusedCaseId(resolveDefaultFocusedCaseId(report));
443
1385
  };
444
1386
  const activateFeedbackMarker = useCallback(async (report) => {
445
- await prepareFeedbackLocation(report);
446
- openReplyComposer(report);
447
- }, [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]);
448
1408
  const toggleConfirmAuthorSelect = () => {
449
1409
  setShowConfirmAuthorSelect((current) => !current);
450
1410
  };
451
- const startDenyReview = () => {
452
- if (!activeReplyReport) {
1411
+ const startDenyReview = (targetReplyId) => {
1412
+ if (!activeReplyReport || !focusedCaseId) {
453
1413
  return;
454
1414
  }
455
- const latest = activeReplyReport.replies[activeReplyReport.replies.length - 1];
456
- 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);
457
1426
  return;
458
1427
  }
459
1428
  setPendingComposer({
460
- type: latest.status === "found_error" ? "recheck" : "deny",
461
- targetReplyId: latest.id,
1429
+ type: latestRoot.status === "found_error" ? "recheck" : "deny",
1430
+ targetReplyId: latestRoot.id,
462
1431
  });
463
1432
  setReplyDraft("");
1433
+ setReplySubmitAsQuestion(false);
464
1434
  };
465
1435
  const startCheckoutReview = (replyId) => {
1436
+ if (!activeReplyReport || !focusedCaseId || !ensureCanActOnFocusedCase(activeReplyReport)) {
1437
+ return;
1438
+ }
466
1439
  setPendingComposer({ type: "checkout", targetReplyId: replyId });
467
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
+ }
468
1465
  };
469
1466
  const cancelPendingComposer = () => {
470
1467
  setPendingComposer(null);
471
1468
  setReplyDraft("");
1469
+ setReplySubmitAsQuestion(false);
472
1470
  };
473
1471
  const toggleReportMode = () => {
474
- if (personalKeyRequired) {
475
- setErrorMessage(messages.errors.personalKeyRequired);
476
- return;
477
- }
478
1472
  setShowTargetPreview(false);
479
1473
  setMode((current) => (current === "report" ? "idle" : "report"));
480
1474
  };
@@ -504,7 +1498,7 @@ export function useReportState({ projectId, environment, appVersion, appearance,
504
1498
  closeReplyComposer();
505
1499
  setMode((current) => (current === "view" ? "idle" : "view"));
506
1500
  stopEditing();
507
- setSelectedReportId(filteredReports[0]?.id ?? null);
1501
+ setSelectedReportId(null);
508
1502
  };
509
1503
  const toggleTargetPreview = () => {
510
1504
  setShowTargetPreview((current) => {
@@ -515,37 +1509,74 @@ export function useReportState({ projectId, environment, appVersion, appearance,
515
1509
  return next;
516
1510
  });
517
1511
  };
518
- // overlay (target pick, create draft, edit)
519
1512
  const handleOverlayMove = (event) => {
520
1513
  if (mode !== "report" || draft) {
521
1514
  return;
522
1515
  }
523
- const targetElement = findTargetByPoint(overlayRef.current, event.clientX, event.clientY);
1516
+ const targetElement = findPickTargetByPoint(overlayRef.current, event.clientX, event.clientY);
524
1517
  hoveredElementRef.current = targetElement;
525
1518
  if (!targetElement) {
526
1519
  scheduleOverlayHoverLeave();
527
1520
  return;
528
1521
  }
529
1522
  clearOverlayHoverLeaveTimeout();
530
- const snapshot = toSnapshot(targetElement);
1523
+ setHoverPointer({ clientX: event.clientX, clientY: event.clientY });
1524
+ const snapshot = toFeedbackHoverSnapshot(targetElement);
531
1525
  setHoveredTarget((previous) => (isSameHoverTarget(previous, snapshot) ? previous : snapshot));
532
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
+ };
533
1555
  const handleOverlayClick = (event) => {
534
1556
  if (mode !== "report") {
535
1557
  return;
536
1558
  }
537
- const targetElement = findTargetByPoint(overlayRef.current, event.clientX, event.clientY);
538
- const snapshot = toSnapshot(targetElement);
539
- if (!targetElement || !snapshot) {
540
- 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);
541
1571
  return;
542
1572
  }
543
1573
  hoveredElementRef.current = targetElement;
544
1574
  selectedElementRef.current = targetElement;
545
- setHoveredTarget(snapshot);
1575
+ draftElementRef.current = targetElement;
1576
+ setHoverPointer(null);
1577
+ setHoveredTarget(null);
546
1578
  setSelectedTarget(snapshot);
547
1579
  setErrorMessage("");
548
- const anchorSnapshot = resolveFeedbackDocumentAnchor(targetElement);
549
1580
  setDraft({
550
1581
  clientX: event.clientX,
551
1582
  clientY: event.clientY,
@@ -555,26 +1586,101 @@ export function useReportState({ projectId, environment, appVersion, appearance,
555
1586
  elementYRatio: clampRatio((event.clientY - snapshot.rect.top) / Math.max(snapshot.rect.height, 1)),
556
1587
  anchorReportId: anchorSnapshot?.id ?? null,
557
1588
  anchorReportType: anchorSnapshot?.type ?? null,
558
- anchorXRatio: anchorSnapshot
559
- ? clampRatio((event.clientX - anchorSnapshot.rect.left) / Math.max(anchorSnapshot.rect.width, 1))
560
- : null,
561
- anchorYRatio: anchorSnapshot
562
- ? clampRatio((event.clientY - anchorSnapshot.rect.top) / Math.max(anchorSnapshot.rect.height, 1))
563
- : 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,
564
1591
  scrollY: window.scrollY,
565
1592
  documentY: Math.round(window.scrollY + event.clientY),
566
1593
  reportId: snapshot.id,
567
1594
  reportType: snapshot.type,
568
- message: "",
1595
+ targetSelector: isTagged ? null : (snapshot.targetSelector ?? null),
1596
+ suggestedReportId: isTagged ? null : (snapshot.suggestedReportId ?? snapshot.id),
1597
+ cases: [createReportCase("")],
1598
+ category: null,
569
1599
  fieldValues: createInitialFieldValues(fields),
570
1600
  });
571
1601
  };
572
1602
  const cancelDraft = () => {
1603
+ resetPickProbeState();
1604
+ draftElementRef.current = null;
1605
+ contextMenuElementRef.current = null;
573
1606
  setDraft(null);
574
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);
575
1673
  };
576
- const updateDraftMessage = (nextMessage) => {
577
- 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
+ });
578
1684
  };
579
1685
  const updateDraftField = (key, nextValue) => {
580
1686
  setDraft((current) => current
@@ -591,50 +1697,72 @@ export function useReportState({ projectId, environment, appVersion, appearance,
591
1697
  if (!draft) {
592
1698
  return null;
593
1699
  }
594
- if (personalKeyRequired) {
595
- setErrorMessage(messages.errors.personalKeyRequired);
596
- return null;
597
- }
598
- const nextError = getFieldError(draft.message, draft.fieldValues, fields, messages.errors);
1700
+ const nextError = getFieldError(draft.cases, draft.fieldValues, fields, messages.errors);
599
1701
  if (nextError) {
600
1702
  setErrorMessage(nextError);
601
1703
  return null;
602
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
+ }));
603
1718
  return {
604
1719
  pathname: currentPathname,
605
1720
  report_id: draft.reportId,
606
1721
  report_type: draft.reportType,
607
- message: draft.message.trim(),
1722
+ ...(draft.targetSelector ? { target_selector: draft.targetSelector } : {}),
1723
+ cases,
608
1724
  status: "open",
1725
+ category: draft.category,
609
1726
  field_values: draft.fieldValues,
610
- x_ratio: draft.xRatio,
611
- y_ratio: draft.yRatio,
612
- element_x_ratio: draft.elementXRatio,
613
- element_y_ratio: draft.elementYRatio,
614
- anchor_report_id: draft.anchorReportId,
615
- anchor_report_type: draft.anchorReportType,
616
- anchor_x_ratio: draft.anchorXRatio,
617
- anchor_y_ratio: draft.anchorYRatio,
618
- scroll_y: draft.scrollY,
619
- document_y: draft.documentY,
620
- viewport_width: window.innerWidth,
621
- viewport_height: window.innerHeight,
622
- design_width: window.innerWidth,
623
- 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
+ },
624
1748
  ...(environment ? { environment } : {}),
625
1749
  ...(appVersion ? { app_version: appVersion } : {}),
626
- ...(activeIdentify || draftAuthorName.trim()
1750
+ ...(sessionActor
627
1751
  ? {
628
- ...(activeIdentify ? { author_id: activeIdentify.id } : {}),
629
- author_name: draftAuthorName.trim() || activeIdentify?.name,
1752
+ author_id: sessionActor.id,
1753
+ author_name: sessionActor.name,
630
1754
  }
631
1755
  : {}),
632
1756
  };
633
1757
  };
634
1758
  const finalizeDraftCreate = () => {
1759
+ resetPickProbeState();
1760
+ draftElementRef.current = null;
1761
+ contextMenuElementRef.current = null;
635
1762
  setDraft(null);
636
1763
  setSelectedTarget(null);
637
1764
  setHoveredTarget(null);
1765
+ setHoverPointer(null);
638
1766
  setErrorMessage("");
639
1767
  setMode("view");
640
1768
  };
@@ -666,7 +1794,7 @@ export function useReportState({ projectId, environment, appVersion, appearance,
666
1794
  const savedFeedback = await createFeedback(await signCreatePayload(payload));
667
1795
  await notifyFeedbackCreate(eventCallbacks, savedFeedback);
668
1796
  const result = await github.onCreate(savedFeedback);
669
- const updatedFeedback = await updateFeedback(savedFeedback.id, await signUpdatePayload(buildGitHubIssueUpdate(savedFeedback, result, messages.resolution.gitIssuedMessage)));
1797
+ const updatedFeedback = await applyGitHubIssueUpdate(savedFeedback, result);
670
1798
  await notifyGitHubIssueCreated(eventCallbacks, {
671
1799
  feedback: updatedFeedback,
672
1800
  issueUrl: result.issueUrl,
@@ -688,7 +1816,7 @@ export function useReportState({ projectId, environment, appVersion, appearance,
688
1816
  }
689
1817
  setEditingReportId(report.id);
690
1818
  setEditableDraft({
691
- message: report.message,
1819
+ cases: report.cases.map((item) => ({ ...item })),
692
1820
  status: report.status,
693
1821
  fieldValues: createInitialFieldValues(fields, report.field_values),
694
1822
  });
@@ -702,14 +1830,19 @@ export function useReportState({ projectId, environment, appVersion, appearance,
702
1830
  setErrorMessage(messages.errors.archivedNotEditable);
703
1831
  return;
704
1832
  }
705
- const nextError = getFieldError(editableDraft.message, editableDraft.fieldValues, fields, messages.errors);
1833
+ const nextError = getFieldError(editableDraft.cases, editableDraft.fieldValues, fields, messages.errors);
706
1834
  if (nextError) {
707
1835
  setErrorMessage(nextError);
708
1836
  return;
709
1837
  }
710
1838
  try {
1839
+ const cases = editableDraft.cases.map((item) => ({
1840
+ ...item,
1841
+ text: item.text.trim(),
1842
+ updated_at: new Date().toISOString(),
1843
+ }));
711
1844
  const updatedFeedback = await updateFeedback(selectedReport.id, await signUpdatePayload({
712
- message: editableDraft.message.trim(),
1845
+ cases,
713
1846
  status: editableDraft.status,
714
1847
  field_values: editableDraft.fieldValues,
715
1848
  }));
@@ -722,10 +1855,22 @@ export function useReportState({ projectId, environment, appVersion, appearance,
722
1855
  }
723
1856
  };
724
1857
  const appendReply = async (report, reply) => {
725
- const payload = await signUpdatePayload({
726
- replies: [...report.replies, reply],
727
- });
728
- 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
+ }
729
1874
  await notifyFeedbackReply(eventCallbacks, {
730
1875
  feedbackId: report.id,
731
1876
  message: reply.message,
@@ -735,61 +1880,203 @@ export function useReportState({ projectId, environment, appVersion, appearance,
735
1880
  if (!activeReplyReport) {
736
1881
  return;
737
1882
  }
738
- if (personalKeyRequired) {
739
- setErrorMessage(messages.errors.personalKeyRequired);
740
- return;
741
- }
742
1883
  if (!replyDraft.trim()) {
743
1884
  setErrorMessage(messages.errors.replyContentRequired);
744
1885
  return;
745
1886
  }
746
- if (!replyAuthorName.trim()) {
1887
+ if (!ensureFocusedCase(activeReplyReport) || !focusedCaseId) {
1888
+ return;
1889
+ }
1890
+ const actorName = sessionActor?.name?.trim() ?? "";
1891
+ if (!actorName) {
747
1892
  setErrorMessage(messages.errors.authorRequired);
748
1893
  return;
749
1894
  }
750
- const replyMessage = replyDraft.trim();
751
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;
752
1905
  const reply = {
753
1906
  id: createReplyId(),
754
1907
  message: replyMessage,
755
1908
  created_at: new Date().toISOString(),
756
- status: createReplyStatusForSubmit(pendingType),
757
- author_type: "manager",
758
- 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,
759
1914
  };
760
1915
  try {
1916
+ setIsSubmittingReply(true);
761
1917
  await appendReply(activeReplyReport, reply);
762
1918
  setErrorMessage("");
763
1919
  setReplyDraft("");
764
- 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
+ }
765
1936
  }
766
1937
  catch (nextError) {
767
1938
  setErrorMessage(nextError instanceof Error ? nextError.message : messages.errors.saveReplyFailed);
768
1939
  }
1940
+ finally {
1941
+ setIsSubmittingReply(false);
1942
+ }
769
1943
  };
770
- const handleConfirmResolution = async () => {
1944
+ const handleClaimAssignee = async () => {
771
1945
  if (!activeReplyReport) {
772
1946
  return;
773
1947
  }
774
- const resolverName = confirmAuthorName.trim() || resolveOriginalFeedbackAuthorName(activeReplyReport);
775
- if (!resolverName) {
776
- 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);
777
1958
  return;
778
1959
  }
779
1960
  const reply = {
780
1961
  id: createReplyId(),
781
- message: messages.resolution.issueResolvedMessage,
1962
+ message: messages.thread.assigneeAssigned,
782
1963
  created_at: new Date().toISOString(),
783
- status: "resolved",
784
- author_type: "user",
785
- author_name: resolverName,
1964
+ status: "assignee_assigned",
1965
+ case_ids: [focusedCaseId],
1966
+ author_type: "manager",
1967
+ author_name: assigneeName,
786
1968
  };
787
1969
  try {
788
- const updatedFeedback = await updateFeedback(activeReplyReport.id, await signUpdatePayload({
789
- status: "resolved",
790
- 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,
791
1975
  }));
792
- 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 }));
793
2080
  setErrorMessage("");
794
2081
  setPendingComposer(null);
795
2082
  setReplyDraft("");
@@ -810,7 +2097,7 @@ export function useReportState({ projectId, environment, appVersion, appearance,
810
2097
  setErrorMessage("");
811
2098
  try {
812
2099
  const result = await github.onCreate(report);
813
- const updatedFeedback = await updateFeedback(report.id, await signUpdatePayload(buildGitHubIssueUpdate(report, result, messages.resolution.gitIssuedMessage)));
2100
+ const updatedFeedback = await applyGitHubIssueUpdate(report, result);
814
2101
  await notifyGitHubIssueCreated(eventCallbacks, {
815
2102
  feedback: updatedFeedback,
816
2103
  issueUrl: result.issueUrl,
@@ -825,10 +2112,6 @@ export function useReportState({ projectId, environment, appVersion, appearance,
825
2112
  }
826
2113
  };
827
2114
  const handleDelete = async (id) => {
828
- if (personalKeyRequired) {
829
- setErrorMessage(messages.errors.personalKeyRequired);
830
- return;
831
- }
832
2115
  try {
833
2116
  await deleteFeedback(id);
834
2117
  await notifyFeedbackDelete(eventCallbacks, id);
@@ -855,11 +2138,13 @@ export function useReportState({ projectId, environment, appVersion, appearance,
855
2138
  showTargetPreview,
856
2139
  activeReplyReportId,
857
2140
  pendingComposer,
2141
+ pickProbeOpen,
858
2142
  toggleReportMode,
859
2143
  toggleTargetPreview,
860
2144
  toggleIssueMode,
861
2145
  cancelDraft,
862
2146
  cancelPendingComposer,
2147
+ closePickProbe,
863
2148
  closeReplyComposer,
864
2149
  handleCreateSubmit,
865
2150
  stopEditing,
@@ -868,8 +2153,12 @@ export function useReportState({ projectId, environment, appVersion, appearance,
868
2153
  selectAdjacentReport,
869
2154
  });
870
2155
  return {
871
- appearance: activeAppearance,
872
- setAppearance,
2156
+ panelAppearance: activePanelAppearance,
2157
+ setPanelAppearance,
2158
+ tooltipAppearance: activeTooltipAppearance,
2159
+ setTooltipAppearance,
2160
+ questionThreadDisplay,
2161
+ setQuestionThreadDisplay,
873
2162
  locale,
874
2163
  setLocale,
875
2164
  messages,
@@ -881,29 +2170,57 @@ export function useReportState({ projectId, environment, appVersion, appearance,
881
2170
  currentPathname,
882
2171
  showFeedbackList,
883
2172
  panelTab,
884
- routeDetailsStats,
2173
+ routeDetailsStats: resolvedRouteDetailsStats,
2174
+ panelCollapsed,
2175
+ setPanelCollapsed,
2176
+ onPanelBootstrap,
885
2177
  canTransferFeedback,
886
2178
  personalKey,
887
2179
  publicKey,
888
2180
  personalKeyRequired,
889
- personalKeyPendingRegistration,
890
2181
  personalKeyCandidates,
2182
+ authDiagnostics,
2183
+ authorSelectionLocked,
2184
+ panelView,
2185
+ completeOnboarding,
2186
+ restoreFromBackup,
2187
+ skipOnboarding,
2188
+ selfProfile,
891
2189
  issuePersonalKey,
892
2190
  rotatePersonalKey,
893
2191
  insertPersonalKey,
894
2192
  canListAllFeedback,
2193
+ onActivitySummary,
895
2194
  visibleShortcutKeys,
896
2195
  searchInputRef,
897
- resolvedAppearance,
2196
+ resolvedPanelAppearance,
2197
+ resolvedTooltipAppearance,
898
2198
  isMobileViewport,
899
2199
  mode,
900
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,
901
2216
  selectableTargets,
902
2217
  filters,
903
2218
  setFilters,
904
2219
  listScope,
905
2220
  setListScope,
906
2221
  reports,
2222
+ currentPageReports,
2223
+ allPageReports,
907
2224
  filteredReports,
908
2225
  isError,
909
2226
  isFetching,
@@ -912,14 +2229,52 @@ export function useReportState({ projectId, environment, appVersion, appearance,
912
2229
  fetchNextPage,
913
2230
  isCreating,
914
2231
  isUpdating,
2232
+ isSubmittingReply,
2233
+ isClaimingAssignee,
915
2234
  isDeleting,
916
2235
  queryErrorMessage,
917
2236
  refetch,
2237
+ replyHistory,
2238
+ replyHistoryByReportId,
2239
+ loadRepliesIfNeeded,
2240
+ loadOlderReplies,
2241
+ goToOlderPaginationPage,
2242
+ goToNewerPaginationPage,
918
2243
  errorMessage,
919
2244
  setErrorMessage,
920
2245
  draft,
921
2246
  hoveredTarget,
2247
+ hoverPointer,
922
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,
923
2278
  markers,
924
2279
  selectedReport,
925
2280
  editingReportId,
@@ -933,20 +2288,53 @@ export function useReportState({ projectId, environment, appVersion, appearance,
933
2288
  tooltipFieldTags,
934
2289
  replyDraft,
935
2290
  setReplyDraft,
2291
+ replySubmitAsQuestion,
2292
+ setReplySubmitAsQuestion,
936
2293
  draftAuthorName,
937
- setDraftAuthorName,
2294
+ setDraftAuthorName: setDraftAuthorNameSafe,
938
2295
  replyAuthorName,
939
- setReplyAuthorName,
2296
+ setReplyAuthorName: setReplyAuthorNameSafe,
2297
+ isPresentationMode,
2298
+ sessionActor,
2299
+ presentationViewers,
2300
+ presentationViewerId: resolvedPresentationViewerId,
2301
+ setPresentationViewerId: applyPresentationViewer,
940
2302
  pendingComposer,
941
2303
  startDenyReview,
942
2304
  startCheckoutReview,
2305
+ startAskQuestion,
2306
+ handleClaimAssignee,
2307
+ handleTransferAssignee,
943
2308
  cancelPendingComposer,
944
2309
  confirmAuthorName,
945
2310
  setConfirmAuthorName,
946
2311
  showConfirmAuthorSelect,
947
2312
  toggleConfirmAuthorSelect,
948
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,
949
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,
950
2338
  statusText,
951
2339
  toggleReportMode,
952
2340
  toggleTargetPreview,
@@ -964,10 +2352,14 @@ export function useReportState({ projectId, environment, appVersion, appearance,
964
2352
  scheduleHoverLeave,
965
2353
  setHoveredMarkerId,
966
2354
  handleOverlayMove,
2355
+ handleOverlayContextMenu,
967
2356
  handleOverlayClick,
968
2357
  cancelDraft,
969
- updateDraftMessage,
2358
+ updateDraftCase,
2359
+ addDraftCase,
2360
+ removeDraftCase,
970
2361
  updateDraftField,
2362
+ updateDraftCategory,
971
2363
  handleCreateSubmit,
972
2364
  startEditing,
973
2365
  stopEditing,