mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-18 06:15: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,4 +1,3 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
/**
|
||||
* HooksConfigMenu is a read-only browser for configured hooks.
|
||||
*
|
||||
@@ -11,567 +10,324 @@ import { c as _c } from "react/compiler-runtime";
|
||||
* command-type hooks and duplicating the settings.json editing surface
|
||||
* in-menu for all four types would be a maintenance burden.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import { useAppState, useAppStateStore } from 'src/state/AppState.js';
|
||||
import type { CommandResultDisplay } from '../../commands.js';
|
||||
import { useSettingsChange } from '../../hooks/useSettingsChange.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { useKeybinding } from '../../keybindings/useKeybinding.js';
|
||||
import { getHookEventMetadata, getHooksForMatcher, getMatcherMetadata, getSortedMatchersForEvent, groupHooksByEventAndMatcher } from '../../utils/hooks/hooksConfigManager.js';
|
||||
import type { IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
|
||||
import { getSettings_DEPRECATED, getSettingsForSource } from '../../utils/settings/settings.js';
|
||||
import { plural } from '../../utils/stringUtils.js';
|
||||
import { Dialog } from '../design-system/Dialog.js';
|
||||
import { SelectEventMode } from './SelectEventMode.js';
|
||||
import { SelectHookMode } from './SelectHookMode.js';
|
||||
import { SelectMatcherMode } from './SelectMatcherMode.js';
|
||||
import { ViewHookMode } from './ViewHookMode.js';
|
||||
import * as React from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import { useAppState, useAppStateStore } from 'src/state/AppState.js'
|
||||
import type { CommandResultDisplay } from '../../commands.js'
|
||||
import { useSettingsChange } from '../../hooks/useSettingsChange.js'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import { useKeybinding } from '../../keybindings/useKeybinding.js'
|
||||
import {
|
||||
getHookEventMetadata,
|
||||
getHooksForMatcher,
|
||||
getMatcherMetadata,
|
||||
getSortedMatchersForEvent,
|
||||
groupHooksByEventAndMatcher,
|
||||
} from '../../utils/hooks/hooksConfigManager.js'
|
||||
import type { IndividualHookConfig } from '../../utils/hooks/hooksSettings.js'
|
||||
import {
|
||||
getSettings_DEPRECATED,
|
||||
getSettingsForSource,
|
||||
} from '../../utils/settings/settings.js'
|
||||
import { plural } from '../../utils/stringUtils.js'
|
||||
import { Dialog } from '../design-system/Dialog.js'
|
||||
import { SelectEventMode } from './SelectEventMode.js'
|
||||
import { SelectHookMode } from './SelectHookMode.js'
|
||||
import { SelectMatcherMode } from './SelectMatcherMode.js'
|
||||
import { ViewHookMode } from './ViewHookMode.js'
|
||||
|
||||
type Props = {
|
||||
toolNames: string[];
|
||||
onExit: (result?: string, options?: {
|
||||
display?: CommandResultDisplay;
|
||||
}) => void;
|
||||
};
|
||||
type ModeState = {
|
||||
mode: 'select-event';
|
||||
} | {
|
||||
mode: 'select-matcher';
|
||||
event: HookEvent;
|
||||
} | {
|
||||
mode: 'select-hook';
|
||||
event: HookEvent;
|
||||
matcher: string;
|
||||
} | {
|
||||
mode: 'view-hook';
|
||||
event: HookEvent;
|
||||
hook: IndividualHookConfig;
|
||||
};
|
||||
export function HooksConfigMenu(t0) {
|
||||
const $ = _c(100);
|
||||
const {
|
||||
toolNames,
|
||||
onExit
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = {
|
||||
mode: "select-event"
|
||||
};
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
const [modeState, setModeState] = useState(t1);
|
||||
const [disabledByPolicy, setDisabledByPolicy] = useState(_temp);
|
||||
const [restrictedByPolicy, setRestrictedByPolicy] = useState(_temp2);
|
||||
let t2;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = source => {
|
||||
if (source === "policySettings") {
|
||||
const settings_0 = getSettings_DEPRECATED();
|
||||
const hooksDisabled_0 = settings_0?.disableAllHooks === true;
|
||||
setDisabledByPolicy(hooksDisabled_0 && getSettingsForSource("policySettings")?.disableAllHooks === true);
|
||||
setRestrictedByPolicy(getSettingsForSource("policySettings")?.allowManagedHooksOnly === true);
|
||||
}
|
||||
};
|
||||
$[1] = t2;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
}
|
||||
useSettingsChange(t2);
|
||||
const mode = modeState.mode;
|
||||
const selectedEvent = "event" in modeState ? modeState.event : "PreToolUse";
|
||||
const selectedMatcher = "matcher" in modeState ? modeState.matcher : null;
|
||||
const mcp = useAppState(_temp3);
|
||||
const appStateStore = useAppStateStore();
|
||||
let t3;
|
||||
if ($[2] !== mcp.tools || $[3] !== toolNames) {
|
||||
t3 = [...toolNames, ...mcp.tools.map(_temp4)];
|
||||
$[2] = mcp.tools;
|
||||
$[3] = toolNames;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
const combinedToolNames = t3;
|
||||
let t4;
|
||||
if ($[5] !== appStateStore || $[6] !== combinedToolNames) {
|
||||
t4 = groupHooksByEventAndMatcher(appStateStore.getState(), combinedToolNames);
|
||||
$[5] = appStateStore;
|
||||
$[6] = combinedToolNames;
|
||||
$[7] = t4;
|
||||
} else {
|
||||
t4 = $[7];
|
||||
}
|
||||
const hooksByEventAndMatcher = t4;
|
||||
let t5;
|
||||
if ($[8] !== hooksByEventAndMatcher || $[9] !== selectedEvent) {
|
||||
t5 = getSortedMatchersForEvent(hooksByEventAndMatcher, selectedEvent);
|
||||
$[8] = hooksByEventAndMatcher;
|
||||
$[9] = selectedEvent;
|
||||
$[10] = t5;
|
||||
} else {
|
||||
t5 = $[10];
|
||||
}
|
||||
const sortedMatchersForSelectedEvent = t5;
|
||||
let t6;
|
||||
if ($[11] !== hooksByEventAndMatcher || $[12] !== selectedEvent || $[13] !== selectedMatcher) {
|
||||
t6 = getHooksForMatcher(hooksByEventAndMatcher, selectedEvent, selectedMatcher);
|
||||
$[11] = hooksByEventAndMatcher;
|
||||
$[12] = selectedEvent;
|
||||
$[13] = selectedMatcher;
|
||||
$[14] = t6;
|
||||
} else {
|
||||
t6 = $[14];
|
||||
}
|
||||
const hooksForSelectedMatcher = t6;
|
||||
let t7;
|
||||
if ($[15] !== onExit) {
|
||||
t7 = () => {
|
||||
onExit("Hooks dialog dismissed", {
|
||||
display: "system"
|
||||
});
|
||||
};
|
||||
$[15] = onExit;
|
||||
$[16] = t7;
|
||||
} else {
|
||||
t7 = $[16];
|
||||
}
|
||||
const handleExit = t7;
|
||||
const t8 = mode === "select-event";
|
||||
let t9;
|
||||
if ($[17] !== t8) {
|
||||
t9 = {
|
||||
context: "Confirmation",
|
||||
isActive: t8
|
||||
};
|
||||
$[17] = t8;
|
||||
$[18] = t9;
|
||||
} else {
|
||||
t9 = $[18];
|
||||
}
|
||||
useKeybinding("confirm:no", handleExit, t9);
|
||||
let t10;
|
||||
if ($[19] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t10 = () => {
|
||||
setModeState({
|
||||
mode: "select-event"
|
||||
});
|
||||
};
|
||||
$[19] = t10;
|
||||
} else {
|
||||
t10 = $[19];
|
||||
}
|
||||
const t11 = mode === "select-matcher";
|
||||
let t12;
|
||||
if ($[20] !== t11) {
|
||||
t12 = {
|
||||
context: "Confirmation",
|
||||
isActive: t11
|
||||
};
|
||||
$[20] = t11;
|
||||
$[21] = t12;
|
||||
} else {
|
||||
t12 = $[21];
|
||||
}
|
||||
useKeybinding("confirm:no", t10, t12);
|
||||
let t13;
|
||||
if ($[22] !== combinedToolNames || $[23] !== modeState) {
|
||||
t13 = () => {
|
||||
if ("event" in modeState) {
|
||||
if (getMatcherMetadata(modeState.event, combinedToolNames) !== undefined) {
|
||||
setModeState({
|
||||
mode: "select-matcher",
|
||||
event: modeState.event
|
||||
});
|
||||
toolNames: string[]
|
||||
onExit: (
|
||||
result?: string,
|
||||
options?: { display?: CommandResultDisplay },
|
||||
) => void
|
||||
}
|
||||
|
||||
type ModeState =
|
||||
| { mode: 'select-event' }
|
||||
| { mode: 'select-matcher'; event: HookEvent }
|
||||
| { mode: 'select-hook'; event: HookEvent; matcher: string }
|
||||
| { mode: 'view-hook'; event: HookEvent; hook: IndividualHookConfig }
|
||||
|
||||
export function HooksConfigMenu({ toolNames, onExit }: Props): React.ReactNode {
|
||||
const [modeState, setModeState] = useState<ModeState>({
|
||||
mode: 'select-event',
|
||||
})
|
||||
// Cache whether hooks are disabled by policy settings.
|
||||
// getSettingsForSource() is expensive (file read + JSON parse + validation),
|
||||
// so we compute it once on mount and only re-compute when policy settings change.
|
||||
// Short-circuit evaluation ensures we skip the expensive check when hooks aren't disabled.
|
||||
const [disabledByPolicy, setDisabledByPolicy] = useState(() => {
|
||||
const settings = getSettings_DEPRECATED()
|
||||
const hooksDisabled = settings?.disableAllHooks === true
|
||||
return (
|
||||
hooksDisabled &&
|
||||
getSettingsForSource('policySettings')?.disableAllHooks === true
|
||||
)
|
||||
})
|
||||
|
||||
// Check if hooks are restricted to managed-only by policy
|
||||
const [restrictedByPolicy, setRestrictedByPolicy] = useState(() => {
|
||||
return (
|
||||
getSettingsForSource('policySettings')?.allowManagedHooksOnly === true
|
||||
)
|
||||
})
|
||||
|
||||
// Update cached values when policy settings change
|
||||
useSettingsChange(source => {
|
||||
if (source === 'policySettings') {
|
||||
const settings = getSettings_DEPRECATED()
|
||||
const hooksDisabled = settings?.disableAllHooks === true
|
||||
setDisabledByPolicy(
|
||||
hooksDisabled &&
|
||||
getSettingsForSource('policySettings')?.disableAllHooks === true,
|
||||
)
|
||||
setRestrictedByPolicy(
|
||||
getSettingsForSource('policySettings')?.allowManagedHooksOnly === true,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// Extract commonly used values from modeState for convenience
|
||||
const mode = modeState.mode
|
||||
const selectedEvent = 'event' in modeState ? modeState.event : 'PreToolUse'
|
||||
const selectedMatcher = 'matcher' in modeState ? modeState.matcher : null
|
||||
|
||||
const mcp = useAppState(s => s.mcp)
|
||||
const appStateStore = useAppStateStore()
|
||||
const combinedToolNames = useMemo(
|
||||
() => [...toolNames, ...mcp.tools.map(tool => tool.name)],
|
||||
[toolNames, mcp.tools],
|
||||
)
|
||||
|
||||
const hooksByEventAndMatcher = useMemo(
|
||||
() =>
|
||||
groupHooksByEventAndMatcher(appStateStore.getState(), combinedToolNames),
|
||||
[combinedToolNames, appStateStore],
|
||||
)
|
||||
|
||||
const sortedMatchersForSelectedEvent = useMemo(
|
||||
() => getSortedMatchersForEvent(hooksByEventAndMatcher, selectedEvent),
|
||||
[hooksByEventAndMatcher, selectedEvent],
|
||||
)
|
||||
|
||||
const hooksForSelectedMatcher = useMemo(
|
||||
() =>
|
||||
getHooksForMatcher(
|
||||
hooksByEventAndMatcher,
|
||||
selectedEvent,
|
||||
selectedMatcher,
|
||||
),
|
||||
[hooksByEventAndMatcher, selectedEvent, selectedMatcher],
|
||||
)
|
||||
|
||||
// Handler for exiting the dialog
|
||||
const handleExit = useCallback(() => {
|
||||
onExit('Hooks dialog dismissed', { display: 'system' })
|
||||
}, [onExit])
|
||||
|
||||
// Escape handling for select-event mode - exit the menu
|
||||
useKeybinding('confirm:no', handleExit, {
|
||||
context: 'Confirmation',
|
||||
isActive: mode === 'select-event',
|
||||
})
|
||||
|
||||
// Escape handling for select-matcher mode - go to select-event
|
||||
useKeybinding(
|
||||
'confirm:no',
|
||||
() => {
|
||||
setModeState({ mode: 'select-event' })
|
||||
},
|
||||
{
|
||||
context: 'Confirmation',
|
||||
isActive: mode === 'select-matcher',
|
||||
},
|
||||
)
|
||||
|
||||
// Escape handling for select-hook mode - go to select-matcher or select-event
|
||||
useKeybinding(
|
||||
'confirm:no',
|
||||
() => {
|
||||
if ('event' in modeState) {
|
||||
if (
|
||||
getMatcherMetadata(modeState.event, combinedToolNames) !== undefined
|
||||
) {
|
||||
setModeState({ mode: 'select-matcher', event: modeState.event })
|
||||
} else {
|
||||
setModeState({
|
||||
mode: "select-event"
|
||||
});
|
||||
setModeState({ mode: 'select-event' })
|
||||
}
|
||||
}
|
||||
};
|
||||
$[22] = combinedToolNames;
|
||||
$[23] = modeState;
|
||||
$[24] = t13;
|
||||
} else {
|
||||
t13 = $[24];
|
||||
}
|
||||
const t14 = mode === "select-hook";
|
||||
let t15;
|
||||
if ($[25] !== t14) {
|
||||
t15 = {
|
||||
context: "Confirmation",
|
||||
isActive: t14
|
||||
};
|
||||
$[25] = t14;
|
||||
$[26] = t15;
|
||||
} else {
|
||||
t15 = $[26];
|
||||
}
|
||||
useKeybinding("confirm:no", t13, t15);
|
||||
let t16;
|
||||
if ($[27] !== modeState) {
|
||||
t16 = () => {
|
||||
if (modeState.mode === "view-hook") {
|
||||
const {
|
||||
event,
|
||||
hook
|
||||
} = modeState;
|
||||
},
|
||||
{
|
||||
context: 'Confirmation',
|
||||
isActive: mode === 'select-hook',
|
||||
},
|
||||
)
|
||||
|
||||
// Escape handling for view-hook mode - go to select-hook
|
||||
useKeybinding(
|
||||
'confirm:no',
|
||||
() => {
|
||||
if (modeState.mode === 'view-hook') {
|
||||
const { event, hook } = modeState
|
||||
setModeState({
|
||||
mode: "select-hook",
|
||||
mode: 'select-hook',
|
||||
event,
|
||||
matcher: hook.matcher || ""
|
||||
});
|
||||
matcher: hook.matcher || '',
|
||||
})
|
||||
}
|
||||
};
|
||||
$[27] = modeState;
|
||||
$[28] = t16;
|
||||
} else {
|
||||
t16 = $[28];
|
||||
}
|
||||
const t17 = mode === "view-hook";
|
||||
let t18;
|
||||
if ($[29] !== t17) {
|
||||
t18 = {
|
||||
context: "Confirmation",
|
||||
isActive: t17
|
||||
};
|
||||
$[29] = t17;
|
||||
$[30] = t18;
|
||||
} else {
|
||||
t18 = $[30];
|
||||
}
|
||||
useKeybinding("confirm:no", t16, t18);
|
||||
let t19;
|
||||
if ($[31] !== combinedToolNames) {
|
||||
t19 = getHookEventMetadata(combinedToolNames);
|
||||
$[31] = combinedToolNames;
|
||||
$[32] = t19;
|
||||
} else {
|
||||
t19 = $[32];
|
||||
}
|
||||
const hookEventMetadata = t19;
|
||||
const settings_1 = getSettings_DEPRECATED();
|
||||
const hooksDisabled_1 = settings_1?.disableAllHooks === true;
|
||||
let t20;
|
||||
if ($[33] !== hooksByEventAndMatcher) {
|
||||
const byEvent = {};
|
||||
let total = 0;
|
||||
for (const [event_0, matchers] of Object.entries(hooksByEventAndMatcher)) {
|
||||
const eventCount = Object.values(matchers).reduce(_temp5, 0);
|
||||
byEvent[event_0 as HookEvent] = eventCount;
|
||||
total = total + eventCount;
|
||||
}
|
||||
t20 = {
|
||||
hooksByEvent: byEvent,
|
||||
totalHooksCount: total
|
||||
};
|
||||
$[33] = hooksByEventAndMatcher;
|
||||
$[34] = t20;
|
||||
} else {
|
||||
t20 = $[34];
|
||||
}
|
||||
const {
|
||||
hooksByEvent,
|
||||
totalHooksCount
|
||||
} = t20;
|
||||
if (hooksDisabled_1) {
|
||||
let t21;
|
||||
if ($[35] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t21 = <Text bold={true}>disabled</Text>;
|
||||
$[35] = t21;
|
||||
} else {
|
||||
t21 = $[35];
|
||||
}
|
||||
const t22 = disabledByPolicy && " by a managed settings file";
|
||||
let t23;
|
||||
if ($[36] !== totalHooksCount) {
|
||||
t23 = <Text bold={true}>{totalHooksCount}</Text>;
|
||||
$[36] = totalHooksCount;
|
||||
$[37] = t23;
|
||||
} else {
|
||||
t23 = $[37];
|
||||
}
|
||||
let t24;
|
||||
if ($[38] !== totalHooksCount) {
|
||||
t24 = plural(totalHooksCount, "hook");
|
||||
$[38] = totalHooksCount;
|
||||
$[39] = t24;
|
||||
} else {
|
||||
t24 = $[39];
|
||||
}
|
||||
let t25;
|
||||
if ($[40] !== totalHooksCount) {
|
||||
t25 = plural(totalHooksCount, "is", "are");
|
||||
$[40] = totalHooksCount;
|
||||
$[41] = t25;
|
||||
} else {
|
||||
t25 = $[41];
|
||||
}
|
||||
let t26;
|
||||
if ($[42] !== t22 || $[43] !== t23 || $[44] !== t24 || $[45] !== t25) {
|
||||
t26 = <Text>All hooks are currently {t21}{t22}. You have{" "}{t23} configured{" "}{t24} that{" "}{t25} not running.</Text>;
|
||||
$[42] = t22;
|
||||
$[43] = t23;
|
||||
$[44] = t24;
|
||||
$[45] = t25;
|
||||
$[46] = t26;
|
||||
} else {
|
||||
t26 = $[46];
|
||||
}
|
||||
let t27;
|
||||
let t28;
|
||||
let t29;
|
||||
let t30;
|
||||
if ($[47] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t27 = <Box marginTop={1}><Text dimColor={true}>When hooks are disabled:</Text></Box>;
|
||||
t28 = <Text dimColor={true}>· No hook commands will execute</Text>;
|
||||
t29 = <Text dimColor={true}>· StatusLine will not be displayed</Text>;
|
||||
t30 = <Text dimColor={true}>· Tool operations will proceed without hook validation</Text>;
|
||||
$[47] = t27;
|
||||
$[48] = t28;
|
||||
$[49] = t29;
|
||||
$[50] = t30;
|
||||
} else {
|
||||
t27 = $[47];
|
||||
t28 = $[48];
|
||||
t29 = $[49];
|
||||
t30 = $[50];
|
||||
}
|
||||
let t31;
|
||||
if ($[51] !== t26) {
|
||||
t31 = <Box flexDirection="column">{t26}{t27}{t28}{t29}{t30}</Box>;
|
||||
$[51] = t26;
|
||||
$[52] = t31;
|
||||
} else {
|
||||
t31 = $[52];
|
||||
}
|
||||
let t32;
|
||||
if ($[53] !== disabledByPolicy) {
|
||||
t32 = !disabledByPolicy && <Text dimColor={true}>To re-enable hooks, remove "disableAllHooks" from settings.json or ask Claude.</Text>;
|
||||
$[53] = disabledByPolicy;
|
||||
$[54] = t32;
|
||||
} else {
|
||||
t32 = $[54];
|
||||
}
|
||||
let t33;
|
||||
if ($[55] !== t31 || $[56] !== t32) {
|
||||
t33 = <Box flexDirection="column" gap={1}>{t31}{t32}</Box>;
|
||||
$[55] = t31;
|
||||
$[56] = t32;
|
||||
$[57] = t33;
|
||||
} else {
|
||||
t33 = $[57];
|
||||
}
|
||||
let t34;
|
||||
if ($[58] !== handleExit || $[59] !== t33) {
|
||||
t34 = <Dialog title="Hook Configuration - Disabled" onCancel={handleExit} inputGuide={_temp6}>{t33}</Dialog>;
|
||||
$[58] = handleExit;
|
||||
$[59] = t33;
|
||||
$[60] = t34;
|
||||
} else {
|
||||
t34 = $[60];
|
||||
}
|
||||
return t34;
|
||||
},
|
||||
{
|
||||
context: 'Confirmation',
|
||||
isActive: mode === 'view-hook',
|
||||
},
|
||||
)
|
||||
|
||||
const hookEventMetadata = getHookEventMetadata(combinedToolNames)
|
||||
|
||||
// Check if hooks are disabled
|
||||
const settings = getSettings_DEPRECATED()
|
||||
const hooksDisabled = settings?.disableAllHooks === true
|
||||
|
||||
// Count hooks per event for the event-selection view, and the total.
|
||||
const { hooksByEvent, totalHooksCount } = useMemo(() => {
|
||||
const byEvent: Partial<Record<HookEvent, number>> = {}
|
||||
let total = 0
|
||||
for (const [event, matchers] of Object.entries(hooksByEventAndMatcher)) {
|
||||
const eventCount = Object.values(matchers).reduce(
|
||||
(sum, hooks) => sum + hooks.length,
|
||||
0,
|
||||
)
|
||||
byEvent[event as HookEvent] = eventCount
|
||||
total += eventCount
|
||||
}
|
||||
return { hooksByEvent: byEvent, totalHooksCount: total }
|
||||
}, [hooksByEventAndMatcher])
|
||||
|
||||
// If hooks are disabled, show an informational screen.
|
||||
// The menu is read-only, so we don't offer a re-enable button —
|
||||
// users can edit settings.json or ask Claude instead.
|
||||
if (hooksDisabled) {
|
||||
return (
|
||||
<Dialog
|
||||
title="Hook Configuration - Disabled"
|
||||
onCancel={handleExit}
|
||||
inputGuide={() => <Text>Esc to close</Text>}
|
||||
>
|
||||
<Box flexDirection="column" gap={1}>
|
||||
<Box flexDirection="column">
|
||||
<Text>
|
||||
All hooks are currently <Text bold>disabled</Text>
|
||||
{disabledByPolicy && ' by a managed settings file'}. You have{' '}
|
||||
<Text bold>{totalHooksCount}</Text> configured{' '}
|
||||
{plural(totalHooksCount, 'hook')} that{' '}
|
||||
{plural(totalHooksCount, 'is', 'are')} not running.
|
||||
</Text>
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>When hooks are disabled:</Text>
|
||||
</Box>
|
||||
<Text dimColor>· No hook commands will execute</Text>
|
||||
<Text dimColor>· StatusLine will not be displayed</Text>
|
||||
<Text dimColor>
|
||||
· Tool operations will proceed without hook validation
|
||||
</Text>
|
||||
</Box>
|
||||
{!disabledByPolicy && (
|
||||
<Text dimColor>
|
||||
To re-enable hooks, remove "disableAllHooks" from
|
||||
settings.json or ask Claude.
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
switch (modeState.mode) {
|
||||
case "select-event":
|
||||
{
|
||||
let t21;
|
||||
if ($[61] !== combinedToolNames) {
|
||||
t21 = event_2 => {
|
||||
if (getMatcherMetadata(event_2, combinedToolNames) !== undefined) {
|
||||
setModeState({
|
||||
mode: "select-matcher",
|
||||
event: event_2
|
||||
});
|
||||
case 'select-event':
|
||||
return (
|
||||
<SelectEventMode
|
||||
hookEventMetadata={hookEventMetadata}
|
||||
hooksByEvent={hooksByEvent}
|
||||
totalHooksCount={totalHooksCount}
|
||||
restrictedByPolicy={restrictedByPolicy}
|
||||
onSelectEvent={event => {
|
||||
if (getMatcherMetadata(event, combinedToolNames) !== undefined) {
|
||||
setModeState({ mode: 'select-matcher', event })
|
||||
} else {
|
||||
setModeState({
|
||||
mode: "select-hook",
|
||||
event: event_2,
|
||||
matcher: ""
|
||||
});
|
||||
setModeState({ mode: 'select-hook', event, matcher: '' })
|
||||
}
|
||||
};
|
||||
$[61] = combinedToolNames;
|
||||
$[62] = t21;
|
||||
} else {
|
||||
t21 = $[62];
|
||||
}
|
||||
let t22;
|
||||
if ($[63] !== handleExit || $[64] !== hookEventMetadata || $[65] !== hooksByEvent || $[66] !== restrictedByPolicy || $[67] !== t21 || $[68] !== totalHooksCount) {
|
||||
t22 = <SelectEventMode hookEventMetadata={hookEventMetadata} hooksByEvent={hooksByEvent} totalHooksCount={totalHooksCount} restrictedByPolicy={restrictedByPolicy} onSelectEvent={t21} onCancel={handleExit} />;
|
||||
$[63] = handleExit;
|
||||
$[64] = hookEventMetadata;
|
||||
$[65] = hooksByEvent;
|
||||
$[66] = restrictedByPolicy;
|
||||
$[67] = t21;
|
||||
$[68] = totalHooksCount;
|
||||
$[69] = t22;
|
||||
} else {
|
||||
t22 = $[69];
|
||||
}
|
||||
return t22;
|
||||
}
|
||||
case "select-matcher":
|
||||
{
|
||||
const t21 = hookEventMetadata[modeState.event];
|
||||
let t22;
|
||||
if ($[70] !== modeState.event) {
|
||||
t22 = matcher => {
|
||||
}}
|
||||
onCancel={handleExit}
|
||||
/>
|
||||
)
|
||||
case 'select-matcher':
|
||||
return (
|
||||
<SelectMatcherMode
|
||||
selectedEvent={modeState.event}
|
||||
matchersForSelectedEvent={sortedMatchersForSelectedEvent}
|
||||
hooksByEventAndMatcher={hooksByEventAndMatcher}
|
||||
eventDescription={hookEventMetadata[modeState.event].description}
|
||||
onSelect={matcher => {
|
||||
setModeState({
|
||||
mode: "select-hook",
|
||||
mode: 'select-hook',
|
||||
event: modeState.event,
|
||||
matcher
|
||||
});
|
||||
};
|
||||
$[70] = modeState.event;
|
||||
$[71] = t22;
|
||||
} else {
|
||||
t22 = $[71];
|
||||
}
|
||||
let t23;
|
||||
if ($[72] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t23 = () => {
|
||||
matcher,
|
||||
})
|
||||
}}
|
||||
onCancel={() => {
|
||||
setModeState({ mode: 'select-event' })
|
||||
}}
|
||||
/>
|
||||
)
|
||||
case 'select-hook':
|
||||
return (
|
||||
<SelectHookMode
|
||||
selectedEvent={modeState.event}
|
||||
selectedMatcher={modeState.matcher}
|
||||
hooksForSelectedMatcher={hooksForSelectedMatcher}
|
||||
hookEventMetadata={hookEventMetadata[modeState.event]}
|
||||
onSelect={hook => {
|
||||
setModeState({
|
||||
mode: "select-event"
|
||||
});
|
||||
};
|
||||
$[72] = t23;
|
||||
} else {
|
||||
t23 = $[72];
|
||||
}
|
||||
let t24;
|
||||
if ($[73] !== hooksByEventAndMatcher || $[74] !== modeState.event || $[75] !== sortedMatchersForSelectedEvent || $[76] !== t21.description || $[77] !== t22) {
|
||||
t24 = <SelectMatcherMode selectedEvent={modeState.event} matchersForSelectedEvent={sortedMatchersForSelectedEvent} hooksByEventAndMatcher={hooksByEventAndMatcher} eventDescription={t21.description} onSelect={t22} onCancel={t23} />;
|
||||
$[73] = hooksByEventAndMatcher;
|
||||
$[74] = modeState.event;
|
||||
$[75] = sortedMatchersForSelectedEvent;
|
||||
$[76] = t21.description;
|
||||
$[77] = t22;
|
||||
$[78] = t24;
|
||||
} else {
|
||||
t24 = $[78];
|
||||
}
|
||||
return t24;
|
||||
}
|
||||
case "select-hook":
|
||||
{
|
||||
const t21 = hookEventMetadata[modeState.event];
|
||||
let t22;
|
||||
if ($[79] !== modeState.event) {
|
||||
t22 = hook_1 => {
|
||||
setModeState({
|
||||
mode: "view-hook",
|
||||
mode: 'view-hook',
|
||||
event: modeState.event,
|
||||
hook: hook_1
|
||||
});
|
||||
};
|
||||
$[79] = modeState.event;
|
||||
$[80] = t22;
|
||||
} else {
|
||||
t22 = $[80];
|
||||
}
|
||||
let t23;
|
||||
if ($[81] !== combinedToolNames || $[82] !== modeState.event) {
|
||||
t23 = () => {
|
||||
if (getMatcherMetadata(modeState.event, combinedToolNames) !== undefined) {
|
||||
hook,
|
||||
})
|
||||
}}
|
||||
onCancel={() => {
|
||||
// Go back to matcher selection or event selection
|
||||
if (
|
||||
getMatcherMetadata(modeState.event, combinedToolNames) !==
|
||||
undefined
|
||||
) {
|
||||
setModeState({
|
||||
mode: "select-matcher",
|
||||
event: modeState.event
|
||||
});
|
||||
mode: 'select-matcher',
|
||||
event: modeState.event,
|
||||
})
|
||||
} else {
|
||||
setModeState({
|
||||
mode: "select-event"
|
||||
});
|
||||
setModeState({ mode: 'select-event' })
|
||||
}
|
||||
};
|
||||
$[81] = combinedToolNames;
|
||||
$[82] = modeState.event;
|
||||
$[83] = t23;
|
||||
} else {
|
||||
t23 = $[83];
|
||||
}
|
||||
let t24;
|
||||
if ($[84] !== hooksForSelectedMatcher || $[85] !== modeState.event || $[86] !== modeState.matcher || $[87] !== t21 || $[88] !== t22 || $[89] !== t23) {
|
||||
t24 = <SelectHookMode selectedEvent={modeState.event} selectedMatcher={modeState.matcher} hooksForSelectedMatcher={hooksForSelectedMatcher} hookEventMetadata={t21} onSelect={t22} onCancel={t23} />;
|
||||
$[84] = hooksForSelectedMatcher;
|
||||
$[85] = modeState.event;
|
||||
$[86] = modeState.matcher;
|
||||
$[87] = t21;
|
||||
$[88] = t22;
|
||||
$[89] = t23;
|
||||
$[90] = t24;
|
||||
} else {
|
||||
t24 = $[90];
|
||||
}
|
||||
return t24;
|
||||
}
|
||||
case "view-hook":
|
||||
{
|
||||
const t21 = modeState.hook;
|
||||
let t22;
|
||||
if ($[91] !== combinedToolNames || $[92] !== modeState.event) {
|
||||
t22 = getMatcherMetadata(modeState.event, combinedToolNames);
|
||||
$[91] = combinedToolNames;
|
||||
$[92] = modeState.event;
|
||||
$[93] = t22;
|
||||
} else {
|
||||
t22 = $[93];
|
||||
}
|
||||
const t23 = t22 !== undefined;
|
||||
let t24;
|
||||
if ($[94] !== modeState) {
|
||||
t24 = () => {
|
||||
const {
|
||||
event: event_1,
|
||||
hook: hook_0
|
||||
} = modeState;
|
||||
}}
|
||||
/>
|
||||
)
|
||||
case 'view-hook':
|
||||
return (
|
||||
<ViewHookMode
|
||||
selectedHook={modeState.hook}
|
||||
eventSupportsMatcher={
|
||||
getMatcherMetadata(modeState.event, combinedToolNames) !== undefined
|
||||
}
|
||||
onCancel={() => {
|
||||
const { event, hook } = modeState
|
||||
setModeState({
|
||||
mode: "select-hook",
|
||||
event: event_1,
|
||||
matcher: hook_0.matcher || ""
|
||||
});
|
||||
};
|
||||
$[94] = modeState;
|
||||
$[95] = t24;
|
||||
} else {
|
||||
t24 = $[95];
|
||||
}
|
||||
let t25;
|
||||
if ($[96] !== modeState.hook || $[97] !== t23 || $[98] !== t24) {
|
||||
t25 = <ViewHookMode selectedHook={t21} eventSupportsMatcher={t23} onCancel={t24} />;
|
||||
$[96] = modeState.hook;
|
||||
$[97] = t23;
|
||||
$[98] = t24;
|
||||
$[99] = t25;
|
||||
} else {
|
||||
t25 = $[99];
|
||||
}
|
||||
return t25;
|
||||
}
|
||||
mode: 'select-hook',
|
||||
event,
|
||||
matcher: hook.matcher || '',
|
||||
})
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
function _temp6() {
|
||||
return <Text>Esc to close</Text>;
|
||||
}
|
||||
function _temp5(sum, hooks) {
|
||||
return sum + hooks.length;
|
||||
}
|
||||
function _temp4(tool) {
|
||||
return tool.name;
|
||||
}
|
||||
function _temp3(s) {
|
||||
return s.mcp;
|
||||
}
|
||||
function _temp2() {
|
||||
return getSettingsForSource("policySettings")?.allowManagedHooksOnly === true;
|
||||
}
|
||||
function _temp() {
|
||||
const settings = getSettings_DEPRECATED();
|
||||
const hooksDisabled = settings?.disableAllHooks === true;
|
||||
return hooksDisabled && getSettingsForSource("policySettings")?.disableAllHooks === true;
|
||||
}
|
||||
|
||||
@@ -1,89 +1,49 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import * as React from 'react';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { useKeybinding } from '../../keybindings/useKeybinding.js';
|
||||
import type { PromptRequest } from '../../types/hooks.js';
|
||||
import { Select } from '../CustomSelect/select.js';
|
||||
import { PermissionDialog } from '../permissions/PermissionDialog.js';
|
||||
import * as React from 'react'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import { useKeybinding } from '../../keybindings/useKeybinding.js'
|
||||
import type { PromptRequest } from '../../types/hooks.js'
|
||||
import { Select } from '../CustomSelect/select.js'
|
||||
import { PermissionDialog } from '../permissions/PermissionDialog.js'
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
toolInputSummary?: string | null;
|
||||
request: PromptRequest;
|
||||
onRespond: (key: string) => void;
|
||||
onAbort: () => void;
|
||||
};
|
||||
export function PromptDialog(t0) {
|
||||
const $ = _c(15);
|
||||
const {
|
||||
title,
|
||||
toolInputSummary,
|
||||
request,
|
||||
onRespond,
|
||||
onAbort
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = {
|
||||
isActive: true
|
||||
};
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
useKeybinding("app:interrupt", onAbort, t1);
|
||||
let t2;
|
||||
if ($[1] !== request.options) {
|
||||
t2 = request.options.map(_temp);
|
||||
$[1] = request.options;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
const options = t2;
|
||||
let t3;
|
||||
if ($[3] !== toolInputSummary) {
|
||||
t3 = toolInputSummary ? <Text dimColor={true}>{toolInputSummary}</Text> : undefined;
|
||||
$[3] = toolInputSummary;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
let t4;
|
||||
if ($[5] !== onRespond) {
|
||||
t4 = value => {
|
||||
onRespond(value);
|
||||
};
|
||||
$[5] = onRespond;
|
||||
$[6] = t4;
|
||||
} else {
|
||||
t4 = $[6];
|
||||
}
|
||||
let t5;
|
||||
if ($[7] !== options || $[8] !== t4) {
|
||||
t5 = <Box flexDirection="column" paddingY={1}><Select options={options} onChange={t4} /></Box>;
|
||||
$[7] = options;
|
||||
$[8] = t4;
|
||||
$[9] = t5;
|
||||
} else {
|
||||
t5 = $[9];
|
||||
}
|
||||
let t6;
|
||||
if ($[10] !== request.message || $[11] !== t3 || $[12] !== t5 || $[13] !== title) {
|
||||
t6 = <PermissionDialog title={title} subtitle={request.message} titleRight={t3}>{t5}</PermissionDialog>;
|
||||
$[10] = request.message;
|
||||
$[11] = t3;
|
||||
$[12] = t5;
|
||||
$[13] = title;
|
||||
$[14] = t6;
|
||||
} else {
|
||||
t6 = $[14];
|
||||
}
|
||||
return t6;
|
||||
title: string
|
||||
toolInputSummary?: string | null
|
||||
request: PromptRequest
|
||||
onRespond: (key: string) => void
|
||||
onAbort: () => void
|
||||
}
|
||||
function _temp(opt) {
|
||||
return {
|
||||
|
||||
export function PromptDialog({
|
||||
title,
|
||||
toolInputSummary,
|
||||
request,
|
||||
onRespond,
|
||||
onAbort,
|
||||
}: Props): React.ReactNode {
|
||||
useKeybinding('app:interrupt', onAbort, { isActive: true })
|
||||
|
||||
const options = request.options.map(opt => ({
|
||||
label: opt.label,
|
||||
value: opt.key,
|
||||
description: opt.description
|
||||
};
|
||||
description: opt.description,
|
||||
}))
|
||||
|
||||
return (
|
||||
<PermissionDialog
|
||||
title={title}
|
||||
subtitle={request.message}
|
||||
titleRight={
|
||||
toolInputSummary ? <Text dimColor>{toolInputSummary}</Text> : undefined
|
||||
}
|
||||
>
|
||||
<Box flexDirection="column" paddingY={1}>
|
||||
<Select
|
||||
options={options}
|
||||
onChange={value => {
|
||||
onRespond(value)
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</PermissionDialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
/**
|
||||
* SelectEventMode is the entrypoint of the Hooks config menu, where the user
|
||||
* sees the list of available hook events.
|
||||
@@ -8,119 +7,84 @@ import { c as _c } from "react/compiler-runtime";
|
||||
* edit settings.json directly or ask Claude.
|
||||
*/
|
||||
|
||||
import figures from 'figures';
|
||||
import * as React from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js';
|
||||
import { Box, Link, Text } from '../../ink.js';
|
||||
import { plural } from '../../utils/stringUtils.js';
|
||||
import { Select } from '../CustomSelect/select.js';
|
||||
import { Dialog } from '../design-system/Dialog.js';
|
||||
import figures from 'figures'
|
||||
import * as React from 'react'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js'
|
||||
import { Box, Link, Text } from '../../ink.js'
|
||||
import { plural } from '../../utils/stringUtils.js'
|
||||
import { Select } from '../CustomSelect/select.js'
|
||||
import { Dialog } from '../design-system/Dialog.js'
|
||||
|
||||
type Props = {
|
||||
hookEventMetadata: Record<HookEvent, HookEventMetadata>;
|
||||
hooksByEvent: Partial<Record<HookEvent, number>>;
|
||||
totalHooksCount: number;
|
||||
restrictedByPolicy: boolean;
|
||||
onSelectEvent: (event: HookEvent) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
export function SelectEventMode(t0) {
|
||||
const $ = _c(23);
|
||||
const {
|
||||
hookEventMetadata,
|
||||
hooksByEvent,
|
||||
totalHooksCount,
|
||||
restrictedByPolicy,
|
||||
onSelectEvent,
|
||||
onCancel
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] !== totalHooksCount) {
|
||||
t1 = plural(totalHooksCount, "hook");
|
||||
$[0] = totalHooksCount;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const subtitle = `${totalHooksCount} ${t1} configured`;
|
||||
let t2;
|
||||
if ($[2] !== restrictedByPolicy) {
|
||||
t2 = restrictedByPolicy && <Box flexDirection="column"><Text color="suggestion">{figures.info} Hooks Restricted by Policy</Text><Text dimColor={true}>Only hooks from managed settings can run. User-defined hooks from ~/.claude/settings.json, .claude/settings.json, and .claude/settings.local.json are blocked.</Text></Box>;
|
||||
$[2] = restrictedByPolicy;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
let t3;
|
||||
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <Box flexDirection="column"><Text dimColor={true}>{figures.info} This menu is read-only. To add or modify hooks, edit settings.json directly or ask Claude.{" "}<Link url="https://code.claude.com/docs/en/hooks">Learn more</Link></Text></Box>;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
let t4;
|
||||
if ($[5] !== onSelectEvent) {
|
||||
t4 = value => {
|
||||
onSelectEvent(value as HookEvent);
|
||||
};
|
||||
$[5] = onSelectEvent;
|
||||
$[6] = t4;
|
||||
} else {
|
||||
t4 = $[6];
|
||||
}
|
||||
let t5;
|
||||
if ($[7] !== hookEventMetadata) {
|
||||
t5 = Object.entries(hookEventMetadata);
|
||||
$[7] = hookEventMetadata;
|
||||
$[8] = t5;
|
||||
} else {
|
||||
t5 = $[8];
|
||||
}
|
||||
let t6;
|
||||
if ($[9] !== hooksByEvent || $[10] !== t5) {
|
||||
t6 = t5.map(t7 => {
|
||||
const [name, metadata] = t7;
|
||||
const count = hooksByEvent[name as HookEvent] || 0;
|
||||
return {
|
||||
label: count > 0 ? <Text>{name} <Text color="suggestion">({count})</Text></Text> : name,
|
||||
value: name,
|
||||
description: metadata.summary
|
||||
};
|
||||
});
|
||||
$[9] = hooksByEvent;
|
||||
$[10] = t5;
|
||||
$[11] = t6;
|
||||
} else {
|
||||
t6 = $[11];
|
||||
}
|
||||
let t7;
|
||||
if ($[12] !== onCancel || $[13] !== t4 || $[14] !== t6) {
|
||||
t7 = <Box flexDirection="column"><Select onChange={t4} onCancel={onCancel} options={t6} /></Box>;
|
||||
$[12] = onCancel;
|
||||
$[13] = t4;
|
||||
$[14] = t6;
|
||||
$[15] = t7;
|
||||
} else {
|
||||
t7 = $[15];
|
||||
}
|
||||
let t8;
|
||||
if ($[16] !== t2 || $[17] !== t7) {
|
||||
t8 = <Box flexDirection="column" gap={1}>{t2}{t3}{t7}</Box>;
|
||||
$[16] = t2;
|
||||
$[17] = t7;
|
||||
$[18] = t8;
|
||||
} else {
|
||||
t8 = $[18];
|
||||
}
|
||||
let t9;
|
||||
if ($[19] !== onCancel || $[20] !== subtitle || $[21] !== t8) {
|
||||
t9 = <Dialog title="Hooks" subtitle={subtitle} onCancel={onCancel}>{t8}</Dialog>;
|
||||
$[19] = onCancel;
|
||||
$[20] = subtitle;
|
||||
$[21] = t8;
|
||||
$[22] = t9;
|
||||
} else {
|
||||
t9 = $[22];
|
||||
}
|
||||
return t9;
|
||||
hookEventMetadata: Record<HookEvent, HookEventMetadata>
|
||||
hooksByEvent: Partial<Record<HookEvent, number>>
|
||||
totalHooksCount: number
|
||||
restrictedByPolicy: boolean
|
||||
onSelectEvent: (event: HookEvent) => void
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
export function SelectEventMode({
|
||||
hookEventMetadata,
|
||||
hooksByEvent,
|
||||
totalHooksCount,
|
||||
restrictedByPolicy,
|
||||
onSelectEvent,
|
||||
onCancel,
|
||||
}: Props): React.ReactNode {
|
||||
const subtitle = `${totalHooksCount} ${plural(totalHooksCount, 'hook')} configured`
|
||||
|
||||
return (
|
||||
<Dialog title="Hooks" subtitle={subtitle} onCancel={onCancel}>
|
||||
<Box flexDirection="column" gap={1}>
|
||||
{restrictedByPolicy && (
|
||||
<Box flexDirection="column">
|
||||
<Text color="suggestion">
|
||||
{figures.info} Hooks Restricted by Policy
|
||||
</Text>
|
||||
<Text dimColor>
|
||||
Only hooks from managed settings can run. User-defined hooks from
|
||||
~/.claude/settings.json, .claude/settings.json, and
|
||||
.claude/settings.local.json are blocked.
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box flexDirection="column">
|
||||
<Text dimColor>
|
||||
{figures.info} This menu is read-only. To add or modify hooks, edit
|
||||
settings.json directly or ask Claude.{' '}
|
||||
<Link url="https://code.claude.com/docs/en/hooks">Learn more</Link>
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="column">
|
||||
<Select
|
||||
onChange={value => {
|
||||
onSelectEvent(value as HookEvent)
|
||||
}}
|
||||
onCancel={onCancel}
|
||||
options={Object.entries(hookEventMetadata).map(
|
||||
([name, metadata]) => {
|
||||
const count = hooksByEvent[name as HookEvent] || 0
|
||||
return {
|
||||
label:
|
||||
count > 0 ? (
|
||||
<Text>
|
||||
{name} <Text color="suggestion">({count})</Text>
|
||||
</Text>
|
||||
) : (
|
||||
name
|
||||
),
|
||||
value: name,
|
||||
description: metadata.summary,
|
||||
}
|
||||
},
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
/**
|
||||
* SelectHookMode shows all hooks configured for a given event+matcher pair.
|
||||
*
|
||||
@@ -6,106 +5,84 @@ import { c as _c } from "react/compiler-runtime";
|
||||
* and selecting a hook shows its read-only details instead of a delete
|
||||
* confirmation.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { getHookDisplayText, hookSourceHeaderDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
|
||||
import { Select } from '../CustomSelect/select.js';
|
||||
import { Dialog } from '../design-system/Dialog.js';
|
||||
import * as React from 'react'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import type { HookEventMetadata } from 'src/utils/hooks/hooksConfigManager.js'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import {
|
||||
getHookDisplayText,
|
||||
hookSourceHeaderDisplayString,
|
||||
type IndividualHookConfig,
|
||||
} from '../../utils/hooks/hooksSettings.js'
|
||||
import { Select } from '../CustomSelect/select.js'
|
||||
import { Dialog } from '../design-system/Dialog.js'
|
||||
|
||||
type Props = {
|
||||
selectedEvent: HookEvent;
|
||||
selectedMatcher: string | null;
|
||||
hooksForSelectedMatcher: IndividualHookConfig[];
|
||||
hookEventMetadata: HookEventMetadata;
|
||||
onSelect: (hook: IndividualHookConfig) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
export function SelectHookMode(t0) {
|
||||
const $ = _c(19);
|
||||
const {
|
||||
selectedEvent,
|
||||
selectedMatcher,
|
||||
hooksForSelectedMatcher,
|
||||
hookEventMetadata,
|
||||
onSelect,
|
||||
onCancel
|
||||
} = t0;
|
||||
const title = hookEventMetadata.matcherMetadata !== undefined ? `${selectedEvent} - Matcher: ${selectedMatcher || "(all)"}` : selectedEvent;
|
||||
selectedEvent: HookEvent
|
||||
selectedMatcher: string | null
|
||||
hooksForSelectedMatcher: IndividualHookConfig[]
|
||||
hookEventMetadata: HookEventMetadata
|
||||
onSelect: (hook: IndividualHookConfig) => void
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
export function SelectHookMode({
|
||||
selectedEvent,
|
||||
selectedMatcher,
|
||||
hooksForSelectedMatcher,
|
||||
hookEventMetadata,
|
||||
onSelect,
|
||||
onCancel,
|
||||
}: Props): React.ReactNode {
|
||||
const title =
|
||||
hookEventMetadata.matcherMetadata !== undefined
|
||||
? `${selectedEvent} - Matcher: ${selectedMatcher || '(all)'}`
|
||||
: selectedEvent
|
||||
|
||||
if (hooksForSelectedMatcher.length === 0) {
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = <Box flexDirection="column" gap={1}><Text dimColor={true}>No hooks configured for this event.</Text><Text dimColor={true}>To add hooks, edit settings.json directly or ask Claude.</Text></Box>;
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
let t2;
|
||||
if ($[1] !== hookEventMetadata.description || $[2] !== onCancel || $[3] !== title) {
|
||||
t2 = <Dialog title={title} subtitle={hookEventMetadata.description} onCancel={onCancel} inputGuide={_temp}>{t1}</Dialog>;
|
||||
$[1] = hookEventMetadata.description;
|
||||
$[2] = onCancel;
|
||||
$[3] = title;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
return t2;
|
||||
return (
|
||||
<Dialog
|
||||
title={title}
|
||||
subtitle={hookEventMetadata.description}
|
||||
onCancel={onCancel}
|
||||
inputGuide={() => <Text>Esc to go back</Text>}
|
||||
>
|
||||
<Box flexDirection="column" gap={1}>
|
||||
<Text dimColor>No hooks configured for this event.</Text>
|
||||
<Text dimColor>
|
||||
To add hooks, edit settings.json directly or ask Claude.
|
||||
</Text>
|
||||
</Box>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
const t1 = hookEventMetadata.description;
|
||||
let t2;
|
||||
if ($[5] !== hooksForSelectedMatcher) {
|
||||
t2 = hooksForSelectedMatcher.map(_temp2);
|
||||
$[5] = hooksForSelectedMatcher;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
let t3;
|
||||
if ($[7] !== hooksForSelectedMatcher || $[8] !== onSelect) {
|
||||
t3 = value => {
|
||||
const index_0 = parseInt(value, 10);
|
||||
const hook_0 = hooksForSelectedMatcher[index_0];
|
||||
if (hook_0) {
|
||||
onSelect(hook_0);
|
||||
}
|
||||
};
|
||||
$[7] = hooksForSelectedMatcher;
|
||||
$[8] = onSelect;
|
||||
$[9] = t3;
|
||||
} else {
|
||||
t3 = $[9];
|
||||
}
|
||||
let t4;
|
||||
if ($[10] !== onCancel || $[11] !== t2 || $[12] !== t3) {
|
||||
t4 = <Box flexDirection="column"><Select options={t2} onChange={t3} onCancel={onCancel} /></Box>;
|
||||
$[10] = onCancel;
|
||||
$[11] = t2;
|
||||
$[12] = t3;
|
||||
$[13] = t4;
|
||||
} else {
|
||||
t4 = $[13];
|
||||
}
|
||||
let t5;
|
||||
if ($[14] !== hookEventMetadata.description || $[15] !== onCancel || $[16] !== t4 || $[17] !== title) {
|
||||
t5 = <Dialog title={title} subtitle={t1} onCancel={onCancel}>{t4}</Dialog>;
|
||||
$[14] = hookEventMetadata.description;
|
||||
$[15] = onCancel;
|
||||
$[16] = t4;
|
||||
$[17] = title;
|
||||
$[18] = t5;
|
||||
} else {
|
||||
t5 = $[18];
|
||||
}
|
||||
return t5;
|
||||
}
|
||||
function _temp2(hook, index) {
|
||||
return {
|
||||
label: `[${hook.config.type}] ${getHookDisplayText(hook.config)}`,
|
||||
value: index.toString(),
|
||||
description: hook.source === "pluginHook" && hook.pluginName ? `${hookSourceHeaderDisplayString(hook.source)} (${hook.pluginName})` : hookSourceHeaderDisplayString(hook.source)
|
||||
};
|
||||
}
|
||||
function _temp() {
|
||||
return <Text>Esc to go back</Text>;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={title}
|
||||
subtitle={hookEventMetadata.description}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Box flexDirection="column">
|
||||
<Select
|
||||
options={hooksForSelectedMatcher.map((hook, index) => ({
|
||||
label: `[${hook.config.type}] ${getHookDisplayText(hook.config)}`,
|
||||
value: index.toString(),
|
||||
description:
|
||||
hook.source === 'pluginHook' && hook.pluginName
|
||||
? `${hookSourceHeaderDisplayString(hook.source)} (${hook.pluginName})`
|
||||
: hookSourceHeaderDisplayString(hook.source),
|
||||
}))}
|
||||
onChange={value => {
|
||||
const index = parseInt(value, 10)
|
||||
const hook = hooksForSelectedMatcher[index]
|
||||
if (hook) {
|
||||
onSelect(hook)
|
||||
}
|
||||
}}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
</Box>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,143 +1,103 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
/**
|
||||
* SelectMatcherMode shows the configured matchers for a selected hook event.
|
||||
*
|
||||
* The /hooks menu is read-only: this view no longer offers "add new matcher"
|
||||
* and simply lets the user drill into each matcher to see its hooks.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { type HookSource, hookSourceInlineDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
|
||||
import { plural } from '../../utils/stringUtils.js';
|
||||
import { Select } from '../CustomSelect/select.js';
|
||||
import { Dialog } from '../design-system/Dialog.js';
|
||||
import * as React from 'react'
|
||||
import type { HookEvent } from 'src/entrypoints/agentSdkTypes.js'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import {
|
||||
type HookSource,
|
||||
hookSourceInlineDisplayString,
|
||||
type IndividualHookConfig,
|
||||
} from '../../utils/hooks/hooksSettings.js'
|
||||
import { plural } from '../../utils/stringUtils.js'
|
||||
import { Select } from '../CustomSelect/select.js'
|
||||
import { Dialog } from '../design-system/Dialog.js'
|
||||
|
||||
type MatcherWithSource = {
|
||||
matcher: string;
|
||||
sources: HookSource[];
|
||||
hookCount: number;
|
||||
};
|
||||
matcher: string
|
||||
sources: HookSource[]
|
||||
hookCount: number
|
||||
}
|
||||
|
||||
type Props = {
|
||||
selectedEvent: HookEvent;
|
||||
matchersForSelectedEvent: string[];
|
||||
hooksByEventAndMatcher: Record<HookEvent, Record<string, IndividualHookConfig[]>>;
|
||||
eventDescription: string;
|
||||
onSelect: (matcher: string) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
export function SelectMatcherMode(t0) {
|
||||
const $ = _c(25);
|
||||
const {
|
||||
selectedEvent,
|
||||
matchersForSelectedEvent,
|
||||
hooksByEventAndMatcher,
|
||||
eventDescription,
|
||||
onSelect,
|
||||
onCancel
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] !== hooksByEventAndMatcher || $[1] !== matchersForSelectedEvent || $[2] !== selectedEvent) {
|
||||
let t2;
|
||||
if ($[4] !== hooksByEventAndMatcher || $[5] !== selectedEvent) {
|
||||
t2 = matcher => {
|
||||
const hooks = hooksByEventAndMatcher[selectedEvent]?.[matcher] || [];
|
||||
const sources = Array.from(new Set(hooks.map(_temp)));
|
||||
return {
|
||||
matcher,
|
||||
sources,
|
||||
hookCount: hooks.length
|
||||
};
|
||||
};
|
||||
$[4] = hooksByEventAndMatcher;
|
||||
$[5] = selectedEvent;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
t1 = matchersForSelectedEvent.map(t2);
|
||||
$[0] = hooksByEventAndMatcher;
|
||||
$[1] = matchersForSelectedEvent;
|
||||
$[2] = selectedEvent;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
}
|
||||
const matchersWithSources = t1;
|
||||
selectedEvent: HookEvent
|
||||
matchersForSelectedEvent: string[]
|
||||
hooksByEventAndMatcher: Record<
|
||||
HookEvent,
|
||||
Record<string, IndividualHookConfig[]>
|
||||
>
|
||||
eventDescription: string
|
||||
onSelect: (matcher: string) => void
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
export function SelectMatcherMode({
|
||||
selectedEvent,
|
||||
matchersForSelectedEvent,
|
||||
hooksByEventAndMatcher,
|
||||
eventDescription,
|
||||
onSelect,
|
||||
onCancel,
|
||||
}: Props): React.ReactNode {
|
||||
// Group matchers with their sources (already sorted by priority in parent)
|
||||
const matchersWithSources: MatcherWithSource[] = React.useMemo(() => {
|
||||
return matchersForSelectedEvent.map(matcher => {
|
||||
const hooks = hooksByEventAndMatcher[selectedEvent]?.[matcher] || []
|
||||
const sources = Array.from(new Set(hooks.map(h => h.source)))
|
||||
return {
|
||||
matcher,
|
||||
sources,
|
||||
hookCount: hooks.length,
|
||||
}
|
||||
})
|
||||
}, [matchersForSelectedEvent, hooksByEventAndMatcher, selectedEvent])
|
||||
|
||||
if (matchersForSelectedEvent.length === 0) {
|
||||
const t2 = `${selectedEvent} - Matchers`;
|
||||
let t3;
|
||||
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <Box flexDirection="column" gap={1}><Text dimColor={true}>No hooks configured for this event.</Text><Text dimColor={true}>To add hooks, edit settings.json directly or ask Claude.</Text></Box>;
|
||||
$[7] = t3;
|
||||
} else {
|
||||
t3 = $[7];
|
||||
}
|
||||
let t4;
|
||||
if ($[8] !== eventDescription || $[9] !== onCancel || $[10] !== t2) {
|
||||
t4 = <Dialog title={t2} subtitle={eventDescription} onCancel={onCancel} inputGuide={_temp2}>{t3}</Dialog>;
|
||||
$[8] = eventDescription;
|
||||
$[9] = onCancel;
|
||||
$[10] = t2;
|
||||
$[11] = t4;
|
||||
} else {
|
||||
t4 = $[11];
|
||||
}
|
||||
return t4;
|
||||
return (
|
||||
<Dialog
|
||||
title={`${selectedEvent} - Matchers`}
|
||||
subtitle={eventDescription}
|
||||
onCancel={onCancel}
|
||||
inputGuide={() => <Text>Esc to go back</Text>}
|
||||
>
|
||||
<Box flexDirection="column" gap={1}>
|
||||
<Text dimColor>No hooks configured for this event.</Text>
|
||||
<Text dimColor>
|
||||
To add hooks, edit settings.json directly or ask Claude.
|
||||
</Text>
|
||||
</Box>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
const t2 = `${selectedEvent} - Matchers`;
|
||||
let t3;
|
||||
if ($[12] !== matchersWithSources) {
|
||||
t3 = matchersWithSources.map(_temp3);
|
||||
$[12] = matchersWithSources;
|
||||
$[13] = t3;
|
||||
} else {
|
||||
t3 = $[13];
|
||||
}
|
||||
let t4;
|
||||
if ($[14] !== onSelect) {
|
||||
t4 = value => {
|
||||
onSelect(value);
|
||||
};
|
||||
$[14] = onSelect;
|
||||
$[15] = t4;
|
||||
} else {
|
||||
t4 = $[15];
|
||||
}
|
||||
let t5;
|
||||
if ($[16] !== onCancel || $[17] !== t3 || $[18] !== t4) {
|
||||
t5 = <Box flexDirection="column"><Select options={t3} onChange={t4} onCancel={onCancel} /></Box>;
|
||||
$[16] = onCancel;
|
||||
$[17] = t3;
|
||||
$[18] = t4;
|
||||
$[19] = t5;
|
||||
} else {
|
||||
t5 = $[19];
|
||||
}
|
||||
let t6;
|
||||
if ($[20] !== eventDescription || $[21] !== onCancel || $[22] !== t2 || $[23] !== t5) {
|
||||
t6 = <Dialog title={t2} subtitle={eventDescription} onCancel={onCancel}>{t5}</Dialog>;
|
||||
$[20] = eventDescription;
|
||||
$[21] = onCancel;
|
||||
$[22] = t2;
|
||||
$[23] = t5;
|
||||
$[24] = t6;
|
||||
} else {
|
||||
t6 = $[24];
|
||||
}
|
||||
return t6;
|
||||
}
|
||||
function _temp3(item) {
|
||||
const sourceText = item.sources.map(hookSourceInlineDisplayString).join(", ");
|
||||
const matcherLabel = item.matcher || "(all)";
|
||||
return {
|
||||
label: `[${sourceText}] ${matcherLabel}`,
|
||||
value: item.matcher,
|
||||
description: `${item.hookCount} ${plural(item.hookCount, "hook")}`
|
||||
};
|
||||
}
|
||||
function _temp2() {
|
||||
return <Text>Esc to go back</Text>;
|
||||
}
|
||||
function _temp(h) {
|
||||
return h.source;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={`${selectedEvent} - Matchers`}
|
||||
subtitle={eventDescription}
|
||||
onCancel={onCancel}
|
||||
>
|
||||
<Box flexDirection="column">
|
||||
<Select
|
||||
options={matchersWithSources.map(item => {
|
||||
const sourceText = item.sources
|
||||
.map(hookSourceInlineDisplayString)
|
||||
.join(', ')
|
||||
const matcherLabel = item.matcher || '(all)'
|
||||
return {
|
||||
label: `[${sourceText}] ${matcherLabel}`,
|
||||
value: item.matcher,
|
||||
description: `${item.hookCount} ${plural(item.hookCount, 'hook')}`,
|
||||
}
|
||||
})}
|
||||
onChange={value => {
|
||||
onSelect(value)
|
||||
}}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
</Box>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,182 +1,100 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
/**
|
||||
* ViewHookMode shows read-only details for a single configured hook.
|
||||
*
|
||||
* The /hooks menu is read-only; this view replaces the former delete-hook
|
||||
* confirmation screen and directs users to settings.json or Claude for edits.
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { hookSourceDescriptionDisplayString, type IndividualHookConfig } from '../../utils/hooks/hooksSettings.js';
|
||||
import { Dialog } from '../design-system/Dialog.js';
|
||||
import * as React from 'react'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import {
|
||||
hookSourceDescriptionDisplayString,
|
||||
type IndividualHookConfig,
|
||||
} from '../../utils/hooks/hooksSettings.js'
|
||||
import { Dialog } from '../design-system/Dialog.js'
|
||||
|
||||
type Props = {
|
||||
selectedHook: IndividualHookConfig;
|
||||
eventSupportsMatcher: boolean;
|
||||
onCancel: () => void;
|
||||
};
|
||||
export function ViewHookMode(t0) {
|
||||
const $ = _c(40);
|
||||
const {
|
||||
selectedHook,
|
||||
eventSupportsMatcher,
|
||||
onCancel
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] !== selectedHook.event) {
|
||||
t1 = <Text>Event: <Text bold={true}>{selectedHook.event}</Text></Text>;
|
||||
$[0] = selectedHook.event;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
let t2;
|
||||
if ($[2] !== eventSupportsMatcher || $[3] !== selectedHook.matcher) {
|
||||
t2 = eventSupportsMatcher && <Text>Matcher: <Text bold={true}>{selectedHook.matcher || "(all)"}</Text></Text>;
|
||||
$[2] = eventSupportsMatcher;
|
||||
$[3] = selectedHook.matcher;
|
||||
$[4] = t2;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
}
|
||||
let t3;
|
||||
if ($[5] !== selectedHook.config.type) {
|
||||
t3 = <Text>Type: <Text bold={true}>{selectedHook.config.type}</Text></Text>;
|
||||
$[5] = selectedHook.config.type;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
let t4;
|
||||
if ($[7] !== selectedHook.source) {
|
||||
t4 = hookSourceDescriptionDisplayString(selectedHook.source);
|
||||
$[7] = selectedHook.source;
|
||||
$[8] = t4;
|
||||
} else {
|
||||
t4 = $[8];
|
||||
}
|
||||
let t5;
|
||||
if ($[9] !== t4) {
|
||||
t5 = <Text>Source:{" "}<Text dimColor={true}>{t4}</Text></Text>;
|
||||
$[9] = t4;
|
||||
$[10] = t5;
|
||||
} else {
|
||||
t5 = $[10];
|
||||
}
|
||||
let t6;
|
||||
if ($[11] !== selectedHook.pluginName) {
|
||||
t6 = selectedHook.pluginName && <Text>Plugin: <Text dimColor={true}>{selectedHook.pluginName}</Text></Text>;
|
||||
$[11] = selectedHook.pluginName;
|
||||
$[12] = t6;
|
||||
} else {
|
||||
t6 = $[12];
|
||||
}
|
||||
let t7;
|
||||
if ($[13] !== t1 || $[14] !== t2 || $[15] !== t3 || $[16] !== t5 || $[17] !== t6) {
|
||||
t7 = <Box flexDirection="column">{t1}{t2}{t3}{t5}{t6}</Box>;
|
||||
$[13] = t1;
|
||||
$[14] = t2;
|
||||
$[15] = t3;
|
||||
$[16] = t5;
|
||||
$[17] = t6;
|
||||
$[18] = t7;
|
||||
} else {
|
||||
t7 = $[18];
|
||||
}
|
||||
let t8;
|
||||
if ($[19] !== selectedHook.config) {
|
||||
t8 = getContentFieldLabel(selectedHook.config);
|
||||
$[19] = selectedHook.config;
|
||||
$[20] = t8;
|
||||
} else {
|
||||
t8 = $[20];
|
||||
}
|
||||
let t9;
|
||||
if ($[21] !== t8) {
|
||||
t9 = <Text dimColor={true}>{t8}:</Text>;
|
||||
$[21] = t8;
|
||||
$[22] = t9;
|
||||
} else {
|
||||
t9 = $[22];
|
||||
}
|
||||
let t10;
|
||||
if ($[23] !== selectedHook.config) {
|
||||
t10 = getContentFieldValue(selectedHook.config);
|
||||
$[23] = selectedHook.config;
|
||||
$[24] = t10;
|
||||
} else {
|
||||
t10 = $[24];
|
||||
}
|
||||
let t11;
|
||||
if ($[25] !== t10) {
|
||||
t11 = <Box borderStyle="round" borderDimColor={true} paddingLeft={1} paddingRight={1}><Text>{t10}</Text></Box>;
|
||||
$[25] = t10;
|
||||
$[26] = t11;
|
||||
} else {
|
||||
t11 = $[26];
|
||||
}
|
||||
let t12;
|
||||
if ($[27] !== t11 || $[28] !== t9) {
|
||||
t12 = <Box flexDirection="column">{t9}{t11}</Box>;
|
||||
$[27] = t11;
|
||||
$[28] = t9;
|
||||
$[29] = t12;
|
||||
} else {
|
||||
t12 = $[29];
|
||||
}
|
||||
let t13;
|
||||
if ($[30] !== selectedHook.config) {
|
||||
t13 = "statusMessage" in selectedHook.config && selectedHook.config.statusMessage && <Text>Status message:{" "}<Text dimColor={true}>{selectedHook.config.statusMessage}</Text></Text>;
|
||||
$[30] = selectedHook.config;
|
||||
$[31] = t13;
|
||||
} else {
|
||||
t13 = $[31];
|
||||
}
|
||||
let t14;
|
||||
if ($[32] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t14 = <Text dimColor={true}>To modify or remove this hook, edit settings.json directly or ask Claude to help.</Text>;
|
||||
$[32] = t14;
|
||||
} else {
|
||||
t14 = $[32];
|
||||
}
|
||||
let t15;
|
||||
if ($[33] !== t12 || $[34] !== t13 || $[35] !== t7) {
|
||||
t15 = <Box flexDirection="column" gap={1}>{t7}{t12}{t13}{t14}</Box>;
|
||||
$[33] = t12;
|
||||
$[34] = t13;
|
||||
$[35] = t7;
|
||||
$[36] = t15;
|
||||
} else {
|
||||
t15 = $[36];
|
||||
}
|
||||
let t16;
|
||||
if ($[37] !== onCancel || $[38] !== t15) {
|
||||
t16 = <Dialog title="Hook details" onCancel={onCancel} inputGuide={_temp}>{t15}</Dialog>;
|
||||
$[37] = onCancel;
|
||||
$[38] = t15;
|
||||
$[39] = t16;
|
||||
} else {
|
||||
t16 = $[39];
|
||||
}
|
||||
return t16;
|
||||
selectedHook: IndividualHookConfig
|
||||
eventSupportsMatcher: boolean
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
export function ViewHookMode({
|
||||
selectedHook,
|
||||
eventSupportsMatcher,
|
||||
onCancel,
|
||||
}: Props): React.ReactNode {
|
||||
return (
|
||||
<Dialog
|
||||
title="Hook details"
|
||||
onCancel={onCancel}
|
||||
inputGuide={() => <Text>Esc to go back</Text>}
|
||||
>
|
||||
<Box flexDirection="column" gap={1}>
|
||||
<Box flexDirection="column">
|
||||
<Text>
|
||||
Event: <Text bold>{selectedHook.event}</Text>
|
||||
</Text>
|
||||
{eventSupportsMatcher && (
|
||||
<Text>
|
||||
Matcher: <Text bold>{selectedHook.matcher || '(all)'}</Text>
|
||||
</Text>
|
||||
)}
|
||||
<Text>
|
||||
Type: <Text bold>{selectedHook.config.type}</Text>
|
||||
</Text>
|
||||
<Text>
|
||||
Source:{' '}
|
||||
<Text dimColor>
|
||||
{hookSourceDescriptionDisplayString(selectedHook.source)}
|
||||
</Text>
|
||||
</Text>
|
||||
{selectedHook.pluginName && (
|
||||
<Text>
|
||||
Plugin: <Text dimColor>{selectedHook.pluginName}</Text>
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Box flexDirection="column">
|
||||
<Text dimColor>{getContentFieldLabel(selectedHook.config)}:</Text>
|
||||
<Box
|
||||
borderStyle="round"
|
||||
borderDimColor
|
||||
paddingLeft={1}
|
||||
paddingRight={1}
|
||||
>
|
||||
<Text>{getContentFieldValue(selectedHook.config)}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
{'statusMessage' in selectedHook.config &&
|
||||
selectedHook.config.statusMessage && (
|
||||
<Text>
|
||||
Status message:{' '}
|
||||
<Text dimColor>{selectedHook.config.statusMessage}</Text>
|
||||
</Text>
|
||||
)}
|
||||
<Text dimColor>
|
||||
To modify or remove this hook, edit settings.json directly or ask
|
||||
Claude to help.
|
||||
</Text>
|
||||
</Box>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a human-readable label for the primary content field of a hook
|
||||
* based on its type.
|
||||
*/
|
||||
function _temp() {
|
||||
return <Text>Esc to go back</Text>;
|
||||
}
|
||||
function getContentFieldLabel(config: IndividualHookConfig['config']): string {
|
||||
switch (config.type) {
|
||||
case 'command':
|
||||
return 'Command';
|
||||
return 'Command'
|
||||
case 'prompt':
|
||||
return 'Prompt';
|
||||
return 'Prompt'
|
||||
case 'agent':
|
||||
return 'Prompt';
|
||||
return 'Prompt'
|
||||
case 'http':
|
||||
return 'URL';
|
||||
return 'URL'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,12 +105,12 @@ function getContentFieldLabel(config: IndividualHookConfig['config']): string {
|
||||
function getContentFieldValue(config: IndividualHookConfig['config']): string {
|
||||
switch (config.type) {
|
||||
case 'command':
|
||||
return config.command;
|
||||
return config.command
|
||||
case 'prompt':
|
||||
return config.prompt;
|
||||
return config.prompt
|
||||
case 'agent':
|
||||
return config.prompt;
|
||||
return config.prompt
|
||||
case 'http':
|
||||
return config.url;
|
||||
return config.url
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user