mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 00:35:51 +00:00
更新大量 tsx 原始文件; 已经迁移 login panel; 部分 (#121)
* style(B1-1): 格式化 ink/buddy/cli/context/screens/tasks/services/keybindings/state (43 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 修复了 Box.tsx 和 ScrollBox.tsx 中无效的 global.d.ts import。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-2): 格式化 commands (79 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-3): 格式化 components/messages,permissions,mcp,sandbox,shell (104 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-4): 格式化 components/PromptInput,FeedbackSurvey,tasks,agents,skills,design-system,wizard (73 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-5): 格式化 components其余 + hooks + tools (232 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-6): 格式化 main/entrypoints/utils/moreright (21 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: 更新 README,新增 Run.ps1/TODO.md,删除 V6.md - README.md: 大幅重写,更详细版本历史和配置示例 - Run.ps1: 新增 Windows 启动脚本 - TODO.md: 新增包完成清单 - V6.md: 删除(架构重构规划已不适用) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: 修复以前的问题 * fix: 修复 login 面板的问题 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,36 +1,43 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React, { type ReactNode, useCallback, useMemo, useState } from 'react';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import type { KeybindingAction } from '../../keybindings/types.js';
|
||||
import { useKeybindings } from '../../keybindings/useKeybinding.js';
|
||||
import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../services/analytics/index.js';
|
||||
import { useSetAppState } from '../../state/AppState.js';
|
||||
import { type OptionWithDescription, Select } from '../CustomSelect/select.js';
|
||||
export type FeedbackType = 'accept' | 'reject';
|
||||
import React, { type ReactNode, useCallback, useMemo, useState } from 'react'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import type { KeybindingAction } from '../../keybindings/types.js'
|
||||
import { useKeybindings } from '../../keybindings/useKeybinding.js'
|
||||
import {
|
||||
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
logEvent,
|
||||
} from '../../services/analytics/index.js'
|
||||
import { useSetAppState } from '../../state/AppState.js'
|
||||
import { type OptionWithDescription, Select } from '../CustomSelect/select.js'
|
||||
|
||||
export type FeedbackType = 'accept' | 'reject'
|
||||
|
||||
export type PermissionPromptOption<T extends string> = {
|
||||
value: T;
|
||||
label: ReactNode;
|
||||
value: T
|
||||
label: ReactNode
|
||||
feedbackConfig?: {
|
||||
type: FeedbackType;
|
||||
placeholder?: string;
|
||||
};
|
||||
keybinding?: KeybindingAction;
|
||||
};
|
||||
type: FeedbackType
|
||||
placeholder?: string
|
||||
}
|
||||
keybinding?: KeybindingAction
|
||||
}
|
||||
|
||||
export type ToolAnalyticsContext = {
|
||||
toolName: string;
|
||||
isMcp: boolean;
|
||||
};
|
||||
toolName: string
|
||||
isMcp: boolean
|
||||
}
|
||||
|
||||
export type PermissionPromptProps<T extends string> = {
|
||||
options: PermissionPromptOption<T>[];
|
||||
onSelect: (value: T, feedback?: string) => void;
|
||||
onCancel?: () => void;
|
||||
question?: string | ReactNode;
|
||||
toolAnalyticsContext?: ToolAnalyticsContext;
|
||||
};
|
||||
options: PermissionPromptOption<T>[]
|
||||
onSelect: (value: T, feedback?: string) => void
|
||||
onCancel?: () => void
|
||||
question?: string | ReactNode
|
||||
toolAnalyticsContext?: ToolAnalyticsContext
|
||||
}
|
||||
|
||||
const DEFAULT_PLACEHOLDERS: Record<FeedbackType, string> = {
|
||||
accept: 'tell Claude what to do next',
|
||||
reject: 'tell Claude what to do differently'
|
||||
};
|
||||
reject: 'tell Claude what to do differently',
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared component for permission prompts with optional feedback input.
|
||||
@@ -42,294 +49,219 @@ const DEFAULT_PLACEHOLDERS: Record<FeedbackType, string> = {
|
||||
* - Analytics events for feedback interactions
|
||||
* - Transforming options to Select-compatible format
|
||||
*/
|
||||
export function PermissionPrompt(t0) {
|
||||
const $ = _c(54);
|
||||
const {
|
||||
options,
|
||||
onSelect,
|
||||
onCancel,
|
||||
question: t1,
|
||||
toolAnalyticsContext
|
||||
} = t0;
|
||||
const question = t1 === undefined ? "Do you want to proceed?" : t1;
|
||||
const setAppState = useSetAppState();
|
||||
const [acceptFeedback, setAcceptFeedback] = useState("");
|
||||
const [rejectFeedback, setRejectFeedback] = useState("");
|
||||
const [acceptInputMode, setAcceptInputMode] = useState(false);
|
||||
const [rejectInputMode, setRejectInputMode] = useState(false);
|
||||
const [focusedValue, setFocusedValue] = useState(null);
|
||||
const [acceptFeedbackModeEntered, setAcceptFeedbackModeEntered] = useState(false);
|
||||
const [rejectFeedbackModeEntered, setRejectFeedbackModeEntered] = useState(false);
|
||||
let t2;
|
||||
if ($[0] !== focusedValue || $[1] !== options) {
|
||||
let t3;
|
||||
if ($[3] !== focusedValue) {
|
||||
t3 = opt => opt.value === focusedValue;
|
||||
$[3] = focusedValue;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
t2 = options.find(t3);
|
||||
$[0] = focusedValue;
|
||||
$[1] = options;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
const focusedOption = t2;
|
||||
const focusedFeedbackType = focusedOption?.feedbackConfig?.type;
|
||||
const showTabHint = focusedFeedbackType === "accept" && !acceptInputMode || focusedFeedbackType === "reject" && !rejectInputMode;
|
||||
let t3;
|
||||
if ($[5] !== acceptInputMode || $[6] !== options || $[7] !== rejectInputMode) {
|
||||
let t4;
|
||||
if ($[9] !== acceptInputMode || $[10] !== rejectInputMode) {
|
||||
t4 = opt_0 => {
|
||||
const {
|
||||
value,
|
||||
label,
|
||||
feedbackConfig
|
||||
} = opt_0;
|
||||
if (!feedbackConfig) {
|
||||
return {
|
||||
label,
|
||||
value
|
||||
};
|
||||
}
|
||||
const {
|
||||
type,
|
||||
placeholder
|
||||
} = feedbackConfig;
|
||||
const isInputMode = type === "accept" ? acceptInputMode : rejectInputMode;
|
||||
const onChange = type === "accept" ? setAcceptFeedback : setRejectFeedback;
|
||||
const defaultPlaceholder = DEFAULT_PLACEHOLDERS[type];
|
||||
if (isInputMode) {
|
||||
return {
|
||||
type: "input" as const,
|
||||
label,
|
||||
value,
|
||||
placeholder: placeholder ?? defaultPlaceholder,
|
||||
onChange,
|
||||
allowEmptySubmitToCancel: true
|
||||
};
|
||||
}
|
||||
export function PermissionPrompt<T extends string>({
|
||||
options,
|
||||
onSelect,
|
||||
onCancel,
|
||||
question = 'Do you want to proceed?',
|
||||
toolAnalyticsContext,
|
||||
}: PermissionPromptProps<T>): React.ReactNode {
|
||||
const setAppState = useSetAppState()
|
||||
const [acceptFeedback, setAcceptFeedback] = useState('')
|
||||
const [rejectFeedback, setRejectFeedback] = useState('')
|
||||
const [acceptInputMode, setAcceptInputMode] = useState(false)
|
||||
const [rejectInputMode, setRejectInputMode] = useState(false)
|
||||
const [focusedValue, setFocusedValue] = useState<T | null>(null)
|
||||
// Track whether user ever entered feedback mode (persists after collapse)
|
||||
const [acceptFeedbackModeEntered, setAcceptFeedbackModeEntered] =
|
||||
useState(false)
|
||||
const [rejectFeedbackModeEntered, setRejectFeedbackModeEntered] =
|
||||
useState(false)
|
||||
|
||||
// Find which option is focused and whether it has feedback config
|
||||
const focusedOption = options.find(opt => opt.value === focusedValue)
|
||||
const focusedFeedbackType = focusedOption?.feedbackConfig?.type
|
||||
|
||||
// Show Tab hint when focused on a feedback-enabled option that's not already in input mode
|
||||
const showTabHint =
|
||||
(focusedFeedbackType === 'accept' && !acceptInputMode) ||
|
||||
(focusedFeedbackType === 'reject' && !rejectInputMode)
|
||||
|
||||
// Transform options to Select-compatible format
|
||||
const selectOptions = useMemo((): OptionWithDescription<T>[] => {
|
||||
return options.map(opt => {
|
||||
const { value, label, feedbackConfig } = opt
|
||||
|
||||
// No feedback config = simple option
|
||||
if (!feedbackConfig) {
|
||||
return {
|
||||
label,
|
||||
value
|
||||
};
|
||||
};
|
||||
$[9] = acceptInputMode;
|
||||
$[10] = rejectInputMode;
|
||||
$[11] = t4;
|
||||
} else {
|
||||
t4 = $[11];
|
||||
}
|
||||
t3 = options.map(t4);
|
||||
$[5] = acceptInputMode;
|
||||
$[6] = options;
|
||||
$[7] = rejectInputMode;
|
||||
$[8] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
}
|
||||
const selectOptions = t3;
|
||||
let t4;
|
||||
if ($[12] !== acceptInputMode || $[13] !== options || $[14] !== rejectInputMode || $[15] !== toolAnalyticsContext?.isMcp || $[16] !== toolAnalyticsContext?.toolName) {
|
||||
t4 = value_0 => {
|
||||
const option = options.find(opt_1 => opt_1.value === value_0);
|
||||
if (!option?.feedbackConfig) {
|
||||
return;
|
||||
value,
|
||||
}
|
||||
}
|
||||
const {
|
||||
type: type_0
|
||||
} = option.feedbackConfig;
|
||||
|
||||
const { type, placeholder } = feedbackConfig
|
||||
const isInputMode = type === 'accept' ? acceptInputMode : rejectInputMode
|
||||
const onChange = type === 'accept' ? setAcceptFeedback : setRejectFeedback
|
||||
const defaultPlaceholder = DEFAULT_PLACEHOLDERS[type]
|
||||
|
||||
// When in input mode, show input field
|
||||
if (isInputMode) {
|
||||
return {
|
||||
type: 'input' as const,
|
||||
label,
|
||||
value,
|
||||
placeholder: placeholder ?? defaultPlaceholder,
|
||||
onChange,
|
||||
allowEmptySubmitToCancel: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Not in input mode - show simple option
|
||||
return {
|
||||
label,
|
||||
value,
|
||||
}
|
||||
})
|
||||
}, [options, acceptInputMode, rejectInputMode])
|
||||
|
||||
// Handle Tab key to toggle input mode
|
||||
const handleInputModeToggle = useCallback(
|
||||
(value: T) => {
|
||||
const option = options.find(opt => opt.value === value)
|
||||
if (!option?.feedbackConfig) return
|
||||
|
||||
const { type } = option.feedbackConfig
|
||||
const analyticsProps = {
|
||||
toolName: toolAnalyticsContext?.toolName as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
isMcp: toolAnalyticsContext?.isMcp ?? false
|
||||
};
|
||||
if (type_0 === "accept") {
|
||||
toolName:
|
||||
toolAnalyticsContext?.toolName as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
isMcp: toolAnalyticsContext?.isMcp ?? false,
|
||||
}
|
||||
|
||||
if (type === 'accept') {
|
||||
if (acceptInputMode) {
|
||||
setAcceptInputMode(false);
|
||||
logEvent("tengu_accept_feedback_mode_collapsed", analyticsProps);
|
||||
setAcceptInputMode(false)
|
||||
logEvent('tengu_accept_feedback_mode_collapsed', analyticsProps)
|
||||
} else {
|
||||
setAcceptInputMode(true);
|
||||
setAcceptFeedbackModeEntered(true);
|
||||
logEvent("tengu_accept_feedback_mode_entered", analyticsProps);
|
||||
setAcceptInputMode(true)
|
||||
setAcceptFeedbackModeEntered(true)
|
||||
logEvent('tengu_accept_feedback_mode_entered', analyticsProps)
|
||||
}
|
||||
} else {
|
||||
if (type_0 === "reject") {
|
||||
if (rejectInputMode) {
|
||||
setRejectInputMode(false);
|
||||
logEvent("tengu_reject_feedback_mode_collapsed", analyticsProps);
|
||||
} else {
|
||||
setRejectInputMode(true);
|
||||
setRejectFeedbackModeEntered(true);
|
||||
logEvent("tengu_reject_feedback_mode_entered", analyticsProps);
|
||||
}
|
||||
} else if (type === 'reject') {
|
||||
if (rejectInputMode) {
|
||||
setRejectInputMode(false)
|
||||
logEvent('tengu_reject_feedback_mode_collapsed', analyticsProps)
|
||||
} else {
|
||||
setRejectInputMode(true)
|
||||
setRejectFeedbackModeEntered(true)
|
||||
logEvent('tengu_reject_feedback_mode_entered', analyticsProps)
|
||||
}
|
||||
}
|
||||
};
|
||||
$[12] = acceptInputMode;
|
||||
$[13] = options;
|
||||
$[14] = rejectInputMode;
|
||||
$[15] = toolAnalyticsContext?.isMcp;
|
||||
$[16] = toolAnalyticsContext?.toolName;
|
||||
$[17] = t4;
|
||||
} else {
|
||||
t4 = $[17];
|
||||
}
|
||||
const handleInputModeToggle = t4;
|
||||
let t5;
|
||||
if ($[18] !== acceptFeedback || $[19] !== acceptFeedbackModeEntered || $[20] !== onSelect || $[21] !== options || $[22] !== rejectFeedback || $[23] !== rejectFeedbackModeEntered || $[24] !== toolAnalyticsContext?.isMcp || $[25] !== toolAnalyticsContext?.toolName) {
|
||||
t5 = value_1 => {
|
||||
const option_0 = options.find(opt_2 => opt_2.value === value_1);
|
||||
if (!option_0) {
|
||||
return;
|
||||
}
|
||||
let feedback;
|
||||
if (option_0.feedbackConfig) {
|
||||
const rawFeedback = option_0.feedbackConfig.type === "accept" ? acceptFeedback : rejectFeedback;
|
||||
const trimmedFeedback = rawFeedback.trim();
|
||||
},
|
||||
[options, acceptInputMode, rejectInputMode, toolAnalyticsContext],
|
||||
)
|
||||
|
||||
// Handle selection
|
||||
const handleSelect = useCallback(
|
||||
(value: T) => {
|
||||
const option = options.find(opt => opt.value === value)
|
||||
if (!option) return
|
||||
|
||||
// Get feedback if applicable
|
||||
let feedback: string | undefined
|
||||
if (option.feedbackConfig) {
|
||||
const rawFeedback =
|
||||
option.feedbackConfig.type === 'accept'
|
||||
? acceptFeedback
|
||||
: rejectFeedback
|
||||
const trimmedFeedback = rawFeedback.trim()
|
||||
|
||||
if (trimmedFeedback) {
|
||||
feedback = trimmedFeedback;
|
||||
feedback = trimmedFeedback
|
||||
}
|
||||
const analyticsProps_0 = {
|
||||
toolName: toolAnalyticsContext?.toolName as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
|
||||
// Log accept/reject submission with feedback context
|
||||
const analyticsProps = {
|
||||
toolName:
|
||||
toolAnalyticsContext?.toolName as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
isMcp: toolAnalyticsContext?.isMcp ?? false,
|
||||
has_instructions: !!trimmedFeedback,
|
||||
instructions_length: trimmedFeedback?.length ?? 0,
|
||||
entered_feedback_mode: option_0.feedbackConfig.type === "accept" ? acceptFeedbackModeEntered : rejectFeedbackModeEntered
|
||||
};
|
||||
if (option_0.feedbackConfig.type === "accept") {
|
||||
logEvent("tengu_accept_submitted", analyticsProps_0);
|
||||
} else {
|
||||
if (option_0.feedbackConfig.type === "reject") {
|
||||
logEvent("tengu_reject_submitted", analyticsProps_0);
|
||||
}
|
||||
entered_feedback_mode:
|
||||
option.feedbackConfig.type === 'accept'
|
||||
? acceptFeedbackModeEntered
|
||||
: rejectFeedbackModeEntered,
|
||||
}
|
||||
|
||||
if (option.feedbackConfig.type === 'accept') {
|
||||
logEvent('tengu_accept_submitted', analyticsProps)
|
||||
} else if (option.feedbackConfig.type === 'reject') {
|
||||
logEvent('tengu_reject_submitted', analyticsProps)
|
||||
}
|
||||
}
|
||||
onSelect(value_1, feedback);
|
||||
};
|
||||
$[18] = acceptFeedback;
|
||||
$[19] = acceptFeedbackModeEntered;
|
||||
$[20] = onSelect;
|
||||
$[21] = options;
|
||||
$[22] = rejectFeedback;
|
||||
$[23] = rejectFeedbackModeEntered;
|
||||
$[24] = toolAnalyticsContext?.isMcp;
|
||||
$[25] = toolAnalyticsContext?.toolName;
|
||||
$[26] = t5;
|
||||
} else {
|
||||
t5 = $[26];
|
||||
}
|
||||
const handleSelect = t5;
|
||||
let handlers;
|
||||
if ($[27] !== handleSelect || $[28] !== options) {
|
||||
handlers = {};
|
||||
for (const opt_3 of options) {
|
||||
if (opt_3.keybinding) {
|
||||
handlers[opt_3.keybinding] = () => handleSelect(opt_3.value);
|
||||
|
||||
onSelect(value, feedback)
|
||||
},
|
||||
[
|
||||
options,
|
||||
acceptFeedback,
|
||||
rejectFeedback,
|
||||
onSelect,
|
||||
toolAnalyticsContext,
|
||||
acceptFeedbackModeEntered,
|
||||
rejectFeedbackModeEntered,
|
||||
],
|
||||
)
|
||||
|
||||
// Register keybinding handlers for options that have a keybinding set
|
||||
const keybindingHandlers = useMemo(() => {
|
||||
const handlers: Record<string, () => void> = {}
|
||||
for (const opt of options) {
|
||||
if (opt.keybinding) {
|
||||
handlers[opt.keybinding] = () => handleSelect(opt.value)
|
||||
}
|
||||
}
|
||||
$[27] = handleSelect;
|
||||
$[28] = options;
|
||||
$[29] = handlers;
|
||||
} else {
|
||||
handlers = $[29];
|
||||
}
|
||||
const keybindingHandlers = handlers;
|
||||
let t6;
|
||||
if ($[30] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t6 = {
|
||||
context: "Confirmation"
|
||||
};
|
||||
$[30] = t6;
|
||||
} else {
|
||||
t6 = $[30];
|
||||
}
|
||||
useKeybindings(keybindingHandlers, t6);
|
||||
let t7;
|
||||
if ($[31] !== onCancel || $[32] !== setAppState) {
|
||||
t7 = () => {
|
||||
logEvent("tengu_permission_request_escape", {});
|
||||
setAppState(_temp);
|
||||
onCancel?.();
|
||||
};
|
||||
$[31] = onCancel;
|
||||
$[32] = setAppState;
|
||||
$[33] = t7;
|
||||
} else {
|
||||
t7 = $[33];
|
||||
}
|
||||
const handleCancel = t7;
|
||||
let t8;
|
||||
if ($[34] !== question) {
|
||||
t8 = typeof question === "string" ? <Text>{question}</Text> : question;
|
||||
$[34] = question;
|
||||
$[35] = t8;
|
||||
} else {
|
||||
t8 = $[35];
|
||||
}
|
||||
let t9;
|
||||
if ($[36] !== acceptFeedback || $[37] !== acceptInputMode || $[38] !== options || $[39] !== rejectFeedback || $[40] !== rejectInputMode) {
|
||||
t9 = value_2 => {
|
||||
const newOption = options.find(opt_4 => opt_4.value === value_2);
|
||||
if (newOption?.feedbackConfig?.type !== "accept" && acceptInputMode && !acceptFeedback.trim()) {
|
||||
setAcceptInputMode(false);
|
||||
}
|
||||
if (newOption?.feedbackConfig?.type !== "reject" && rejectInputMode && !rejectFeedback.trim()) {
|
||||
setRejectInputMode(false);
|
||||
}
|
||||
setFocusedValue(value_2);
|
||||
};
|
||||
$[36] = acceptFeedback;
|
||||
$[37] = acceptInputMode;
|
||||
$[38] = options;
|
||||
$[39] = rejectFeedback;
|
||||
$[40] = rejectInputMode;
|
||||
$[41] = t9;
|
||||
} else {
|
||||
t9 = $[41];
|
||||
}
|
||||
let t10;
|
||||
if ($[42] !== handleCancel || $[43] !== handleInputModeToggle || $[44] !== handleSelect || $[45] !== selectOptions || $[46] !== t9) {
|
||||
t10 = <Select options={selectOptions} inlineDescriptions={true} onChange={handleSelect} onCancel={handleCancel} onFocus={t9} onInputModeToggle={handleInputModeToggle} />;
|
||||
$[42] = handleCancel;
|
||||
$[43] = handleInputModeToggle;
|
||||
$[44] = handleSelect;
|
||||
$[45] = selectOptions;
|
||||
$[46] = t9;
|
||||
$[47] = t10;
|
||||
} else {
|
||||
t10 = $[47];
|
||||
}
|
||||
const t11 = showTabHint && " \xB7 Tab to amend";
|
||||
let t12;
|
||||
if ($[48] !== t11) {
|
||||
t12 = <Box marginTop={1}><Text dimColor={true}>Esc to cancel{t11}</Text></Box>;
|
||||
$[48] = t11;
|
||||
$[49] = t12;
|
||||
} else {
|
||||
t12 = $[49];
|
||||
}
|
||||
let t13;
|
||||
if ($[50] !== t10 || $[51] !== t12 || $[52] !== t8) {
|
||||
t13 = <Box flexDirection="column">{t8}{t10}{t12}</Box>;
|
||||
$[50] = t10;
|
||||
$[51] = t12;
|
||||
$[52] = t8;
|
||||
$[53] = t13;
|
||||
} else {
|
||||
t13 = $[53];
|
||||
}
|
||||
return t13;
|
||||
}
|
||||
function _temp(prev) {
|
||||
return {
|
||||
...prev,
|
||||
attribution: {
|
||||
...prev.attribution,
|
||||
escapeCount: prev.attribution.escapeCount + 1
|
||||
}
|
||||
};
|
||||
return handlers
|
||||
}, [options, handleSelect])
|
||||
|
||||
useKeybindings(keybindingHandlers, { context: 'Confirmation' })
|
||||
|
||||
// Handle cancel (Esc)
|
||||
const handleCancel = useCallback(() => {
|
||||
logEvent('tengu_permission_request_escape', {})
|
||||
// Increment escape count for attribution tracking
|
||||
setAppState(prev => ({
|
||||
...prev,
|
||||
attribution: {
|
||||
...prev.attribution,
|
||||
escapeCount: prev.attribution.escapeCount + 1,
|
||||
},
|
||||
}))
|
||||
onCancel?.()
|
||||
}, [onCancel, setAppState])
|
||||
|
||||
return (
|
||||
<Box flexDirection="column">
|
||||
{typeof question === 'string' ? <Text>{question}</Text> : question}
|
||||
<Select
|
||||
options={selectOptions}
|
||||
inlineDescriptions
|
||||
onChange={handleSelect}
|
||||
onCancel={handleCancel}
|
||||
onFocus={value => {
|
||||
// Reset input mode when navigating away, but only if no text typed
|
||||
const newOption = options.find(opt => opt.value === value)
|
||||
if (
|
||||
newOption?.feedbackConfig?.type !== 'accept' &&
|
||||
acceptInputMode &&
|
||||
!acceptFeedback.trim()
|
||||
) {
|
||||
setAcceptInputMode(false)
|
||||
}
|
||||
if (
|
||||
newOption?.feedbackConfig?.type !== 'reject' &&
|
||||
rejectInputMode &&
|
||||
!rejectFeedback.trim()
|
||||
) {
|
||||
setRejectInputMode(false)
|
||||
}
|
||||
setFocusedValue(value)
|
||||
}}
|
||||
onInputModeToggle={handleInputModeToggle}
|
||||
/>
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>Esc to cancel{showTabHint && ' · Tab to amend'}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user