| import { useState } from 'react' |
| import { |
| type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| logEvent, |
| } from '../../services/analytics/index.js' |
| import { sanitizeToolNameForAnalytics } from '../../services/analytics/metadata.js' |
| import { useSetAppState } from '../../state/AppState.js' |
| import type { ToolUseConfirm } from './PermissionRequest.js' |
| import { logUnaryPermissionEvent } from './utils.js' |
|
|
| |
| |
| |
| |
| |
| export function useShellPermissionFeedback({ |
| toolUseConfirm, |
| onDone, |
| onReject, |
| explainerVisible, |
| }: { |
| toolUseConfirm: ToolUseConfirm |
| onDone: () => void |
| onReject: () => void |
| explainerVisible: boolean |
| }): { |
| yesInputMode: boolean |
| noInputMode: boolean |
| yesFeedbackModeEntered: boolean |
| noFeedbackModeEntered: boolean |
| acceptFeedback: string |
| rejectFeedback: string |
| setAcceptFeedback: (v: string) => void |
| setRejectFeedback: (v: string) => void |
| focusedOption: string |
| handleInputModeToggle: (option: string) => void |
| handleReject: (feedback?: string) => void |
| handleFocus: (value: string) => void |
| } { |
| const setAppState = useSetAppState() |
| const [rejectFeedback, setRejectFeedback] = useState('') |
| const [acceptFeedback, setAcceptFeedback] = useState('') |
| const [yesInputMode, setYesInputMode] = useState(false) |
| const [noInputMode, setNoInputMode] = useState(false) |
| const [focusedOption, setFocusedOption] = useState('yes') |
| |
| const [yesFeedbackModeEntered, setYesFeedbackModeEntered] = useState(false) |
| const [noFeedbackModeEntered, setNoFeedbackModeEntered] = useState(false) |
|
|
| |
| function handleInputModeToggle(option: string) { |
| |
| toolUseConfirm.onUserInteraction() |
| const analyticsProps = { |
| toolName: sanitizeToolNameForAnalytics( |
| toolUseConfirm.tool.name, |
| ) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, |
| isMcp: toolUseConfirm.tool.isMcp ?? false, |
| } |
|
|
| if (option === 'yes') { |
| if (yesInputMode) { |
| setYesInputMode(false) |
| logEvent('tengu_accept_feedback_mode_collapsed', analyticsProps) |
| } else { |
| setYesInputMode(true) |
| setYesFeedbackModeEntered(true) |
| logEvent('tengu_accept_feedback_mode_entered', analyticsProps) |
| } |
| } else if (option === 'no') { |
| if (noInputMode) { |
| setNoInputMode(false) |
| logEvent('tengu_reject_feedback_mode_collapsed', analyticsProps) |
| } else { |
| setNoInputMode(true) |
| setNoFeedbackModeEntered(true) |
| logEvent('tengu_reject_feedback_mode_entered', analyticsProps) |
| } |
| } |
| } |
|
|
| function handleReject(feedback?: string) { |
| const trimmedFeedback = feedback?.trim() |
| const hasFeedback = !!trimmedFeedback |
|
|
| |
| if (!hasFeedback) { |
| logEvent('tengu_permission_request_escape', { |
| explainer_visible: explainerVisible, |
| }) |
| |
| setAppState(prev => ({ |
| ...prev, |
| attribution: { |
| ...prev.attribution, |
| escapeCount: prev.attribution.escapeCount + 1, |
| }, |
| })) |
| } |
|
|
| logUnaryPermissionEvent( |
| 'tool_use_single', |
| toolUseConfirm, |
| 'reject', |
| hasFeedback, |
| ) |
|
|
| if (trimmedFeedback) { |
| toolUseConfirm.onReject(trimmedFeedback) |
| } else { |
| toolUseConfirm.onReject() |
| } |
|
|
| onReject() |
| onDone() |
| } |
|
|
| function handleFocus(value: string) { |
| |
| |
| if (value !== focusedOption) { |
| toolUseConfirm.onUserInteraction() |
| } |
| |
| if (value !== 'yes' && yesInputMode && !acceptFeedback.trim()) { |
| setYesInputMode(false) |
| } |
| if (value !== 'no' && noInputMode && !rejectFeedback.trim()) { |
| setNoInputMode(false) |
| } |
| setFocusedOption(value) |
| } |
|
|
| return { |
| yesInputMode, |
| noInputMode, |
| yesFeedbackModeEntered, |
| noFeedbackModeEntered, |
| acceptFeedback, |
| rejectFeedback, |
| setAcceptFeedback, |
| setRejectFeedback, |
| focusedOption, |
| handleInputModeToggle, |
| handleReject, |
| handleFocus, |
| } |
| } |
|
|