diff --git a/src/components/LogoV2/EmergencyTip.tsx b/src/components/LogoV2/EmergencyTip.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ff6a83e375b42306eb0fde87c8b4ee9c3e544615 --- /dev/null +++ b/src/components/LogoV2/EmergencyTip.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import { useEffect, useMemo } from 'react'; +import { Box, Text } from 'src/ink.js'; +import { getDynamicConfig_CACHED_MAY_BE_STALE } from 'src/services/analytics/growthbook.js'; +import { getGlobalConfig, saveGlobalConfig } from 'src/utils/config.js'; +const CONFIG_NAME = 'tengu-top-of-feed-tip'; +export function EmergencyTip(): React.ReactNode { + const tip = useMemo(getTipOfFeed, []); + // Memoize to prevent re-reads after we save - we want the value at mount time + const lastShownTip = useMemo(() => getGlobalConfig().lastShownEmergencyTip, []); + + // Only show if this is a new/different tip + const shouldShow = tip.tip && tip.tip !== lastShownTip; + + // Save the tip we're showing so we don't show it again + useEffect(() => { + if (shouldShow) { + saveGlobalConfig(current => { + if (current.lastShownEmergencyTip === tip.tip) return current; + return { + ...current, + lastShownEmergencyTip: tip.tip + }; + }); + } + }, [shouldShow, tip.tip]); + if (!shouldShow) { + return null; + } + return + + {tip.tip} + + ; +} +type TipOfFeed = { + tip: string; + color?: 'dim' | 'warning' | 'error'; +}; +const DEFAULT_TIP: TipOfFeed = { + tip: '', + color: 'dim' +}; + +/** + * Get the tip of the feed from dynamic config with caching + * Returns cached value immediately, updates in background + */ +function getTipOfFeed(): TipOfFeed { + return getDynamicConfig_CACHED_MAY_BE_STALE(CONFIG_NAME, DEFAULT_TIP); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJCb3giLCJUZXh0IiwiZ2V0RHluYW1pY0NvbmZpZ19DQUNIRURfTUFZX0JFX1NUQUxFIiwiZ2V0R2xvYmFsQ29uZmlnIiwic2F2ZUdsb2JhbENvbmZpZyIsIkNPTkZJR19OQU1FIiwiRW1lcmdlbmN5VGlwIiwiUmVhY3ROb2RlIiwidGlwIiwiZ2V0VGlwT2ZGZWVkIiwibGFzdFNob3duVGlwIiwibGFzdFNob3duRW1lcmdlbmN5VGlwIiwic2hvdWxkU2hvdyIsImN1cnJlbnQiLCJjb2xvciIsImRpbUNvbG9yIiwiVGlwT2ZGZWVkIiwiREVGQVVMVF9USVAiXSwic291cmNlcyI6WyJFbWVyZ2VuY3lUaXAudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VNZW1vIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICdzcmMvaW5rLmpzJ1xuaW1wb3J0IHsgZ2V0RHluYW1pY0NvbmZpZ19DQUNIRURfTUFZX0JFX1NUQUxFIH0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9ncm93dGhib29rLmpzJ1xuaW1wb3J0IHsgZ2V0R2xvYmFsQ29uZmlnLCBzYXZlR2xvYmFsQ29uZmlnIH0gZnJvbSAnc3JjL3V0aWxzL2NvbmZpZy5qcydcblxuY29uc3QgQ09ORklHX05BTUUgPSAndGVuZ3UtdG9wLW9mLWZlZWQtdGlwJ1xuXG5leHBvcnQgZnVuY3Rpb24gRW1lcmdlbmN5VGlwKCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHRpcCA9IHVzZU1lbW8oZ2V0VGlwT2ZGZWVkLCBbXSlcbiAgLy8gTWVtb2l6ZSB0byBwcmV2ZW50IHJlLXJlYWRzIGFmdGVyIHdlIHNhdmUgLSB3ZSB3YW50IHRoZSB2YWx1ZSBhdCBtb3VudCB0aW1lXG4gIGNvbnN0IGxhc3RTaG93blRpcCA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZ2V0R2xvYmFsQ29uZmlnKCkubGFzdFNob3duRW1lcmdlbmN5VGlwLFxuICAgIFtdLFxuICApXG5cbiAgLy8gT25seSBzaG93IGlmIHRoaXMgaXMgYSBuZXcvZGlmZmVyZW50IHRpcFxuICBjb25zdCBzaG91bGRTaG93ID0gdGlwLnRpcCAmJiB0aXAudGlwICE9PSBsYXN0U2hvd25UaXBcblxuICAvLyBTYXZlIHRoZSB0aXAgd2UncmUgc2hvd2luZyBzbyB3ZSBkb24ndCBzaG93IGl0IGFnYWluXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKHNob3VsZFNob3cpIHtcbiAgICAgIHNhdmVHbG9iYWxDb25maWcoY3VycmVudCA9PiB7XG4gICAgICAgIGlmIChjdXJyZW50Lmxhc3RTaG93bkVtZXJnZW5jeVRpcCA9PT0gdGlwLnRpcCkgcmV0dXJuIGN1cnJlbnRcbiAgICAgICAgcmV0dXJuIHsgLi4uY3VycmVudCwgbGFzdFNob3duRW1lcmdlbmN5VGlwOiB0aXAudGlwIH1cbiAgICAgIH0pXG4gICAgfVxuICB9LCBbc2hvdWxkU2hvdywgdGlwLnRpcF0pXG5cbiAgaWYgKCFzaG91bGRTaG93KSB7XG4gICAgcmV0dXJuIG51bGxcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBwYWRkaW5nTGVmdD17Mn0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPFRleHRcbiAgICAgICAgey4uLih0aXAuY29sb3IgPT09ICd3YXJuaW5nJ1xuICAgICAgICAgID8geyBjb2xvcjogJ3dhcm5pbmcnIH1cbiAgICAgICAgICA6IHRpcC5jb2xvciA9PT0gJ2Vycm9yJ1xuICAgICAgICAgICAgPyB7IGNvbG9yOiAnZXJyb3InIH1cbiAgICAgICAgICAgIDogeyBkaW1Db2xvcjogdHJ1ZSB9KX1cbiAgICAgID5cbiAgICAgICAge3RpcC50aXB9XG4gICAgICA8L1RleHQ+XG4gICAgPC9Cb3g+XG4gIClcbn1cblxudHlwZSBUaXBPZkZlZWQgPSB7XG4gIHRpcDogc3RyaW5nXG4gIGNvbG9yPzogJ2RpbScgfCAnd2FybmluZycgfCAnZXJyb3InXG59XG5cbmNvbnN0IERFRkFVTFRfVElQOiBUaXBPZkZlZWQgPSB7IHRpcDogJycsIGNvbG9yOiAnZGltJyB9XG5cbi8qKlxuICogR2V0IHRoZSB0aXAgb2YgdGhlIGZlZWQgZnJvbSBkeW5hbWljIGNvbmZpZyB3aXRoIGNhY2hpbmdcbiAqIFJldHVybnMgY2FjaGVkIHZhbHVlIGltbWVkaWF0ZWx5LCB1cGRhdGVzIGluIGJhY2tncm91bmRcbiAqL1xuZnVuY3Rpb24gZ2V0VGlwT2ZGZWVkKCk6IFRpcE9mRmVlZCB7XG4gIHJldHVybiBnZXREeW5hbWljQ29uZmlnX0NBQ0hFRF9NQVlfQkVfU1RBTEU8VGlwT2ZGZWVkPihcbiAgICBDT05GSUdfTkFNRSxcbiAgICBERUZBVUxUX1RJUCxcbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFNBQVMsRUFBRUMsT0FBTyxRQUFRLE9BQU87QUFDMUMsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsWUFBWTtBQUN0QyxTQUFTQyxvQ0FBb0MsUUFBUSxzQ0FBc0M7QUFDM0YsU0FBU0MsZUFBZSxFQUFFQyxnQkFBZ0IsUUFBUSxxQkFBcUI7QUFFdkUsTUFBTUMsV0FBVyxHQUFHLHVCQUF1QjtBQUUzQyxPQUFPLFNBQVNDLFlBQVlBLENBQUEsQ0FBRSxFQUFFVCxLQUFLLENBQUNVLFNBQVMsQ0FBQztFQUM5QyxNQUFNQyxHQUFHLEdBQUdULE9BQU8sQ0FBQ1UsWUFBWSxFQUFFLEVBQUUsQ0FBQztFQUNyQztFQUNBLE1BQU1DLFlBQVksR0FBR1gsT0FBTyxDQUMxQixNQUFNSSxlQUFlLENBQUMsQ0FBQyxDQUFDUSxxQkFBcUIsRUFDN0MsRUFDRixDQUFDOztFQUVEO0VBQ0EsTUFBTUMsVUFBVSxHQUFHSixHQUFHLENBQUNBLEdBQUcsSUFBSUEsR0FBRyxDQUFDQSxHQUFHLEtBQUtFLFlBQVk7O0VBRXREO0VBQ0FaLFNBQVMsQ0FBQyxNQUFNO0lBQ2QsSUFBSWMsVUFBVSxFQUFFO01BQ2RSLGdCQUFnQixDQUFDUyxPQUFPLElBQUk7UUFDMUIsSUFBSUEsT0FBTyxDQUFDRixxQkFBcUIsS0FBS0gsR0FBRyxDQUFDQSxHQUFHLEVBQUUsT0FBT0ssT0FBTztRQUM3RCxPQUFPO1VBQUUsR0FBR0EsT0FBTztVQUFFRixxQkFBcUIsRUFBRUgsR0FBRyxDQUFDQTtRQUFJLENBQUM7TUFDdkQsQ0FBQyxDQUFDO0lBQ0o7RUFDRixDQUFDLEVBQUUsQ0FBQ0ksVUFBVSxFQUFFSixHQUFHLENBQUNBLEdBQUcsQ0FBQyxDQUFDO0VBRXpCLElBQUksQ0FBQ0ksVUFBVSxFQUFFO0lBQ2YsT0FBTyxJQUFJO0VBQ2I7RUFFQSxPQUNFLENBQUMsR0FBRyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxRQUFRO0FBQy9DLE1BQU0sQ0FBQyxJQUFJLENBQ0gsSUFBS0osR0FBRyxDQUFDTSxLQUFLLEtBQUssU0FBUyxHQUN4QjtNQUFFQSxLQUFLLEVBQUU7SUFBVSxDQUFDLEdBQ3BCTixHQUFHLENBQUNNLEtBQUssS0FBSyxPQUFPLEdBQ25CO01BQUVBLEtBQUssRUFBRTtJQUFRLENBQUMsR0FDbEI7TUFBRUMsUUFBUSxFQUFFO0lBQUssQ0FBRSxDQUFDO0FBRWxDLFFBQVEsQ0FBQ1AsR0FBRyxDQUFDQSxHQUFHO0FBQ2hCLE1BQU0sRUFBRSxJQUFJO0FBQ1osSUFBSSxFQUFFLEdBQUcsQ0FBQztBQUVWO0FBRUEsS0FBS1EsU0FBUyxHQUFHO0VBQ2ZSLEdBQUcsRUFBRSxNQUFNO0VBQ1hNLEtBQUssQ0FBQyxFQUFFLEtBQUssR0FBRyxTQUFTLEdBQUcsT0FBTztBQUNyQyxDQUFDO0FBRUQsTUFBTUcsV0FBVyxFQUFFRCxTQUFTLEdBQUc7RUFBRVIsR0FBRyxFQUFFLEVBQUU7RUFBRU0sS0FBSyxFQUFFO0FBQU0sQ0FBQzs7QUFFeEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTTCxZQUFZQSxDQUFBLENBQUUsRUFBRU8sU0FBUyxDQUFDO0VBQ2pDLE9BQU9kLG9DQUFvQyxDQUFDYyxTQUFTLENBQUMsQ0FDcERYLFdBQVcsRUFDWFksV0FDRixDQUFDO0FBQ0giLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/src/components/PromptInput/PromptInput.tsx b/src/components/PromptInput/PromptInput.tsx new file mode 100644 index 0000000000000000000000000000000000000000..128e73cb3f4070bef7c8c770edfbfab2fe7ac28f --- /dev/null +++ b/src/components/PromptInput/PromptInput.tsx @@ -0,0 +1,2339 @@ +import { feature } from 'bun:bundle'; +import chalk from 'chalk'; +import * as path from 'path'; +import * as React from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react'; +import { useNotifications } from 'src/context/notifications.js'; +import { useCommandQueue } from 'src/hooks/useCommandQueue.js'; +import { type IDEAtMentioned, useIdeAtMentioned } from 'src/hooks/useIdeAtMentioned.js'; +import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js'; +import { type AppState, useAppState, useAppStateStore, useSetAppState } from 'src/state/AppState.js'; +import type { FooterItem } from 'src/state/AppStateStore.js'; +import { getCwd } from 'src/utils/cwd.js'; +import { isQueuedCommandEditable, popAllEditable } from 'src/utils/messageQueueManager.js'; +import stripAnsi from 'strip-ansi'; +import { companionReservedColumns } from '../../buddy/CompanionSprite.js'; +import { findBuddyTriggerPositions, useBuddyNotification } from '../../buddy/useBuddyNotification.js'; +import { FastModePicker } from '../../commands/fast/fast.js'; +import { isUltrareviewEnabled } from '../../commands/review/ultrareviewEnabled.js'; +import { getNativeCSIuTerminalDisplayName } from '../../commands/terminalSetup/terminalSetup.js'; +import { type Command, hasCommand } from '../../commands.js'; +import { useIsModalOverlayActive } from '../../context/overlayContext.js'; +import { useSetPromptOverlayDialog } from '../../context/promptOverlayContext.js'; +import { formatImageRef, formatPastedTextRef, getPastedTextRefNumLines, parseReferences } from '../../history.js'; +import type { VerificationStatus } from '../../hooks/useApiKeyVerification.js'; +import { type HistoryMode, useArrowKeyHistory } from '../../hooks/useArrowKeyHistory.js'; +import { useDoublePress } from '../../hooks/useDoublePress.js'; +import { useHistorySearch } from '../../hooks/useHistorySearch.js'; +import type { IDESelection } from '../../hooks/useIdeSelection.js'; +import { useInputBuffer } from '../../hooks/useInputBuffer.js'; +import { useMainLoopModel } from '../../hooks/useMainLoopModel.js'; +import { usePromptSuggestion } from '../../hooks/usePromptSuggestion.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { useTypeahead } from '../../hooks/useTypeahead.js'; +import type { BorderTextOptions } from '../../ink/render-border.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, type ClickEvent, type Key, Text, useInput } from '../../ink.js'; +import { useOptionalKeybindingContext } from '../../keybindings/KeybindingContext.js'; +import { getShortcutDisplay } from '../../keybindings/shortcutFormat.js'; +import { useKeybinding, useKeybindings } from '../../keybindings/useKeybinding.js'; +import type { MCPServerConnection } from '../../services/mcp/types.js'; +import { abortPromptSuggestion, logSuggestionSuppressed } from '../../services/PromptSuggestion/promptSuggestion.js'; +import { type ActiveSpeculationState, abortSpeculation } from '../../services/PromptSuggestion/speculation.js'; +import { getActiveAgentForInput, getViewedTeammateTask } from '../../state/selectors.js'; +import { enterTeammateView, exitTeammateView, stopOrDismissAgent } from '../../state/teammateViewHelpers.js'; +import type { ToolPermissionContext } from '../../Tool.js'; +import { getRunningTeammatesSorted } from '../../tasks/InProcessTeammateTask/InProcessTeammateTask.js'; +import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js'; +import { isPanelAgentTask, type LocalAgentTaskState } from '../../tasks/LocalAgentTask/LocalAgentTask.js'; +import { isBackgroundTask } from '../../tasks/types.js'; +import { AGENT_COLOR_TO_THEME_COLOR, AGENT_COLORS, type AgentColorName } from '../../tools/AgentTool/agentColorManager.js'; +import type { AgentDefinition } from '../../tools/AgentTool/loadAgentsDir.js'; +import type { Message } from '../../types/message.js'; +import type { PermissionMode } from '../../types/permissions.js'; +import type { BaseTextInputProps, PromptInputMode, VimMode } from '../../types/textInputTypes.js'; +import { isAgentSwarmsEnabled } from '../../utils/agentSwarmsEnabled.js'; +import { count } from '../../utils/array.js'; +import type { AutoUpdaterResult } from '../../utils/autoUpdater.js'; +import { Cursor } from '../../utils/Cursor.js'; +import { getGlobalConfig, type PastedContent, saveGlobalConfig } from '../../utils/config.js'; +import { logForDebugging } from '../../utils/debug.js'; +import { parseDirectMemberMessage, sendDirectMemberMessage } from '../../utils/directMemberMessage.js'; +import type { EffortLevel } from '../../utils/effort.js'; +import { env } from '../../utils/env.js'; +import { errorMessage } from '../../utils/errors.js'; +import { isBilledAsExtraUsage } from '../../utils/extraUsage.js'; +import { getFastModeUnavailableReason, isFastModeAvailable, isFastModeCooldown, isFastModeEnabled, isFastModeSupportedByModel } from '../../utils/fastMode.js'; +import { isFullscreenEnvEnabled } from '../../utils/fullscreen.js'; +import type { PromptInputHelpers } from '../../utils/handlePromptSubmit.js'; +import { getImageFromClipboard, PASTE_THRESHOLD } from '../../utils/imagePaste.js'; +import type { ImageDimensions } from '../../utils/imageResizer.js'; +import { cacheImagePath, storeImage } from '../../utils/imageStore.js'; +import { isMacosOptionChar, MACOS_OPTION_SPECIAL_CHARS } from '../../utils/keyboardShortcuts.js'; +import { logError } from '../../utils/log.js'; +import { isOpus1mMergeEnabled, modelDisplayString } from '../../utils/model/model.js'; +import { setAutoModeActive } from '../../utils/permissions/autoModeState.js'; +import { cyclePermissionMode, getNextPermissionMode } from '../../utils/permissions/getNextPermissionMode.js'; +import { transitionPermissionMode } from '../../utils/permissions/permissionSetup.js'; +import { getPlatform } from '../../utils/platform.js'; +import type { ProcessUserInputContext } from '../../utils/processUserInput/processUserInput.js'; +import { editPromptInEditor } from '../../utils/promptEditor.js'; +import { hasAutoModeOptIn } from '../../utils/settings/settings.js'; +import { findBtwTriggerPositions } from '../../utils/sideQuestion.js'; +import { findSlashCommandPositions } from '../../utils/suggestions/commandSuggestions.js'; +import { findSlackChannelPositions, getKnownChannelsVersion, hasSlackMcpServer, subscribeKnownChannels } from '../../utils/suggestions/slackChannelSuggestions.js'; +import { isInProcessEnabled } from '../../utils/swarm/backends/registry.js'; +import { syncTeammateMode } from '../../utils/swarm/teamHelpers.js'; +import type { TeamSummary } from '../../utils/teamDiscovery.js'; +import { getTeammateColor } from '../../utils/teammate.js'; +import { isInProcessTeammate } from '../../utils/teammateContext.js'; +import { writeToMailbox } from '../../utils/teammateMailbox.js'; +import type { TextHighlight } from '../../utils/textHighlighting.js'; +import type { Theme } from '../../utils/theme.js'; +import { findThinkingTriggerPositions, getRainbowColor, isUltrathinkEnabled } from '../../utils/thinking.js'; +import { findTokenBudgetPositions } from '../../utils/tokenBudget.js'; +import { findUltraplanTriggerPositions, findUltrareviewTriggerPositions } from '../../utils/ultraplan/keyword.js'; +import { AutoModeOptInDialog } from '../AutoModeOptInDialog.js'; +import { BridgeDialog } from '../BridgeDialog.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { getVisibleAgentTasks, useCoordinatorTaskCount } from '../CoordinatorAgentStatus.js'; +import { getEffortNotificationText } from '../EffortIndicator.js'; +import { getFastIconString } from '../FastIcon.js'; +import { GlobalSearchDialog } from '../GlobalSearchDialog.js'; +import { HistorySearchDialog } from '../HistorySearchDialog.js'; +import { ModelPicker } from '../ModelPicker.js'; +import { QuickOpenDialog } from '../QuickOpenDialog.js'; +import TextInput from '../TextInput.js'; +import { ThinkingToggle } from '../ThinkingToggle.js'; +import { BackgroundTasksDialog } from '../tasks/BackgroundTasksDialog.js'; +import { shouldHideTasksFooter } from '../tasks/taskStatusUtils.js'; +import { TeamsDialog } from '../teams/TeamsDialog.js'; +import VimTextInput from '../VimTextInput.js'; +import { getModeFromInput, getValueFromInput } from './inputModes.js'; +import { FOOTER_TEMPORARY_STATUS_TIMEOUT, Notifications } from './Notifications.js'; +import PromptInputFooter from './PromptInputFooter.js'; +import type { SuggestionItem } from './PromptInputFooterSuggestions.js'; +import { PromptInputModeIndicator } from './PromptInputModeIndicator.js'; +import { PromptInputQueuedCommands } from './PromptInputQueuedCommands.js'; +import { PromptInputStashNotice } from './PromptInputStashNotice.js'; +import { useMaybeTruncateInput } from './useMaybeTruncateInput.js'; +import { usePromptInputPlaceholder } from './usePromptInputPlaceholder.js'; +import { useShowFastIconHint } from './useShowFastIconHint.js'; +import { useSwarmBanner } from './useSwarmBanner.js'; +import { isNonSpacePrintable, isVimModeEnabled } from './utils.js'; +type Props = { + debug: boolean; + ideSelection: IDESelection | undefined; + toolPermissionContext: ToolPermissionContext; + setToolPermissionContext: (ctx: ToolPermissionContext) => void; + apiKeyStatus: VerificationStatus; + commands: Command[]; + agents: AgentDefinition[]; + isLoading: boolean; + verbose: boolean; + messages: Message[]; + onAutoUpdaterResult: (result: AutoUpdaterResult) => void; + autoUpdaterResult: AutoUpdaterResult | null; + input: string; + onInputChange: (value: string) => void; + mode: PromptInputMode; + onModeChange: (mode: PromptInputMode) => void; + stashedPrompt: { + text: string; + cursorOffset: number; + pastedContents: Record; + } | undefined; + setStashedPrompt: (value: { + text: string; + cursorOffset: number; + pastedContents: Record; + } | undefined) => void; + submitCount: number; + onShowMessageSelector: () => void; + /** Fullscreen message actions: shift+↑ enters cursor. */ + onMessageActionsEnter?: () => void; + mcpClients: MCPServerConnection[]; + pastedContents: Record; + setPastedContents: React.Dispatch>>; + vimMode: VimMode; + setVimMode: (mode: VimMode) => void; + showBashesDialog: string | boolean; + setShowBashesDialog: (show: string | boolean) => void; + onExit: () => void; + getToolUseContext: (messages: Message[], newMessages: Message[], abortController: AbortController, mainLoopModel: string) => ProcessUserInputContext; + onSubmit: (input: string, helpers: PromptInputHelpers, speculationAccept?: { + state: ActiveSpeculationState; + speculationSessionTimeSavedMs: number; + setAppState: (f: (prev: AppState) => AppState) => void; + }, options?: { + fromKeybinding?: boolean; + }) => Promise; + onAgentSubmit?: (input: string, task: InProcessTeammateTaskState | LocalAgentTaskState, helpers: PromptInputHelpers) => Promise; + isSearchingHistory: boolean; + setIsSearchingHistory: (isSearching: boolean) => void; + onDismissSideQuestion?: () => void; + isSideQuestionVisible?: boolean; + helpOpen: boolean; + setHelpOpen: React.Dispatch>; + hasSuppressedDialogs?: boolean; + isLocalJSXCommandActive?: boolean; + insertTextRef?: React.MutableRefObject<{ + insert: (text: string) => void; + setInputWithCursor: (value: string, cursor: number) => void; + cursorOffset: number; + } | null>; + voiceInterimRange?: { + start: number; + end: number; + } | null; +}; + +// Bottom slot has maxHeight="50%"; reserve lines for footer, border, status. +const PROMPT_FOOTER_LINES = 5; +const MIN_INPUT_VIEWPORT_LINES = 3; +function PromptInput({ + debug, + ideSelection, + toolPermissionContext, + setToolPermissionContext, + apiKeyStatus, + commands, + agents, + isLoading, + verbose, + messages, + onAutoUpdaterResult, + autoUpdaterResult, + input, + onInputChange, + mode, + onModeChange, + stashedPrompt, + setStashedPrompt, + submitCount, + onShowMessageSelector, + onMessageActionsEnter, + mcpClients, + pastedContents, + setPastedContents, + vimMode, + setVimMode, + showBashesDialog, + setShowBashesDialog, + onExit, + getToolUseContext, + onSubmit: onSubmitProp, + onAgentSubmit, + isSearchingHistory, + setIsSearchingHistory, + onDismissSideQuestion, + isSideQuestionVisible, + helpOpen, + setHelpOpen, + hasSuppressedDialogs, + isLocalJSXCommandActive = false, + insertTextRef, + voiceInterimRange +}: Props): React.ReactNode { + const mainLoopModel = useMainLoopModel(); + // A local-jsx command (e.g., /mcp while agent is running) renders a full- + // screen dialog on top of PromptInput via the immediate-command path with + // shouldHidePromptInput: false. Those dialogs don't register in the overlay + // system, so treat them as a modal overlay here to stop navigation keys from + // leaking into TextInput/footer handlers and stacking a second dialog. + const isModalOverlayActive = useIsModalOverlayActive() || isLocalJSXCommandActive; + const [isAutoUpdating, setIsAutoUpdating] = useState(false); + const [exitMessage, setExitMessage] = useState<{ + show: boolean; + key?: string; + }>({ + show: false + }); + const [cursorOffset, setCursorOffset] = useState(input.length); + // Track the last input value set via internal handlers so we can detect + // external input changes (e.g. speech-to-text injection) and move cursor to end. + const lastInternalInputRef = React.useRef(input); + if (input !== lastInternalInputRef.current) { + // Input changed externally (not through any internal handler) — move cursor to end + setCursorOffset(input.length); + lastInternalInputRef.current = input; + } + // Wrap onInputChange to track internal changes before they trigger re-render + const trackAndSetInput = React.useCallback((value: string) => { + lastInternalInputRef.current = value; + onInputChange(value); + }, [onInputChange]); + // Expose an insertText function so callers (e.g. STT) can splice text at the + // current cursor position instead of replacing the entire input. + if (insertTextRef) { + insertTextRef.current = { + cursorOffset, + insert: (text: string) => { + const needsSpace = cursorOffset === input.length && input.length > 0 && !/\s$/.test(input); + const insertText = needsSpace ? ' ' + text : text; + const newValue = input.slice(0, cursorOffset) + insertText + input.slice(cursorOffset); + lastInternalInputRef.current = newValue; + onInputChange(newValue); + setCursorOffset(cursorOffset + insertText.length); + }, + setInputWithCursor: (value: string, cursor: number) => { + lastInternalInputRef.current = value; + onInputChange(value); + setCursorOffset(cursor); + } + }; + } + const store = useAppStateStore(); + const setAppState = useSetAppState(); + const tasks = useAppState(s => s.tasks); + const replBridgeConnected = useAppState(s => s.replBridgeConnected); + const replBridgeExplicit = useAppState(s => s.replBridgeExplicit); + const replBridgeReconnecting = useAppState(s => s.replBridgeReconnecting); + // Must match BridgeStatusIndicator's render condition (PromptInputFooter.tsx) — + // the pill returns null for implicit-and-not-reconnecting, so nav must too, + // otherwise bridge becomes an invisible selection stop. + const bridgeFooterVisible = replBridgeConnected && (replBridgeExplicit || replBridgeReconnecting); + // Tmux pill (ant-only) — visible when there's an active tungsten session + const hasTungstenSession = useAppState(s => "external" === 'ant' && s.tungstenActiveSession !== undefined); + const tmuxFooterVisible = "external" === 'ant' && hasTungstenSession; + // WebBrowser pill — visible when a browser is open + const bagelFooterVisible = useAppState(s => false); + const teamContext = useAppState(s => s.teamContext); + const queuedCommands = useCommandQueue(); + const promptSuggestionState = useAppState(s => s.promptSuggestion); + const speculation = useAppState(s => s.speculation); + const speculationSessionTimeSavedMs = useAppState(s => s.speculationSessionTimeSavedMs); + const viewingAgentTaskId = useAppState(s => s.viewingAgentTaskId); + const viewSelectionMode = useAppState(s => s.viewSelectionMode); + const showSpinnerTree = useAppState(s => s.expandedView) === 'teammates'; + const { + companion: _companion, + companionMuted + } = feature('BUDDY') ? getGlobalConfig() : { + companion: undefined, + companionMuted: undefined + }; + const companionFooterVisible = !!_companion && !companionMuted; + // Brief mode: BriefSpinner/BriefIdleStatus own the 2-row footprint above + // the input. Dropping marginTop here lets the spinner sit flush against + // the input bar. viewingAgentTaskId mirrors the gate on both (Spinner.tsx, + // REPL.tsx) — teammate view falls back to SpinnerWithVerbInner which has + // its own marginTop, so the gap stays even without ours. + const briefOwnsGap = feature('KAIROS') || feature('KAIROS_BRIEF') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s => s.isBriefOnly) && !viewingAgentTaskId : false; + const mainLoopModel_ = useAppState(s => s.mainLoopModel); + const mainLoopModelForSession = useAppState(s => s.mainLoopModelForSession); + const thinkingEnabled = useAppState(s => s.thinkingEnabled); + const isFastMode = useAppState(s => isFastModeEnabled() ? s.fastMode : false); + const effortValue = useAppState(s => s.effortValue); + const viewedTeammate = getViewedTeammateTask(store.getState()); + const viewingAgentName = viewedTeammate?.identity.agentName; + // identity.color is typed as `string | undefined` (not AgentColorName) because + // teammate identity comes from file-based config. Validate before casting to + // ensure we only use valid color names (falls back to cyan if invalid). + const viewingAgentColor = viewedTeammate?.identity.color && AGENT_COLORS.includes(viewedTeammate.identity.color as AgentColorName) ? viewedTeammate.identity.color as AgentColorName : undefined; + // In-process teammates sorted alphabetically for footer team selector + const inProcessTeammates = useMemo(() => getRunningTeammatesSorted(tasks), [tasks]); + + // Team mode: all background tasks are in-process teammates + const isTeammateMode = inProcessTeammates.length > 0 || viewedTeammate !== undefined; + + // When viewing a teammate, show their permission mode in the footer instead of the leader's + const effectiveToolPermissionContext = useMemo((): ToolPermissionContext => { + if (viewedTeammate) { + return { + ...toolPermissionContext, + mode: viewedTeammate.permissionMode + }; + } + return toolPermissionContext; + }, [viewedTeammate, toolPermissionContext]); + const { + historyQuery, + setHistoryQuery, + historyMatch, + historyFailedMatch + } = useHistorySearch(entry => { + setPastedContents(entry.pastedContents); + void onSubmit(entry.display); + }, input, trackAndSetInput, setCursorOffset, cursorOffset, onModeChange, mode, isSearchingHistory, setIsSearchingHistory, setPastedContents, pastedContents); + // Counter for paste IDs (shared between images and text). + // Compute initial value once from existing messages (for --continue/--resume). + // useRef(fn()) evaluates fn() on every render and discards the result after + // mount — getInitialPasteId walks all messages + regex-scans text blocks, + // so guard with a lazy-init pattern to run it exactly once. + const nextPasteIdRef = useRef(-1); + if (nextPasteIdRef.current === -1) { + nextPasteIdRef.current = getInitialPasteId(messages); + } + // Armed by onImagePaste; if the very next keystroke is a non-space + // printable, inputFilter prepends a space before it. Any other input + // (arrow, escape, backspace, paste, space) disarms without inserting. + const pendingSpaceAfterPillRef = useRef(false); + const [showTeamsDialog, setShowTeamsDialog] = useState(false); + const [showBridgeDialog, setShowBridgeDialog] = useState(false); + const [teammateFooterIndex, setTeammateFooterIndex] = useState(0); + // -1 sentinel: tasks pill is selected but no specific agent row is selected yet. + // First ↓ selects the pill, second ↓ moves to row 0. Prevents double-select + // of pill + row when both bg tasks (pill) and forked agents (rows) are visible. + const coordinatorTaskIndex = useAppState(s => s.coordinatorTaskIndex); + const setCoordinatorTaskIndex = useCallback((v: number | ((prev: number) => number)) => setAppState(prev => { + const next = typeof v === 'function' ? v(prev.coordinatorTaskIndex) : v; + if (next === prev.coordinatorTaskIndex) return prev; + return { + ...prev, + coordinatorTaskIndex: next + }; + }), [setAppState]); + const coordinatorTaskCount = useCoordinatorTaskCount(); + // The pill (BackgroundTaskStatus) only renders when non-local_agent bg tasks + // exist. When only local_agent tasks are running (coordinator/fork mode), the + // pill is absent, so the -1 sentinel would leave nothing visually selected. + // In that case, skip -1 and treat 0 as the minimum selectable index. + const hasBgTaskPill = useMemo(() => Object.values(tasks).some(t => isBackgroundTask(t) && !("external" === 'ant' && isPanelAgentTask(t))), [tasks]); + const minCoordinatorIndex = hasBgTaskPill ? -1 : 0; + // Clamp index when tasks complete and the list shrinks beneath the cursor + useEffect(() => { + if (coordinatorTaskIndex >= coordinatorTaskCount) { + setCoordinatorTaskIndex(Math.max(minCoordinatorIndex, coordinatorTaskCount - 1)); + } else if (coordinatorTaskIndex < minCoordinatorIndex) { + setCoordinatorTaskIndex(minCoordinatorIndex); + } + }, [coordinatorTaskCount, coordinatorTaskIndex, minCoordinatorIndex]); + const [isPasting, setIsPasting] = useState(false); + const [isExternalEditorActive, setIsExternalEditorActive] = useState(false); + const [showModelPicker, setShowModelPicker] = useState(false); + const [showQuickOpen, setShowQuickOpen] = useState(false); + const [showGlobalSearch, setShowGlobalSearch] = useState(false); + const [showHistoryPicker, setShowHistoryPicker] = useState(false); + const [showFastModePicker, setShowFastModePicker] = useState(false); + const [showThinkingToggle, setShowThinkingToggle] = useState(false); + const [showAutoModeOptIn, setShowAutoModeOptIn] = useState(false); + const [previousModeBeforeAuto, setPreviousModeBeforeAuto] = useState(null); + const autoModeOptInTimeoutRef = useRef(null); + + // Check if cursor is on the first line of input + const isCursorOnFirstLine = useMemo(() => { + const firstNewlineIndex = input.indexOf('\n'); + if (firstNewlineIndex === -1) { + return true; // No newlines, cursor is always on first line + } + return cursorOffset <= firstNewlineIndex; + }, [input, cursorOffset]); + const isCursorOnLastLine = useMemo(() => { + const lastNewlineIndex = input.lastIndexOf('\n'); + if (lastNewlineIndex === -1) { + return true; // No newlines, cursor is always on last line + } + return cursorOffset > lastNewlineIndex; + }, [input, cursorOffset]); + + // Derive team info from teamContext (no filesystem I/O needed) + // A session can only lead one team at a time + const cachedTeams: TeamSummary[] = useMemo(() => { + if (!isAgentSwarmsEnabled()) return []; + // In-process mode uses Shift+Down/Up navigation instead of footer menu + if (isInProcessEnabled()) return []; + if (!teamContext) { + return []; + } + const teammateCount = count(Object.values(teamContext.teammates), t => t.name !== 'team-lead'); + return [{ + name: teamContext.teamName, + memberCount: teammateCount, + runningCount: 0, + idleCount: 0 + }]; + }, [teamContext]); + + // ─── Footer pill navigation ───────────────────────────────────────────── + // Which pills render below the input box. Order here IS the nav order + // (down/right = forward, up/left = back). Selection lives in AppState so + // pills rendered outside PromptInput (CompanionSprite) can read focus. + const runningTaskCount = useMemo(() => count(Object.values(tasks), t => t.status === 'running'), [tasks]); + // Panel shows retained-completed agents too (getVisibleAgentTasks), so the + // pill must stay navigable whenever the panel has rows — not just when + // something is running. + const tasksFooterVisible = (runningTaskCount > 0 || "external" === 'ant' && coordinatorTaskCount > 0) && !shouldHideTasksFooter(tasks, showSpinnerTree); + const teamsFooterVisible = cachedTeams.length > 0; + const footerItems = useMemo(() => [tasksFooterVisible && 'tasks', tmuxFooterVisible && 'tmux', bagelFooterVisible && 'bagel', teamsFooterVisible && 'teams', bridgeFooterVisible && 'bridge', companionFooterVisible && 'companion'].filter(Boolean) as FooterItem[], [tasksFooterVisible, tmuxFooterVisible, bagelFooterVisible, teamsFooterVisible, bridgeFooterVisible, companionFooterVisible]); + + // Effective selection: null if the selected pill stopped rendering (bridge + // disconnected, task finished). The derivation makes the UI correct + // immediately; the useEffect below clears the raw state so it doesn't + // resurrect when the same pill reappears (new task starts → focus stolen). + const rawFooterSelection = useAppState(s => s.footerSelection); + const footerItemSelected = rawFooterSelection && footerItems.includes(rawFooterSelection) ? rawFooterSelection : null; + useEffect(() => { + if (rawFooterSelection && !footerItemSelected) { + setAppState(prev => prev.footerSelection === null ? prev : { + ...prev, + footerSelection: null + }); + } + }, [rawFooterSelection, footerItemSelected, setAppState]); + const tasksSelected = footerItemSelected === 'tasks'; + const tmuxSelected = footerItemSelected === 'tmux'; + const bagelSelected = footerItemSelected === 'bagel'; + const teamsSelected = footerItemSelected === 'teams'; + const bridgeSelected = footerItemSelected === 'bridge'; + function selectFooterItem(item: FooterItem | null): void { + setAppState(prev => prev.footerSelection === item ? prev : { + ...prev, + footerSelection: item + }); + if (item === 'tasks') { + setTeammateFooterIndex(0); + setCoordinatorTaskIndex(minCoordinatorIndex); + } + } + + // delta: +1 = down/right, -1 = up/left. Returns true if nav happened + // (including deselecting at the start), false if at a boundary. + function navigateFooter(delta: 1 | -1, exitAtStart = false): boolean { + const idx = footerItemSelected ? footerItems.indexOf(footerItemSelected) : -1; + const next = footerItems[idx + delta]; + if (next) { + selectFooterItem(next); + return true; + } + if (delta < 0 && exitAtStart) { + selectFooterItem(null); + return true; + } + return false; + } + + // Prompt suggestion hook - reads suggestions generated by forked agent in query loop + const { + suggestion: promptSuggestion, + markAccepted, + logOutcomeAtSubmission, + markShown + } = usePromptSuggestion({ + inputValue: input, + isAssistantResponding: isLoading + }); + const displayedValue = useMemo(() => isSearchingHistory && historyMatch ? getValueFromInput(typeof historyMatch === 'string' ? historyMatch : historyMatch.display) : input, [isSearchingHistory, historyMatch, input]); + const thinkTriggers = useMemo(() => findThinkingTriggerPositions(displayedValue), [displayedValue]); + const ultraplanSessionUrl = useAppState(s => s.ultraplanSessionUrl); + const ultraplanLaunching = useAppState(s => s.ultraplanLaunching); + const ultraplanTriggers = useMemo(() => feature('ULTRAPLAN') && !ultraplanSessionUrl && !ultraplanLaunching ? findUltraplanTriggerPositions(displayedValue) : [], [displayedValue, ultraplanSessionUrl, ultraplanLaunching]); + const ultrareviewTriggers = useMemo(() => isUltrareviewEnabled() ? findUltrareviewTriggerPositions(displayedValue) : [], [displayedValue]); + const btwTriggers = useMemo(() => findBtwTriggerPositions(displayedValue), [displayedValue]); + const buddyTriggers = useMemo(() => findBuddyTriggerPositions(displayedValue), [displayedValue]); + const slashCommandTriggers = useMemo(() => { + const positions = findSlashCommandPositions(displayedValue); + // Only highlight valid commands + return positions.filter(pos => { + const commandName = displayedValue.slice(pos.start + 1, pos.end); // +1 to skip "/" + return hasCommand(commandName, commands); + }); + }, [displayedValue, commands]); + const tokenBudgetTriggers = useMemo(() => feature('TOKEN_BUDGET') ? findTokenBudgetPositions(displayedValue) : [], [displayedValue]); + const knownChannelsVersion = useSyncExternalStore(subscribeKnownChannels, getKnownChannelsVersion); + const slackChannelTriggers = useMemo(() => hasSlackMcpServer(store.getState().mcp.clients) ? findSlackChannelPositions(displayedValue) : [], + // eslint-disable-next-line react-hooks/exhaustive-deps -- store is a stable ref + [displayedValue, knownChannelsVersion]); + + // Find @name mentions and highlight with team member's color + const memberMentionHighlights = useMemo((): Array<{ + start: number; + end: number; + themeColor: keyof Theme; + }> => { + if (!isAgentSwarmsEnabled()) return []; + if (!teamContext?.teammates) return []; + const highlights: Array<{ + start: number; + end: number; + themeColor: keyof Theme; + }> = []; + const members = teamContext.teammates; + if (!members) return highlights; + + // Find all @name patterns in the input + const regex = /(^|\s)@([\w-]+)/g; + const memberValues = Object.values(members); + let match; + while ((match = regex.exec(displayedValue)) !== null) { + const leadingSpace = match[1] ?? ''; + const nameStart = match.index + leadingSpace.length; + const fullMatch = match[0].trimStart(); + const name = match[2]; + + // Check if this name matches a team member + const member = memberValues.find(t => t.name === name); + if (member?.color) { + const themeColor = AGENT_COLOR_TO_THEME_COLOR[member.color as AgentColorName]; + if (themeColor) { + highlights.push({ + start: nameStart, + end: nameStart + fullMatch.length, + themeColor + }); + } + } + } + return highlights; + }, [displayedValue, teamContext]); + const imageRefPositions = useMemo(() => parseReferences(displayedValue).filter(r => r.match.startsWith('[Image')).map(r => ({ + start: r.index, + end: r.index + r.match.length + })), [displayedValue]); + + // chip.start is the "selected" state: the inverted chip IS the cursor. + // chip.end stays a normal position so you can park the cursor right after + // `]` like any other character. + const cursorAtImageChip = imageRefPositions.some(r => r.start === cursorOffset); + + // up/down movement or a fullscreen click can land the cursor strictly + // inside a chip; snap to the nearer boundary so it's never editable + // char-by-char. + useEffect(() => { + const inside = imageRefPositions.find(r => cursorOffset > r.start && cursorOffset < r.end); + if (inside) { + const mid = (inside.start + inside.end) / 2; + setCursorOffset(cursorOffset < mid ? inside.start : inside.end); + } + }, [cursorOffset, imageRefPositions, setCursorOffset]); + const combinedHighlights = useMemo((): TextHighlight[] => { + const highlights: TextHighlight[] = []; + + // Invert the [Image #N] chip when the cursor is at chip.start (the + // "selected" state) so backspace-to-delete is visually obvious. + for (const ref of imageRefPositions) { + if (cursorOffset === ref.start) { + highlights.push({ + start: ref.start, + end: ref.end, + color: undefined, + inverse: true, + priority: 8 + }); + } + } + if (isSearchingHistory && historyMatch && !historyFailedMatch) { + highlights.push({ + start: cursorOffset, + end: cursorOffset + historyQuery.length, + color: 'warning', + priority: 20 + }); + } + + // Add "btw" highlighting (solid yellow) + for (const trigger of btwTriggers) { + highlights.push({ + start: trigger.start, + end: trigger.end, + color: 'warning', + priority: 15 + }); + } + + // Add /command highlighting (blue) + for (const trigger of slashCommandTriggers) { + highlights.push({ + start: trigger.start, + end: trigger.end, + color: 'suggestion', + priority: 5 + }); + } + + // Add token budget highlighting (blue) + for (const trigger of tokenBudgetTriggers) { + highlights.push({ + start: trigger.start, + end: trigger.end, + color: 'suggestion', + priority: 5 + }); + } + for (const trigger of slackChannelTriggers) { + highlights.push({ + start: trigger.start, + end: trigger.end, + color: 'suggestion', + priority: 5 + }); + } + + // Add @name highlighting with team member's color + for (const mention of memberMentionHighlights) { + highlights.push({ + start: mention.start, + end: mention.end, + color: mention.themeColor, + priority: 5 + }); + } + + // Dim interim voice dictation text + if (voiceInterimRange) { + highlights.push({ + start: voiceInterimRange.start, + end: voiceInterimRange.end, + color: undefined, + dimColor: true, + priority: 1 + }); + } + + // Rainbow highlighting for ultrathink keyword (per-character cycling colors) + if (isUltrathinkEnabled()) { + for (const trigger of thinkTriggers) { + for (let i = trigger.start; i < trigger.end; i++) { + highlights.push({ + start: i, + end: i + 1, + color: getRainbowColor(i - trigger.start), + shimmerColor: getRainbowColor(i - trigger.start, true), + priority: 10 + }); + } + } + } + + // Same rainbow treatment for the ultraplan keyword + if (feature('ULTRAPLAN')) { + for (const trigger of ultraplanTriggers) { + for (let i = trigger.start; i < trigger.end; i++) { + highlights.push({ + start: i, + end: i + 1, + color: getRainbowColor(i - trigger.start), + shimmerColor: getRainbowColor(i - trigger.start, true), + priority: 10 + }); + } + } + } + + // Same rainbow treatment for the ultrareview keyword + for (const trigger of ultrareviewTriggers) { + for (let i = trigger.start; i < trigger.end; i++) { + highlights.push({ + start: i, + end: i + 1, + color: getRainbowColor(i - trigger.start), + shimmerColor: getRainbowColor(i - trigger.start, true), + priority: 10 + }); + } + } + + // Rainbow for /buddy + for (const trigger of buddyTriggers) { + for (let i = trigger.start; i < trigger.end; i++) { + highlights.push({ + start: i, + end: i + 1, + color: getRainbowColor(i - trigger.start), + shimmerColor: getRainbowColor(i - trigger.start, true), + priority: 10 + }); + } + } + return highlights; + }, [isSearchingHistory, historyQuery, historyMatch, historyFailedMatch, cursorOffset, btwTriggers, imageRefPositions, memberMentionHighlights, slashCommandTriggers, tokenBudgetTriggers, slackChannelTriggers, displayedValue, voiceInterimRange, thinkTriggers, ultraplanTriggers, ultrareviewTriggers, buddyTriggers]); + const { + addNotification, + removeNotification + } = useNotifications(); + + // Show ultrathink notification + useEffect(() => { + if (thinkTriggers.length && isUltrathinkEnabled()) { + addNotification({ + key: 'ultrathink-active', + text: 'Effort set to high for this turn', + priority: 'immediate', + timeoutMs: 5000 + }); + } else { + removeNotification('ultrathink-active'); + } + }, [addNotification, removeNotification, thinkTriggers.length]); + useEffect(() => { + if (feature('ULTRAPLAN') && ultraplanTriggers.length) { + addNotification({ + key: 'ultraplan-active', + text: 'This prompt will launch an ultraplan session in Claude Code on the web', + priority: 'immediate', + timeoutMs: 5000 + }); + } else { + removeNotification('ultraplan-active'); + } + }, [addNotification, removeNotification, ultraplanTriggers.length]); + useEffect(() => { + if (isUltrareviewEnabled() && ultrareviewTriggers.length) { + addNotification({ + key: 'ultrareview-active', + text: 'Run /ultrareview after Claude finishes to review these changes in the cloud', + priority: 'immediate', + timeoutMs: 5000 + }); + } + }, [addNotification, ultrareviewTriggers.length]); + + // Track input length for stash hint + const prevInputLengthRef = useRef(input.length); + const peakInputLengthRef = useRef(input.length); + + // Dismiss stash hint when user makes any input change + const dismissStashHint = useCallback(() => { + removeNotification('stash-hint'); + }, [removeNotification]); + + // Show stash hint when user gradually clears substantial input + useEffect(() => { + const prevLength = prevInputLengthRef.current; + const peakLength = peakInputLengthRef.current; + const currentLength = input.length; + prevInputLengthRef.current = currentLength; + + // Update peak when input grows + if (currentLength > peakLength) { + peakInputLengthRef.current = currentLength; + return; + } + + // Reset state when input is empty + if (currentLength === 0) { + peakInputLengthRef.current = 0; + return; + } + + // Detect gradual clear: peak was high, current is low, but this wasn't a single big jump + // (rapid clears like esc-esc go from 20+ to 0 in one step) + const clearedSubstantialInput = peakLength >= 20 && currentLength <= 5; + const wasRapidClear = prevLength >= 20 && currentLength <= 5; + if (clearedSubstantialInput && !wasRapidClear) { + const config = getGlobalConfig(); + if (!config.hasUsedStash) { + addNotification({ + key: 'stash-hint', + jsx: + Tip:{' '} + + , + priority: 'immediate', + timeoutMs: FOOTER_TEMPORARY_STATUS_TIMEOUT + }); + } + peakInputLengthRef.current = currentLength; + } + }, [input.length, addNotification]); + + // Initialize input buffer for undo functionality + const { + pushToBuffer, + undo, + canUndo, + clearBuffer + } = useInputBuffer({ + maxBufferSize: 50, + debounceMs: 1000 + }); + useMaybeTruncateInput({ + input, + pastedContents, + onInputChange: trackAndSetInput, + setCursorOffset, + setPastedContents + }); + const defaultPlaceholder = usePromptInputPlaceholder({ + input, + submitCount, + viewingAgentName + }); + const onChange = useCallback((value: string) => { + if (value === '?') { + logEvent('tengu_help_toggled', {}); + setHelpOpen(v => !v); + return; + } + setHelpOpen(false); + + // Dismiss stash hint when user makes any input change + dismissStashHint(); + + // Cancel any pending prompt suggestion and speculation when user types + abortPromptSuggestion(); + abortSpeculation(setAppState); + + // Check if this is a single character insertion at the start + const isSingleCharInsertion = value.length === input.length + 1; + const insertedAtStart = cursorOffset === 0; + const mode = getModeFromInput(value); + if (insertedAtStart && mode !== 'prompt') { + if (isSingleCharInsertion) { + onModeChange(mode); + return; + } + // Multi-char insertion into empty input (e.g. tab-accepting "! gcloud auth login") + if (input.length === 0) { + onModeChange(mode); + const valueWithoutMode = getValueFromInput(value).replaceAll('\t', ' '); + pushToBuffer(input, cursorOffset, pastedContents); + trackAndSetInput(valueWithoutMode); + setCursorOffset(valueWithoutMode.length); + return; + } + } + const processedValue = value.replaceAll('\t', ' '); + + // Push current state to buffer before making changes + if (input !== processedValue) { + pushToBuffer(input, cursorOffset, pastedContents); + } + + // Deselect footer items when user types + setAppState(prev => prev.footerSelection === null ? prev : { + ...prev, + footerSelection: null + }); + trackAndSetInput(processedValue); + }, [trackAndSetInput, onModeChange, input, cursorOffset, pushToBuffer, pastedContents, dismissStashHint, setAppState]); + const { + resetHistory, + onHistoryUp, + onHistoryDown, + dismissSearchHint, + historyIndex + } = useArrowKeyHistory((value: string, historyMode: HistoryMode, pastedContents: Record) => { + onChange(value); + onModeChange(historyMode); + setPastedContents(pastedContents); + }, input, pastedContents, setCursorOffset, mode); + + // Dismiss search hint when user starts searching + useEffect(() => { + if (isSearchingHistory) { + dismissSearchHint(); + } + }, [isSearchingHistory, dismissSearchHint]); + + // Only use history navigation when there are 0 or 1 slash command suggestions. + // Footer nav is NOT here — when a pill is selected, TextInput focus=false so + // these never fire. The Footer keybinding context handles ↑/↓ instead. + function handleHistoryUp() { + if (suggestions.length > 1) { + return; + } + + // Only navigate history when cursor is on the first line. + // In multiline inputs, up arrow should move the cursor (handled by TextInput) + // and only trigger history when at the top of the input. + if (!isCursorOnFirstLine) { + return; + } + + // If there's an editable queued command, move it to the input for editing when UP is pressed + const hasEditableCommand = queuedCommands.some(isQueuedCommandEditable); + if (hasEditableCommand) { + void popAllCommandsFromQueue(); + return; + } + onHistoryUp(); + } + function handleHistoryDown() { + if (suggestions.length > 1) { + return; + } + + // Only navigate history/footer when cursor is on the last line. + // In multiline inputs, down arrow should move the cursor (handled by TextInput) + // and only trigger navigation when at the bottom of the input. + if (!isCursorOnLastLine) { + return; + } + + // At bottom of history → enter footer at first visible pill + if (onHistoryDown() && footerItems.length > 0) { + const first = footerItems[0]!; + selectFooterItem(first); + if (first === 'tasks' && !getGlobalConfig().hasSeenTasksHint) { + saveGlobalConfig(c => c.hasSeenTasksHint ? c : { + ...c, + hasSeenTasksHint: true + }); + } + } + } + + // Create a suggestions state directly - we'll sync it with useTypeahead later + const [suggestionsState, setSuggestionsStateRaw] = useState<{ + suggestions: SuggestionItem[]; + selectedSuggestion: number; + commandArgumentHint?: string; + }>({ + suggestions: [], + selectedSuggestion: -1, + commandArgumentHint: undefined + }); + + // Setter for suggestions state + const setSuggestionsState = useCallback((updater: typeof suggestionsState | ((prev: typeof suggestionsState) => typeof suggestionsState)) => { + setSuggestionsStateRaw(prev => typeof updater === 'function' ? updater(prev) : updater); + }, []); + const onSubmit = useCallback(async (inputParam: string, isSubmittingSlashCommand = false) => { + inputParam = inputParam.trimEnd(); + + // Don't submit if a footer indicator is being opened. Read fresh from + // store — footer:openSelected calls selectFooterItem(null) then onSubmit + // in the same tick, and the closure value hasn't updated yet. Apply the + // same "still visible?" derivation as footerItemSelected so a stale + // selection (pill disappeared) doesn't swallow Enter. + const state = store.getState(); + if (state.footerSelection && footerItems.includes(state.footerSelection)) { + return; + } + + // Enter in selection modes confirms selection (useBackgroundTaskNavigation). + // BaseTextInput's useInput registers before that hook (child effects fire first), + // so without this guard Enter would double-fire and auto-submit the suggestion. + if (state.viewSelectionMode === 'selecting-agent') { + return; + } + + // Check for images early - we need this for suggestion logic below + const hasImages = Object.values(pastedContents).some(c => c.type === 'image'); + + // If input is empty OR matches the suggestion, submit it + // But if there are images attached, don't auto-accept the suggestion - + // the user wants to submit just the image(s). + // Only in leader view — promptSuggestion is leader-context, not teammate. + const suggestionText = promptSuggestionState.text; + const inputMatchesSuggestion = inputParam.trim() === '' || inputParam === suggestionText; + if (inputMatchesSuggestion && suggestionText && !hasImages && !state.viewingAgentTaskId) { + // If speculation is active, inject messages immediately as they stream + if (speculation.status === 'active') { + markAccepted(); + // skipReset: resetSuggestion would abort the speculation before we accept it + logOutcomeAtSubmission(suggestionText, { + skipReset: true + }); + void onSubmitProp(suggestionText, { + setCursorOffset, + clearBuffer, + resetHistory + }, { + state: speculation, + speculationSessionTimeSavedMs: speculationSessionTimeSavedMs, + setAppState + }); + return; // Skip normal query - speculation handled it + } + + // Regular suggestion acceptance (requires shownAt > 0) + if (promptSuggestionState.shownAt > 0) { + markAccepted(); + inputParam = suggestionText; + } + } + + // Handle @name direct message + if (isAgentSwarmsEnabled()) { + const directMessage = parseDirectMemberMessage(inputParam); + if (directMessage) { + const result = await sendDirectMemberMessage(directMessage.recipientName, directMessage.message, teamContext, writeToMailbox); + if (result.success) { + addNotification({ + key: 'direct-message-sent', + text: `Sent to @${result.recipientName}`, + priority: 'immediate', + timeoutMs: 3000 + }); + trackAndSetInput(''); + setCursorOffset(0); + clearBuffer(); + resetHistory(); + return; + } else if (result.error === 'no_team_context') { + // No team context - fall through to normal prompt submission + } else { + // Unknown recipient - fall through to normal prompt submission + // This allows e.g. "@utils explain this code" to be sent as a prompt + } + } + } + + // Allow submission if there are images attached, even without text + if (inputParam.trim() === '' && !hasImages) { + return; + } + + // PromptInput UX: Check if suggestions dropdown is showing + // For directory suggestions, allow submission (Tab is used for completion) + const hasDirectorySuggestions = suggestionsState.suggestions.length > 0 && suggestionsState.suggestions.every(s => s.description === 'directory'); + if (suggestionsState.suggestions.length > 0 && !isSubmittingSlashCommand && !hasDirectorySuggestions) { + logForDebugging(`[onSubmit] early return: suggestions showing (count=${suggestionsState.suggestions.length})`); + return; // Don't submit, user needs to clear suggestions first + } + + // Log suggestion outcome if one exists + if (promptSuggestionState.text && promptSuggestionState.shownAt > 0) { + logOutcomeAtSubmission(inputParam); + } + + // Clear stash hint notification on submit + removeNotification('stash-hint'); + + // Route input to viewed agent (in-process teammate or named local_agent). + const activeAgent = getActiveAgentForInput(store.getState()); + if (activeAgent.type !== 'leader' && onAgentSubmit) { + logEvent('tengu_transcript_input_to_teammate', {}); + await onAgentSubmit(inputParam, activeAgent.task, { + setCursorOffset, + clearBuffer, + resetHistory + }); + return; + } + + // Normal leader submission + await onSubmitProp(inputParam, { + setCursorOffset, + clearBuffer, + resetHistory + }); + }, [promptSuggestionState, speculation, speculationSessionTimeSavedMs, teamContext, store, footerItems, suggestionsState.suggestions, onSubmitProp, onAgentSubmit, clearBuffer, resetHistory, logOutcomeAtSubmission, setAppState, markAccepted, pastedContents, removeNotification]); + const { + suggestions, + selectedSuggestion, + commandArgumentHint, + inlineGhostText, + maxColumnWidth + } = useTypeahead({ + commands, + onInputChange: trackAndSetInput, + onSubmit, + setCursorOffset, + input, + cursorOffset, + mode, + agents, + setSuggestionsState, + suggestionsState, + suppressSuggestions: isSearchingHistory || historyIndex > 0, + markAccepted, + onModeChange + }); + + // Track if prompt suggestion should be shown (computed later with terminal width). + // Hidden in teammate view — suggestion is leader-context only. + const showPromptSuggestion = mode === 'prompt' && suggestions.length === 0 && promptSuggestion && !viewingAgentTaskId; + if (showPromptSuggestion) { + markShown(); + } + + // If suggestion was generated but can't be shown due to timing, log suppression. + // Exclude teammate view: markShown() is gated above, so shownAt stays 0 there — + // but that's not a timing failure, the suggestion is valid when returning to leader. + if (promptSuggestionState.text && !promptSuggestion && promptSuggestionState.shownAt === 0 && !viewingAgentTaskId) { + logSuggestionSuppressed('timing', promptSuggestionState.text); + setAppState(prev => ({ + ...prev, + promptSuggestion: { + text: null, + promptId: null, + shownAt: 0, + acceptedAt: 0, + generationRequestId: null + } + })); + } + function onImagePaste(image: string, mediaType?: string, filename?: string, dimensions?: ImageDimensions, sourcePath?: string) { + logEvent('tengu_paste_image', {}); + onModeChange('prompt'); + const pasteId = nextPasteIdRef.current++; + const newContent: PastedContent = { + id: pasteId, + type: 'image', + content: image, + mediaType: mediaType || 'image/png', + // default to PNG if not provided + filename: filename || 'Pasted image', + dimensions, + sourcePath + }; + + // Cache path immediately (fast) so links work on render + cacheImagePath(newContent); + + // Store image to disk in background + void storeImage(newContent); + + // Update UI + setPastedContents(prev => ({ + ...prev, + [pasteId]: newContent + })); + // Multi-image paste calls onImagePaste in a loop. If the ref is already + // armed, the previous pill's lazy space fires now (before this pill) + // rather than being lost. + const prefix = pendingSpaceAfterPillRef.current ? ' ' : ''; + insertTextAtCursor(prefix + formatImageRef(pasteId)); + pendingSpaceAfterPillRef.current = true; + } + + // Prune images whose [Image #N] placeholder is no longer in the input text. + // Covers pill backspace, Ctrl+U, char-by-char deletion — any edit that drops + // the ref. onImagePaste batches setPastedContents + insertTextAtCursor in the + // same event, so this effect sees the placeholder already present. + useEffect(() => { + const referencedIds = new Set(parseReferences(input).map(r => r.id)); + setPastedContents(prev => { + const orphaned = Object.values(prev).filter(c => c.type === 'image' && !referencedIds.has(c.id)); + if (orphaned.length === 0) return prev; + const next = { + ...prev + }; + for (const img of orphaned) delete next[img.id]; + return next; + }); + }, [input, setPastedContents]); + function onTextPaste(rawText: string) { + pendingSpaceAfterPillRef.current = false; + // Clean up pasted text - strip ANSI escape codes and normalize line endings and tabs + let text = stripAnsi(rawText).replace(/\r/g, '\n').replaceAll('\t', ' '); + + // Match typed/auto-suggest: `!cmd` pasted into empty input enters bash mode. + if (input.length === 0) { + const pastedMode = getModeFromInput(text); + if (pastedMode !== 'prompt') { + onModeChange(pastedMode); + text = getValueFromInput(text); + } + } + const numLines = getPastedTextRefNumLines(text); + // Limit the number of lines to show in the input + // If the overall layout is too high then Ink will repaint + // the entire terminal. + // The actual required height is dependent on the content, this + // is just an estimate. + const maxLines = Math.min(rows - 10, 2); + + // Use special handling for long pasted text (>PASTE_THRESHOLD chars) + // or if it exceeds the number of lines we want to show + if (text.length > PASTE_THRESHOLD || numLines > maxLines) { + const pasteId = nextPasteIdRef.current++; + const newContent: PastedContent = { + id: pasteId, + type: 'text', + content: text + }; + setPastedContents(prev => ({ + ...prev, + [pasteId]: newContent + })); + insertTextAtCursor(formatPastedTextRef(pasteId, numLines)); + } else { + // For shorter pastes, just insert the text normally + insertTextAtCursor(text); + } + } + const lazySpaceInputFilter = useCallback((input: string, key: Key): string => { + if (!pendingSpaceAfterPillRef.current) return input; + pendingSpaceAfterPillRef.current = false; + if (isNonSpacePrintable(input, key)) return ' ' + input; + return input; + }, []); + function insertTextAtCursor(text: string) { + // Push current state to buffer before inserting + pushToBuffer(input, cursorOffset, pastedContents); + const newInput = input.slice(0, cursorOffset) + text + input.slice(cursorOffset); + trackAndSetInput(newInput); + setCursorOffset(cursorOffset + text.length); + } + const doublePressEscFromEmpty = useDoublePress(() => {}, () => onShowMessageSelector()); + + // Function to get the queued command for editing. Returns true if commands were popped. + const popAllCommandsFromQueue = useCallback((): boolean => { + const result = popAllEditable(input, cursorOffset); + if (!result) { + return false; + } + trackAndSetInput(result.text); + onModeChange('prompt'); // Always prompt mode for queued commands + setCursorOffset(result.cursorOffset); + + // Restore images from queued commands to pastedContents + if (result.images.length > 0) { + setPastedContents(prev => { + const newContents = { + ...prev + }; + for (const image of result.images) { + newContents[image.id] = image; + } + return newContents; + }); + } + return true; + }, [trackAndSetInput, onModeChange, input, cursorOffset, setPastedContents]); + + // Insert the at-mentioned reference (the file and, optionally, a line range) when + // we receive an at-mentioned notification the IDE. + const onIdeAtMentioned = function (atMentioned: IDEAtMentioned) { + logEvent('tengu_ext_at_mentioned', {}); + let atMentionedText: string; + const relativePath = path.relative(getCwd(), atMentioned.filePath); + if (atMentioned.lineStart && atMentioned.lineEnd) { + atMentionedText = atMentioned.lineStart === atMentioned.lineEnd ? `@${relativePath}#L${atMentioned.lineStart} ` : `@${relativePath}#L${atMentioned.lineStart}-${atMentioned.lineEnd} `; + } else { + atMentionedText = `@${relativePath} `; + } + const cursorChar = input[cursorOffset - 1] ?? ' '; + if (!/\s/.test(cursorChar)) { + atMentionedText = ` ${atMentionedText}`; + } + insertTextAtCursor(atMentionedText); + }; + useIdeAtMentioned(mcpClients, onIdeAtMentioned); + + // Handler for chat:undo - undo last edit + const handleUndo = useCallback(() => { + if (canUndo) { + const previousState = undo(); + if (previousState) { + trackAndSetInput(previousState.text); + setCursorOffset(previousState.cursorOffset); + setPastedContents(previousState.pastedContents); + } + } + }, [canUndo, undo, trackAndSetInput, setPastedContents]); + + // Handler for chat:newline - insert a newline at the cursor position + const handleNewline = useCallback(() => { + pushToBuffer(input, cursorOffset, pastedContents); + const newInput = input.slice(0, cursorOffset) + '\n' + input.slice(cursorOffset); + trackAndSetInput(newInput); + setCursorOffset(cursorOffset + 1); + }, [input, cursorOffset, trackAndSetInput, setCursorOffset, pushToBuffer, pastedContents]); + + // Handler for chat:externalEditor - edit in $EDITOR + const handleExternalEditor = useCallback(async () => { + logEvent('tengu_external_editor_used', {}); + setIsExternalEditorActive(true); + try { + // Pass pastedContents to expand collapsed text references + const result = await editPromptInEditor(input, pastedContents); + if (result.error) { + addNotification({ + key: 'external-editor-error', + text: result.error, + color: 'warning', + priority: 'high' + }); + } + if (result.content !== null && result.content !== input) { + // Push current state to buffer before making changes + pushToBuffer(input, cursorOffset, pastedContents); + trackAndSetInput(result.content); + setCursorOffset(result.content.length); + } + } catch (err) { + if (err instanceof Error) { + logError(err); + } + addNotification({ + key: 'external-editor-error', + text: `External editor failed: ${errorMessage(err)}`, + color: 'warning', + priority: 'high' + }); + } finally { + setIsExternalEditorActive(false); + } + }, [input, cursorOffset, pastedContents, pushToBuffer, trackAndSetInput, addNotification]); + + // Handler for chat:stash - stash/unstash prompt + const handleStash = useCallback(() => { + if (input.trim() === '' && stashedPrompt !== undefined) { + // Pop stash when input is empty + trackAndSetInput(stashedPrompt.text); + setCursorOffset(stashedPrompt.cursorOffset); + setPastedContents(stashedPrompt.pastedContents); + setStashedPrompt(undefined); + } else if (input.trim() !== '') { + // Push to stash (save text, cursor position, and pasted contents) + setStashedPrompt({ + text: input, + cursorOffset, + pastedContents + }); + trackAndSetInput(''); + setCursorOffset(0); + setPastedContents({}); + // Track usage for /discover and stop showing hint + saveGlobalConfig(c => { + if (c.hasUsedStash) return c; + return { + ...c, + hasUsedStash: true + }; + }); + } + }, [input, cursorOffset, stashedPrompt, trackAndSetInput, setStashedPrompt, pastedContents, setPastedContents]); + + // Handler for chat:modelPicker - toggle model picker + const handleModelPicker = useCallback(() => { + setShowModelPicker(prev => !prev); + if (helpOpen) { + setHelpOpen(false); + } + }, [helpOpen]); + + // Handler for chat:fastMode - toggle fast mode picker + const handleFastModePicker = useCallback(() => { + setShowFastModePicker(prev => !prev); + if (helpOpen) { + setHelpOpen(false); + } + }, [helpOpen]); + + // Handler for chat:thinkingToggle - toggle thinking mode + const handleThinkingToggle = useCallback(() => { + setShowThinkingToggle(prev => !prev); + if (helpOpen) { + setHelpOpen(false); + } + }, [helpOpen]); + + // Handler for chat:cycleMode - cycle through permission modes + const handleCycleMode = useCallback(() => { + // When viewing a teammate, cycle their mode instead of the leader's + if (isAgentSwarmsEnabled() && viewedTeammate && viewingAgentTaskId) { + const teammateContext: ToolPermissionContext = { + ...toolPermissionContext, + mode: viewedTeammate.permissionMode + }; + // Pass undefined for teamContext (unused but kept for API compatibility) + const nextMode = getNextPermissionMode(teammateContext, undefined); + logEvent('tengu_mode_cycle', { + to: nextMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + const teammateTaskId = viewingAgentTaskId; + setAppState(prev => { + const task = prev.tasks[teammateTaskId]; + if (!task || task.type !== 'in_process_teammate') { + return prev; + } + if (task.permissionMode === nextMode) { + return prev; + } + return { + ...prev, + tasks: { + ...prev.tasks, + [teammateTaskId]: { + ...task, + permissionMode: nextMode + } + } + }; + }); + if (helpOpen) { + setHelpOpen(false); + } + return; + } + + // Compute the next mode without triggering side effects first + logForDebugging(`[auto-mode] handleCycleMode: currentMode=${toolPermissionContext.mode} isAutoModeAvailable=${toolPermissionContext.isAutoModeAvailable} showAutoModeOptIn=${showAutoModeOptIn} timeoutPending=${!!autoModeOptInTimeoutRef.current}`); + const nextMode = getNextPermissionMode(toolPermissionContext, teamContext); + + // Check if user is entering auto mode for the first time. Gated on the + // persistent settings flag (hasAutoModeOptIn) rather than the broader + // hasAutoModeOptInAnySource so that --enable-auto-mode users still see + // the warning dialog once — the CLI flag should grant carousel access, + // not bypass the safety text. + let isEnteringAutoModeFirstTime = false; + if (feature('TRANSCRIPT_CLASSIFIER')) { + isEnteringAutoModeFirstTime = nextMode === 'auto' && toolPermissionContext.mode !== 'auto' && !hasAutoModeOptIn() && !viewingAgentTaskId; // Only show for primary agent, not subagents + } + if (feature('TRANSCRIPT_CLASSIFIER')) { + if (isEnteringAutoModeFirstTime) { + // Store previous mode so we can revert if user declines + setPreviousModeBeforeAuto(toolPermissionContext.mode); + + // Only update the UI mode label — do NOT call transitionPermissionMode + // or cyclePermissionMode yet; we haven't confirmed with the user. + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...prev.toolPermissionContext, + mode: 'auto' + } + })); + setToolPermissionContext({ + ...toolPermissionContext, + mode: 'auto' + }); + + // Show opt-in dialog after 400ms debounce + if (autoModeOptInTimeoutRef.current) { + clearTimeout(autoModeOptInTimeoutRef.current); + } + autoModeOptInTimeoutRef.current = setTimeout((setShowAutoModeOptIn, autoModeOptInTimeoutRef) => { + setShowAutoModeOptIn(true); + autoModeOptInTimeoutRef.current = null; + }, 400, setShowAutoModeOptIn, autoModeOptInTimeoutRef); + if (helpOpen) { + setHelpOpen(false); + } + return; + } + } + + // Dismiss auto mode opt-in dialog if showing or pending (user is cycling away). + // Do NOT revert to previousModeBeforeAuto here — shift+tab means "advance the + // carousel", not "decline". Reverting causes a ping-pong loop: auto reverts to + // the prior mode, whose next mode is auto again, forever. + // The dialog's own decline button (handleAutoModeOptInDecline) handles revert. + if (feature('TRANSCRIPT_CLASSIFIER')) { + if (showAutoModeOptIn || autoModeOptInTimeoutRef.current) { + if (showAutoModeOptIn) { + logEvent('tengu_auto_mode_opt_in_dialog_decline', {}); + } + setShowAutoModeOptIn(false); + if (autoModeOptInTimeoutRef.current) { + clearTimeout(autoModeOptInTimeoutRef.current); + autoModeOptInTimeoutRef.current = null; + } + setPreviousModeBeforeAuto(null); + // Fall through — mode is 'auto', cyclePermissionMode below goes to 'default'. + } + } + + // Now that we know this is NOT the first-time auto mode path, + // call cyclePermissionMode to apply side effects (e.g. strip + // dangerous permissions, activate classifier) + const { + context: preparedContext + } = cyclePermissionMode(toolPermissionContext, teamContext); + logEvent('tengu_mode_cycle', { + to: nextMode as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + + // Track when user enters plan mode + if (nextMode === 'plan') { + saveGlobalConfig(current => ({ + ...current, + lastPlanModeUse: Date.now() + })); + } + + // Set the mode via setAppState directly because setToolPermissionContext + // intentionally preserves the existing mode (to prevent coordinator mode + // corruption from workers). Then call setToolPermissionContext to trigger + // recheck of queued permission prompts. + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...preparedContext, + mode: nextMode + } + })); + setToolPermissionContext({ + ...preparedContext, + mode: nextMode + }); + + // If this is a teammate, update config.json so team lead sees the change + syncTeammateMode(nextMode, teamContext?.teamName); + + // Close help tips if they're open when mode is cycled + if (helpOpen) { + setHelpOpen(false); + } + }, [toolPermissionContext, teamContext, viewingAgentTaskId, viewedTeammate, setAppState, setToolPermissionContext, helpOpen, showAutoModeOptIn]); + + // Handler for auto mode opt-in dialog acceptance + const handleAutoModeOptInAccept = useCallback(() => { + if (feature('TRANSCRIPT_CLASSIFIER')) { + setShowAutoModeOptIn(false); + setPreviousModeBeforeAuto(null); + + // Now that the user accepted, apply the full transition: activate the + // auto mode backend (classifier, beta headers) and strip dangerous + // permissions (e.g. Bash(*) always-allow rules). + const strippedContext = transitionPermissionMode(previousModeBeforeAuto ?? toolPermissionContext.mode, 'auto', toolPermissionContext); + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...strippedContext, + mode: 'auto' + } + })); + setToolPermissionContext({ + ...strippedContext, + mode: 'auto' + }); + + // Close help tips if they're open when auto mode is enabled + if (helpOpen) { + setHelpOpen(false); + } + } + }, [helpOpen, setHelpOpen, previousModeBeforeAuto, toolPermissionContext, setAppState, setToolPermissionContext]); + + // Handler for auto mode opt-in dialog decline + const handleAutoModeOptInDecline = useCallback(() => { + if (feature('TRANSCRIPT_CLASSIFIER')) { + logForDebugging(`[auto-mode] handleAutoModeOptInDecline: reverting to ${previousModeBeforeAuto}, setting isAutoModeAvailable=false`); + setShowAutoModeOptIn(false); + if (autoModeOptInTimeoutRef.current) { + clearTimeout(autoModeOptInTimeoutRef.current); + autoModeOptInTimeoutRef.current = null; + } + + // Revert to previous mode and remove auto from the carousel + // for the rest of this session + if (previousModeBeforeAuto) { + setAutoModeActive(false); + setAppState(prev => ({ + ...prev, + toolPermissionContext: { + ...prev.toolPermissionContext, + mode: previousModeBeforeAuto, + isAutoModeAvailable: false + } + })); + setToolPermissionContext({ + ...toolPermissionContext, + mode: previousModeBeforeAuto, + isAutoModeAvailable: false + }); + setPreviousModeBeforeAuto(null); + } + } + }, [previousModeBeforeAuto, toolPermissionContext, setAppState, setToolPermissionContext]); + + // Handler for chat:imagePaste - paste image from clipboard + const handleImagePaste = useCallback(() => { + void getImageFromClipboard().then(imageData => { + if (imageData) { + onImagePaste(imageData.base64, imageData.mediaType); + } else { + const shortcutDisplay = getShortcutDisplay('chat:imagePaste', 'Chat', 'ctrl+v'); + const message = env.isSSH() ? "No image found in clipboard. You're SSH'd; try scp?" : `No image found in clipboard. Use ${shortcutDisplay} to paste images.`; + addNotification({ + key: 'no-image-in-clipboard', + text: message, + priority: 'immediate', + timeoutMs: 1000 + }); + } + }); + }, [addNotification, onImagePaste]); + + // Register chat:submit handler directly in the handler registry (not via + // useKeybindings) so that only the ChordInterceptor can invoke it for chord + // completions (e.g., "ctrl+e s"). The default Enter binding for submit is + // handled by TextInput directly (via onSubmit prop) and useTypeahead (for + // autocomplete acceptance). Using useKeybindings would cause + // stopImmediatePropagation on Enter, blocking autocomplete from seeing the key. + const keybindingContext = useOptionalKeybindingContext(); + useEffect(() => { + if (!keybindingContext || isModalOverlayActive) return; + return keybindingContext.registerHandler({ + action: 'chat:submit', + context: 'Chat', + handler: () => { + void onSubmit(input); + } + }); + }, [keybindingContext, isModalOverlayActive, onSubmit, input]); + + // Chat context keybindings for editing shortcuts + // Note: history:previous/history:next are NOT handled here. They are passed as + // onHistoryUp/onHistoryDown props to TextInput, so that useTextInput's + // upOrHistoryUp/downOrHistoryDown can try cursor movement first and only + // fall through to history when the cursor can't move further. + const chatHandlers = useMemo(() => ({ + 'chat:undo': handleUndo, + 'chat:newline': handleNewline, + 'chat:externalEditor': handleExternalEditor, + 'chat:stash': handleStash, + 'chat:modelPicker': handleModelPicker, + 'chat:thinkingToggle': handleThinkingToggle, + 'chat:cycleMode': handleCycleMode, + 'chat:imagePaste': handleImagePaste + }), [handleUndo, handleNewline, handleExternalEditor, handleStash, handleModelPicker, handleThinkingToggle, handleCycleMode, handleImagePaste]); + useKeybindings(chatHandlers, { + context: 'Chat', + isActive: !isModalOverlayActive + }); + + // Shift+↑ enters message-actions cursor. Separate isActive so ctrl+r search + // doesn't leave stale isSearchingHistory on cursor-exit remount. + useKeybinding('chat:messageActions', () => onMessageActionsEnter?.(), { + context: 'Chat', + isActive: !isModalOverlayActive && !isSearchingHistory + }); + + // Fast mode keybinding is only active when fast mode is enabled and available + useKeybinding('chat:fastMode', handleFastModePicker, { + context: 'Chat', + isActive: !isModalOverlayActive && isFastModeEnabled() && isFastModeAvailable() + }); + + // Handle help:dismiss keybinding (ESC closes help menu) + // This is registered separately from Chat context so it has priority over + // CancelRequestHandler when help menu is open + useKeybinding('help:dismiss', () => { + setHelpOpen(false); + }, { + context: 'Help', + isActive: helpOpen + }); + + // Quick Open / Global Search. Hook calls are unconditional (Rules of Hooks); + // the handler body is feature()-gated so the setState calls and component + // references get tree-shaken in external builds. + const quickSearchActive = feature('QUICK_SEARCH') ? !isModalOverlayActive : false; + useKeybinding('app:quickOpen', () => { + if (feature('QUICK_SEARCH')) { + setShowQuickOpen(true); + setHelpOpen(false); + } + }, { + context: 'Global', + isActive: quickSearchActive + }); + useKeybinding('app:globalSearch', () => { + if (feature('QUICK_SEARCH')) { + setShowGlobalSearch(true); + setHelpOpen(false); + } + }, { + context: 'Global', + isActive: quickSearchActive + }); + useKeybinding('history:search', () => { + if (feature('HISTORY_PICKER')) { + setShowHistoryPicker(true); + setHelpOpen(false); + } + }, { + context: 'Global', + isActive: feature('HISTORY_PICKER') ? !isModalOverlayActive : false + }); + + // Handle Ctrl+C to abort speculation when idle (not loading) + // CancelRequestHandler only handles Ctrl+C during active tasks + useKeybinding('app:interrupt', () => { + abortSpeculation(setAppState); + }, { + context: 'Global', + isActive: !isLoading && speculation.status === 'active' + }); + + // Footer indicator navigation keybindings. ↑/↓ live here (not in + // handleHistoryUp/Down) because TextInput focus=false when a pill is + // selected — its useInput is inactive, so this is the only path. + useKeybindings({ + 'footer:up': () => { + // ↑ scrolls within the coordinator task list before leaving the pill + if (tasksSelected && "external" === 'ant' && coordinatorTaskCount > 0 && coordinatorTaskIndex > minCoordinatorIndex) { + setCoordinatorTaskIndex(prev => prev - 1); + return; + } + navigateFooter(-1, true); + }, + 'footer:down': () => { + // ↓ scrolls within the coordinator task list, never leaves the pill + if (tasksSelected && "external" === 'ant' && coordinatorTaskCount > 0) { + if (coordinatorTaskIndex < coordinatorTaskCount - 1) { + setCoordinatorTaskIndex(prev => prev + 1); + } + return; + } + if (tasksSelected && !isTeammateMode) { + setShowBashesDialog(true); + selectFooterItem(null); + return; + } + navigateFooter(1); + }, + 'footer:next': () => { + // Teammate mode: ←/→ cycles within the team member list + if (tasksSelected && isTeammateMode) { + const totalAgents = 1 + inProcessTeammates.length; + setTeammateFooterIndex(prev => (prev + 1) % totalAgents); + return; + } + navigateFooter(1); + }, + 'footer:previous': () => { + if (tasksSelected && isTeammateMode) { + const totalAgents = 1 + inProcessTeammates.length; + setTeammateFooterIndex(prev => (prev - 1 + totalAgents) % totalAgents); + return; + } + navigateFooter(-1); + }, + 'footer:openSelected': () => { + if (viewSelectionMode === 'selecting-agent') { + return; + } + switch (footerItemSelected) { + case 'companion': + if (feature('BUDDY')) { + selectFooterItem(null); + void onSubmit('/buddy'); + } + break; + case 'tasks': + if (isTeammateMode) { + // Enter switches to the selected agent's view + if (teammateFooterIndex === 0) { + exitTeammateView(setAppState); + } else { + const teammate = inProcessTeammates[teammateFooterIndex - 1]; + if (teammate) enterTeammateView(teammate.id, setAppState); + } + } else if (coordinatorTaskIndex === 0 && coordinatorTaskCount > 0) { + exitTeammateView(setAppState); + } else { + const selectedTaskId = getVisibleAgentTasks(tasks)[coordinatorTaskIndex - 1]?.id; + if (selectedTaskId) { + enterTeammateView(selectedTaskId, setAppState); + } else { + setShowBashesDialog(true); + selectFooterItem(null); + } + } + break; + case 'tmux': + if ("external" === 'ant') { + setAppState(prev => prev.tungstenPanelAutoHidden ? { + ...prev, + tungstenPanelAutoHidden: false + } : { + ...prev, + tungstenPanelVisible: !(prev.tungstenPanelVisible ?? true) + }); + } + break; + case 'bagel': + break; + case 'teams': + setShowTeamsDialog(true); + selectFooterItem(null); + break; + case 'bridge': + setShowBridgeDialog(true); + selectFooterItem(null); + break; + } + }, + 'footer:clearSelection': () => { + selectFooterItem(null); + }, + 'footer:close': () => { + if (tasksSelected && coordinatorTaskIndex >= 1) { + const task = getVisibleAgentTasks(tasks)[coordinatorTaskIndex - 1]; + if (!task) return false; + // When the selected row IS the viewed agent, 'x' types into the + // steering input. Any other row — dismiss it. + if (viewSelectionMode === 'viewing-agent' && task.id === viewingAgentTaskId) { + onChange(input.slice(0, cursorOffset) + 'x' + input.slice(cursorOffset)); + setCursorOffset(cursorOffset + 1); + return; + } + stopOrDismissAgent(task.id, setAppState); + if (task.status !== 'running') { + setCoordinatorTaskIndex(i => Math.max(minCoordinatorIndex, i - 1)); + } + return; + } + // Not handled — let 'x' fall through to type-to-exit + return false; + } + }, { + context: 'Footer', + isActive: !!footerItemSelected && !isModalOverlayActive + }); + useInput((char, key) => { + // Skip all input handling when a full-screen dialog is open. These dialogs + // render via early return, but hooks run unconditionally — so without this + // guard, Escape inside a dialog leaks to the double-press message-selector. + if (showTeamsDialog || showQuickOpen || showGlobalSearch || showHistoryPicker) { + return; + } + + // Detect failed Alt shortcuts on macOS (Option key produces special characters) + if (getPlatform() === 'macos' && isMacosOptionChar(char)) { + const shortcut = MACOS_OPTION_SPECIAL_CHARS[char]; + const terminalName = getNativeCSIuTerminalDisplayName(); + const jsx = terminalName ? + To enable {shortcut}, set Option as Meta in{' '} + {terminalName} preferences (⌘,) + : To enable {shortcut}, run /terminal-setup; + addNotification({ + key: 'option-meta-hint', + jsx, + priority: 'immediate', + timeoutMs: 5000 + }); + // Don't return - let the character be typed so user sees the issue + } + + // Footer navigation is handled via useKeybindings above (Footer context) + + // NOTE: ctrl+_, ctrl+g, ctrl+s are handled via Chat context keybindings above + + // Type-to-exit footer: printable chars while a pill is selected refocus + // the input and type the char. Nav keys are captured by useKeybindings + // above, so anything reaching here is genuinely not a footer action. + // onChange clears footerSelection, so no explicit deselect. + if (footerItemSelected && char && !key.ctrl && !key.meta && !key.escape && !key.return) { + onChange(input.slice(0, cursorOffset) + char + input.slice(cursorOffset)); + setCursorOffset(cursorOffset + char.length); + return; + } + + // Exit special modes when backspace/escape/delete/ctrl+u is pressed at cursor position 0 + if (cursorOffset === 0 && (key.escape || key.backspace || key.delete || key.ctrl && char === 'u')) { + onModeChange('prompt'); + setHelpOpen(false); + } + + // Exit help mode when backspace is pressed and input is empty + if (helpOpen && input === '' && (key.backspace || key.delete)) { + setHelpOpen(false); + } + + // esc is a little overloaded: + // - when we're loading a response, it's used to cancel the request + // - otherwise, it's used to show the message selector + // - when double pressed, it's used to clear the input + // - when input is empty, pop from command queue + + // Handle ESC key press + if (key.escape) { + // Abort active speculation + if (speculation.status === 'active') { + abortSpeculation(setAppState); + return; + } + + // Dismiss side question response if visible + if (isSideQuestionVisible && onDismissSideQuestion) { + onDismissSideQuestion(); + return; + } + + // Close help menu if open + if (helpOpen) { + setHelpOpen(false); + return; + } + + // Footer selection clearing is now handled via Footer context keybindings + // (footer:clearSelection action bound to escape) + // If a footer item is selected, let the Footer keybinding handle it + if (footerItemSelected) { + return; + } + + // If there's an editable queued command, move it to the input for editing when ESC is pressed + const hasEditableCommand = queuedCommands.some(isQueuedCommandEditable); + if (hasEditableCommand) { + void popAllCommandsFromQueue(); + return; + } + if (messages.length > 0 && !input && !isLoading) { + doublePressEscFromEmpty(); + } + } + if (key.return && helpOpen) { + setHelpOpen(false); + } + }); + const swarmBanner = useSwarmBanner(); + const fastModeCooldown = isFastModeEnabled() ? isFastModeCooldown() : false; + const showFastIcon = isFastModeEnabled() ? isFastMode && (isFastModeAvailable() || fastModeCooldown) : false; + const showFastIconHint = useShowFastIconHint(showFastIcon ?? false); + + // Show effort notification on startup and when effort changes. + // Suppressed in brief/assistant mode — the value reflects the local + // client's effort, not the connected agent's. + const effortNotificationText = briefOwnsGap ? undefined : getEffortNotificationText(effortValue, mainLoopModel); + useEffect(() => { + if (!effortNotificationText) { + removeNotification('effort-level'); + return; + } + addNotification({ + key: 'effort-level', + text: effortNotificationText, + priority: 'high', + timeoutMs: 12_000 + }); + }, [effortNotificationText, addNotification, removeNotification]); + useBuddyNotification(); + const companionSpeaking = feature('BUDDY') ? + // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant + useAppState(s => s.companionReaction !== undefined) : false; + const { + columns, + rows + } = useTerminalSize(); + const textInputColumns = columns - 3 - companionReservedColumns(columns, companionSpeaking); + + // POC: click-to-position-cursor. Mouse tracking is only enabled inside + // , so this is dormant in the normal main-screen REPL. + // localCol/localRow are relative to the onClick Box's top-left; the Box + // tightly wraps the text input so they map directly to (column, line) + // in the Cursor wrap model. MeasuredText.getOffsetFromPosition handles + // wide chars, wrapped lines, and clamps past-end clicks to line end. + const maxVisibleLines = isFullscreenEnvEnabled() ? Math.max(MIN_INPUT_VIEWPORT_LINES, Math.floor(rows / 2) - PROMPT_FOOTER_LINES) : undefined; + const handleInputClick = useCallback((e: ClickEvent) => { + // During history search the displayed text is historyMatch, not + // input, and showCursor is false anyway — skip rather than + // compute an offset against the wrong string. + if (!input || isSearchingHistory) return; + const c = Cursor.fromText(input, textInputColumns, cursorOffset); + const viewportStart = c.getViewportStartLine(maxVisibleLines); + const offset = c.measuredText.getOffsetFromPosition({ + line: e.localRow + viewportStart, + column: e.localCol + }); + setCursorOffset(offset); + }, [input, textInputColumns, isSearchingHistory, cursorOffset, maxVisibleLines]); + const handleOpenTasksDialog = useCallback((taskId?: string) => setShowBashesDialog(taskId ?? true), [setShowBashesDialog]); + const placeholder = showPromptSuggestion && promptSuggestion ? promptSuggestion : defaultPlaceholder; + + // Calculate if input has multiple lines + const isInputWrapped = useMemo(() => input.includes('\n'), [input]); + + // Memoized callbacks for model picker to prevent re-renders when unrelated + // state (like notifications) changes. This prevents the inline model picker + // from visually "jumping" when notifications arrive. + const handleModelSelect = useCallback((model: string | null, _effort: EffortLevel | undefined) => { + let wasFastModeDisabled = false; + setAppState(prev => { + wasFastModeDisabled = isFastModeEnabled() && !isFastModeSupportedByModel(model) && !!prev.fastMode; + return { + ...prev, + mainLoopModel: model, + mainLoopModelForSession: null, + // Turn off fast mode if switching to a model that doesn't support it + ...(wasFastModeDisabled && { + fastMode: false + }) + }; + }); + setShowModelPicker(false); + const effectiveFastMode = (isFastMode ?? false) && !wasFastModeDisabled; + let message = `Model set to ${modelDisplayString(model)}`; + if (isBilledAsExtraUsage(model, effectiveFastMode, isOpus1mMergeEnabled())) { + message += ' · Billed as extra usage'; + } + if (wasFastModeDisabled) { + message += ' · Fast mode OFF'; + } + addNotification({ + key: 'model-switched', + jsx: {message}, + priority: 'immediate', + timeoutMs: 3000 + }); + logEvent('tengu_model_picker_hotkey', { + model: model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS + }); + }, [setAppState, addNotification, isFastMode]); + const handleModelCancel = useCallback(() => { + setShowModelPicker(false); + }, []); + + // Memoize the model picker element to prevent unnecessary re-renders + // when AppState changes for unrelated reasons (e.g., notifications arriving) + const modelPickerElement = useMemo(() => { + if (!showModelPicker) return null; + return + + ; + }, [showModelPicker, mainLoopModel_, mainLoopModelForSession, handleModelSelect, handleModelCancel]); + const handleFastModeSelect = useCallback((result?: string) => { + setShowFastModePicker(false); + if (result) { + addNotification({ + key: 'fast-mode-toggled', + jsx: {result}, + priority: 'immediate', + timeoutMs: 3000 + }); + } + }, [addNotification]); + + // Memoize the fast mode picker element + const fastModePickerElement = useMemo(() => { + if (!showFastModePicker) return null; + return + + ; + }, [showFastModePicker, handleFastModeSelect]); + + // Memoized callbacks for thinking toggle + const handleThinkingSelect = useCallback((enabled: boolean) => { + setAppState(prev => ({ + ...prev, + thinkingEnabled: enabled + })); + setShowThinkingToggle(false); + logEvent('tengu_thinking_toggled_hotkey', { + enabled + }); + addNotification({ + key: 'thinking-toggled-hotkey', + jsx: + Thinking {enabled ? 'on' : 'off'} + , + priority: 'immediate', + timeoutMs: 3000 + }); + }, [setAppState, addNotification]); + const handleThinkingCancel = useCallback(() => { + setShowThinkingToggle(false); + }, []); + + // Memoize the thinking toggle element + const thinkingToggleElement = useMemo(() => { + if (!showThinkingToggle) return null; + return + m.type === 'assistant')} /> + ; + }, [showThinkingToggle, thinkingEnabled, handleThinkingSelect, handleThinkingCancel, messages.length]); + + // Portal dialog to DialogOverlay in fullscreen so it escapes the bottom + // slot's overflowY:hidden clip (same pattern as SuggestionsOverlay). + // Must be called before early returns below to satisfy rules-of-hooks. + // Memoized so the portal useEffect doesn't churn on every PromptInput render. + const autoModeOptInDialog = useMemo(() => feature('TRANSCRIPT_CLASSIFIER') && showAutoModeOptIn ? : null, [showAutoModeOptIn, handleAutoModeOptInAccept, handleAutoModeOptInDecline]); + useSetPromptOverlayDialog(isFullscreenEnvEnabled() ? autoModeOptInDialog : null); + if (showBashesDialog) { + return setShowBashesDialog(false)} toolUseContext={getToolUseContext(messages, [], new AbortController(), mainLoopModel)} initialDetailTaskId={typeof showBashesDialog === 'string' ? showBashesDialog : undefined} />; + } + if (isAgentSwarmsEnabled() && showTeamsDialog) { + return { + setShowTeamsDialog(false); + }} />; + } + if (feature('QUICK_SEARCH')) { + const insertWithSpacing = (text: string) => { + const cursorChar = input[cursorOffset - 1] ?? ' '; + insertTextAtCursor(/\s/.test(cursorChar) ? text : ` ${text}`); + }; + if (showQuickOpen) { + return setShowQuickOpen(false)} onInsert={insertWithSpacing} />; + } + if (showGlobalSearch) { + return setShowGlobalSearch(false)} onInsert={insertWithSpacing} />; + } + } + if (feature('HISTORY_PICKER') && showHistoryPicker) { + return { + const entryMode = getModeFromInput(entry.display); + const value = getValueFromInput(entry.display); + onModeChange(entryMode); + trackAndSetInput(value); + setPastedContents(entry.pastedContents); + setCursorOffset(value.length); + setShowHistoryPicker(false); + }} onCancel={() => setShowHistoryPicker(false)} />; + } + + // Show loop mode menu when requested (ant-only, eliminated from external builds) + if (modelPickerElement) { + return modelPickerElement; + } + if (fastModePickerElement) { + return fastModePickerElement; + } + if (thinkingToggleElement) { + return thinkingToggleElement; + } + if (showBridgeDialog) { + return { + setShowBridgeDialog(false); + selectFooterItem(null); + }} />; + } + const baseProps: BaseTextInputProps = { + multiline: true, + onSubmit, + onChange, + value: historyMatch ? getValueFromInput(typeof historyMatch === 'string' ? historyMatch : historyMatch.display) : input, + // History navigation is handled via TextInput props (onHistoryUp/onHistoryDown), + // NOT via useKeybindings. This allows useTextInput's upOrHistoryUp/downOrHistoryDown + // to try cursor movement first and only fall through to history navigation when the + // cursor can't move further (important for wrapped text and multi-line input). + onHistoryUp: handleHistoryUp, + onHistoryDown: handleHistoryDown, + onHistoryReset: resetHistory, + placeholder, + onExit, + onExitMessage: (show, key) => setExitMessage({ + show, + key + }), + onImagePaste, + columns: textInputColumns, + maxVisibleLines, + disableCursorMovementForUpDownKeys: suggestions.length > 0 || !!footerItemSelected, + disableEscapeDoublePress: suggestions.length > 0, + cursorOffset, + onChangeCursorOffset: setCursorOffset, + onPaste: onTextPaste, + onIsPastingChange: setIsPasting, + focus: !isSearchingHistory && !isModalOverlayActive && !footerItemSelected, + showCursor: !footerItemSelected && !isSearchingHistory && !cursorAtImageChip, + argumentHint: commandArgumentHint, + onUndo: canUndo ? () => { + const previousState = undo(); + if (previousState) { + trackAndSetInput(previousState.text); + setCursorOffset(previousState.cursorOffset); + setPastedContents(previousState.pastedContents); + } + } : undefined, + highlights: combinedHighlights, + inlineGhostText, + inputFilter: lazySpaceInputFilter + }; + const getBorderColor = (): keyof Theme => { + const modeColors: Record = { + bash: 'bashBorder' + }; + + // Mode colors take priority, then teammate color, then default + if (modeColors[mode]) { + return modeColors[mode]; + } + + // In-process teammates run headless - don't apply teammate colors to leader UI + if (isInProcessTeammate()) { + return 'promptBorder'; + } + + // Check for teammate color from environment + const teammateColorName = getTeammateColor(); + if (teammateColorName && AGENT_COLORS.includes(teammateColorName as AgentColorName)) { + return AGENT_COLOR_TO_THEME_COLOR[teammateColorName as AgentColorName]; + } + return 'promptBorder'; + }; + if (isExternalEditorActive) { + return + + Save and close editor to continue... + + ; + } + const textInputElement = isVimModeEnabled() ? : ; + return + {!isFullscreenEnvEnabled() && } + {hasSuppressedDialogs && + Waiting for permission… + } + + {swarmBanner ? <> + + {swarmBanner.text ? <> + {'─'.repeat(Math.max(0, columns - stringWidth(swarmBanner.text) - 4))} + + {' '} + {swarmBanner.text}{' '} + + {'──'} + : '─'.repeat(columns)} + + + + + {textInputElement} + + + {'─'.repeat(columns)} + : + + + {textInputElement} + + } + 0} isLoading={isLoading} tasksSelected={tasksSelected} teamsSelected={teamsSelected} bridgeSelected={bridgeSelected} tmuxSelected={tmuxSelected} teammateFooterIndex={teammateFooterIndex} ideSelection={ideSelection} mcpClients={mcpClients} isPasting={isPasting} isInputWrapped={isInputWrapped} messages={messages} isSearching={isSearchingHistory} historyQuery={historyQuery} setHistoryQuery={setHistoryQuery} historyFailedMatch={historyFailedMatch} onOpenTasksDialog={isFullscreenEnvEnabled() ? handleOpenTasksDialog : undefined} /> + {isFullscreenEnvEnabled() ? null : autoModeOptInDialog} + {isFullscreenEnvEnabled() ? + // position=absolute takes zero layout height so the spinner + // doesn't shift when a notification appears/disappears. Yoga + // anchors absolute children at the parent's content-box origin; + // marginTop=-1 pulls it into the marginTop=1 gap row above the + // prompt border. In brief mode there is no such gap (briefOwnsGap + // strips our marginTop) and BriefSpinner sits flush against the + // border — marginTop=-2 skips over the spinner content into + // BriefSpinner's own marginTop=1 blank row. height=1 + + // overflow=hidden clips multi-line notifications to a single row. + // flex-end anchors the bottom line so the visible row is always + // the most recent. Suppressed while the slash overlay or + // auto-mode opt-in dialog is up by height=0 (NOT unmount) — this + // Box renders later in tree order so it would paint over their + // bottom row. Keeping Notifications mounted prevents AutoUpdater's + // initial-check effect from re-firing on every slash-completion + // toggle (PR#22413). + + + : null} + ; +} + +/** + * Compute the initial paste ID by finding the max ID used in existing messages. + * This handles --continue/--resume scenarios where we need to avoid ID collisions. + */ +function getInitialPasteId(messages: Message[]): number { + let maxId = 0; + for (const message of messages) { + if (message.type === 'user') { + // Check image paste IDs + if (message.imagePasteIds) { + for (const id of message.imagePasteIds) { + if (id > maxId) maxId = id; + } + } + // Check text paste references in message content + if (Array.isArray(message.message.content)) { + for (const block of message.message.content) { + if (block.type === 'text') { + const refs = parseReferences(block.text); + for (const ref of refs) { + if (ref.id > maxId) maxId = ref.id; + } + } + } + } + } + } + return maxId + 1; +} +function buildBorderText(showFastIcon: boolean, showFastIconHint: boolean, fastModeCooldown: boolean): BorderTextOptions | undefined { + if (!showFastIcon) return undefined; + const fastSeg = showFastIconHint ? `${getFastIconString(true, fastModeCooldown)} ${chalk.dim('/fast')}` : getFastIconString(true, fastModeCooldown); + return { + content: ` ${fastSeg} `, + position: 'top', + align: 'end', + offset: 0 + }; +} +export default React.memo(PromptInput); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiY2hhbGsiLCJwYXRoIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsInVzZVN5bmNFeHRlcm5hbFN0b3JlIiwidXNlTm90aWZpY2F0aW9ucyIsInVzZUNvbW1hbmRRdWV1ZSIsIklERUF0TWVudGlvbmVkIiwidXNlSWRlQXRNZW50aW9uZWQiLCJBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTIiwibG9nRXZlbnQiLCJBcHBTdGF0ZSIsInVzZUFwcFN0YXRlIiwidXNlQXBwU3RhdGVTdG9yZSIsInVzZVNldEFwcFN0YXRlIiwiRm9vdGVySXRlbSIsImdldEN3ZCIsImlzUXVldWVkQ29tbWFuZEVkaXRhYmxlIiwicG9wQWxsRWRpdGFibGUiLCJzdHJpcEFuc2kiLCJjb21wYW5pb25SZXNlcnZlZENvbHVtbnMiLCJmaW5kQnVkZHlUcmlnZ2VyUG9zaXRpb25zIiwidXNlQnVkZHlOb3RpZmljYXRpb24iLCJGYXN0TW9kZVBpY2tlciIsImlzVWx0cmFyZXZpZXdFbmFibGVkIiwiZ2V0TmF0aXZlQ1NJdVRlcm1pbmFsRGlzcGxheU5hbWUiLCJDb21tYW5kIiwiaGFzQ29tbWFuZCIsInVzZUlzTW9kYWxPdmVybGF5QWN0aXZlIiwidXNlU2V0UHJvbXB0T3ZlcmxheURpYWxvZyIsImZvcm1hdEltYWdlUmVmIiwiZm9ybWF0UGFzdGVkVGV4dFJlZiIsImdldFBhc3RlZFRleHRSZWZOdW1MaW5lcyIsInBhcnNlUmVmZXJlbmNlcyIsIlZlcmlmaWNhdGlvblN0YXR1cyIsIkhpc3RvcnlNb2RlIiwidXNlQXJyb3dLZXlIaXN0b3J5IiwidXNlRG91YmxlUHJlc3MiLCJ1c2VIaXN0b3J5U2VhcmNoIiwiSURFU2VsZWN0aW9uIiwidXNlSW5wdXRCdWZmZXIiLCJ1c2VNYWluTG9vcE1vZGVsIiwidXNlUHJvbXB0U3VnZ2VzdGlvbiIsInVzZVRlcm1pbmFsU2l6ZSIsInVzZVR5cGVhaGVhZCIsIkJvcmRlclRleHRPcHRpb25zIiwic3RyaW5nV2lkdGgiLCJCb3giLCJDbGlja0V2ZW50IiwiS2V5IiwiVGV4dCIsInVzZUlucHV0IiwidXNlT3B0aW9uYWxLZXliaW5kaW5nQ29udGV4dCIsImdldFNob3J0Y3V0RGlzcGxheSIsInVzZUtleWJpbmRpbmciLCJ1c2VLZXliaW5kaW5ncyIsIk1DUFNlcnZlckNvbm5lY3Rpb24iLCJhYm9ydFByb21wdFN1Z2dlc3Rpb24iLCJsb2dTdWdnZXN0aW9uU3VwcHJlc3NlZCIsIkFjdGl2ZVNwZWN1bGF0aW9uU3RhdGUiLCJhYm9ydFNwZWN1bGF0aW9uIiwiZ2V0QWN0aXZlQWdlbnRGb3JJbnB1dCIsImdldFZpZXdlZFRlYW1tYXRlVGFzayIsImVudGVyVGVhbW1hdGVWaWV3IiwiZXhpdFRlYW1tYXRlVmlldyIsInN0b3BPckRpc21pc3NBZ2VudCIsIlRvb2xQZXJtaXNzaW9uQ29udGV4dCIsImdldFJ1bm5pbmdUZWFtbWF0ZXNTb3J0ZWQiLCJJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZSIsImlzUGFuZWxBZ2VudFRhc2siLCJMb2NhbEFnZW50VGFza1N0YXRlIiwiaXNCYWNrZ3JvdW5kVGFzayIsIkFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SIiwiQUdFTlRfQ09MT1JTIiwiQWdlbnRDb2xvck5hbWUiLCJBZ2VudERlZmluaXRpb24iLCJNZXNzYWdlIiwiUGVybWlzc2lvbk1vZGUiLCJCYXNlVGV4dElucHV0UHJvcHMiLCJQcm9tcHRJbnB1dE1vZGUiLCJWaW1Nb2RlIiwiaXNBZ2VudFN3YXJtc0VuYWJsZWQiLCJjb3VudCIsIkF1dG9VcGRhdGVyUmVzdWx0IiwiQ3Vyc29yIiwiZ2V0R2xvYmFsQ29uZmlnIiwiUGFzdGVkQ29udGVudCIsInNhdmVHbG9iYWxDb25maWciLCJsb2dGb3JEZWJ1Z2dpbmciLCJwYXJzZURpcmVjdE1lbWJlck1lc3NhZ2UiLCJzZW5kRGlyZWN0TWVtYmVyTWVzc2FnZSIsIkVmZm9ydExldmVsIiwiZW52IiwiZXJyb3JNZXNzYWdlIiwiaXNCaWxsZWRBc0V4dHJhVXNhZ2UiLCJnZXRGYXN0TW9kZVVuYXZhaWxhYmxlUmVhc29uIiwiaXNGYXN0TW9kZUF2YWlsYWJsZSIsImlzRmFzdE1vZGVDb29sZG93biIsImlzRmFzdE1vZGVFbmFibGVkIiwiaXNGYXN0TW9kZVN1cHBvcnRlZEJ5TW9kZWwiLCJpc0Z1bGxzY3JlZW5FbnZFbmFibGVkIiwiUHJvbXB0SW5wdXRIZWxwZXJzIiwiZ2V0SW1hZ2VGcm9tQ2xpcGJvYXJkIiwiUEFTVEVfVEhSRVNIT0xEIiwiSW1hZ2VEaW1lbnNpb25zIiwiY2FjaGVJbWFnZVBhdGgiLCJzdG9yZUltYWdlIiwiaXNNYWNvc09wdGlvbkNoYXIiLCJNQUNPU19PUFRJT05fU1BFQ0lBTF9DSEFSUyIsImxvZ0Vycm9yIiwiaXNPcHVzMW1NZXJnZUVuYWJsZWQiLCJtb2RlbERpc3BsYXlTdHJpbmciLCJzZXRBdXRvTW9kZUFjdGl2ZSIsImN5Y2xlUGVybWlzc2lvbk1vZGUiLCJnZXROZXh0UGVybWlzc2lvbk1vZGUiLCJ0cmFuc2l0aW9uUGVybWlzc2lvbk1vZGUiLCJnZXRQbGF0Zm9ybSIsIlByb2Nlc3NVc2VySW5wdXRDb250ZXh0IiwiZWRpdFByb21wdEluRWRpdG9yIiwiaGFzQXV0b01vZGVPcHRJbiIsImZpbmRCdHdUcmlnZ2VyUG9zaXRpb25zIiwiZmluZFNsYXNoQ29tbWFuZFBvc2l0aW9ucyIsImZpbmRTbGFja0NoYW5uZWxQb3NpdGlvbnMiLCJnZXRLbm93bkNoYW5uZWxzVmVyc2lvbiIsImhhc1NsYWNrTWNwU2VydmVyIiwic3Vic2NyaWJlS25vd25DaGFubmVscyIsImlzSW5Qcm9jZXNzRW5hYmxlZCIsInN5bmNUZWFtbWF0ZU1vZGUiLCJUZWFtU3VtbWFyeSIsImdldFRlYW1tYXRlQ29sb3IiLCJpc0luUHJvY2Vzc1RlYW1tYXRlIiwid3JpdGVUb01haWxib3giLCJUZXh0SGlnaGxpZ2h0IiwiVGhlbWUiLCJmaW5kVGhpbmtpbmdUcmlnZ2VyUG9zaXRpb25zIiwiZ2V0UmFpbmJvd0NvbG9yIiwiaXNVbHRyYXRoaW5rRW5hYmxlZCIsImZpbmRUb2tlbkJ1ZGdldFBvc2l0aW9ucyIsImZpbmRVbHRyYXBsYW5UcmlnZ2VyUG9zaXRpb25zIiwiZmluZFVsdHJhcmV2aWV3VHJpZ2dlclBvc2l0aW9ucyIsIkF1dG9Nb2RlT3B0SW5EaWFsb2ciLCJCcmlkZ2VEaWFsb2ciLCJDb25maWd1cmFibGVTaG9ydGN1dEhpbnQiLCJnZXRWaXNpYmxlQWdlbnRUYXNrcyIsInVzZUNvb3JkaW5hdG9yVGFza0NvdW50IiwiZ2V0RWZmb3J0Tm90aWZpY2F0aW9uVGV4dCIsImdldEZhc3RJY29uU3RyaW5nIiwiR2xvYmFsU2VhcmNoRGlhbG9nIiwiSGlzdG9yeVNlYXJjaERpYWxvZyIsIk1vZGVsUGlja2VyIiwiUXVpY2tPcGVuRGlhbG9nIiwiVGV4dElucHV0IiwiVGhpbmtpbmdUb2dnbGUiLCJCYWNrZ3JvdW5kVGFza3NEaWFsb2ciLCJzaG91bGRIaWRlVGFza3NGb290ZXIiLCJUZWFtc0RpYWxvZyIsIlZpbVRleHRJbnB1dCIsImdldE1vZGVGcm9tSW5wdXQiLCJnZXRWYWx1ZUZyb21JbnB1dCIsIkZPT1RFUl9URU1QT1JBUllfU1RBVFVTX1RJTUVPVVQiLCJOb3RpZmljYXRpb25zIiwiUHJvbXB0SW5wdXRGb290ZXIiLCJTdWdnZXN0aW9uSXRlbSIsIlByb21wdElucHV0TW9kZUluZGljYXRvciIsIlByb21wdElucHV0UXVldWVkQ29tbWFuZHMiLCJQcm9tcHRJbnB1dFN0YXNoTm90aWNlIiwidXNlTWF5YmVUcnVuY2F0ZUlucHV0IiwidXNlUHJvbXB0SW5wdXRQbGFjZWhvbGRlciIsInVzZVNob3dGYXN0SWNvbkhpbnQiLCJ1c2VTd2FybUJhbm5lciIsImlzTm9uU3BhY2VQcmludGFibGUiLCJpc1ZpbU1vZGVFbmFibGVkIiwiUHJvcHMiLCJkZWJ1ZyIsImlkZVNlbGVjdGlvbiIsInRvb2xQZXJtaXNzaW9uQ29udGV4dCIsInNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCIsImN0eCIsImFwaUtleVN0YXR1cyIsImNvbW1hbmRzIiwiYWdlbnRzIiwiaXNMb2FkaW5nIiwidmVyYm9zZSIsIm1lc3NhZ2VzIiwib25BdXRvVXBkYXRlclJlc3VsdCIsInJlc3VsdCIsImF1dG9VcGRhdGVyUmVzdWx0IiwiaW5wdXQiLCJvbklucHV0Q2hhbmdlIiwidmFsdWUiLCJtb2RlIiwib25Nb2RlQ2hhbmdlIiwic3Rhc2hlZFByb21wdCIsInRleHQiLCJjdXJzb3JPZmZzZXQiLCJwYXN0ZWRDb250ZW50cyIsIlJlY29yZCIsInNldFN0YXNoZWRQcm9tcHQiLCJzdWJtaXRDb3VudCIsIm9uU2hvd01lc3NhZ2VTZWxlY3RvciIsIm9uTWVzc2FnZUFjdGlvbnNFbnRlciIsIm1jcENsaWVudHMiLCJzZXRQYXN0ZWRDb250ZW50cyIsIkRpc3BhdGNoIiwiU2V0U3RhdGVBY3Rpb24iLCJ2aW1Nb2RlIiwic2V0VmltTW9kZSIsInNob3dCYXNoZXNEaWFsb2ciLCJzZXRTaG93QmFzaGVzRGlhbG9nIiwic2hvdyIsIm9uRXhpdCIsImdldFRvb2xVc2VDb250ZXh0IiwibmV3TWVzc2FnZXMiLCJhYm9ydENvbnRyb2xsZXIiLCJBYm9ydENvbnRyb2xsZXIiLCJtYWluTG9vcE1vZGVsIiwib25TdWJtaXQiLCJoZWxwZXJzIiwic3BlY3VsYXRpb25BY2NlcHQiLCJzdGF0ZSIsInNwZWN1bGF0aW9uU2Vzc2lvblRpbWVTYXZlZE1zIiwic2V0QXBwU3RhdGUiLCJmIiwicHJldiIsIm9wdGlvbnMiLCJmcm9tS2V5YmluZGluZyIsIlByb21pc2UiLCJvbkFnZW50U3VibWl0IiwidGFzayIsImlzU2VhcmNoaW5nSGlzdG9yeSIsInNldElzU2VhcmNoaW5nSGlzdG9yeSIsImlzU2VhcmNoaW5nIiwib25EaXNtaXNzU2lkZVF1ZXN0aW9uIiwiaXNTaWRlUXVlc3Rpb25WaXNpYmxlIiwiaGVscE9wZW4iLCJzZXRIZWxwT3BlbiIsImhhc1N1cHByZXNzZWREaWFsb2dzIiwiaXNMb2NhbEpTWENvbW1hbmRBY3RpdmUiLCJpbnNlcnRUZXh0UmVmIiwiTXV0YWJsZVJlZk9iamVjdCIsImluc2VydCIsInNldElucHV0V2l0aEN1cnNvciIsImN1cnNvciIsInZvaWNlSW50ZXJpbVJhbmdlIiwic3RhcnQiLCJlbmQiLCJQUk9NUFRfRk9PVEVSX0xJTkVTIiwiTUlOX0lOUFVUX1ZJRVdQT1JUX0xJTkVTIiwiUHJvbXB0SW5wdXQiLCJvblN1Ym1pdFByb3AiLCJSZWFjdE5vZGUiLCJpc01vZGFsT3ZlcmxheUFjdGl2ZSIsImlzQXV0b1VwZGF0aW5nIiwic2V0SXNBdXRvVXBkYXRpbmciLCJleGl0TWVzc2FnZSIsInNldEV4aXRNZXNzYWdlIiwia2V5Iiwic2V0Q3Vyc29yT2Zmc2V0IiwibGVuZ3RoIiwibGFzdEludGVybmFsSW5wdXRSZWYiLCJjdXJyZW50IiwidHJhY2tBbmRTZXRJbnB1dCIsIm5lZWRzU3BhY2UiLCJ0ZXN0IiwiaW5zZXJ0VGV4dCIsIm5ld1ZhbHVlIiwic2xpY2UiLCJzdG9yZSIsInRhc2tzIiwicyIsInJlcGxCcmlkZ2VDb25uZWN0ZWQiLCJyZXBsQnJpZGdlRXhwbGljaXQiLCJyZXBsQnJpZGdlUmVjb25uZWN0aW5nIiwiYnJpZGdlRm9vdGVyVmlzaWJsZSIsImhhc1R1bmdzdGVuU2Vzc2lvbiIsInR1bmdzdGVuQWN0aXZlU2Vzc2lvbiIsInVuZGVmaW5lZCIsInRtdXhGb290ZXJWaXNpYmxlIiwiYmFnZWxGb290ZXJWaXNpYmxlIiwidGVhbUNvbnRleHQiLCJxdWV1ZWRDb21tYW5kcyIsInByb21wdFN1Z2dlc3Rpb25TdGF0ZSIsInByb21wdFN1Z2dlc3Rpb24iLCJzcGVjdWxhdGlvbiIsInZpZXdpbmdBZ2VudFRhc2tJZCIsInZpZXdTZWxlY3Rpb25Nb2RlIiwic2hvd1NwaW5uZXJUcmVlIiwiZXhwYW5kZWRWaWV3IiwiY29tcGFuaW9uIiwiX2NvbXBhbmlvbiIsImNvbXBhbmlvbk11dGVkIiwiY29tcGFuaW9uRm9vdGVyVmlzaWJsZSIsImJyaWVmT3duc0dhcCIsImlzQnJpZWZPbmx5IiwibWFpbkxvb3BNb2RlbF8iLCJtYWluTG9vcE1vZGVsRm9yU2Vzc2lvbiIsInRoaW5raW5nRW5hYmxlZCIsImlzRmFzdE1vZGUiLCJmYXN0TW9kZSIsImVmZm9ydFZhbHVlIiwidmlld2VkVGVhbW1hdGUiLCJnZXRTdGF0ZSIsInZpZXdpbmdBZ2VudE5hbWUiLCJpZGVudGl0eSIsImFnZW50TmFtZSIsInZpZXdpbmdBZ2VudENvbG9yIiwiY29sb3IiLCJpbmNsdWRlcyIsImluUHJvY2Vzc1RlYW1tYXRlcyIsImlzVGVhbW1hdGVNb2RlIiwiZWZmZWN0aXZlVG9vbFBlcm1pc3Npb25Db250ZXh0IiwicGVybWlzc2lvbk1vZGUiLCJoaXN0b3J5UXVlcnkiLCJzZXRIaXN0b3J5UXVlcnkiLCJoaXN0b3J5TWF0Y2giLCJoaXN0b3J5RmFpbGVkTWF0Y2giLCJlbnRyeSIsImRpc3BsYXkiLCJuZXh0UGFzdGVJZFJlZiIsImdldEluaXRpYWxQYXN0ZUlkIiwicGVuZGluZ1NwYWNlQWZ0ZXJQaWxsUmVmIiwic2hvd1RlYW1zRGlhbG9nIiwic2V0U2hvd1RlYW1zRGlhbG9nIiwic2hvd0JyaWRnZURpYWxvZyIsInNldFNob3dCcmlkZ2VEaWFsb2ciLCJ0ZWFtbWF0ZUZvb3RlckluZGV4Iiwic2V0VGVhbW1hdGVGb290ZXJJbmRleCIsImNvb3JkaW5hdG9yVGFza0luZGV4Iiwic2V0Q29vcmRpbmF0b3JUYXNrSW5kZXgiLCJ2IiwibmV4dCIsImNvb3JkaW5hdG9yVGFza0NvdW50IiwiaGFzQmdUYXNrUGlsbCIsIk9iamVjdCIsInZhbHVlcyIsInNvbWUiLCJ0IiwibWluQ29vcmRpbmF0b3JJbmRleCIsIk1hdGgiLCJtYXgiLCJpc1Bhc3RpbmciLCJzZXRJc1Bhc3RpbmciLCJpc0V4dGVybmFsRWRpdG9yQWN0aXZlIiwic2V0SXNFeHRlcm5hbEVkaXRvckFjdGl2ZSIsInNob3dNb2RlbFBpY2tlciIsInNldFNob3dNb2RlbFBpY2tlciIsInNob3dRdWlja09wZW4iLCJzZXRTaG93UXVpY2tPcGVuIiwic2hvd0dsb2JhbFNlYXJjaCIsInNldFNob3dHbG9iYWxTZWFyY2giLCJzaG93SGlzdG9yeVBpY2tlciIsInNldFNob3dIaXN0b3J5UGlja2VyIiwic2hvd0Zhc3RNb2RlUGlja2VyIiwic2V0U2hvd0Zhc3RNb2RlUGlja2VyIiwic2hvd1RoaW5raW5nVG9nZ2xlIiwic2V0U2hvd1RoaW5raW5nVG9nZ2xlIiwic2hvd0F1dG9Nb2RlT3B0SW4iLCJzZXRTaG93QXV0b01vZGVPcHRJbiIsInByZXZpb3VzTW9kZUJlZm9yZUF1dG8iLCJzZXRQcmV2aW91c01vZGVCZWZvcmVBdXRvIiwiYXV0b01vZGVPcHRJblRpbWVvdXRSZWYiLCJOb2RlSlMiLCJUaW1lb3V0IiwiaXNDdXJzb3JPbkZpcnN0TGluZSIsImZpcnN0TmV3bGluZUluZGV4IiwiaW5kZXhPZiIsImlzQ3Vyc29yT25MYXN0TGluZSIsImxhc3ROZXdsaW5lSW5kZXgiLCJsYXN0SW5kZXhPZiIsImNhY2hlZFRlYW1zIiwidGVhbW1hdGVDb3VudCIsInRlYW1tYXRlcyIsIm5hbWUiLCJ0ZWFtTmFtZSIsIm1lbWJlckNvdW50IiwicnVubmluZ0NvdW50IiwiaWRsZUNvdW50IiwicnVubmluZ1Rhc2tDb3VudCIsInN0YXR1cyIsInRhc2tzRm9vdGVyVmlzaWJsZSIsInRlYW1zRm9vdGVyVmlzaWJsZSIsImZvb3Rlckl0ZW1zIiwiZmlsdGVyIiwiQm9vbGVhbiIsInJhd0Zvb3RlclNlbGVjdGlvbiIsImZvb3RlclNlbGVjdGlvbiIsImZvb3Rlckl0ZW1TZWxlY3RlZCIsInRhc2tzU2VsZWN0ZWQiLCJ0bXV4U2VsZWN0ZWQiLCJiYWdlbFNlbGVjdGVkIiwidGVhbXNTZWxlY3RlZCIsImJyaWRnZVNlbGVjdGVkIiwic2VsZWN0Rm9vdGVySXRlbSIsIml0ZW0iLCJuYXZpZ2F0ZUZvb3RlciIsImRlbHRhIiwiZXhpdEF0U3RhcnQiLCJpZHgiLCJzdWdnZXN0aW9uIiwibWFya0FjY2VwdGVkIiwibG9nT3V0Y29tZUF0U3VibWlzc2lvbiIsIm1hcmtTaG93biIsImlucHV0VmFsdWUiLCJpc0Fzc2lzdGFudFJlc3BvbmRpbmciLCJkaXNwbGF5ZWRWYWx1ZSIsInRoaW5rVHJpZ2dlcnMiLCJ1bHRyYXBsYW5TZXNzaW9uVXJsIiwidWx0cmFwbGFuTGF1bmNoaW5nIiwidWx0cmFwbGFuVHJpZ2dlcnMiLCJ1bHRyYXJldmlld1RyaWdnZXJzIiwiYnR3VHJpZ2dlcnMiLCJidWRkeVRyaWdnZXJzIiwic2xhc2hDb21tYW5kVHJpZ2dlcnMiLCJwb3NpdGlvbnMiLCJwb3MiLCJjb21tYW5kTmFtZSIsInRva2VuQnVkZ2V0VHJpZ2dlcnMiLCJrbm93bkNoYW5uZWxzVmVyc2lvbiIsInNsYWNrQ2hhbm5lbFRyaWdnZXJzIiwibWNwIiwiY2xpZW50cyIsIm1lbWJlck1lbnRpb25IaWdobGlnaHRzIiwiQXJyYXkiLCJ0aGVtZUNvbG9yIiwiaGlnaGxpZ2h0cyIsIm1lbWJlcnMiLCJyZWdleCIsIm1lbWJlclZhbHVlcyIsIm1hdGNoIiwiZXhlYyIsImxlYWRpbmdTcGFjZSIsIm5hbWVTdGFydCIsImluZGV4IiwiZnVsbE1hdGNoIiwidHJpbVN0YXJ0IiwibWVtYmVyIiwiZmluZCIsInB1c2giLCJpbWFnZVJlZlBvc2l0aW9ucyIsInIiLCJzdGFydHNXaXRoIiwibWFwIiwiY3Vyc29yQXRJbWFnZUNoaXAiLCJpbnNpZGUiLCJtaWQiLCJjb21iaW5lZEhpZ2hsaWdodHMiLCJyZWYiLCJpbnZlcnNlIiwicHJpb3JpdHkiLCJ0cmlnZ2VyIiwibWVudGlvbiIsImRpbUNvbG9yIiwiaSIsInNoaW1tZXJDb2xvciIsImFkZE5vdGlmaWNhdGlvbiIsInJlbW92ZU5vdGlmaWNhdGlvbiIsInRpbWVvdXRNcyIsInByZXZJbnB1dExlbmd0aFJlZiIsInBlYWtJbnB1dExlbmd0aFJlZiIsImRpc21pc3NTdGFzaEhpbnQiLCJwcmV2TGVuZ3RoIiwicGVha0xlbmd0aCIsImN1cnJlbnRMZW5ndGgiLCJjbGVhcmVkU3Vic3RhbnRpYWxJbnB1dCIsIndhc1JhcGlkQ2xlYXIiLCJjb25maWciLCJoYXNVc2VkU3Rhc2giLCJqc3giLCJwdXNoVG9CdWZmZXIiLCJ1bmRvIiwiY2FuVW5kbyIsImNsZWFyQnVmZmVyIiwibWF4QnVmZmVyU2l6ZSIsImRlYm91bmNlTXMiLCJkZWZhdWx0UGxhY2Vob2xkZXIiLCJvbkNoYW5nZSIsImlzU2luZ2xlQ2hhckluc2VydGlvbiIsImluc2VydGVkQXRTdGFydCIsInZhbHVlV2l0aG91dE1vZGUiLCJyZXBsYWNlQWxsIiwicHJvY2Vzc2VkVmFsdWUiLCJyZXNldEhpc3RvcnkiLCJvbkhpc3RvcnlVcCIsIm9uSGlzdG9yeURvd24iLCJkaXNtaXNzU2VhcmNoSGludCIsImhpc3RvcnlJbmRleCIsImhpc3RvcnlNb2RlIiwiaGFuZGxlSGlzdG9yeVVwIiwic3VnZ2VzdGlvbnMiLCJoYXNFZGl0YWJsZUNvbW1hbmQiLCJwb3BBbGxDb21tYW5kc0Zyb21RdWV1ZSIsImhhbmRsZUhpc3RvcnlEb3duIiwiZmlyc3QiLCJoYXNTZWVuVGFza3NIaW50IiwiYyIsInN1Z2dlc3Rpb25zU3RhdGUiLCJzZXRTdWdnZXN0aW9uc1N0YXRlUmF3Iiwic2VsZWN0ZWRTdWdnZXN0aW9uIiwiY29tbWFuZEFyZ3VtZW50SGludCIsInNldFN1Z2dlc3Rpb25zU3RhdGUiLCJ1cGRhdGVyIiwiaW5wdXRQYXJhbSIsImlzU3VibWl0dGluZ1NsYXNoQ29tbWFuZCIsInRyaW1FbmQiLCJoYXNJbWFnZXMiLCJ0eXBlIiwic3VnZ2VzdGlvblRleHQiLCJpbnB1dE1hdGNoZXNTdWdnZXN0aW9uIiwidHJpbSIsInNraXBSZXNldCIsInNob3duQXQiLCJkaXJlY3RNZXNzYWdlIiwicmVjaXBpZW50TmFtZSIsIm1lc3NhZ2UiLCJzdWNjZXNzIiwiZXJyb3IiLCJoYXNEaXJlY3RvcnlTdWdnZXN0aW9ucyIsImV2ZXJ5IiwiZGVzY3JpcHRpb24iLCJhY3RpdmVBZ2VudCIsImlubGluZUdob3N0VGV4dCIsIm1heENvbHVtbldpZHRoIiwic3VwcHJlc3NTdWdnZXN0aW9ucyIsInNob3dQcm9tcHRTdWdnZXN0aW9uIiwicHJvbXB0SWQiLCJhY2NlcHRlZEF0IiwiZ2VuZXJhdGlvblJlcXVlc3RJZCIsIm9uSW1hZ2VQYXN0ZSIsImltYWdlIiwibWVkaWFUeXBlIiwiZmlsZW5hbWUiLCJkaW1lbnNpb25zIiwic291cmNlUGF0aCIsInBhc3RlSWQiLCJuZXdDb250ZW50IiwiaWQiLCJjb250ZW50IiwicHJlZml4IiwiaW5zZXJ0VGV4dEF0Q3Vyc29yIiwicmVmZXJlbmNlZElkcyIsIlNldCIsIm9ycGhhbmVkIiwiaGFzIiwiaW1nIiwib25UZXh0UGFzdGUiLCJyYXdUZXh0IiwicmVwbGFjZSIsInBhc3RlZE1vZGUiLCJudW1MaW5lcyIsIm1heExpbmVzIiwibWluIiwicm93cyIsImxhenlTcGFjZUlucHV0RmlsdGVyIiwibmV3SW5wdXQiLCJkb3VibGVQcmVzc0VzY0Zyb21FbXB0eSIsImltYWdlcyIsIm5ld0NvbnRlbnRzIiwib25JZGVBdE1lbnRpb25lZCIsImF0TWVudGlvbmVkIiwiYXRNZW50aW9uZWRUZXh0IiwicmVsYXRpdmVQYXRoIiwicmVsYXRpdmUiLCJmaWxlUGF0aCIsImxpbmVTdGFydCIsImxpbmVFbmQiLCJjdXJzb3JDaGFyIiwiaGFuZGxlVW5kbyIsInByZXZpb3VzU3RhdGUiLCJoYW5kbGVOZXdsaW5lIiwiaGFuZGxlRXh0ZXJuYWxFZGl0b3IiLCJlcnIiLCJFcnJvciIsImhhbmRsZVN0YXNoIiwiaGFuZGxlTW9kZWxQaWNrZXIiLCJoYW5kbGVGYXN0TW9kZVBpY2tlciIsImhhbmRsZVRoaW5raW5nVG9nZ2xlIiwiaGFuZGxlQ3ljbGVNb2RlIiwidGVhbW1hdGVDb250ZXh0IiwibmV4dE1vZGUiLCJ0byIsInRlYW1tYXRlVGFza0lkIiwiaXNBdXRvTW9kZUF2YWlsYWJsZSIsImlzRW50ZXJpbmdBdXRvTW9kZUZpcnN0VGltZSIsImNsZWFyVGltZW91dCIsInNldFRpbWVvdXQiLCJjb250ZXh0IiwicHJlcGFyZWRDb250ZXh0IiwibGFzdFBsYW5Nb2RlVXNlIiwiRGF0ZSIsIm5vdyIsImhhbmRsZUF1dG9Nb2RlT3B0SW5BY2NlcHQiLCJzdHJpcHBlZENvbnRleHQiLCJoYW5kbGVBdXRvTW9kZU9wdEluRGVjbGluZSIsImhhbmRsZUltYWdlUGFzdGUiLCJ0aGVuIiwiaW1hZ2VEYXRhIiwiYmFzZTY0Iiwic2hvcnRjdXREaXNwbGF5IiwiaXNTU0giLCJrZXliaW5kaW5nQ29udGV4dCIsInJlZ2lzdGVySGFuZGxlciIsImFjdGlvbiIsImhhbmRsZXIiLCJjaGF0SGFuZGxlcnMiLCJpc0FjdGl2ZSIsInF1aWNrU2VhcmNoQWN0aXZlIiwiZm9vdGVyOnVwIiwiZm9vdGVyOmRvd24iLCJmb290ZXI6bmV4dCIsInRvdGFsQWdlbnRzIiwiZm9vdGVyOnByZXZpb3VzIiwiZm9vdGVyOm9wZW5TZWxlY3RlZCIsInRlYW1tYXRlIiwic2VsZWN0ZWRUYXNrSWQiLCJ0dW5nc3RlblBhbmVsQXV0b0hpZGRlbiIsInR1bmdzdGVuUGFuZWxWaXNpYmxlIiwiZm9vdGVyOmNsZWFyU2VsZWN0aW9uIiwiZm9vdGVyOmNsb3NlIiwiY2hhciIsInNob3J0Y3V0IiwidGVybWluYWxOYW1lIiwiY3RybCIsIm1ldGEiLCJlc2NhcGUiLCJyZXR1cm4iLCJiYWNrc3BhY2UiLCJkZWxldGUiLCJzd2FybUJhbm5lciIsImZhc3RNb2RlQ29vbGRvd24iLCJzaG93RmFzdEljb24iLCJzaG93RmFzdEljb25IaW50IiwiZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCIsImNvbXBhbmlvblNwZWFraW5nIiwiY29tcGFuaW9uUmVhY3Rpb24iLCJjb2x1bW5zIiwidGV4dElucHV0Q29sdW1ucyIsIm1heFZpc2libGVMaW5lcyIsImZsb29yIiwiaGFuZGxlSW5wdXRDbGljayIsImUiLCJmcm9tVGV4dCIsInZpZXdwb3J0U3RhcnQiLCJnZXRWaWV3cG9ydFN0YXJ0TGluZSIsIm9mZnNldCIsIm1lYXN1cmVkVGV4dCIsImdldE9mZnNldEZyb21Qb3NpdGlvbiIsImxpbmUiLCJsb2NhbFJvdyIsImNvbHVtbiIsImxvY2FsQ29sIiwiaGFuZGxlT3BlblRhc2tzRGlhbG9nIiwidGFza0lkIiwicGxhY2Vob2xkZXIiLCJpc0lucHV0V3JhcHBlZCIsImhhbmRsZU1vZGVsU2VsZWN0IiwibW9kZWwiLCJfZWZmb3J0Iiwid2FzRmFzdE1vZGVEaXNhYmxlZCIsImVmZmVjdGl2ZUZhc3RNb2RlIiwiaGFuZGxlTW9kZWxDYW5jZWwiLCJtb2RlbFBpY2tlckVsZW1lbnQiLCJoYW5kbGVGYXN0TW9kZVNlbGVjdCIsImZhc3RNb2RlUGlja2VyRWxlbWVudCIsImhhbmRsZVRoaW5raW5nU2VsZWN0IiwiZW5hYmxlZCIsImhhbmRsZVRoaW5raW5nQ2FuY2VsIiwidGhpbmtpbmdUb2dnbGVFbGVtZW50IiwibSIsImF1dG9Nb2RlT3B0SW5EaWFsb2ciLCJpbnNlcnRXaXRoU3BhY2luZyIsImVudHJ5TW9kZSIsImJhc2VQcm9wcyIsIm11bHRpbGluZSIsIm9uSGlzdG9yeVJlc2V0Iiwib25FeGl0TWVzc2FnZSIsImRpc2FibGVDdXJzb3JNb3ZlbWVudEZvclVwRG93bktleXMiLCJkaXNhYmxlRXNjYXBlRG91YmxlUHJlc3MiLCJvbkNoYW5nZUN1cnNvck9mZnNldCIsIm9uUGFzdGUiLCJvbklzUGFzdGluZ0NoYW5nZSIsImZvY3VzIiwic2hvd0N1cnNvciIsImFyZ3VtZW50SGludCIsIm9uVW5kbyIsImlucHV0RmlsdGVyIiwiZ2V0Qm9yZGVyQ29sb3IiLCJtb2RlQ29sb3JzIiwiYmFzaCIsInRlYW1tYXRlQ29sb3JOYW1lIiwidGV4dElucHV0RWxlbWVudCIsImJnQ29sb3IiLCJyZXBlYXQiLCJidWlsZEJvcmRlclRleHQiLCJtYXhJZCIsImltYWdlUGFzdGVJZHMiLCJpc0FycmF5IiwiYmxvY2siLCJyZWZzIiwiZmFzdFNlZyIsImRpbSIsInBvc2l0aW9uIiwiYWxpZ24iLCJtZW1vIl0sInNvdXJjZXMiOlsiUHJvbXB0SW5wdXQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGZlYXR1cmUgfSBmcm9tICdidW46YnVuZGxlJ1xuaW1wb3J0IGNoYWxrIGZyb20gJ2NoYWxrJ1xuaW1wb3J0ICogYXMgcGF0aCBmcm9tICdwYXRoJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICB1c2VDYWxsYmFjayxcbiAgdXNlRWZmZWN0LFxuICB1c2VNZW1vLFxuICB1c2VSZWYsXG4gIHVzZVN0YXRlLFxuICB1c2VTeW5jRXh0ZXJuYWxTdG9yZSxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnc3JjL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB7IHVzZUNvbW1hbmRRdWV1ZSB9IGZyb20gJ3NyYy9ob29rcy91c2VDb21tYW5kUXVldWUuanMnXG5pbXBvcnQge1xuICB0eXBlIElERUF0TWVudGlvbmVkLFxuICB1c2VJZGVBdE1lbnRpb25lZCxcbn0gZnJvbSAnc3JjL2hvb2tzL3VzZUlkZUF0TWVudGlvbmVkLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICBsb2dFdmVudCxcbn0gZnJvbSAnc3JjL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB7XG4gIHR5cGUgQXBwU3RhdGUsXG4gIHVzZUFwcFN0YXRlLFxuICB1c2VBcHBTdGF0ZVN0b3JlLFxuICB1c2VTZXRBcHBTdGF0ZSxcbn0gZnJvbSAnc3JjL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHR5cGUgeyBGb290ZXJJdGVtIH0gZnJvbSAnc3JjL3N0YXRlL0FwcFN0YXRlU3RvcmUuanMnXG5pbXBvcnQgeyBnZXRDd2QgfSBmcm9tICdzcmMvdXRpbHMvY3dkLmpzJ1xuaW1wb3J0IHtcbiAgaXNRdWV1ZWRDb21tYW5kRWRpdGFibGUsXG4gIHBvcEFsbEVkaXRhYmxlLFxufSBmcm9tICdzcmMvdXRpbHMvbWVzc2FnZVF1ZXVlTWFuYWdlci5qcydcbmltcG9ydCBzdHJpcEFuc2kgZnJvbSAnc3RyaXAtYW5zaSdcbmltcG9ydCB7IGNvbXBhbmlvblJlc2VydmVkQ29sdW1ucyB9IGZyb20gJy4uLy4uL2J1ZGR5L0NvbXBhbmlvblNwcml0ZS5qcydcbmltcG9ydCB7XG4gIGZpbmRCdWRkeVRyaWdnZXJQb3NpdGlvbnMsXG4gIHVzZUJ1ZGR5Tm90aWZpY2F0aW9uLFxufSBmcm9tICcuLi8uLi9idWRkeS91c2VCdWRkeU5vdGlmaWNhdGlvbi5qcydcbmltcG9ydCB7IEZhc3RNb2RlUGlja2VyIH0gZnJvbSAnLi4vLi4vY29tbWFuZHMvZmFzdC9mYXN0LmpzJ1xuaW1wb3J0IHsgaXNVbHRyYXJldmlld0VuYWJsZWQgfSBmcm9tICcuLi8uLi9jb21tYW5kcy9yZXZpZXcvdWx0cmFyZXZpZXdFbmFibGVkLmpzJ1xuaW1wb3J0IHsgZ2V0TmF0aXZlQ1NJdVRlcm1pbmFsRGlzcGxheU5hbWUgfSBmcm9tICcuLi8uLi9jb21tYW5kcy90ZXJtaW5hbFNldHVwL3Rlcm1pbmFsU2V0dXAuanMnXG5pbXBvcnQgeyB0eXBlIENvbW1hbmQsIGhhc0NvbW1hbmQgfSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcbmltcG9ydCB7IHVzZUlzTW9kYWxPdmVybGF5QWN0aXZlIH0gZnJvbSAnLi4vLi4vY29udGV4dC9vdmVybGF5Q29udGV4dC5qcydcbmltcG9ydCB7IHVzZVNldFByb21wdE92ZXJsYXlEaWFsb2cgfSBmcm9tICcuLi8uLi9jb250ZXh0L3Byb21wdE92ZXJsYXlDb250ZXh0LmpzJ1xuaW1wb3J0IHtcbiAgZm9ybWF0SW1hZ2VSZWYsXG4gIGZvcm1hdFBhc3RlZFRleHRSZWYsXG4gIGdldFBhc3RlZFRleHRSZWZOdW1MaW5lcyxcbiAgcGFyc2VSZWZlcmVuY2VzLFxufSBmcm9tICcuLi8uLi9oaXN0b3J5LmpzJ1xuaW1wb3J0IHR5cGUgeyBWZXJpZmljYXRpb25TdGF0dXMgfSBmcm9tICcuLi8uLi9ob29rcy91c2VBcGlLZXlWZXJpZmljYXRpb24uanMnXG5pbXBvcnQge1xuICB0eXBlIEhpc3RvcnlNb2RlLFxuICB1c2VBcnJvd0tleUhpc3RvcnksXG59IGZyb20gJy4uLy4uL2hvb2tzL3VzZUFycm93S2V5SGlzdG9yeS5qcydcbmltcG9ydCB7IHVzZURvdWJsZVByZXNzIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlRG91YmxlUHJlc3MuanMnXG5pbXBvcnQgeyB1c2VIaXN0b3J5U2VhcmNoIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlSGlzdG9yeVNlYXJjaC5qcydcbmltcG9ydCB0eXBlIHsgSURFU2VsZWN0aW9uIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlSWRlU2VsZWN0aW9uLmpzJ1xuaW1wb3J0IHsgdXNlSW5wdXRCdWZmZXIgfSBmcm9tICcuLi8uLi9ob29rcy91c2VJbnB1dEJ1ZmZlci5qcydcbmltcG9ydCB7IHVzZU1haW5Mb29wTW9kZWwgfSBmcm9tICcuLi8uLi9ob29rcy91c2VNYWluTG9vcE1vZGVsLmpzJ1xuaW1wb3J0IHsgdXNlUHJvbXB0U3VnZ2VzdGlvbiB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZVByb21wdFN1Z2dlc3Rpb24uanMnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyB1c2VUeXBlYWhlYWQgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUeXBlYWhlYWQuanMnXG5pbXBvcnQgdHlwZSB7IEJvcmRlclRleHRPcHRpb25zIH0gZnJvbSAnLi4vLi4vaW5rL3JlbmRlci1ib3JkZXIuanMnXG5pbXBvcnQgeyBzdHJpbmdXaWR0aCB9IGZyb20gJy4uLy4uL2luay9zdHJpbmdXaWR0aC5qcydcbmltcG9ydCB7IEJveCwgdHlwZSBDbGlja0V2ZW50LCB0eXBlIEtleSwgVGV4dCwgdXNlSW5wdXQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0IH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvS2V5YmluZGluZ0NvbnRleHQuanMnXG5pbXBvcnQgeyBnZXRTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy9zaG9ydGN1dEZvcm1hdC5qcydcbmltcG9ydCB7XG4gIHVzZUtleWJpbmRpbmcsXG4gIHVzZUtleWJpbmRpbmdzLFxufSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VLZXliaW5kaW5nLmpzJ1xuaW1wb3J0IHR5cGUgeyBNQ1BTZXJ2ZXJDb25uZWN0aW9uIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvbWNwL3R5cGVzLmpzJ1xuaW1wb3J0IHtcbiAgYWJvcnRQcm9tcHRTdWdnZXN0aW9uLFxuICBsb2dTdWdnZXN0aW9uU3VwcHJlc3NlZCxcbn0gZnJvbSAnLi4vLi4vc2VydmljZXMvUHJvbXB0U3VnZ2VzdGlvbi9wcm9tcHRTdWdnZXN0aW9uLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBBY3RpdmVTcGVjdWxhdGlvblN0YXRlLFxuICBhYm9ydFNwZWN1bGF0aW9uLFxufSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9Qcm9tcHRTdWdnZXN0aW9uL3NwZWN1bGF0aW9uLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0QWN0aXZlQWdlbnRGb3JJbnB1dCxcbiAgZ2V0Vmlld2VkVGVhbW1hdGVUYXNrLFxufSBmcm9tICcuLi8uLi9zdGF0ZS9zZWxlY3RvcnMuanMnXG5pbXBvcnQge1xuICBlbnRlclRlYW1tYXRlVmlldyxcbiAgZXhpdFRlYW1tYXRlVmlldyxcbiAgc3RvcE9yRGlzbWlzc0FnZW50LFxufSBmcm9tICcuLi8uLi9zdGF0ZS90ZWFtbWF0ZVZpZXdIZWxwZXJzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUb29sUGVybWlzc2lvbkNvbnRleHQgfSBmcm9tICcuLi8uLi9Ub29sLmpzJ1xuaW1wb3J0IHsgZ2V0UnVubmluZ1RlYW1tYXRlc1NvcnRlZCB9IGZyb20gJy4uLy4uL3Rhc2tzL0luUHJvY2Vzc1RlYW1tYXRlVGFzay9JblByb2Nlc3NUZWFtbWF0ZVRhc2suanMnXG5pbXBvcnQgdHlwZSB7IEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlIH0gZnJvbSAnLi4vLi4vdGFza3MvSW5Qcm9jZXNzVGVhbW1hdGVUYXNrL3R5cGVzLmpzJ1xuaW1wb3J0IHtcbiAgaXNQYW5lbEFnZW50VGFzayxcbiAgdHlwZSBMb2NhbEFnZW50VGFza1N0YXRlLFxufSBmcm9tICcuLi8uLi90YXNrcy9Mb2NhbEFnZW50VGFzay9Mb2NhbEFnZW50VGFzay5qcydcbmltcG9ydCB7IGlzQmFja2dyb3VuZFRhc2sgfSBmcm9tICcuLi8uLi90YXNrcy90eXBlcy5qcydcbmltcG9ydCB7XG4gIEFHRU5UX0NPTE9SX1RPX1RIRU1FX0NPTE9SLFxuICBBR0VOVF9DT0xPUlMsXG4gIHR5cGUgQWdlbnRDb2xvck5hbWUsXG59IGZyb20gJy4uLy4uL3Rvb2xzL0FnZW50VG9vbC9hZ2VudENvbG9yTWFuYWdlci5qcydcbmltcG9ydCB0eXBlIHsgQWdlbnREZWZpbml0aW9uIH0gZnJvbSAnLi4vLi4vdG9vbHMvQWdlbnRUb29sL2xvYWRBZ2VudHNEaXIuanMnXG5pbXBvcnQgdHlwZSB7IE1lc3NhZ2UgfSBmcm9tICcuLi8uLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHR5cGUgeyBQZXJtaXNzaW9uTW9kZSB9IGZyb20gJy4uLy4uL3R5cGVzL3Blcm1pc3Npb25zLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBCYXNlVGV4dElucHV0UHJvcHMsXG4gIFByb21wdElucHV0TW9kZSxcbiAgVmltTW9kZSxcbn0gZnJvbSAnLi4vLi4vdHlwZXMvdGV4dElucHV0VHlwZXMuanMnXG5pbXBvcnQgeyBpc0FnZW50U3dhcm1zRW5hYmxlZCB9IGZyb20gJy4uLy4uL3V0aWxzL2FnZW50U3dhcm1zRW5hYmxlZC5qcydcbmltcG9ydCB7IGNvdW50IH0gZnJvbSAnLi4vLi4vdXRpbHMvYXJyYXkuanMnXG5pbXBvcnQgdHlwZSB7IEF1dG9VcGRhdGVyUmVzdWx0IH0gZnJvbSAnLi4vLi4vdXRpbHMvYXV0b1VwZGF0ZXIuanMnXG5pbXBvcnQgeyBDdXJzb3IgfSBmcm9tICcuLi8uLi91dGlscy9DdXJzb3IuanMnXG5pbXBvcnQge1xuICBnZXRHbG9iYWxDb25maWcsXG4gIHR5cGUgUGFzdGVkQ29udGVudCxcbiAgc2F2ZUdsb2JhbENvbmZpZyxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHsgbG9nRm9yRGVidWdnaW5nIH0gZnJvbSAnLi4vLi4vdXRpbHMvZGVidWcuanMnXG5pbXBvcnQge1xuICBwYXJzZURpcmVjdE1lbWJlck1lc3NhZ2UsXG4gIHNlbmREaXJlY3RNZW1iZXJNZXNzYWdlLFxufSBmcm9tICcuLi8uLi91dGlscy9kaXJlY3RNZW1iZXJNZXNzYWdlLmpzJ1xuaW1wb3J0IHR5cGUgeyBFZmZvcnRMZXZlbCB9IGZyb20gJy4uLy4uL3V0aWxzL2VmZm9ydC5qcydcbmltcG9ydCB7IGVudiB9IGZyb20gJy4uLy4uL3V0aWxzL2Vudi5qcydcbmltcG9ydCB7IGVycm9yTWVzc2FnZSB9IGZyb20gJy4uLy4uL3V0aWxzL2Vycm9ycy5qcydcbmltcG9ydCB7IGlzQmlsbGVkQXNFeHRyYVVzYWdlIH0gZnJvbSAnLi4vLi4vdXRpbHMvZXh0cmFVc2FnZS5qcydcbmltcG9ydCB7XG4gIGdldEZhc3RNb2RlVW5hdmFpbGFibGVSZWFzb24sXG4gIGlzRmFzdE1vZGVBdmFpbGFibGUsXG4gIGlzRmFzdE1vZGVDb29sZG93bixcbiAgaXNGYXN0TW9kZUVuYWJsZWQsXG4gIGlzRmFzdE1vZGVTdXBwb3J0ZWRCeU1vZGVsLFxufSBmcm9tICcuLi8uLi91dGlscy9mYXN0TW9kZS5qcydcbmltcG9ydCB7IGlzRnVsbHNjcmVlbkVudkVuYWJsZWQgfSBmcm9tICcuLi8uLi91dGlscy9mdWxsc2NyZWVuLmpzJ1xuaW1wb3J0IHR5cGUgeyBQcm9tcHRJbnB1dEhlbHBlcnMgfSBmcm9tICcuLi8uLi91dGlscy9oYW5kbGVQcm9tcHRTdWJtaXQuanMnXG5pbXBvcnQge1xuICBnZXRJbWFnZUZyb21DbGlwYm9hcmQsXG4gIFBBU1RFX1RIUkVTSE9MRCxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvaW1hZ2VQYXN0ZS5qcydcbmltcG9ydCB0eXBlIHsgSW1hZ2VEaW1lbnNpb25zIH0gZnJvbSAnLi4vLi4vdXRpbHMvaW1hZ2VSZXNpemVyLmpzJ1xuaW1wb3J0IHsgY2FjaGVJbWFnZVBhdGgsIHN0b3JlSW1hZ2UgfSBmcm9tICcuLi8uLi91dGlscy9pbWFnZVN0b3JlLmpzJ1xuaW1wb3J0IHtcbiAgaXNNYWNvc09wdGlvbkNoYXIsXG4gIE1BQ09TX09QVElPTl9TUEVDSUFMX0NIQVJTLFxufSBmcm9tICcuLi8uLi91dGlscy9rZXlib2FyZFNob3J0Y3V0cy5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nLmpzJ1xuaW1wb3J0IHtcbiAgaXNPcHVzMW1NZXJnZUVuYWJsZWQsXG4gIG1vZGVsRGlzcGxheVN0cmluZyxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvbW9kZWwvbW9kZWwuanMnXG5pbXBvcnQgeyBzZXRBdXRvTW9kZUFjdGl2ZSB9IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL2F1dG9Nb2RlU3RhdGUuanMnXG5pbXBvcnQge1xuICBjeWNsZVBlcm1pc3Npb25Nb2RlLFxuICBnZXROZXh0UGVybWlzc2lvbk1vZGUsXG59IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL2dldE5leHRQZXJtaXNzaW9uTW9kZS5qcydcbmltcG9ydCB7IHRyYW5zaXRpb25QZXJtaXNzaW9uTW9kZSB9IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL3Blcm1pc3Npb25TZXR1cC5qcydcbmltcG9ydCB7IGdldFBsYXRmb3JtIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGxhdGZvcm0uanMnXG5pbXBvcnQgdHlwZSB7IFByb2Nlc3NVc2VySW5wdXRDb250ZXh0IH0gZnJvbSAnLi4vLi4vdXRpbHMvcHJvY2Vzc1VzZXJJbnB1dC9wcm9jZXNzVXNlcklucHV0LmpzJ1xuaW1wb3J0IHsgZWRpdFByb21wdEluRWRpdG9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvcHJvbXB0RWRpdG9yLmpzJ1xuaW1wb3J0IHsgaGFzQXV0b01vZGVPcHRJbiB9IGZyb20gJy4uLy4uL3V0aWxzL3NldHRpbmdzL3NldHRpbmdzLmpzJ1xuaW1wb3J0IHsgZmluZEJ0d1RyaWdnZXJQb3NpdGlvbnMgfSBmcm9tICcuLi8uLi91dGlscy9zaWRlUXVlc3Rpb24uanMnXG5pbXBvcnQgeyBmaW5kU2xhc2hDb21tYW5kUG9zaXRpb25zIH0gZnJvbSAnLi4vLi4vdXRpbHMvc3VnZ2VzdGlvbnMvY29tbWFuZFN1Z2dlc3Rpb25zLmpzJ1xuaW1wb3J0IHtcbiAgZmluZFNsYWNrQ2hhbm5lbFBvc2l0aW9ucyxcbiAgZ2V0S25vd25DaGFubmVsc1ZlcnNpb24sXG4gIGhhc1NsYWNrTWNwU2VydmVyLFxuICBzdWJzY3JpYmVLbm93bkNoYW5uZWxzLFxufSBmcm9tICcuLi8uLi91dGlscy9zdWdnZXN0aW9ucy9zbGFja0NoYW5uZWxTdWdnZXN0aW9ucy5qcydcbmltcG9ydCB7IGlzSW5Qcm9jZXNzRW5hYmxlZCB9IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL2JhY2tlbmRzL3JlZ2lzdHJ5LmpzJ1xuaW1wb3J0IHsgc3luY1RlYW1tYXRlTW9kZSB9IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL3RlYW1IZWxwZXJzLmpzJ1xuaW1wb3J0IHR5cGUgeyBUZWFtU3VtbWFyeSB9IGZyb20gJy4uLy4uL3V0aWxzL3RlYW1EaXNjb3ZlcnkuanMnXG5pbXBvcnQgeyBnZXRUZWFtbWF0ZUNvbG9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGVhbW1hdGUuanMnXG5pbXBvcnQgeyBpc0luUHJvY2Vzc1RlYW1tYXRlIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGVhbW1hdGVDb250ZXh0LmpzJ1xuaW1wb3J0IHsgd3JpdGVUb01haWxib3ggfSBmcm9tICcuLi8uLi91dGlscy90ZWFtbWF0ZU1haWxib3guanMnXG5pbXBvcnQgdHlwZSB7IFRleHRIaWdobGlnaHQgfSBmcm9tICcuLi8uLi91dGlscy90ZXh0SGlnaGxpZ2h0aW5nLmpzJ1xuaW1wb3J0IHR5cGUgeyBUaGVtZSB9IGZyb20gJy4uLy4uL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHtcbiAgZmluZFRoaW5raW5nVHJpZ2dlclBvc2l0aW9ucyxcbiAgZ2V0UmFpbmJvd0NvbG9yLFxuICBpc1VsdHJhdGhpbmtFbmFibGVkLFxufSBmcm9tICcuLi8uLi91dGlscy90aGlua2luZy5qcydcbmltcG9ydCB7IGZpbmRUb2tlbkJ1ZGdldFBvc2l0aW9ucyB9IGZyb20gJy4uLy4uL3V0aWxzL3Rva2VuQnVkZ2V0LmpzJ1xuaW1wb3J0IHtcbiAgZmluZFVsdHJhcGxhblRyaWdnZXJQb3NpdGlvbnMsXG4gIGZpbmRVbHRyYXJldmlld1RyaWdnZXJQb3NpdGlvbnMsXG59IGZyb20gJy4uLy4uL3V0aWxzL3VsdHJhcGxhbi9rZXl3b3JkLmpzJ1xuaW1wb3J0IHsgQXV0b01vZGVPcHRJbkRpYWxvZyB9IGZyb20gJy4uL0F1dG9Nb2RlT3B0SW5EaWFsb2cuanMnXG5pbXBvcnQgeyBCcmlkZ2VEaWFsb2cgfSBmcm9tICcuLi9CcmlkZ2VEaWFsb2cuanMnXG5pbXBvcnQgeyBDb25maWd1cmFibGVTaG9ydGN1dEhpbnQgfSBmcm9tICcuLi9Db25maWd1cmFibGVTaG9ydGN1dEhpbnQuanMnXG5pbXBvcnQge1xuICBnZXRWaXNpYmxlQWdlbnRUYXNrcyxcbiAgdXNlQ29vcmRpbmF0b3JUYXNrQ291bnQsXG59IGZyb20gJy4uL0Nvb3JkaW5hdG9yQWdlbnRTdGF0dXMuanMnXG5pbXBvcnQgeyBnZXRFZmZvcnROb3RpZmljYXRpb25UZXh0IH0gZnJvbSAnLi4vRWZmb3J0SW5kaWNhdG9yLmpzJ1xuaW1wb3J0IHsgZ2V0RmFzdEljb25TdHJpbmcgfSBmcm9tICcuLi9GYXN0SWNvbi5qcydcbmltcG9ydCB7IEdsb2JhbFNlYXJjaERpYWxvZyB9IGZyb20gJy4uL0dsb2JhbFNlYXJjaERpYWxvZy5qcydcbmltcG9ydCB7IEhpc3RvcnlTZWFyY2hEaWFsb2cgfSBmcm9tICcuLi9IaXN0b3J5U2VhcmNoRGlhbG9nLmpzJ1xuaW1wb3J0IHsgTW9kZWxQaWNrZXIgfSBmcm9tICcuLi9Nb2RlbFBpY2tlci5qcydcbmltcG9ydCB7IFF1aWNrT3BlbkRpYWxvZyB9IGZyb20gJy4uL1F1aWNrT3BlbkRpYWxvZy5qcydcbmltcG9ydCBUZXh0SW5wdXQgZnJvbSAnLi4vVGV4dElucHV0LmpzJ1xuaW1wb3J0IHsgVGhpbmtpbmdUb2dnbGUgfSBmcm9tICcuLi9UaGlua2luZ1RvZ2dsZS5qcydcbmltcG9ydCB7IEJhY2tncm91bmRUYXNrc0RpYWxvZyB9IGZyb20gJy4uL3Rhc2tzL0JhY2tncm91bmRUYXNrc0RpYWxvZy5qcydcbmltcG9ydCB7IHNob3VsZEhpZGVUYXNrc0Zvb3RlciB9IGZyb20gJy4uL3Rhc2tzL3Rhc2tTdGF0dXNVdGlscy5qcydcbmltcG9ydCB7IFRlYW1zRGlhbG9nIH0gZnJvbSAnLi4vdGVhbXMvVGVhbXNEaWFsb2cuanMnXG5pbXBvcnQgVmltVGV4dElucHV0IGZyb20gJy4uL1ZpbVRleHRJbnB1dC5qcydcbmltcG9ydCB7IGdldE1vZGVGcm9tSW5wdXQsIGdldFZhbHVlRnJvbUlucHV0IH0gZnJvbSAnLi9pbnB1dE1vZGVzLmpzJ1xuaW1wb3J0IHtcbiAgRk9PVEVSX1RFTVBPUkFSWV9TVEFUVVNfVElNRU9VVCxcbiAgTm90aWZpY2F0aW9ucyxcbn0gZnJvbSAnLi9Ob3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IFByb21wdElucHV0Rm9vdGVyIGZyb20gJy4vUHJvbXB0SW5wdXRGb290ZXIuanMnXG5pbXBvcnQgdHlwZSB7IFN1Z2dlc3Rpb25JdGVtIH0gZnJvbSAnLi9Qcm9tcHRJbnB1dEZvb3RlclN1Z2dlc3Rpb25zLmpzJ1xuaW1wb3J0IHsgUHJvbXB0SW5wdXRNb2RlSW5kaWNhdG9yIH0gZnJvbSAnLi9Qcm9tcHRJbnB1dE1vZGVJbmRpY2F0b3IuanMnXG5pbXBvcnQgeyBQcm9tcHRJbnB1dFF1ZXVlZENvbW1hbmRzIH0gZnJvbSAnLi9Qcm9tcHRJbnB1dFF1ZXVlZENvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgUHJvbXB0SW5wdXRTdGFzaE5vdGljZSB9IGZyb20gJy4vUHJvbXB0SW5wdXRTdGFzaE5vdGljZS5qcydcbmltcG9ydCB7IHVzZU1heWJlVHJ1bmNhdGVJbnB1dCB9IGZyb20gJy4vdXNlTWF5YmVUcnVuY2F0ZUlucHV0LmpzJ1xuaW1wb3J0IHsgdXNlUHJvbXB0SW5wdXRQbGFjZWhvbGRlciB9IGZyb20gJy4vdXNlUHJvbXB0SW5wdXRQbGFjZWhvbGRlci5qcydcbmltcG9ydCB7IHVzZVNob3dGYXN0SWNvbkhpbnQgfSBmcm9tICcuL3VzZVNob3dGYXN0SWNvbkhpbnQuanMnXG5pbXBvcnQgeyB1c2VTd2FybUJhbm5lciB9IGZyb20gJy4vdXNlU3dhcm1CYW5uZXIuanMnXG5pbXBvcnQgeyBpc05vblNwYWNlUHJpbnRhYmxlLCBpc1ZpbU1vZGVFbmFibGVkIH0gZnJvbSAnLi91dGlscy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgZGVidWc6IGJvb2xlYW5cbiAgaWRlU2VsZWN0aW9uOiBJREVTZWxlY3Rpb24gfCB1bmRlZmluZWRcbiAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHRcbiAgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0OiAoY3R4OiBUb29sUGVybWlzc2lvbkNvbnRleHQpID0+IHZvaWRcbiAgYXBpS2V5U3RhdHVzOiBWZXJpZmljYXRpb25TdGF0dXNcbiAgY29tbWFuZHM6IENvbW1hbmRbXVxuICBhZ2VudHM6IEFnZW50RGVmaW5pdGlvbltdXG4gIGlzTG9hZGluZzogYm9vbGVhblxuICB2ZXJib3NlOiBib29sZWFuXG4gIG1lc3NhZ2VzOiBNZXNzYWdlW11cbiAgb25BdXRvVXBkYXRlclJlc3VsdDogKHJlc3VsdDogQXV0b1VwZGF0ZXJSZXN1bHQpID0+IHZvaWRcbiAgYXV0b1VwZGF0ZXJSZXN1bHQ6IEF1dG9VcGRhdGVyUmVzdWx0IHwgbnVsbFxuICBpbnB1dDogc3RyaW5nXG4gIG9uSW5wdXRDaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkXG4gIG1vZGU6IFByb21wdElucHV0TW9kZVxuICBvbk1vZGVDaGFuZ2U6IChtb2RlOiBQcm9tcHRJbnB1dE1vZGUpID0+IHZvaWRcbiAgc3Rhc2hlZFByb21wdDpcbiAgICB8IHtcbiAgICAgICAgdGV4dDogc3RyaW5nXG4gICAgICAgIGN1cnNvck9mZnNldDogbnVtYmVyXG4gICAgICAgIHBhc3RlZENvbnRlbnRzOiBSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50PlxuICAgICAgfVxuICAgIHwgdW5kZWZpbmVkXG4gIHNldFN0YXNoZWRQcm9tcHQ6IChcbiAgICB2YWx1ZTpcbiAgICAgIHwge1xuICAgICAgICAgIHRleHQ6IHN0cmluZ1xuICAgICAgICAgIGN1cnNvck9mZnNldDogbnVtYmVyXG4gICAgICAgICAgcGFzdGVkQ29udGVudHM6IFJlY29yZDxudW1iZXIsIFBhc3RlZENvbnRlbnQ+XG4gICAgICAgIH1cbiAgICAgIHwgdW5kZWZpbmVkLFxuICApID0+IHZvaWRcbiAgc3VibWl0Q291bnQ6IG51bWJlclxuICBvblNob3dNZXNzYWdlU2VsZWN0b3I6ICgpID0+IHZvaWRcbiAgLyoqIEZ1bGxzY3JlZW4gbWVzc2FnZSBhY3Rpb25zOiBzaGlmdCvihpEgZW50ZXJzIGN1cnNvci4gKi9cbiAgb25NZXNzYWdlQWN0aW9uc0VudGVyPzogKCkgPT4gdm9pZFxuICBtY3BDbGllbnRzOiBNQ1BTZXJ2ZXJDb25uZWN0aW9uW11cbiAgcGFzdGVkQ29udGVudHM6IFJlY29yZDxudW1iZXIsIFBhc3RlZENvbnRlbnQ+XG4gIHNldFBhc3RlZENvbnRlbnRzOiBSZWFjdC5EaXNwYXRjaDxcbiAgICBSZWFjdC5TZXRTdGF0ZUFjdGlvbjxSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50Pj5cbiAgPlxuICB2aW1Nb2RlOiBWaW1Nb2RlXG4gIHNldFZpbU1vZGU6IChtb2RlOiBWaW1Nb2RlKSA9PiB2b2lkXG4gIHNob3dCYXNoZXNEaWFsb2c6IHN0cmluZyB8IGJvb2xlYW5cbiAgc2V0U2hvd0Jhc2hlc0RpYWxvZzogKHNob3c6IHN0cmluZyB8IGJvb2xlYW4pID0+IHZvaWRcbiAgb25FeGl0OiAoKSA9PiB2b2lkXG4gIGdldFRvb2xVc2VDb250ZXh0OiAoXG4gICAgbWVzc2FnZXM6IE1lc3NhZ2VbXSxcbiAgICBuZXdNZXNzYWdlczogTWVzc2FnZVtdLFxuICAgIGFib3J0Q29udHJvbGxlcjogQWJvcnRDb250cm9sbGVyLFxuICAgIG1haW5Mb29wTW9kZWw6IHN0cmluZyxcbiAgKSA9PiBQcm9jZXNzVXNlcklucHV0Q29udGV4dFxuICBvblN1Ym1pdDogKFxuICAgIGlucHV0OiBzdHJpbmcsXG4gICAgaGVscGVyczogUHJvbXB0SW5wdXRIZWxwZXJzLFxuICAgIHNwZWN1bGF0aW9uQWNjZXB0Pzoge1xuICAgICAgc3RhdGU6IEFjdGl2ZVNwZWN1bGF0aW9uU3RhdGVcbiAgICAgIHNwZWN1bGF0aW9uU2Vzc2lvblRpbWVTYXZlZE1zOiBudW1iZXJcbiAgICAgIHNldEFwcFN0YXRlOiAoZjogKHByZXY6IEFwcFN0YXRlKSA9PiBBcHBTdGF0ZSkgPT4gdm9pZFxuICAgIH0sXG4gICAgb3B0aW9ucz86IHsgZnJvbUtleWJpbmRpbmc/OiBib29sZWFuIH0sXG4gICkgPT4gUHJvbWlzZTx2b2lkPlxuICBvbkFnZW50U3VibWl0PzogKFxuICAgIGlucHV0OiBzdHJpbmcsXG4gICAgdGFzazogSW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGUgfCBMb2NhbEFnZW50VGFza1N0YXRlLFxuICAgIGhlbHBlcnM6IFByb21wdElucHV0SGVscGVycyxcbiAgKSA9PiBQcm9taXNlPHZvaWQ+XG4gIGlzU2VhcmNoaW5nSGlzdG9yeTogYm9vbGVhblxuICBzZXRJc1NlYXJjaGluZ0hpc3Rvcnk6IChpc1NlYXJjaGluZzogYm9vbGVhbikgPT4gdm9pZFxuICBvbkRpc21pc3NTaWRlUXVlc3Rpb24/OiAoKSA9PiB2b2lkXG4gIGlzU2lkZVF1ZXN0aW9uVmlzaWJsZT86IGJvb2xlYW5cbiAgaGVscE9wZW46IGJvb2xlYW5cbiAgc2V0SGVscE9wZW46IFJlYWN0LkRpc3BhdGNoPFJlYWN0LlNldFN0YXRlQWN0aW9uPGJvb2xlYW4+PlxuICBoYXNTdXBwcmVzc2VkRGlhbG9ncz86IGJvb2xlYW5cbiAgaXNMb2NhbEpTWENvbW1hbmRBY3RpdmU/OiBib29sZWFuXG4gIGluc2VydFRleHRSZWY/OiBSZWFjdC5NdXRhYmxlUmVmT2JqZWN0PHtcbiAgICBpbnNlcnQ6ICh0ZXh0OiBzdHJpbmcpID0+IHZvaWRcbiAgICBzZXRJbnB1dFdpdGhDdXJzb3I6ICh2YWx1ZTogc3RyaW5nLCBjdXJzb3I6IG51bWJlcikgPT4gdm9pZFxuICAgIGN1cnNvck9mZnNldDogbnVtYmVyXG4gIH0gfCBudWxsPlxuICB2b2ljZUludGVyaW1SYW5nZT86IHsgc3RhcnQ6IG51bWJlcjsgZW5kOiBudW1iZXIgfSB8IG51bGxcbn1cblxuLy8gQm90dG9tIHNsb3QgaGFzIG1heEhlaWdodD1cIjUwJVwiOyByZXNlcnZlIGxpbmVzIGZvciBmb290ZXIsIGJvcmRlciwgc3RhdHVzLlxuY29uc3QgUFJPTVBUX0ZPT1RFUl9MSU5FUyA9IDVcbmNvbnN0IE1JTl9JTlBVVF9WSUVXUE9SVF9MSU5FUyA9IDNcblxuZnVuY3Rpb24gUHJvbXB0SW5wdXQoe1xuICBkZWJ1ZyxcbiAgaWRlU2VsZWN0aW9uLFxuICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgYXBpS2V5U3RhdHVzLFxuICBjb21tYW5kcyxcbiAgYWdlbnRzLFxuICBpc0xvYWRpbmcsXG4gIHZlcmJvc2UsXG4gIG1lc3NhZ2VzLFxuICBvbkF1dG9VcGRhdGVyUmVzdWx0LFxuICBhdXRvVXBkYXRlclJlc3VsdCxcbiAgaW5wdXQsXG4gIG9uSW5wdXRDaGFuZ2UsXG4gIG1vZGUsXG4gIG9uTW9kZUNoYW5nZSxcbiAgc3Rhc2hlZFByb21wdCxcbiAgc2V0U3Rhc2hlZFByb21wdCxcbiAgc3VibWl0Q291bnQsXG4gIG9uU2hvd01lc3NhZ2VTZWxlY3RvcixcbiAgb25NZXNzYWdlQWN0aW9uc0VudGVyLFxuICBtY3BDbGllbnRzLFxuICBwYXN0ZWRDb250ZW50cyxcbiAgc2V0UGFzdGVkQ29udGVudHMsXG4gIHZpbU1vZGUsXG4gIHNldFZpbU1vZGUsXG4gIHNob3dCYXNoZXNEaWFsb2csXG4gIHNldFNob3dCYXNoZXNEaWFsb2csXG4gIG9uRXhpdCxcbiAgZ2V0VG9vbFVzZUNvbnRleHQsXG4gIG9uU3VibWl0OiBvblN1Ym1pdFByb3AsXG4gIG9uQWdlbnRTdWJtaXQsXG4gIGlzU2VhcmNoaW5nSGlzdG9yeSxcbiAgc2V0SXNTZWFyY2hpbmdIaXN0b3J5LFxuICBvbkRpc21pc3NTaWRlUXVlc3Rpb24sXG4gIGlzU2lkZVF1ZXN0aW9uVmlzaWJsZSxcbiAgaGVscE9wZW4sXG4gIHNldEhlbHBPcGVuLFxuICBoYXNTdXBwcmVzc2VkRGlhbG9ncyxcbiAgaXNMb2NhbEpTWENvbW1hbmRBY3RpdmUgPSBmYWxzZSxcbiAgaW5zZXJ0VGV4dFJlZixcbiAgdm9pY2VJbnRlcmltUmFuZ2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IG1haW5Mb29wTW9kZWwgPSB1c2VNYWluTG9vcE1vZGVsKClcbiAgLy8gQSBsb2NhbC1qc3ggY29tbWFuZCAoZS5nLiwgL21jcCB3aGlsZSBhZ2VudCBpcyBydW5uaW5nKSByZW5kZXJzIGEgZnVsbC1cbiAgLy8gc2NyZWVuIGRpYWxvZyBvbiB0b3Agb2YgUHJvbXB0SW5wdXQgdmlhIHRoZSBpbW1lZGlhdGUtY29tbWFuZCBwYXRoIHdpdGhcbiAgLy8gc2hvdWxkSGlkZVByb21wdElucHV0OiBmYWxzZS4gVGhvc2UgZGlhbG9ncyBkb24ndCByZWdpc3RlciBpbiB0aGUgb3ZlcmxheVxuICAvLyBzeXN0ZW0sIHNvIHRyZWF0IHRoZW0gYXMgYSBtb2RhbCBvdmVybGF5IGhlcmUgdG8gc3RvcCBuYXZpZ2F0aW9uIGtleXMgZnJvbVxuICAvLyBsZWFraW5nIGludG8gVGV4dElucHV0L2Zvb3RlciBoYW5kbGVycyBhbmQgc3RhY2tpbmcgYSBzZWNvbmQgZGlhbG9nLlxuICBjb25zdCBpc01vZGFsT3ZlcmxheUFjdGl2ZSA9XG4gICAgdXNlSXNNb2RhbE92ZXJsYXlBY3RpdmUoKSB8fCBpc0xvY2FsSlNYQ29tbWFuZEFjdGl2ZVxuICBjb25zdCBbaXNBdXRvVXBkYXRpbmcsIHNldElzQXV0b1VwZGF0aW5nXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbZXhpdE1lc3NhZ2UsIHNldEV4aXRNZXNzYWdlXSA9IHVzZVN0YXRlPHtcbiAgICBzaG93OiBib29sZWFuXG4gICAga2V5Pzogc3RyaW5nXG4gIH0+KHsgc2hvdzogZmFsc2UgfSlcbiAgY29uc3QgW2N1cnNvck9mZnNldCwgc2V0Q3Vyc29yT2Zmc2V0XSA9IHVzZVN0YXRlPG51bWJlcj4oaW5wdXQubGVuZ3RoKVxuICAvLyBUcmFjayB0aGUgbGFzdCBpbnB1dCB2YWx1ZSBzZXQgdmlhIGludGVybmFsIGhhbmRsZXJzIHNvIHdlIGNhbiBkZXRlY3RcbiAgLy8gZXh0ZXJuYWwgaW5wdXQgY2hhbmdlcyAoZS5nLiBzcGVlY2gtdG8tdGV4dCBpbmplY3Rpb24pIGFuZCBtb3ZlIGN1cnNvciB0byBlbmQuXG4gIGNvbnN0IGxhc3RJbnRlcm5hbElucHV0UmVmID0gUmVhY3QudXNlUmVmKGlucHV0KVxuICBpZiAoaW5wdXQgIT09IGxhc3RJbnRlcm5hbElucHV0UmVmLmN1cnJlbnQpIHtcbiAgICAvLyBJbnB1dCBjaGFuZ2VkIGV4dGVybmFsbHkgKG5vdCB0aHJvdWdoIGFueSBpbnRlcm5hbCBoYW5kbGVyKSDigJQgbW92ZSBjdXJzb3IgdG8gZW5kXG4gICAgc2V0Q3Vyc29yT2Zmc2V0KGlucHV0Lmxlbmd0aClcbiAgICBsYXN0SW50ZXJuYWxJbnB1dFJlZi5jdXJyZW50ID0gaW5wdXRcbiAgfVxuICAvLyBXcmFwIG9uSW5wdXRDaGFuZ2UgdG8gdHJhY2sgaW50ZXJuYWwgY2hhbmdlcyBiZWZvcmUgdGhleSB0cmlnZ2VyIHJlLXJlbmRlclxuICBjb25zdCB0cmFja0FuZFNldElucHV0ID0gUmVhY3QudXNlQ2FsbGJhY2soXG4gICAgKHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICAgIGxhc3RJbnRlcm5hbElucHV0UmVmLmN1cnJlbnQgPSB2YWx1ZVxuICAgICAgb25JbnB1dENoYW5nZSh2YWx1ZSlcbiAgICB9LFxuICAgIFtvbklucHV0Q2hhbmdlXSxcbiAgKVxuICAvLyBFeHBvc2UgYW4gaW5zZXJ0VGV4dCBmdW5jdGlvbiBzbyBjYWxsZXJzIChlLmcuIFNUVCkgY2FuIHNwbGljZSB0ZXh0IGF0IHRoZVxuICAvLyBjdXJyZW50IGN1cnNvciBwb3NpdGlvbiBpbnN0ZWFkIG9mIHJlcGxhY2luZyB0aGUgZW50aXJlIGlucHV0LlxuICBpZiAoaW5zZXJ0VGV4dFJlZikge1xuICAgIGluc2VydFRleHRSZWYuY3VycmVudCA9IHtcbiAgICAgIGN1cnNvck9mZnNldCxcbiAgICAgIGluc2VydDogKHRleHQ6IHN0cmluZykgPT4ge1xuICAgICAgICBjb25zdCBuZWVkc1NwYWNlID1cbiAgICAgICAgICBjdXJzb3JPZmZzZXQgPT09IGlucHV0Lmxlbmd0aCAmJlxuICAgICAgICAgIGlucHV0Lmxlbmd0aCA+IDAgJiZcbiAgICAgICAgICAhL1xccyQvLnRlc3QoaW5wdXQpXG4gICAgICAgIGNvbnN0IGluc2VydFRleHQgPSBuZWVkc1NwYWNlID8gJyAnICsgdGV4dCA6IHRleHRcbiAgICAgICAgY29uc3QgbmV3VmFsdWUgPVxuICAgICAgICAgIGlucHV0LnNsaWNlKDAsIGN1cnNvck9mZnNldCkgKyBpbnNlcnRUZXh0ICsgaW5wdXQuc2xpY2UoY3Vyc29yT2Zmc2V0KVxuICAgICAgICBsYXN0SW50ZXJuYWxJbnB1dFJlZi5jdXJyZW50ID0gbmV3VmFsdWVcbiAgICAgICAgb25JbnB1dENoYW5nZShuZXdWYWx1ZSlcbiAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KGN1cnNvck9mZnNldCArIGluc2VydFRleHQubGVuZ3RoKVxuICAgICAgfSxcbiAgICAgIHNldElucHV0V2l0aEN1cnNvcjogKHZhbHVlOiBzdHJpbmcsIGN1cnNvcjogbnVtYmVyKSA9PiB7XG4gICAgICAgIGxhc3RJbnRlcm5hbElucHV0UmVmLmN1cnJlbnQgPSB2YWx1ZVxuICAgICAgICBvbklucHV0Q2hhbmdlKHZhbHVlKVxuICAgICAgICBzZXRDdXJzb3JPZmZzZXQoY3Vyc29yKVxuICAgICAgfSxcbiAgICB9XG4gIH1cbiAgY29uc3Qgc3RvcmUgPSB1c2VBcHBTdGF0ZVN0b3JlKClcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG4gIGNvbnN0IHRhc2tzID0gdXNlQXBwU3RhdGUocyA9PiBzLnRhc2tzKVxuICBjb25zdCByZXBsQnJpZGdlQ29ubmVjdGVkID0gdXNlQXBwU3RhdGUocyA9PiBzLnJlcGxCcmlkZ2VDb25uZWN0ZWQpXG4gIGNvbnN0IHJlcGxCcmlkZ2VFeHBsaWNpdCA9IHVzZUFwcFN0YXRlKHMgPT4gcy5yZXBsQnJpZGdlRXhwbGljaXQpXG4gIGNvbnN0IHJlcGxCcmlkZ2VSZWNvbm5lY3RpbmcgPSB1c2VBcHBTdGF0ZShzID0+IHMucmVwbEJyaWRnZVJlY29ubmVjdGluZylcbiAgLy8gTXVzdCBtYXRjaCBCcmlkZ2VTdGF0dXNJbmRpY2F0b3IncyByZW5kZXIgY29uZGl0aW9uIChQcm9tcHRJbnB1dEZvb3Rlci50c3gpIOKAlFxuICAvLyB0aGUgcGlsbCByZXR1cm5zIG51bGwgZm9yIGltcGxpY2l0LWFuZC1ub3QtcmVjb25uZWN0aW5nLCBzbyBuYXYgbXVzdCB0b28sXG4gIC8vIG90aGVyd2lzZSBicmlkZ2UgYmVjb21lcyBhbiBpbnZpc2libGUgc2VsZWN0aW9uIHN0b3AuXG4gIGNvbnN0IGJyaWRnZUZvb3RlclZpc2libGUgPVxuICAgIHJlcGxCcmlkZ2VDb25uZWN0ZWQgJiYgKHJlcGxCcmlkZ2VFeHBsaWNpdCB8fCByZXBsQnJpZGdlUmVjb25uZWN0aW5nKVxuICAvLyBUbXV4IHBpbGwgKGFudC1vbmx5KSDigJQgdmlzaWJsZSB3aGVuIHRoZXJlJ3MgYW4gYWN0aXZlIHR1bmdzdGVuIHNlc3Npb25cbiAgY29uc3QgaGFzVHVuZ3N0ZW5TZXNzaW9uID0gdXNlQXBwU3RhdGUoXG4gICAgcyA9PlxuICAgICAgXCJleHRlcm5hbFwiID09PSAnYW50JyAmJiBzLnR1bmdzdGVuQWN0aXZlU2Vzc2lvbiAhPT0gdW5kZWZpbmVkLFxuICApXG4gIGNvbnN0IHRtdXhGb290ZXJWaXNpYmxlID1cbiAgICBcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIGhhc1R1bmdzdGVuU2Vzc2lvblxuICAvLyBXZWJCcm93c2VyIHBpbGwg4oCUIHZpc2libGUgd2hlbiBhIGJyb3dzZXIgaXMgb3BlblxuICBjb25zdCBiYWdlbEZvb3RlclZpc2libGUgPSB1c2VBcHBTdGF0ZShzID0+XG4gICAgICAgIGZhbHNlLFxuICApXG4gIGNvbnN0IHRlYW1Db250ZXh0ID0gdXNlQXBwU3RhdGUocyA9PiBzLnRlYW1Db250ZXh0KVxuICBjb25zdCBxdWV1ZWRDb21tYW5kcyA9IHVzZUNvbW1hbmRRdWV1ZSgpXG4gIGNvbnN0IHByb21wdFN1Z2dlc3Rpb25TdGF0ZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5wcm9tcHRTdWdnZXN0aW9uKVxuICBjb25zdCBzcGVjdWxhdGlvbiA9IHVzZUFwcFN0YXRlKHMgPT4gcy5zcGVjdWxhdGlvbilcbiAgY29uc3Qgc3BlY3VsYXRpb25TZXNzaW9uVGltZVNhdmVkTXMgPSB1c2VBcHBTdGF0ZShcbiAgICBzID0+IHMuc3BlY3VsYXRpb25TZXNzaW9uVGltZVNhdmVkTXMsXG4gIClcbiAgY29uc3Qgdmlld2luZ0FnZW50VGFza0lkID0gdXNlQXBwU3RhdGUocyA9PiBzLnZpZXdpbmdBZ2VudFRhc2tJZClcbiAgY29uc3Qgdmlld1NlbGVjdGlvbk1vZGUgPSB1c2VBcHBTdGF0ZShzID0+IHMudmlld1NlbGVjdGlvbk1vZGUpXG4gIGNvbnN0IHNob3dTcGlubmVyVHJlZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy5leHBhbmRlZFZpZXcpID09PSAndGVhbW1hdGVzJ1xuICBjb25zdCB7IGNvbXBhbmlvbjogX2NvbXBhbmlvbiwgY29tcGFuaW9uTXV0ZWQgfSA9IGZlYXR1cmUoJ0JVRERZJylcbiAgICA/IGdldEdsb2JhbENvbmZpZygpXG4gICAgOiB7IGNvbXBhbmlvbjogdW5kZWZpbmVkLCBjb21wYW5pb25NdXRlZDogdW5kZWZpbmVkIH1cbiAgY29uc3QgY29tcGFuaW9uRm9vdGVyVmlzaWJsZSA9ICEhX2NvbXBhbmlvbiAmJiAhY29tcGFuaW9uTXV0ZWRcbiAgLy8gQnJpZWYgbW9kZTogQnJpZWZTcGlubmVyL0JyaWVmSWRsZVN0YXR1cyBvd24gdGhlIDItcm93IGZvb3RwcmludCBhYm92ZVxuICAvLyB0aGUgaW5wdXQuIERyb3BwaW5nIG1hcmdpblRvcCBoZXJlIGxldHMgdGhlIHNwaW5uZXIgc2l0IGZsdXNoIGFnYWluc3RcbiAgLy8gdGhlIGlucHV0IGJhci4gdmlld2luZ0FnZW50VGFza0lkIG1pcnJvcnMgdGhlIGdhdGUgb24gYm90aCAoU3Bpbm5lci50c3gsXG4gIC8vIFJFUEwudHN4KSDigJQgdGVhbW1hdGUgdmlldyBmYWxscyBiYWNrIHRvIFNwaW5uZXJXaXRoVmVyYklubmVyIHdoaWNoIGhhc1xuICAvLyBpdHMgb3duIG1hcmdpblRvcCwgc28gdGhlIGdhcCBzdGF5cyBldmVuIHdpdGhvdXQgb3Vycy5cbiAgY29uc3QgYnJpZWZPd25zR2FwID1cbiAgICBmZWF0dXJlKCdLQUlST1MnKSB8fCBmZWF0dXJlKCdLQUlST1NfQlJJRUYnKVxuICAgICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICAgIHVzZUFwcFN0YXRlKHMgPT4gcy5pc0JyaWVmT25seSkgJiYgIXZpZXdpbmdBZ2VudFRhc2tJZFxuICAgICAgOiBmYWxzZVxuICBjb25zdCBtYWluTG9vcE1vZGVsXyA9IHVzZUFwcFN0YXRlKHMgPT4gcy5tYWluTG9vcE1vZGVsKVxuICBjb25zdCBtYWluTG9vcE1vZGVsRm9yU2Vzc2lvbiA9IHVzZUFwcFN0YXRlKHMgPT4gcy5tYWluTG9vcE1vZGVsRm9yU2Vzc2lvbilcbiAgY29uc3QgdGhpbmtpbmdFbmFibGVkID0gdXNlQXBwU3RhdGUocyA9PiBzLnRoaW5raW5nRW5hYmxlZClcbiAgY29uc3QgaXNGYXN0TW9kZSA9IHVzZUFwcFN0YXRlKHMgPT5cbiAgICBpc0Zhc3RNb2RlRW5hYmxlZCgpID8gcy5mYXN0TW9kZSA6IGZhbHNlLFxuICApXG4gIGNvbnN0IGVmZm9ydFZhbHVlID0gdXNlQXBwU3RhdGUocyA9PiBzLmVmZm9ydFZhbHVlKVxuICBjb25zdCB2aWV3ZWRUZWFtbWF0ZSA9IGdldFZpZXdlZFRlYW1tYXRlVGFzayhzdG9yZS5nZXRTdGF0ZSgpKVxuICBjb25zdCB2aWV3aW5nQWdlbnROYW1lID0gdmlld2VkVGVhbW1hdGU/LmlkZW50aXR5LmFnZW50TmFtZVxuICAvLyBpZGVudGl0eS5jb2xvciBpcyB0eXBlZCBhcyBgc3RyaW5nIHwgdW5kZWZpbmVkYCAobm90IEFnZW50Q29sb3JOYW1lKSBiZWNhdXNlXG4gIC8vIHRlYW1tYXRlIGlkZW50aXR5IGNvbWVzIGZyb20gZmlsZS1iYXNlZCBjb25maWcuIFZhbGlkYXRlIGJlZm9yZSBjYXN0aW5nIHRvXG4gIC8vIGVuc3VyZSB3ZSBvbmx5IHVzZSB2YWxpZCBjb2xvciBuYW1lcyAoZmFsbHMgYmFjayB0byBjeWFuIGlmIGludmFsaWQpLlxuICBjb25zdCB2aWV3aW5nQWdlbnRDb2xvciA9XG4gICAgdmlld2VkVGVhbW1hdGU/LmlkZW50aXR5LmNvbG9yICYmXG4gICAgQUdFTlRfQ09MT1JTLmluY2x1ZGVzKHZpZXdlZFRlYW1tYXRlLmlkZW50aXR5LmNvbG9yIGFzIEFnZW50Q29sb3JOYW1lKVxuICAgICAgPyAodmlld2VkVGVhbW1hdGUuaWRlbnRpdHkuY29sb3IgYXMgQWdlbnRDb2xvck5hbWUpXG4gICAgICA6IHVuZGVmaW5lZFxuICAvLyBJbi1wcm9jZXNzIHRlYW1tYXRlcyBzb3J0ZWQgYWxwaGFiZXRpY2FsbHkgZm9yIGZvb3RlciB0ZWFtIHNlbGVjdG9yXG4gIGNvbnN0IGluUHJvY2Vzc1RlYW1tYXRlcyA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZ2V0UnVubmluZ1RlYW1tYXRlc1NvcnRlZCh0YXNrcyksXG4gICAgW3Rhc2tzXSxcbiAgKVxuXG4gIC8vIFRlYW0gbW9kZTogYWxsIGJhY2tncm91bmQgdGFza3MgYXJlIGluLXByb2Nlc3MgdGVhbW1hdGVzXG4gIGNvbnN0IGlzVGVhbW1hdGVNb2RlID1cbiAgICBpblByb2Nlc3NUZWFtbWF0ZXMubGVuZ3RoID4gMCB8fCB2aWV3ZWRUZWFtbWF0ZSAhPT0gdW5kZWZpbmVkXG5cbiAgLy8gV2hlbiB2aWV3aW5nIGEgdGVhbW1hdGUsIHNob3cgdGhlaXIgcGVybWlzc2lvbiBtb2RlIGluIHRoZSBmb290ZXIgaW5zdGVhZCBvZiB0aGUgbGVhZGVyJ3NcbiAgY29uc3QgZWZmZWN0aXZlVG9vbFBlcm1pc3Npb25Db250ZXh0ID0gdXNlTWVtbygoKTogVG9vbFBlcm1pc3Npb25Db250ZXh0ID0+IHtcbiAgICBpZiAodmlld2VkVGVhbW1hdGUpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgbW9kZTogdmlld2VkVGVhbW1hdGUucGVybWlzc2lvbk1vZGUsXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0b29sUGVybWlzc2lvbkNvbnRleHRcbiAgfSwgW3ZpZXdlZFRlYW1tYXRlLCB0b29sUGVybWlzc2lvbkNvbnRleHRdKVxuICBjb25zdCB7IGhpc3RvcnlRdWVyeSwgc2V0SGlzdG9yeVF1ZXJ5LCBoaXN0b3J5TWF0Y2gsIGhpc3RvcnlGYWlsZWRNYXRjaCB9ID1cbiAgICB1c2VIaXN0b3J5U2VhcmNoKFxuICAgICAgZW50cnkgPT4ge1xuICAgICAgICBzZXRQYXN0ZWRDb250ZW50cyhlbnRyeS5wYXN0ZWRDb250ZW50cylcbiAgICAgICAgdm9pZCBvblN1Ym1pdChlbnRyeS5kaXNwbGF5KVxuICAgICAgfSxcbiAgICAgIGlucHV0LFxuICAgICAgdHJhY2tBbmRTZXRJbnB1dCxcbiAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgIGN1cnNvck9mZnNldCxcbiAgICAgIG9uTW9kZUNoYW5nZSxcbiAgICAgIG1vZGUsXG4gICAgICBpc1NlYXJjaGluZ0hpc3RvcnksXG4gICAgICBzZXRJc1NlYXJjaGluZ0hpc3RvcnksXG4gICAgICBzZXRQYXN0ZWRDb250ZW50cyxcbiAgICAgIHBhc3RlZENvbnRlbnRzLFxuICAgIClcbiAgLy8gQ291bnRlciBmb3IgcGFzdGUgSURzIChzaGFyZWQgYmV0d2VlbiBpbWFnZXMgYW5kIHRleHQpLlxuICAvLyBDb21wdXRlIGluaXRpYWwgdmFsdWUgb25jZSBmcm9tIGV4aXN0aW5nIG1lc3NhZ2VzIChmb3IgLS1jb250aW51ZS8tLXJlc3VtZSkuXG4gIC8vIHVzZVJlZihmbigpKSBldmFsdWF0ZXMgZm4oKSBvbiBldmVyeSByZW5kZXIgYW5kIGRpc2NhcmRzIHRoZSByZXN1bHQgYWZ0ZXJcbiAgLy8gbW91bnQg4oCUIGdldEluaXRpYWxQYXN0ZUlkIHdhbGtzIGFsbCBtZXNzYWdlcyArIHJlZ2V4LXNjYW5zIHRleHQgYmxvY2tzLFxuICAvLyBzbyBndWFyZCB3aXRoIGEgbGF6eS1pbml0IHBhdHRlcm4gdG8gcnVuIGl0IGV4YWN0bHkgb25jZS5cbiAgY29uc3QgbmV4dFBhc3RlSWRSZWYgPSB1c2VSZWYoLTEpXG4gIGlmIChuZXh0UGFzdGVJZFJlZi5jdXJyZW50ID09PSAtMSkge1xuICAgIG5leHRQYXN0ZUlkUmVmLmN1cnJlbnQgPSBnZXRJbml0aWFsUGFzdGVJZChtZXNzYWdlcylcbiAgfVxuICAvLyBBcm1lZCBieSBvbkltYWdlUGFzdGU7IGlmIHRoZSB2ZXJ5IG5leHQga2V5c3Ryb2tlIGlzIGEgbm9uLXNwYWNlXG4gIC8vIHByaW50YWJsZSwgaW5wdXRGaWx0ZXIgcHJlcGVuZHMgYSBzcGFjZSBiZWZvcmUgaXQuIEFueSBvdGhlciBpbnB1dFxuICAvLyAoYXJyb3csIGVzY2FwZSwgYmFja3NwYWNlLCBwYXN0ZSwgc3BhY2UpIGRpc2FybXMgd2l0aG91dCBpbnNlcnRpbmcuXG4gIGNvbnN0IHBlbmRpbmdTcGFjZUFmdGVyUGlsbFJlZiA9IHVzZVJlZihmYWxzZSlcblxuICBjb25zdCBbc2hvd1RlYW1zRGlhbG9nLCBzZXRTaG93VGVhbXNEaWFsb2ddID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtzaG93QnJpZGdlRGlhbG9nLCBzZXRTaG93QnJpZGdlRGlhbG9nXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbdGVhbW1hdGVGb290ZXJJbmRleCwgc2V0VGVhbW1hdGVGb290ZXJJbmRleF0gPSB1c2VTdGF0ZSgwKVxuICAvLyAtMSBzZW50aW5lbDogdGFza3MgcGlsbCBpcyBzZWxlY3RlZCBidXQgbm8gc3BlY2lmaWMgYWdlbnQgcm93IGlzIHNlbGVjdGVkIHlldC5cbiAgLy8gRmlyc3Qg4oaTIHNlbGVjdHMgdGhlIHBpbGwsIHNlY29uZCDihpMgbW92ZXMgdG8gcm93IDAuIFByZXZlbnRzIGRvdWJsZS1zZWxlY3RcbiAgLy8gb2YgcGlsbCArIHJvdyB3aGVuIGJvdGggYmcgdGFza3MgKHBpbGwpIGFuZCBmb3JrZWQgYWdlbnRzIChyb3dzKSBhcmUgdmlzaWJsZS5cbiAgY29uc3QgY29vcmRpbmF0b3JUYXNrSW5kZXggPSB1c2VBcHBTdGF0ZShzID0+IHMuY29vcmRpbmF0b3JUYXNrSW5kZXgpXG4gIGNvbnN0IHNldENvb3JkaW5hdG9yVGFza0luZGV4ID0gdXNlQ2FsbGJhY2soXG4gICAgKHY6IG51bWJlciB8ICgocHJldjogbnVtYmVyKSA9PiBudW1iZXIpKSA9PlxuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICAgIGNvbnN0IG5leHQgPSB0eXBlb2YgdiA9PT0gJ2Z1bmN0aW9uJyA/IHYocHJldi5jb29yZGluYXRvclRhc2tJbmRleCkgOiB2XG4gICAgICAgIGlmIChuZXh0ID09PSBwcmV2LmNvb3JkaW5hdG9yVGFza0luZGV4KSByZXR1cm4gcHJldlxuICAgICAgICByZXR1cm4geyAuLi5wcmV2LCBjb29yZGluYXRvclRhc2tJbmRleDogbmV4dCB9XG4gICAgICB9KSxcbiAgICBbc2V0QXBwU3RhdGVdLFxuICApXG4gIGNvbnN0IGNvb3JkaW5hdG9yVGFza0NvdW50ID0gdXNlQ29vcmRpbmF0b3JUYXNrQ291bnQoKVxuICAvLyBUaGUgcGlsbCAoQmFja2dyb3VuZFRhc2tTdGF0dXMpIG9ubHkgcmVuZGVycyB3aGVuIG5vbi1sb2NhbF9hZ2VudCBiZyB0YXNrc1xuICAvLyBleGlzdC4gV2hlbiBvbmx5IGxvY2FsX2FnZW50IHRhc2tzIGFyZSBydW5uaW5nIChjb29yZGluYXRvci9mb3JrIG1vZGUpLCB0aGVcbiAgLy8gcGlsbCBpcyBhYnNlbnQsIHNvIHRoZSAtMSBzZW50aW5lbCB3b3VsZCBsZWF2ZSBub3RoaW5nIHZpc3VhbGx5IHNlbGVjdGVkLlxuICAvLyBJbiB0aGF0IGNhc2UsIHNraXAgLTEgYW5kIHRyZWF0IDAgYXMgdGhlIG1pbmltdW0gc2VsZWN0YWJsZSBpbmRleC5cbiAgY29uc3QgaGFzQmdUYXNrUGlsbCA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIE9iamVjdC52YWx1ZXModGFza3MpLnNvbWUoXG4gICAgICAgIHQgPT5cbiAgICAgICAgICBpc0JhY2tncm91bmRUYXNrKHQpICYmXG4gICAgICAgICAgIShcImV4dGVybmFsXCIgPT09ICdhbnQnICYmIGlzUGFuZWxBZ2VudFRhc2sodCkpLFxuICAgICAgKSxcbiAgICBbdGFza3NdLFxuICApXG4gIGNvbnN0IG1pbkNvb3JkaW5hdG9ySW5kZXggPSBoYXNCZ1Rhc2tQaWxsID8gLTEgOiAwXG4gIC8vIENsYW1wIGluZGV4IHdoZW4gdGFza3MgY29tcGxldGUgYW5kIHRoZSBsaXN0IHNocmlua3MgYmVuZWF0aCB0aGUgY3Vyc29yXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGNvb3JkaW5hdG9yVGFza0luZGV4ID49IGNvb3JkaW5hdG9yVGFza0NvdW50KSB7XG4gICAgICBzZXRDb29yZGluYXRvclRhc2tJbmRleChcbiAgICAgICAgTWF0aC5tYXgobWluQ29vcmRpbmF0b3JJbmRleCwgY29vcmRpbmF0b3JUYXNrQ291bnQgLSAxKSxcbiAgICAgIClcbiAgICB9IGVsc2UgaWYgKGNvb3JkaW5hdG9yVGFza0luZGV4IDwgbWluQ29vcmRpbmF0b3JJbmRleCkge1xuICAgICAgc2V0Q29vcmRpbmF0b3JUYXNrSW5kZXgobWluQ29vcmRpbmF0b3JJbmRleClcbiAgICB9XG4gIH0sIFtjb29yZGluYXRvclRhc2tDb3VudCwgY29vcmRpbmF0b3JUYXNrSW5kZXgsIG1pbkNvb3JkaW5hdG9ySW5kZXhdKVxuICBjb25zdCBbaXNQYXN0aW5nLCBzZXRJc1Bhc3RpbmddID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtpc0V4dGVybmFsRWRpdG9yQWN0aXZlLCBzZXRJc0V4dGVybmFsRWRpdG9yQWN0aXZlXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbc2hvd01vZGVsUGlja2VyLCBzZXRTaG93TW9kZWxQaWNrZXJdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtzaG93UXVpY2tPcGVuLCBzZXRTaG93UXVpY2tPcGVuXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbc2hvd0dsb2JhbFNlYXJjaCwgc2V0U2hvd0dsb2JhbFNlYXJjaF0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW3Nob3dIaXN0b3J5UGlja2VyLCBzZXRTaG93SGlzdG9yeVBpY2tlcl0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW3Nob3dGYXN0TW9kZVBpY2tlciwgc2V0U2hvd0Zhc3RNb2RlUGlja2VyXSA9IHVzZVN0YXRlKGZhbHNlKVxuICBjb25zdCBbc2hvd1RoaW5raW5nVG9nZ2xlLCBzZXRTaG93VGhpbmtpbmdUb2dnbGVdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtzaG93QXV0b01vZGVPcHRJbiwgc2V0U2hvd0F1dG9Nb2RlT3B0SW5dID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtwcmV2aW91c01vZGVCZWZvcmVBdXRvLCBzZXRQcmV2aW91c01vZGVCZWZvcmVBdXRvXSA9XG4gICAgdXNlU3RhdGU8UGVybWlzc2lvbk1vZGUgfCBudWxsPihudWxsKVxuICBjb25zdCBhdXRvTW9kZU9wdEluVGltZW91dFJlZiA9IHVzZVJlZjxOb2RlSlMuVGltZW91dCB8IG51bGw+KG51bGwpXG5cbiAgLy8gQ2hlY2sgaWYgY3Vyc29yIGlzIG9uIHRoZSBmaXJzdCBsaW5lIG9mIGlucHV0XG4gIGNvbnN0IGlzQ3Vyc29yT25GaXJzdExpbmUgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBjb25zdCBmaXJzdE5ld2xpbmVJbmRleCA9IGlucHV0LmluZGV4T2YoJ1xcbicpXG4gICAgaWYgKGZpcnN0TmV3bGluZUluZGV4ID09PSAtMSkge1xuICAgICAgcmV0dXJuIHRydWUgLy8gTm8gbmV3bGluZXMsIGN1cnNvciBpcyBhbHdheXMgb24gZmlyc3QgbGluZVxuICAgIH1cbiAgICByZXR1cm4gY3Vyc29yT2Zmc2V0IDw9IGZpcnN0TmV3bGluZUluZGV4XG4gIH0sIFtpbnB1dCwgY3Vyc29yT2Zmc2V0XSlcblxuICBjb25zdCBpc0N1cnNvck9uTGFzdExpbmUgPSB1c2VNZW1vKCgpID0+IHtcbiAgICBjb25zdCBsYXN0TmV3bGluZUluZGV4ID0gaW5wdXQubGFzdEluZGV4T2YoJ1xcbicpXG4gICAgaWYgKGxhc3ROZXdsaW5lSW5kZXggPT09IC0xKSB7XG4gICAgICByZXR1cm4gdHJ1ZSAvLyBObyBuZXdsaW5lcywgY3Vyc29yIGlzIGFsd2F5cyBvbiBsYXN0IGxpbmVcbiAgICB9XG4gICAgcmV0dXJuIGN1cnNvck9mZnNldCA+IGxhc3ROZXdsaW5lSW5kZXhcbiAgfSwgW2lucHV0LCBjdXJzb3JPZmZzZXRdKVxuXG4gIC8vIERlcml2ZSB0ZWFtIGluZm8gZnJvbSB0ZWFtQ29udGV4dCAobm8gZmlsZXN5c3RlbSBJL08gbmVlZGVkKVxuICAvLyBBIHNlc3Npb24gY2FuIG9ubHkgbGVhZCBvbmUgdGVhbSBhdCBhIHRpbWVcbiAgY29uc3QgY2FjaGVkVGVhbXM6IFRlYW1TdW1tYXJ5W10gPSB1c2VNZW1vKCgpID0+IHtcbiAgICBpZiAoIWlzQWdlbnRTd2FybXNFbmFibGVkKCkpIHJldHVybiBbXVxuICAgIC8vIEluLXByb2Nlc3MgbW9kZSB1c2VzIFNoaWZ0K0Rvd24vVXAgbmF2aWdhdGlvbiBpbnN0ZWFkIG9mIGZvb3RlciBtZW51XG4gICAgaWYgKGlzSW5Qcm9jZXNzRW5hYmxlZCgpKSByZXR1cm4gW11cbiAgICBpZiAoIXRlYW1Db250ZXh0KSB7XG4gICAgICByZXR1cm4gW11cbiAgICB9XG4gICAgY29uc3QgdGVhbW1hdGVDb3VudCA9IGNvdW50KFxuICAgICAgT2JqZWN0LnZhbHVlcyh0ZWFtQ29udGV4dC50ZWFtbWF0ZXMpLFxuICAgICAgdCA9PiB0Lm5hbWUgIT09ICd0ZWFtLWxlYWQnLFxuICAgIClcbiAgICByZXR1cm4gW1xuICAgICAge1xuICAgICAgICBuYW1lOiB0ZWFtQ29udGV4dC50ZWFtTmFtZSxcbiAgICAgICAgbWVtYmVyQ291bnQ6IHRlYW1tYXRlQ291bnQsXG4gICAgICAgIHJ1bm5pbmdDb3VudDogMCxcbiAgICAgICAgaWRsZUNvdW50OiAwLFxuICAgICAgfSxcbiAgICBdXG4gIH0sIFt0ZWFtQ29udGV4dF0pXG5cbiAgLy8g4pSA4pSA4pSAIEZvb3RlciBwaWxsIG5hdmlnYXRpb24g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSAXG4gIC8vIFdoaWNoIHBpbGxzIHJlbmRlciBiZWxvdyB0aGUgaW5wdXQgYm94LiBPcmRlciBoZXJlIElTIHRoZSBuYXYgb3JkZXJcbiAgLy8gKGRvd24vcmlnaHQgPSBmb3J3YXJkLCB1cC9sZWZ0ID0gYmFjaykuIFNlbGVjdGlvbiBsaXZlcyBpbiBBcHBTdGF0ZSBzb1xuICAvLyBwaWxscyByZW5kZXJlZCBvdXRzaWRlIFByb21wdElucHV0IChDb21wYW5pb25TcHJpdGUpIGNhbiByZWFkIGZvY3VzLlxuICBjb25zdCBydW5uaW5nVGFza0NvdW50ID0gdXNlTWVtbyhcbiAgICAoKSA9PiBjb3VudChPYmplY3QudmFsdWVzKHRhc2tzKSwgdCA9PiB0LnN0YXR1cyA9PT0gJ3J1bm5pbmcnKSxcbiAgICBbdGFza3NdLFxuICApXG4gIC8vIFBhbmVsIHNob3dzIHJldGFpbmVkLWNvbXBsZXRlZCBhZ2VudHMgdG9vIChnZXRWaXNpYmxlQWdlbnRUYXNrcyksIHNvIHRoZVxuICAvLyBwaWxsIG11c3Qgc3RheSBuYXZpZ2FibGUgd2hlbmV2ZXIgdGhlIHBhbmVsIGhhcyByb3dzIOKAlCBub3QganVzdCB3aGVuXG4gIC8vIHNvbWV0aGluZyBpcyBydW5uaW5nLlxuICBjb25zdCB0YXNrc0Zvb3RlclZpc2libGUgPVxuICAgIChydW5uaW5nVGFza0NvdW50ID4gMCB8fFxuICAgICAgKFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiYgY29vcmRpbmF0b3JUYXNrQ291bnQgPiAwKSkgJiZcbiAgICAhc2hvdWxkSGlkZVRhc2tzRm9vdGVyKHRhc2tzLCBzaG93U3Bpbm5lclRyZWUpXG4gIGNvbnN0IHRlYW1zRm9vdGVyVmlzaWJsZSA9IGNhY2hlZFRlYW1zLmxlbmd0aCA+IDBcblxuICBjb25zdCBmb290ZXJJdGVtcyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIFtcbiAgICAgICAgdGFza3NGb290ZXJWaXNpYmxlICYmICd0YXNrcycsXG4gICAgICAgIHRtdXhGb290ZXJWaXNpYmxlICYmICd0bXV4JyxcbiAgICAgICAgYmFnZWxGb290ZXJWaXNpYmxlICYmICdiYWdlbCcsXG4gICAgICAgIHRlYW1zRm9vdGVyVmlzaWJsZSAmJiAndGVhbXMnLFxuICAgICAgICBicmlkZ2VGb290ZXJWaXNpYmxlICYmICdicmlkZ2UnLFxuICAgICAgICBjb21wYW5pb25Gb290ZXJWaXNpYmxlICYmICdjb21wYW5pb24nLFxuICAgICAgXS5maWx0ZXIoQm9vbGVhbikgYXMgRm9vdGVySXRlbVtdLFxuICAgIFtcbiAgICAgIHRhc2tzRm9vdGVyVmlzaWJsZSxcbiAgICAgIHRtdXhGb290ZXJWaXNpYmxlLFxuICAgICAgYmFnZWxGb290ZXJWaXNpYmxlLFxuICAgICAgdGVhbXNGb290ZXJWaXNpYmxlLFxuICAgICAgYnJpZGdlRm9vdGVyVmlzaWJsZSxcbiAgICAgIGNvbXBhbmlvbkZvb3RlclZpc2libGUsXG4gICAgXSxcbiAgKVxuXG4gIC8vIEVmZmVjdGl2ZSBzZWxlY3Rpb246IG51bGwgaWYgdGhlIHNlbGVjdGVkIHBpbGwgc3RvcHBlZCByZW5kZXJpbmcgKGJyaWRnZVxuICAvLyBkaXNjb25uZWN0ZWQsIHRhc2sgZmluaXNoZWQpLiBUaGUgZGVyaXZhdGlvbiBtYWtlcyB0aGUgVUkgY29ycmVjdFxuICAvLyBpbW1lZGlhdGVseTsgdGhlIHVzZUVmZmVjdCBiZWxvdyBjbGVhcnMgdGhlIHJhdyBzdGF0ZSBzbyBpdCBkb2Vzbid0XG4gIC8vIHJlc3VycmVjdCB3aGVuIHRoZSBzYW1lIHBpbGwgcmVhcHBlYXJzIChuZXcgdGFzayBzdGFydHMg4oaSIGZvY3VzIHN0b2xlbikuXG4gIGNvbnN0IHJhd0Zvb3RlclNlbGVjdGlvbiA9IHVzZUFwcFN0YXRlKHMgPT4gcy5mb290ZXJTZWxlY3Rpb24pXG4gIGNvbnN0IGZvb3Rlckl0ZW1TZWxlY3RlZCA9XG4gICAgcmF3Rm9vdGVyU2VsZWN0aW9uICYmIGZvb3Rlckl0ZW1zLmluY2x1ZGVzKHJhd0Zvb3RlclNlbGVjdGlvbilcbiAgICAgID8gcmF3Rm9vdGVyU2VsZWN0aW9uXG4gICAgICA6IG51bGxcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmIChyYXdGb290ZXJTZWxlY3Rpb24gJiYgIWZvb3Rlckl0ZW1TZWxlY3RlZCkge1xuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PlxuICAgICAgICBwcmV2LmZvb3RlclNlbGVjdGlvbiA9PT0gbnVsbFxuICAgICAgICAgID8gcHJldlxuICAgICAgICAgIDogeyAuLi5wcmV2LCBmb290ZXJTZWxlY3Rpb246IG51bGwgfSxcbiAgICAgIClcbiAgICB9XG4gIH0sIFtyYXdGb290ZXJTZWxlY3Rpb24sIGZvb3Rlckl0ZW1TZWxlY3RlZCwgc2V0QXBwU3RhdGVdKVxuXG4gIGNvbnN0IHRhc2tzU2VsZWN0ZWQgPSBmb290ZXJJdGVtU2VsZWN0ZWQgPT09ICd0YXNrcydcbiAgY29uc3QgdG11eFNlbGVjdGVkID0gZm9vdGVySXRlbVNlbGVjdGVkID09PSAndG11eCdcbiAgY29uc3QgYmFnZWxTZWxlY3RlZCA9IGZvb3Rlckl0ZW1TZWxlY3RlZCA9PT0gJ2JhZ2VsJ1xuICBjb25zdCB0ZWFtc1NlbGVjdGVkID0gZm9vdGVySXRlbVNlbGVjdGVkID09PSAndGVhbXMnXG4gIGNvbnN0IGJyaWRnZVNlbGVjdGVkID0gZm9vdGVySXRlbVNlbGVjdGVkID09PSAnYnJpZGdlJ1xuXG4gIGZ1bmN0aW9uIHNlbGVjdEZvb3Rlckl0ZW0oaXRlbTogRm9vdGVySXRlbSB8IG51bGwpOiB2b2lkIHtcbiAgICBzZXRBcHBTdGF0ZShwcmV2ID0+XG4gICAgICBwcmV2LmZvb3RlclNlbGVjdGlvbiA9PT0gaXRlbSA/IHByZXYgOiB7IC4uLnByZXYsIGZvb3RlclNlbGVjdGlvbjogaXRlbSB9LFxuICAgIClcbiAgICBpZiAoaXRlbSA9PT0gJ3Rhc2tzJykge1xuICAgICAgc2V0VGVhbW1hdGVGb290ZXJJbmRleCgwKVxuICAgICAgc2V0Q29vcmRpbmF0b3JUYXNrSW5kZXgobWluQ29vcmRpbmF0b3JJbmRleClcbiAgICB9XG4gIH1cblxuICAvLyBkZWx0YTogKzEgPSBkb3duL3JpZ2h0LCAtMSA9IHVwL2xlZnQuIFJldHVybnMgdHJ1ZSBpZiBuYXYgaGFwcGVuZWRcbiAgLy8gKGluY2x1ZGluZyBkZXNlbGVjdGluZyBhdCB0aGUgc3RhcnQpLCBmYWxzZSBpZiBhdCBhIGJvdW5kYXJ5LlxuICBmdW5jdGlvbiBuYXZpZ2F0ZUZvb3RlcihkZWx0YTogMSB8IC0xLCBleGl0QXRTdGFydCA9IGZhbHNlKTogYm9vbGVhbiB7XG4gICAgY29uc3QgaWR4ID0gZm9vdGVySXRlbVNlbGVjdGVkXG4gICAgICA/IGZvb3Rlckl0ZW1zLmluZGV4T2YoZm9vdGVySXRlbVNlbGVjdGVkKVxuICAgICAgOiAtMVxuICAgIGNvbnN0IG5leHQgPSBmb290ZXJJdGVtc1tpZHggKyBkZWx0YV1cbiAgICBpZiAobmV4dCkge1xuICAgICAgc2VsZWN0Rm9vdGVySXRlbShuZXh0KVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG4gICAgaWYgKGRlbHRhIDwgMCAmJiBleGl0QXRTdGFydCkge1xuICAgICAgc2VsZWN0Rm9vdGVySXRlbShudWxsKVxuICAgICAgcmV0dXJuIHRydWVcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlXG4gIH1cblxuICAvLyBQcm9tcHQgc3VnZ2VzdGlvbiBob29rIC0gcmVhZHMgc3VnZ2VzdGlvbnMgZ2VuZXJhdGVkIGJ5IGZvcmtlZCBhZ2VudCBpbiBxdWVyeSBsb29wXG4gIGNvbnN0IHtcbiAgICBzdWdnZXN0aW9uOiBwcm9tcHRTdWdnZXN0aW9uLFxuICAgIG1hcmtBY2NlcHRlZCxcbiAgICBsb2dPdXRjb21lQXRTdWJtaXNzaW9uLFxuICAgIG1hcmtTaG93bixcbiAgfSA9IHVzZVByb21wdFN1Z2dlc3Rpb24oe1xuICAgIGlucHV0VmFsdWU6IGlucHV0LFxuICAgIGlzQXNzaXN0YW50UmVzcG9uZGluZzogaXNMb2FkaW5nLFxuICB9KVxuXG4gIGNvbnN0IGRpc3BsYXllZFZhbHVlID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgaXNTZWFyY2hpbmdIaXN0b3J5ICYmIGhpc3RvcnlNYXRjaFxuICAgICAgICA/IGdldFZhbHVlRnJvbUlucHV0KFxuICAgICAgICAgICAgdHlwZW9mIGhpc3RvcnlNYXRjaCA9PT0gJ3N0cmluZydcbiAgICAgICAgICAgICAgPyBoaXN0b3J5TWF0Y2hcbiAgICAgICAgICAgICAgOiBoaXN0b3J5TWF0Y2guZGlzcGxheSxcbiAgICAgICAgICApXG4gICAgICAgIDogaW5wdXQsXG4gICAgW2lzU2VhcmNoaW5nSGlzdG9yeSwgaGlzdG9yeU1hdGNoLCBpbnB1dF0sXG4gIClcblxuICBjb25zdCB0aGlua1RyaWdnZXJzID0gdXNlTWVtbyhcbiAgICAoKSA9PiBmaW5kVGhpbmtpbmdUcmlnZ2VyUG9zaXRpb25zKGRpc3BsYXllZFZhbHVlKSxcbiAgICBbZGlzcGxheWVkVmFsdWVdLFxuICApXG5cbiAgY29uc3QgdWx0cmFwbGFuU2Vzc2lvblVybCA9IHVzZUFwcFN0YXRlKHMgPT4gcy51bHRyYXBsYW5TZXNzaW9uVXJsKVxuICBjb25zdCB1bHRyYXBsYW5MYXVuY2hpbmcgPSB1c2VBcHBTdGF0ZShzID0+IHMudWx0cmFwbGFuTGF1bmNoaW5nKVxuICBjb25zdCB1bHRyYXBsYW5UcmlnZ2VycyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIGZlYXR1cmUoJ1VMVFJBUExBTicpICYmICF1bHRyYXBsYW5TZXNzaW9uVXJsICYmICF1bHRyYXBsYW5MYXVuY2hpbmdcbiAgICAgICAgPyBmaW5kVWx0cmFwbGFuVHJpZ2dlclBvc2l0aW9ucyhkaXNwbGF5ZWRWYWx1ZSlcbiAgICAgICAgOiBbXSxcbiAgICBbZGlzcGxheWVkVmFsdWUsIHVsdHJhcGxhblNlc3Npb25VcmwsIHVsdHJhcGxhbkxhdW5jaGluZ10sXG4gIClcblxuICBjb25zdCB1bHRyYXJldmlld1RyaWdnZXJzID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgaXNVbHRyYXJldmlld0VuYWJsZWQoKVxuICAgICAgICA/IGZpbmRVbHRyYXJldmlld1RyaWdnZXJQb3NpdGlvbnMoZGlzcGxheWVkVmFsdWUpXG4gICAgICAgIDogW10sXG4gICAgW2Rpc3BsYXllZFZhbHVlXSxcbiAgKVxuXG4gIGNvbnN0IGJ0d1RyaWdnZXJzID0gdXNlTWVtbyhcbiAgICAoKSA9PiBmaW5kQnR3VHJpZ2dlclBvc2l0aW9ucyhkaXNwbGF5ZWRWYWx1ZSksXG4gICAgW2Rpc3BsYXllZFZhbHVlXSxcbiAgKVxuXG4gIGNvbnN0IGJ1ZGR5VHJpZ2dlcnMgPSB1c2VNZW1vKFxuICAgICgpID0+IGZpbmRCdWRkeVRyaWdnZXJQb3NpdGlvbnMoZGlzcGxheWVkVmFsdWUpLFxuICAgIFtkaXNwbGF5ZWRWYWx1ZV0sXG4gIClcblxuICBjb25zdCBzbGFzaENvbW1hbmRUcmlnZ2VycyA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGNvbnN0IHBvc2l0aW9ucyA9IGZpbmRTbGFzaENvbW1hbmRQb3NpdGlvbnMoZGlzcGxheWVkVmFsdWUpXG4gICAgLy8gT25seSBoaWdobGlnaHQgdmFsaWQgY29tbWFuZHNcbiAgICByZXR1cm4gcG9zaXRpb25zLmZpbHRlcihwb3MgPT4ge1xuICAgICAgY29uc3QgY29tbWFuZE5hbWUgPSBkaXNwbGF5ZWRWYWx1ZS5zbGljZShwb3Muc3RhcnQgKyAxLCBwb3MuZW5kKSAvLyArMSB0byBza2lwIFwiL1wiXG4gICAgICByZXR1cm4gaGFzQ29tbWFuZChjb21tYW5kTmFtZSwgY29tbWFuZHMpXG4gICAgfSlcbiAgfSwgW2Rpc3BsYXllZFZhbHVlLCBjb21tYW5kc10pXG5cbiAgY29uc3QgdG9rZW5CdWRnZXRUcmlnZ2VycyA9IHVzZU1lbW8oXG4gICAgKCkgPT5cbiAgICAgIGZlYXR1cmUoJ1RPS0VOX0JVREdFVCcpID8gZmluZFRva2VuQnVkZ2V0UG9zaXRpb25zKGRpc3BsYXllZFZhbHVlKSA6IFtdLFxuICAgIFtkaXNwbGF5ZWRWYWx1ZV0sXG4gIClcblxuICBjb25zdCBrbm93bkNoYW5uZWxzVmVyc2lvbiA9IHVzZVN5bmNFeHRlcm5hbFN0b3JlKFxuICAgIHN1YnNjcmliZUtub3duQ2hhbm5lbHMsXG4gICAgZ2V0S25vd25DaGFubmVsc1ZlcnNpb24sXG4gIClcbiAgY29uc3Qgc2xhY2tDaGFubmVsVHJpZ2dlcnMgPSB1c2VNZW1vKFxuICAgICgpID0+XG4gICAgICBoYXNTbGFja01jcFNlcnZlcihzdG9yZS5nZXRTdGF0ZSgpLm1jcC5jbGllbnRzKVxuICAgICAgICA/IGZpbmRTbGFja0NoYW5uZWxQb3NpdGlvbnMoZGlzcGxheWVkVmFsdWUpXG4gICAgICAgIDogW10sXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlYWN0LWhvb2tzL2V4aGF1c3RpdmUtZGVwcyAtLSBzdG9yZSBpcyBhIHN0YWJsZSByZWZcbiAgICBbZGlzcGxheWVkVmFsdWUsIGtub3duQ2hhbm5lbHNWZXJzaW9uXSxcbiAgKVxuXG4gIC8vIEZpbmQgQG5hbWUgbWVudGlvbnMgYW5kIGhpZ2hsaWdodCB3aXRoIHRlYW0gbWVtYmVyJ3MgY29sb3JcbiAgY29uc3QgbWVtYmVyTWVudGlvbkhpZ2hsaWdodHMgPSB1c2VNZW1vKCgpOiBBcnJheTx7XG4gICAgc3RhcnQ6IG51bWJlclxuICAgIGVuZDogbnVtYmVyXG4gICAgdGhlbWVDb2xvcjoga2V5b2YgVGhlbWVcbiAgfT4gPT4ge1xuICAgIGlmICghaXNBZ2VudFN3YXJtc0VuYWJsZWQoKSkgcmV0dXJuIFtdXG4gICAgaWYgKCF0ZWFtQ29udGV4dD8udGVhbW1hdGVzKSByZXR1cm4gW11cblxuICAgIGNvbnN0IGhpZ2hsaWdodHM6IEFycmF5PHtcbiAgICAgIHN0YXJ0OiBudW1iZXJcbiAgICAgIGVuZDogbnVtYmVyXG4gICAgICB0aGVtZUNvbG9yOiBrZXlvZiBUaGVtZVxuICAgIH0+ID0gW11cbiAgICBjb25zdCBtZW1iZXJzID0gdGVhbUNvbnRleHQudGVhbW1hdGVzXG4gICAgaWYgKCFtZW1iZXJzKSByZXR1cm4gaGlnaGxpZ2h0c1xuXG4gICAgLy8gRmluZCBhbGwgQG5hbWUgcGF0dGVybnMgaW4gdGhlIGlucHV0XG4gICAgY29uc3QgcmVnZXggPSAvKF58XFxzKUAoW1xcdy1dKykvZ1xuICAgIGNvbnN0IG1lbWJlclZhbHVlcyA9IE9iamVjdC52YWx1ZXMobWVtYmVycylcbiAgICBsZXQgbWF0Y2hcbiAgICB3aGlsZSAoKG1hdGNoID0gcmVnZXguZXhlYyhkaXNwbGF5ZWRWYWx1ZSkpICE9PSBudWxsKSB7XG4gICAgICBjb25zdCBsZWFkaW5nU3BhY2UgPSBtYXRjaFsxXSA/PyAnJ1xuICAgICAgY29uc3QgbmFtZVN0YXJ0ID0gbWF0Y2guaW5kZXggKyBsZWFkaW5nU3BhY2UubGVuZ3RoXG4gICAgICBjb25zdCBmdWxsTWF0Y2ggPSBtYXRjaFswXS50cmltU3RhcnQoKVxuICAgICAgY29uc3QgbmFtZSA9IG1hdGNoWzJdXG5cbiAgICAgIC8vIENoZWNrIGlmIHRoaXMgbmFtZSBtYXRjaGVzIGEgdGVhbSBtZW1iZXJcbiAgICAgIGNvbnN0IG1lbWJlciA9IG1lbWJlclZhbHVlcy5maW5kKHQgPT4gdC5uYW1lID09PSBuYW1lKVxuICAgICAgaWYgKG1lbWJlcj8uY29sb3IpIHtcbiAgICAgICAgY29uc3QgdGhlbWVDb2xvciA9XG4gICAgICAgICAgQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1JbbWVtYmVyLmNvbG9yIGFzIEFnZW50Q29sb3JOYW1lXVxuICAgICAgICBpZiAodGhlbWVDb2xvcikge1xuICAgICAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgICAgICBzdGFydDogbmFtZVN0YXJ0LFxuICAgICAgICAgICAgZW5kOiBuYW1lU3RhcnQgKyBmdWxsTWF0Y2gubGVuZ3RoLFxuICAgICAgICAgICAgdGhlbWVDb2xvcixcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBoaWdobGlnaHRzXG4gIH0sIFtkaXNwbGF5ZWRWYWx1ZSwgdGVhbUNvbnRleHRdKVxuXG4gIGNvbnN0IGltYWdlUmVmUG9zaXRpb25zID0gdXNlTWVtbyhcbiAgICAoKSA9PlxuICAgICAgcGFyc2VSZWZlcmVuY2VzKGRpc3BsYXllZFZhbHVlKVxuICAgICAgICAuZmlsdGVyKHIgPT4gci5tYXRjaC5zdGFydHNXaXRoKCdbSW1hZ2UnKSlcbiAgICAgICAgLm1hcChyID0+ICh7IHN0YXJ0OiByLmluZGV4LCBlbmQ6IHIuaW5kZXggKyByLm1hdGNoLmxlbmd0aCB9KSksXG4gICAgW2Rpc3BsYXllZFZhbHVlXSxcbiAgKVxuXG4gIC8vIGNoaXAuc3RhcnQgaXMgdGhlIFwic2VsZWN0ZWRcIiBzdGF0ZTogdGhlIGludmVydGVkIGNoaXAgSVMgdGhlIGN1cnNvci5cbiAgLy8gY2hpcC5lbmQgc3RheXMgYSBub3JtYWwgcG9zaXRpb24gc28geW91IGNhbiBwYXJrIHRoZSBjdXJzb3IgcmlnaHQgYWZ0ZXJcbiAgLy8gYF1gIGxpa2UgYW55IG90aGVyIGNoYXJhY3Rlci5cbiAgY29uc3QgY3Vyc29yQXRJbWFnZUNoaXAgPSBpbWFnZVJlZlBvc2l0aW9ucy5zb21lKFxuICAgIHIgPT4gci5zdGFydCA9PT0gY3Vyc29yT2Zmc2V0LFxuICApXG5cbiAgLy8gdXAvZG93biBtb3ZlbWVudCBvciBhIGZ1bGxzY3JlZW4gY2xpY2sgY2FuIGxhbmQgdGhlIGN1cnNvciBzdHJpY3RseVxuICAvLyBpbnNpZGUgYSBjaGlwOyBzbmFwIHRvIHRoZSBuZWFyZXIgYm91bmRhcnkgc28gaXQncyBuZXZlciBlZGl0YWJsZVxuICAvLyBjaGFyLWJ5LWNoYXIuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc3QgaW5zaWRlID0gaW1hZ2VSZWZQb3NpdGlvbnMuZmluZChcbiAgICAgIHIgPT4gY3Vyc29yT2Zmc2V0ID4gci5zdGFydCAmJiBjdXJzb3JPZmZzZXQgPCByLmVuZCxcbiAgICApXG4gICAgaWYgKGluc2lkZSkge1xuICAgICAgY29uc3QgbWlkID0gKGluc2lkZS5zdGFydCArIGluc2lkZS5lbmQpIC8gMlxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KGN1cnNvck9mZnNldCA8IG1pZCA/IGluc2lkZS5zdGFydCA6IGluc2lkZS5lbmQpXG4gICAgfVxuICB9LCBbY3Vyc29yT2Zmc2V0LCBpbWFnZVJlZlBvc2l0aW9ucywgc2V0Q3Vyc29yT2Zmc2V0XSlcblxuICBjb25zdCBjb21iaW5lZEhpZ2hsaWdodHMgPSB1c2VNZW1vKCgpOiBUZXh0SGlnaGxpZ2h0W10gPT4ge1xuICAgIGNvbnN0IGhpZ2hsaWdodHM6IFRleHRIaWdobGlnaHRbXSA9IFtdXG5cbiAgICAvLyBJbnZlcnQgdGhlIFtJbWFnZSAjTl0gY2hpcCB3aGVuIHRoZSBjdXJzb3IgaXMgYXQgY2hpcC5zdGFydCAodGhlXG4gICAgLy8gXCJzZWxlY3RlZFwiIHN0YXRlKSBzbyBiYWNrc3BhY2UtdG8tZGVsZXRlIGlzIHZpc3VhbGx5IG9idmlvdXMuXG4gICAgZm9yIChjb25zdCByZWYgb2YgaW1hZ2VSZWZQb3NpdGlvbnMpIHtcbiAgICAgIGlmIChjdXJzb3JPZmZzZXQgPT09IHJlZi5zdGFydCkge1xuICAgICAgICBoaWdobGlnaHRzLnB1c2goe1xuICAgICAgICAgIHN0YXJ0OiByZWYuc3RhcnQsXG4gICAgICAgICAgZW5kOiByZWYuZW5kLFxuICAgICAgICAgIGNvbG9yOiB1bmRlZmluZWQsXG4gICAgICAgICAgaW52ZXJzZTogdHJ1ZSxcbiAgICAgICAgICBwcmlvcml0eTogOCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoaXNTZWFyY2hpbmdIaXN0b3J5ICYmIGhpc3RvcnlNYXRjaCAmJiAhaGlzdG9yeUZhaWxlZE1hdGNoKSB7XG4gICAgICBoaWdobGlnaHRzLnB1c2goe1xuICAgICAgICBzdGFydDogY3Vyc29yT2Zmc2V0LFxuICAgICAgICBlbmQ6IGN1cnNvck9mZnNldCArIGhpc3RvcnlRdWVyeS5sZW5ndGgsXG4gICAgICAgIGNvbG9yOiAnd2FybmluZycsXG4gICAgICAgIHByaW9yaXR5OiAyMCxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gQWRkIFwiYnR3XCIgaGlnaGxpZ2h0aW5nIChzb2xpZCB5ZWxsb3cpXG4gICAgZm9yIChjb25zdCB0cmlnZ2VyIG9mIGJ0d1RyaWdnZXJzKSB7XG4gICAgICBoaWdobGlnaHRzLnB1c2goe1xuICAgICAgICBzdGFydDogdHJpZ2dlci5zdGFydCxcbiAgICAgICAgZW5kOiB0cmlnZ2VyLmVuZCxcbiAgICAgICAgY29sb3I6ICd3YXJuaW5nJyxcbiAgICAgICAgcHJpb3JpdHk6IDE1LFxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBBZGQgL2NvbW1hbmQgaGlnaGxpZ2h0aW5nIChibHVlKVxuICAgIGZvciAoY29uc3QgdHJpZ2dlciBvZiBzbGFzaENvbW1hbmRUcmlnZ2Vycykge1xuICAgICAgaGlnaGxpZ2h0cy5wdXNoKHtcbiAgICAgICAgc3RhcnQ6IHRyaWdnZXIuc3RhcnQsXG4gICAgICAgIGVuZDogdHJpZ2dlci5lbmQsXG4gICAgICAgIGNvbG9yOiAnc3VnZ2VzdGlvbicsXG4gICAgICAgIHByaW9yaXR5OiA1LFxuICAgICAgfSlcbiAgICB9XG5cbiAgICAvLyBBZGQgdG9rZW4gYnVkZ2V0IGhpZ2hsaWdodGluZyAoYmx1ZSlcbiAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2YgdG9rZW5CdWRnZXRUcmlnZ2Vycykge1xuICAgICAgaGlnaGxpZ2h0cy5wdXNoKHtcbiAgICAgICAgc3RhcnQ6IHRyaWdnZXIuc3RhcnQsXG4gICAgICAgIGVuZDogdHJpZ2dlci5lbmQsXG4gICAgICAgIGNvbG9yOiAnc3VnZ2VzdGlvbicsXG4gICAgICAgIHByaW9yaXR5OiA1LFxuICAgICAgfSlcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2Ygc2xhY2tDaGFubmVsVHJpZ2dlcnMpIHtcbiAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgIHN0YXJ0OiB0cmlnZ2VyLnN0YXJ0LFxuICAgICAgICBlbmQ6IHRyaWdnZXIuZW5kLFxuICAgICAgICBjb2xvcjogJ3N1Z2dlc3Rpb24nLFxuICAgICAgICBwcmlvcml0eTogNSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gQWRkIEBuYW1lIGhpZ2hsaWdodGluZyB3aXRoIHRlYW0gbWVtYmVyJ3MgY29sb3JcbiAgICBmb3IgKGNvbnN0IG1lbnRpb24gb2YgbWVtYmVyTWVudGlvbkhpZ2hsaWdodHMpIHtcbiAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgIHN0YXJ0OiBtZW50aW9uLnN0YXJ0LFxuICAgICAgICBlbmQ6IG1lbnRpb24uZW5kLFxuICAgICAgICBjb2xvcjogbWVudGlvbi50aGVtZUNvbG9yLFxuICAgICAgICBwcmlvcml0eTogNSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gRGltIGludGVyaW0gdm9pY2UgZGljdGF0aW9uIHRleHRcbiAgICBpZiAodm9pY2VJbnRlcmltUmFuZ2UpIHtcbiAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgIHN0YXJ0OiB2b2ljZUludGVyaW1SYW5nZS5zdGFydCxcbiAgICAgICAgZW5kOiB2b2ljZUludGVyaW1SYW5nZS5lbmQsXG4gICAgICAgIGNvbG9yOiB1bmRlZmluZWQsXG4gICAgICAgIGRpbUNvbG9yOiB0cnVlLFxuICAgICAgICBwcmlvcml0eTogMSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgLy8gUmFpbmJvdyBoaWdobGlnaHRpbmcgZm9yIHVsdHJhdGhpbmsga2V5d29yZCAocGVyLWNoYXJhY3RlciBjeWNsaW5nIGNvbG9ycylcbiAgICBpZiAoaXNVbHRyYXRoaW5rRW5hYmxlZCgpKSB7XG4gICAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2YgdGhpbmtUcmlnZ2Vycykge1xuICAgICAgICBmb3IgKGxldCBpID0gdHJpZ2dlci5zdGFydDsgaSA8IHRyaWdnZXIuZW5kOyBpKyspIHtcbiAgICAgICAgICBoaWdobGlnaHRzLnB1c2goe1xuICAgICAgICAgICAgc3RhcnQ6IGksXG4gICAgICAgICAgICBlbmQ6IGkgKyAxLFxuICAgICAgICAgICAgY29sb3I6IGdldFJhaW5ib3dDb2xvcihpIC0gdHJpZ2dlci5zdGFydCksXG4gICAgICAgICAgICBzaGltbWVyQ29sb3I6IGdldFJhaW5ib3dDb2xvcihpIC0gdHJpZ2dlci5zdGFydCwgdHJ1ZSksXG4gICAgICAgICAgICBwcmlvcml0eTogMTAsXG4gICAgICAgICAgfSlcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFNhbWUgcmFpbmJvdyB0cmVhdG1lbnQgZm9yIHRoZSB1bHRyYXBsYW4ga2V5d29yZFxuICAgIGlmIChmZWF0dXJlKCdVTFRSQVBMQU4nKSkge1xuICAgICAgZm9yIChjb25zdCB0cmlnZ2VyIG9mIHVsdHJhcGxhblRyaWdnZXJzKSB7XG4gICAgICAgIGZvciAobGV0IGkgPSB0cmlnZ2VyLnN0YXJ0OyBpIDwgdHJpZ2dlci5lbmQ7IGkrKykge1xuICAgICAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgICAgICBzdGFydDogaSxcbiAgICAgICAgICAgIGVuZDogaSArIDEsXG4gICAgICAgICAgICBjb2xvcjogZ2V0UmFpbmJvd0NvbG9yKGkgLSB0cmlnZ2VyLnN0YXJ0KSxcbiAgICAgICAgICAgIHNoaW1tZXJDb2xvcjogZ2V0UmFpbmJvd0NvbG9yKGkgLSB0cmlnZ2VyLnN0YXJ0LCB0cnVlKSxcbiAgICAgICAgICAgIHByaW9yaXR5OiAxMCxcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gU2FtZSByYWluYm93IHRyZWF0bWVudCBmb3IgdGhlIHVsdHJhcmV2aWV3IGtleXdvcmRcbiAgICBmb3IgKGNvbnN0IHRyaWdnZXIgb2YgdWx0cmFyZXZpZXdUcmlnZ2Vycykge1xuICAgICAgZm9yIChsZXQgaSA9IHRyaWdnZXIuc3RhcnQ7IGkgPCB0cmlnZ2VyLmVuZDsgaSsrKSB7XG4gICAgICAgIGhpZ2hsaWdodHMucHVzaCh7XG4gICAgICAgICAgc3RhcnQ6IGksXG4gICAgICAgICAgZW5kOiBpICsgMSxcbiAgICAgICAgICBjb2xvcjogZ2V0UmFpbmJvd0NvbG9yKGkgLSB0cmlnZ2VyLnN0YXJ0KSxcbiAgICAgICAgICBzaGltbWVyQ29sb3I6IGdldFJhaW5ib3dDb2xvcihpIC0gdHJpZ2dlci5zdGFydCwgdHJ1ZSksXG4gICAgICAgICAgcHJpb3JpdHk6IDEwLFxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFJhaW5ib3cgZm9yIC9idWRkeVxuICAgIGZvciAoY29uc3QgdHJpZ2dlciBvZiBidWRkeVRyaWdnZXJzKSB7XG4gICAgICBmb3IgKGxldCBpID0gdHJpZ2dlci5zdGFydDsgaSA8IHRyaWdnZXIuZW5kOyBpKyspIHtcbiAgICAgICAgaGlnaGxpZ2h0cy5wdXNoKHtcbiAgICAgICAgICBzdGFydDogaSxcbiAgICAgICAgICBlbmQ6IGkgKyAxLFxuICAgICAgICAgIGNvbG9yOiBnZXRSYWluYm93Q29sb3IoaSAtIHRyaWdnZXIuc3RhcnQpLFxuICAgICAgICAgIHNoaW1tZXJDb2xvcjogZ2V0UmFpbmJvd0NvbG9yKGkgLSB0cmlnZ2VyLnN0YXJ0LCB0cnVlKSxcbiAgICAgICAgICBwcmlvcml0eTogMTAsXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGhpZ2hsaWdodHNcbiAgfSwgW1xuICAgIGlzU2VhcmNoaW5nSGlzdG9yeSxcbiAgICBoaXN0b3J5UXVlcnksXG4gICAgaGlzdG9yeU1hdGNoLFxuICAgIGhpc3RvcnlGYWlsZWRNYXRjaCxcbiAgICBjdXJzb3JPZmZzZXQsXG4gICAgYnR3VHJpZ2dlcnMsXG4gICAgaW1hZ2VSZWZQb3NpdGlvbnMsXG4gICAgbWVtYmVyTWVudGlvbkhpZ2hsaWdodHMsXG4gICAgc2xhc2hDb21tYW5kVHJpZ2dlcnMsXG4gICAgdG9rZW5CdWRnZXRUcmlnZ2VycyxcbiAgICBzbGFja0NoYW5uZWxUcmlnZ2VycyxcbiAgICBkaXNwbGF5ZWRWYWx1ZSxcbiAgICB2b2ljZUludGVyaW1SYW5nZSxcbiAgICB0aGlua1RyaWdnZXJzLFxuICAgIHVsdHJhcGxhblRyaWdnZXJzLFxuICAgIHVsdHJhcmV2aWV3VHJpZ2dlcnMsXG4gICAgYnVkZHlUcmlnZ2VycyxcbiAgXSlcblxuICBjb25zdCB7IGFkZE5vdGlmaWNhdGlvbiwgcmVtb3ZlTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcblxuICAvLyBTaG93IHVsdHJhdGhpbmsgbm90aWZpY2F0aW9uXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKHRoaW5rVHJpZ2dlcnMubGVuZ3RoICYmIGlzVWx0cmF0aGlua0VuYWJsZWQoKSkge1xuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAndWx0cmF0aGluay1hY3RpdmUnLFxuICAgICAgICB0ZXh0OiAnRWZmb3J0IHNldCB0byBoaWdoIGZvciB0aGlzIHR1cm4nLFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogNTAwMCxcbiAgICAgIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHJlbW92ZU5vdGlmaWNhdGlvbigndWx0cmF0aGluay1hY3RpdmUnKVxuICAgIH1cbiAgfSwgW2FkZE5vdGlmaWNhdGlvbiwgcmVtb3ZlTm90aWZpY2F0aW9uLCB0aGlua1RyaWdnZXJzLmxlbmd0aF0pXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZmVhdHVyZSgnVUxUUkFQTEFOJykgJiYgdWx0cmFwbGFuVHJpZ2dlcnMubGVuZ3RoKSB7XG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICd1bHRyYXBsYW4tYWN0aXZlJyxcbiAgICAgICAgdGV4dDogJ1RoaXMgcHJvbXB0IHdpbGwgbGF1bmNoIGFuIHVsdHJhcGxhbiBzZXNzaW9uIGluIENsYXVkZSBDb2RlIG9uIHRoZSB3ZWInLFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogNTAwMCxcbiAgICAgIH0pXG4gICAgfSBlbHNlIHtcbiAgICAgIHJlbW92ZU5vdGlmaWNhdGlvbigndWx0cmFwbGFuLWFjdGl2ZScpXG4gICAgfVxuICB9LCBbYWRkTm90aWZpY2F0aW9uLCByZW1vdmVOb3RpZmljYXRpb24sIHVsdHJhcGxhblRyaWdnZXJzLmxlbmd0aF0pXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoaXNVbHRyYXJldmlld0VuYWJsZWQoKSAmJiB1bHRyYXJldmlld1RyaWdnZXJzLmxlbmd0aCkge1xuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAndWx0cmFyZXZpZXctYWN0aXZlJyxcbiAgICAgICAgdGV4dDogJ1J1biAvdWx0cmFyZXZpZXcgYWZ0ZXIgQ2xhdWRlIGZpbmlzaGVzIHRvIHJldmlldyB0aGVzZSBjaGFuZ2VzIGluIHRoZSBjbG91ZCcsXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgdGltZW91dE1zOiA1MDAwLFxuICAgICAgfSlcbiAgICB9XG4gIH0sIFthZGROb3RpZmljYXRpb24sIHVsdHJhcmV2aWV3VHJpZ2dlcnMubGVuZ3RoXSlcblxuICAvLyBUcmFjayBpbnB1dCBsZW5ndGggZm9yIHN0YXNoIGhpbnRcbiAgY29uc3QgcHJldklucHV0TGVuZ3RoUmVmID0gdXNlUmVmKGlucHV0Lmxlbmd0aClcbiAgY29uc3QgcGVha0lucHV0TGVuZ3RoUmVmID0gdXNlUmVmKGlucHV0Lmxlbmd0aClcblxuICAvLyBEaXNtaXNzIHN0YXNoIGhpbnQgd2hlbiB1c2VyIG1ha2VzIGFueSBpbnB1dCBjaGFuZ2VcbiAgY29uc3QgZGlzbWlzc1N0YXNoSGludCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICByZW1vdmVOb3RpZmljYXRpb24oJ3N0YXNoLWhpbnQnKVxuICB9LCBbcmVtb3ZlTm90aWZpY2F0aW9uXSlcblxuICAvLyBTaG93IHN0YXNoIGhpbnQgd2hlbiB1c2VyIGdyYWR1YWxseSBjbGVhcnMgc3Vic3RhbnRpYWwgaW5wdXRcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBjb25zdCBwcmV2TGVuZ3RoID0gcHJldklucHV0TGVuZ3RoUmVmLmN1cnJlbnRcbiAgICBjb25zdCBwZWFrTGVuZ3RoID0gcGVha0lucHV0TGVuZ3RoUmVmLmN1cnJlbnRcbiAgICBjb25zdCBjdXJyZW50TGVuZ3RoID0gaW5wdXQubGVuZ3RoXG4gICAgcHJldklucHV0TGVuZ3RoUmVmLmN1cnJlbnQgPSBjdXJyZW50TGVuZ3RoXG5cbiAgICAvLyBVcGRhdGUgcGVhayB3aGVuIGlucHV0IGdyb3dzXG4gICAgaWYgKGN1cnJlbnRMZW5ndGggPiBwZWFrTGVuZ3RoKSB7XG4gICAgICBwZWFrSW5wdXRMZW5ndGhSZWYuY3VycmVudCA9IGN1cnJlbnRMZW5ndGhcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIFJlc2V0IHN0YXRlIHdoZW4gaW5wdXQgaXMgZW1wdHlcbiAgICBpZiAoY3VycmVudExlbmd0aCA9PT0gMCkge1xuICAgICAgcGVha0lucHV0TGVuZ3RoUmVmLmN1cnJlbnQgPSAwXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBEZXRlY3QgZ3JhZHVhbCBjbGVhcjogcGVhayB3YXMgaGlnaCwgY3VycmVudCBpcyBsb3csIGJ1dCB0aGlzIHdhc24ndCBhIHNpbmdsZSBiaWcganVtcFxuICAgIC8vIChyYXBpZCBjbGVhcnMgbGlrZSBlc2MtZXNjIGdvIGZyb20gMjArIHRvIDAgaW4gb25lIHN0ZXApXG4gICAgY29uc3QgY2xlYXJlZFN1YnN0YW50aWFsSW5wdXQgPSBwZWFrTGVuZ3RoID49IDIwICYmIGN1cnJlbnRMZW5ndGggPD0gNVxuICAgIGNvbnN0IHdhc1JhcGlkQ2xlYXIgPSBwcmV2TGVuZ3RoID49IDIwICYmIGN1cnJlbnRMZW5ndGggPD0gNVxuXG4gICAgaWYgKGNsZWFyZWRTdWJzdGFudGlhbElucHV0ICYmICF3YXNSYXBpZENsZWFyKSB7XG4gICAgICBjb25zdCBjb25maWcgPSBnZXRHbG9iYWxDb25maWcoKVxuICAgICAgaWYgKCFjb25maWcuaGFzVXNlZFN0YXNoKSB7XG4gICAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgICAga2V5OiAnc3Rhc2gtaGludCcsXG4gICAgICAgICAganN4OiAoXG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgVGlwOnsnICd9XG4gICAgICAgICAgICAgIDxDb25maWd1cmFibGVTaG9ydGN1dEhpbnRcbiAgICAgICAgICAgICAgICBhY3Rpb249XCJjaGF0OnN0YXNoXCJcbiAgICAgICAgICAgICAgICBjb250ZXh0PVwiQ2hhdFwiXG4gICAgICAgICAgICAgICAgZmFsbGJhY2s9XCJjdHJsK3NcIlxuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uPVwic3Rhc2hcIlxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICksXG4gICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgIHRpbWVvdXRNczogRk9PVEVSX1RFTVBPUkFSWV9TVEFUVVNfVElNRU9VVCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICAgIHBlYWtJbnB1dExlbmd0aFJlZi5jdXJyZW50ID0gY3VycmVudExlbmd0aFxuICAgIH1cbiAgfSwgW2lucHV0Lmxlbmd0aCwgYWRkTm90aWZpY2F0aW9uXSlcblxuICAvLyBJbml0aWFsaXplIGlucHV0IGJ1ZmZlciBmb3IgdW5kbyBmdW5jdGlvbmFsaXR5XG4gIGNvbnN0IHsgcHVzaFRvQnVmZmVyLCB1bmRvLCBjYW5VbmRvLCBjbGVhckJ1ZmZlciB9ID0gdXNlSW5wdXRCdWZmZXIoe1xuICAgIG1heEJ1ZmZlclNpemU6IDUwLFxuICAgIGRlYm91bmNlTXM6IDEwMDAsXG4gIH0pXG5cbiAgdXNlTWF5YmVUcnVuY2F0ZUlucHV0KHtcbiAgICBpbnB1dCxcbiAgICBwYXN0ZWRDb250ZW50cyxcbiAgICBvbklucHV0Q2hhbmdlOiB0cmFja0FuZFNldElucHV0LFxuICAgIHNldEN1cnNvck9mZnNldCxcbiAgICBzZXRQYXN0ZWRDb250ZW50cyxcbiAgfSlcblxuICBjb25zdCBkZWZhdWx0UGxhY2Vob2xkZXIgPSB1c2VQcm9tcHRJbnB1dFBsYWNlaG9sZGVyKHtcbiAgICBpbnB1dCxcbiAgICBzdWJtaXRDb3VudCxcbiAgICB2aWV3aW5nQWdlbnROYW1lLFxuICB9KVxuXG4gIGNvbnN0IG9uQ2hhbmdlID0gdXNlQ2FsbGJhY2soXG4gICAgKHZhbHVlOiBzdHJpbmcpID0+IHtcbiAgICAgIGlmICh2YWx1ZSA9PT0gJz8nKSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9oZWxwX3RvZ2dsZWQnLCB7fSlcbiAgICAgICAgc2V0SGVscE9wZW4odiA9PiAhdilcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG4gICAgICBzZXRIZWxwT3BlbihmYWxzZSlcblxuICAgICAgLy8gRGlzbWlzcyBzdGFzaCBoaW50IHdoZW4gdXNlciBtYWtlcyBhbnkgaW5wdXQgY2hhbmdlXG4gICAgICBkaXNtaXNzU3Rhc2hIaW50KClcblxuICAgICAgLy8gQ2FuY2VsIGFueSBwZW5kaW5nIHByb21wdCBzdWdnZXN0aW9uIGFuZCBzcGVjdWxhdGlvbiB3aGVuIHVzZXIgdHlwZXNcbiAgICAgIGFib3J0UHJvbXB0U3VnZ2VzdGlvbigpXG4gICAgICBhYm9ydFNwZWN1bGF0aW9uKHNldEFwcFN0YXRlKVxuXG4gICAgICAvLyBDaGVjayBpZiB0aGlzIGlzIGEgc2luZ2xlIGNoYXJhY3RlciBpbnNlcnRpb24gYXQgdGhlIHN0YXJ0XG4gICAgICBjb25zdCBpc1NpbmdsZUNoYXJJbnNlcnRpb24gPSB2YWx1ZS5sZW5ndGggPT09IGlucHV0Lmxlbmd0aCArIDFcbiAgICAgIGNvbnN0IGluc2VydGVkQXRTdGFydCA9IGN1cnNvck9mZnNldCA9PT0gMFxuICAgICAgY29uc3QgbW9kZSA9IGdldE1vZGVGcm9tSW5wdXQodmFsdWUpXG5cbiAgICAgIGlmIChpbnNlcnRlZEF0U3RhcnQgJiYgbW9kZSAhPT0gJ3Byb21wdCcpIHtcbiAgICAgICAgaWYgKGlzU2luZ2xlQ2hhckluc2VydGlvbikge1xuICAgICAgICAgIG9uTW9kZUNoYW5nZShtb2RlKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIE11bHRpLWNoYXIgaW5zZXJ0aW9uIGludG8gZW1wdHkgaW5wdXQgKGUuZy4gdGFiLWFjY2VwdGluZyBcIiEgZ2Nsb3VkIGF1dGggbG9naW5cIilcbiAgICAgICAgaWYgKGlucHV0Lmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgIG9uTW9kZUNoYW5nZShtb2RlKVxuICAgICAgICAgIGNvbnN0IHZhbHVlV2l0aG91dE1vZGUgPSBnZXRWYWx1ZUZyb21JbnB1dCh2YWx1ZSkucmVwbGFjZUFsbChcbiAgICAgICAgICAgICdcXHQnLFxuICAgICAgICAgICAgJyAgICAnLFxuICAgICAgICAgIClcbiAgICAgICAgICBwdXNoVG9CdWZmZXIoaW5wdXQsIGN1cnNvck9mZnNldCwgcGFzdGVkQ29udGVudHMpXG4gICAgICAgICAgdHJhY2tBbmRTZXRJbnB1dCh2YWx1ZVdpdGhvdXRNb2RlKVxuICAgICAgICAgIHNldEN1cnNvck9mZnNldCh2YWx1ZVdpdGhvdXRNb2RlLmxlbmd0aClcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjb25zdCBwcm9jZXNzZWRWYWx1ZSA9IHZhbHVlLnJlcGxhY2VBbGwoJ1xcdCcsICcgICAgJylcblxuICAgICAgLy8gUHVzaCBjdXJyZW50IHN0YXRlIHRvIGJ1ZmZlciBiZWZvcmUgbWFraW5nIGNoYW5nZXNcbiAgICAgIGlmIChpbnB1dCAhPT0gcHJvY2Vzc2VkVmFsdWUpIHtcbiAgICAgICAgcHVzaFRvQnVmZmVyKGlucHV0LCBjdXJzb3JPZmZzZXQsIHBhc3RlZENvbnRlbnRzKVxuICAgICAgfVxuXG4gICAgICAvLyBEZXNlbGVjdCBmb290ZXIgaXRlbXMgd2hlbiB1c2VyIHR5cGVzXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+XG4gICAgICAgIHByZXYuZm9vdGVyU2VsZWN0aW9uID09PSBudWxsXG4gICAgICAgICAgPyBwcmV2XG4gICAgICAgICAgOiB7IC4uLnByZXYsIGZvb3RlclNlbGVjdGlvbjogbnVsbCB9LFxuICAgICAgKVxuXG4gICAgICB0cmFja0FuZFNldElucHV0KHByb2Nlc3NlZFZhbHVlKVxuICAgIH0sXG4gICAgW1xuICAgICAgdHJhY2tBbmRTZXRJbnB1dCxcbiAgICAgIG9uTW9kZUNoYW5nZSxcbiAgICAgIGlucHV0LFxuICAgICAgY3Vyc29yT2Zmc2V0LFxuICAgICAgcHVzaFRvQnVmZmVyLFxuICAgICAgcGFzdGVkQ29udGVudHMsXG4gICAgICBkaXNtaXNzU3Rhc2hIaW50LFxuICAgICAgc2V0QXBwU3RhdGUsXG4gICAgXSxcbiAgKVxuXG4gIGNvbnN0IHtcbiAgICByZXNldEhpc3RvcnksXG4gICAgb25IaXN0b3J5VXAsXG4gICAgb25IaXN0b3J5RG93bixcbiAgICBkaXNtaXNzU2VhcmNoSGludCxcbiAgICBoaXN0b3J5SW5kZXgsXG4gIH0gPSB1c2VBcnJvd0tleUhpc3RvcnkoXG4gICAgKFxuICAgICAgdmFsdWU6IHN0cmluZyxcbiAgICAgIGhpc3RvcnlNb2RlOiBIaXN0b3J5TW9kZSxcbiAgICAgIHBhc3RlZENvbnRlbnRzOiBSZWNvcmQ8bnVtYmVyLCBQYXN0ZWRDb250ZW50PixcbiAgICApID0+IHtcbiAgICAgIG9uQ2hhbmdlKHZhbHVlKVxuICAgICAgb25Nb2RlQ2hhbmdlKGhpc3RvcnlNb2RlKVxuICAgICAgc2V0UGFzdGVkQ29udGVudHMocGFzdGVkQ29udGVudHMpXG4gICAgfSxcbiAgICBpbnB1dCxcbiAgICBwYXN0ZWRDb250ZW50cyxcbiAgICBzZXRDdXJzb3JPZmZzZXQsXG4gICAgbW9kZSxcbiAgKVxuXG4gIC8vIERpc21pc3Mgc2VhcmNoIGhpbnQgd2hlbiB1c2VyIHN0YXJ0cyBzZWFyY2hpbmdcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoaXNTZWFyY2hpbmdIaXN0b3J5KSB7XG4gICAgICBkaXNtaXNzU2VhcmNoSGludCgpXG4gICAgfVxuICB9LCBbaXNTZWFyY2hpbmdIaXN0b3J5LCBkaXNtaXNzU2VhcmNoSGludF0pXG5cbiAgLy8gT25seSB1c2UgaGlzdG9yeSBuYXZpZ2F0aW9uIHdoZW4gdGhlcmUgYXJlIDAgb3IgMSBzbGFzaCBjb21tYW5kIHN1Z2dlc3Rpb25zLlxuICAvLyBGb290ZXIgbmF2IGlzIE5PVCBoZXJlIOKAlCB3aGVuIGEgcGlsbCBpcyBzZWxlY3RlZCwgVGV4dElucHV0IGZvY3VzPWZhbHNlIHNvXG4gIC8vIHRoZXNlIG5ldmVyIGZpcmUuIFRoZSBGb290ZXIga2V5YmluZGluZyBjb250ZXh0IGhhbmRsZXMg4oaRL+KGkyBpbnN0ZWFkLlxuICBmdW5jdGlvbiBoYW5kbGVIaXN0b3J5VXAoKSB7XG4gICAgaWYgKHN1Z2dlc3Rpb25zLmxlbmd0aCA+IDEpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIE9ubHkgbmF2aWdhdGUgaGlzdG9yeSB3aGVuIGN1cnNvciBpcyBvbiB0aGUgZmlyc3QgbGluZS5cbiAgICAvLyBJbiBtdWx0aWxpbmUgaW5wdXRzLCB1cCBhcnJvdyBzaG91bGQgbW92ZSB0aGUgY3Vyc29yIChoYW5kbGVkIGJ5IFRleHRJbnB1dClcbiAgICAvLyBhbmQgb25seSB0cmlnZ2VyIGhpc3Rvcnkgd2hlbiBhdCB0aGUgdG9wIG9mIHRoZSBpbnB1dC5cbiAgICBpZiAoIWlzQ3Vyc29yT25GaXJzdExpbmUpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIElmIHRoZXJlJ3MgYW4gZWRpdGFibGUgcXVldWVkIGNvbW1hbmQsIG1vdmUgaXQgdG8gdGhlIGlucHV0IGZvciBlZGl0aW5nIHdoZW4gVVAgaXMgcHJlc3NlZFxuICAgIGNvbnN0IGhhc0VkaXRhYmxlQ29tbWFuZCA9IHF1ZXVlZENvbW1hbmRzLnNvbWUoaXNRdWV1ZWRDb21tYW5kRWRpdGFibGUpXG4gICAgaWYgKGhhc0VkaXRhYmxlQ29tbWFuZCkge1xuICAgICAgdm9pZCBwb3BBbGxDb21tYW5kc0Zyb21RdWV1ZSgpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBvbkhpc3RvcnlVcCgpXG4gIH1cblxuICBmdW5jdGlvbiBoYW5kbGVIaXN0b3J5RG93bigpIHtcbiAgICBpZiAoc3VnZ2VzdGlvbnMubGVuZ3RoID4gMSkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gT25seSBuYXZpZ2F0ZSBoaXN0b3J5L2Zvb3RlciB3aGVuIGN1cnNvciBpcyBvbiB0aGUgbGFzdCBsaW5lLlxuICAgIC8vIEluIG11bHRpbGluZSBpbnB1dHMsIGRvd24gYXJyb3cgc2hvdWxkIG1vdmUgdGhlIGN1cnNvciAoaGFuZGxlZCBieSBUZXh0SW5wdXQpXG4gICAgLy8gYW5kIG9ubHkgdHJpZ2dlciBuYXZpZ2F0aW9uIHdoZW4gYXQgdGhlIGJvdHRvbSBvZiB0aGUgaW5wdXQuXG4gICAgaWYgKCFpc0N1cnNvck9uTGFzdExpbmUpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIEF0IGJvdHRvbSBvZiBoaXN0b3J5IOKGkiBlbnRlciBmb290ZXIgYXQgZmlyc3QgdmlzaWJsZSBwaWxsXG4gICAgaWYgKG9uSGlzdG9yeURvd24oKSAmJiBmb290ZXJJdGVtcy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBmaXJzdCA9IGZvb3Rlckl0ZW1zWzBdIVxuICAgICAgc2VsZWN0Rm9vdGVySXRlbShmaXJzdClcbiAgICAgIGlmIChmaXJzdCA9PT0gJ3Rhc2tzJyAmJiAhZ2V0R2xvYmFsQ29uZmlnKCkuaGFzU2VlblRhc2tzSGludCkge1xuICAgICAgICBzYXZlR2xvYmFsQ29uZmlnKGMgPT5cbiAgICAgICAgICBjLmhhc1NlZW5UYXNrc0hpbnQgPyBjIDogeyAuLi5jLCBoYXNTZWVuVGFza3NIaW50OiB0cnVlIH0sXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBDcmVhdGUgYSBzdWdnZXN0aW9ucyBzdGF0ZSBkaXJlY3RseSAtIHdlJ2xsIHN5bmMgaXQgd2l0aCB1c2VUeXBlYWhlYWQgbGF0ZXJcbiAgY29uc3QgW3N1Z2dlc3Rpb25zU3RhdGUsIHNldFN1Z2dlc3Rpb25zU3RhdGVSYXddID0gdXNlU3RhdGU8e1xuICAgIHN1Z2dlc3Rpb25zOiBTdWdnZXN0aW9uSXRlbVtdXG4gICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiBudW1iZXJcbiAgICBjb21tYW5kQXJndW1lbnRIaW50Pzogc3RyaW5nXG4gIH0+KHtcbiAgICBzdWdnZXN0aW9uczogW10sXG4gICAgc2VsZWN0ZWRTdWdnZXN0aW9uOiAtMSxcbiAgICBjb21tYW5kQXJndW1lbnRIaW50OiB1bmRlZmluZWQsXG4gIH0pXG5cbiAgLy8gU2V0dGVyIGZvciBzdWdnZXN0aW9ucyBzdGF0ZVxuICBjb25zdCBzZXRTdWdnZXN0aW9uc1N0YXRlID0gdXNlQ2FsbGJhY2soXG4gICAgKFxuICAgICAgdXBkYXRlcjpcbiAgICAgICAgfCB0eXBlb2Ygc3VnZ2VzdGlvbnNTdGF0ZVxuICAgICAgICB8ICgocHJldjogdHlwZW9mIHN1Z2dlc3Rpb25zU3RhdGUpID0+IHR5cGVvZiBzdWdnZXN0aW9uc1N0YXRlKSxcbiAgICApID0+IHtcbiAgICAgIHNldFN1Z2dlc3Rpb25zU3RhdGVSYXcocHJldiA9PlxuICAgICAgICB0eXBlb2YgdXBkYXRlciA9PT0gJ2Z1bmN0aW9uJyA/IHVwZGF0ZXIocHJldikgOiB1cGRhdGVyLFxuICAgICAgKVxuICAgIH0sXG4gICAgW10sXG4gIClcblxuICBjb25zdCBvblN1Ym1pdCA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jIChpbnB1dFBhcmFtOiBzdHJpbmcsIGlzU3VibWl0dGluZ1NsYXNoQ29tbWFuZCA9IGZhbHNlKSA9PiB7XG4gICAgICBpbnB1dFBhcmFtID0gaW5wdXRQYXJhbS50cmltRW5kKClcblxuICAgICAgLy8gRG9uJ3Qgc3VibWl0IGlmIGEgZm9vdGVyIGluZGljYXRvciBpcyBiZWluZyBvcGVuZWQuIFJlYWQgZnJlc2ggZnJvbVxuICAgICAgLy8gc3RvcmUg4oCUIGZvb3RlcjpvcGVuU2VsZWN0ZWQgY2FsbHMgc2VsZWN0Rm9vdGVySXRlbShudWxsKSB0aGVuIG9uU3VibWl0XG4gICAgICAvLyBpbiB0aGUgc2FtZSB0aWNrLCBhbmQgdGhlIGNsb3N1cmUgdmFsdWUgaGFzbid0IHVwZGF0ZWQgeWV0LiBBcHBseSB0aGVcbiAgICAgIC8vIHNhbWUgXCJzdGlsbCB2aXNpYmxlP1wiIGRlcml2YXRpb24gYXMgZm9vdGVySXRlbVNlbGVjdGVkIHNvIGEgc3RhbGVcbiAgICAgIC8vIHNlbGVjdGlvbiAocGlsbCBkaXNhcHBlYXJlZCkgZG9lc24ndCBzd2FsbG93IEVudGVyLlxuICAgICAgY29uc3Qgc3RhdGUgPSBzdG9yZS5nZXRTdGF0ZSgpXG4gICAgICBpZiAoXG4gICAgICAgIHN0YXRlLmZvb3RlclNlbGVjdGlvbiAmJlxuICAgICAgICBmb290ZXJJdGVtcy5pbmNsdWRlcyhzdGF0ZS5mb290ZXJTZWxlY3Rpb24pXG4gICAgICApIHtcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIEVudGVyIGluIHNlbGVjdGlvbiBtb2RlcyBjb25maXJtcyBzZWxlY3Rpb24gKHVzZUJhY2tncm91bmRUYXNrTmF2aWdhdGlvbikuXG4gICAgICAvLyBCYXNlVGV4dElucHV0J3MgdXNlSW5wdXQgcmVnaXN0ZXJzIGJlZm9yZSB0aGF0IGhvb2sgKGNoaWxkIGVmZmVjdHMgZmlyZSBmaXJzdCksXG4gICAgICAvLyBzbyB3aXRob3V0IHRoaXMgZ3VhcmQgRW50ZXIgd291bGQgZG91YmxlLWZpcmUgYW5kIGF1dG8tc3VibWl0IHRoZSBzdWdnZXN0aW9uLlxuICAgICAgaWYgKHN0YXRlLnZpZXdTZWxlY3Rpb25Nb2RlID09PSAnc2VsZWN0aW5nLWFnZW50Jykge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gQ2hlY2sgZm9yIGltYWdlcyBlYXJseSAtIHdlIG5lZWQgdGhpcyBmb3Igc3VnZ2VzdGlvbiBsb2dpYyBiZWxvd1xuICAgICAgY29uc3QgaGFzSW1hZ2VzID0gT2JqZWN0LnZhbHVlcyhwYXN0ZWRDb250ZW50cykuc29tZShcbiAgICAgICAgYyA9PiBjLnR5cGUgPT09ICdpbWFnZScsXG4gICAgICApXG5cbiAgICAgIC8vIElmIGlucHV0IGlzIGVtcHR5IE9SIG1hdGNoZXMgdGhlIHN1Z2dlc3Rpb24sIHN1Ym1pdCBpdFxuICAgICAgLy8gQnV0IGlmIHRoZXJlIGFyZSBpbWFnZXMgYXR0YWNoZWQsIGRvbid0IGF1dG8tYWNjZXB0IHRoZSBzdWdnZXN0aW9uIC1cbiAgICAgIC8vIHRoZSB1c2VyIHdhbnRzIHRvIHN1Ym1pdCBqdXN0IHRoZSBpbWFnZShzKS5cbiAgICAgIC8vIE9ubHkgaW4gbGVhZGVyIHZpZXcg4oCUIHByb21wdFN1Z2dlc3Rpb24gaXMgbGVhZGVyLWNvbnRleHQsIG5vdCB0ZWFtbWF0ZS5cbiAgICAgIGNvbnN0IHN1Z2dlc3Rpb25UZXh0ID0gcHJvbXB0U3VnZ2VzdGlvblN0YXRlLnRleHRcbiAgICAgIGNvbnN0IGlucHV0TWF0Y2hlc1N1Z2dlc3Rpb24gPVxuICAgICAgICBpbnB1dFBhcmFtLnRyaW0oKSA9PT0gJycgfHwgaW5wdXRQYXJhbSA9PT0gc3VnZ2VzdGlvblRleHRcbiAgICAgIGlmIChcbiAgICAgICAgaW5wdXRNYXRjaGVzU3VnZ2VzdGlvbiAmJlxuICAgICAgICBzdWdnZXN0aW9uVGV4dCAmJlxuICAgICAgICAhaGFzSW1hZ2VzICYmXG4gICAgICAgICFzdGF0ZS52aWV3aW5nQWdlbnRUYXNrSWRcbiAgICAgICkge1xuICAgICAgICAvLyBJZiBzcGVjdWxhdGlvbiBpcyBhY3RpdmUsIGluamVjdCBtZXNzYWdlcyBpbW1lZGlhdGVseSBhcyB0aGV5IHN0cmVhbVxuICAgICAgICBpZiAoc3BlY3VsYXRpb24uc3RhdHVzID09PSAnYWN0aXZlJykge1xuICAgICAgICAgIG1hcmtBY2NlcHRlZCgpXG4gICAgICAgICAgLy8gc2tpcFJlc2V0OiByZXNldFN1Z2dlc3Rpb24gd291bGQgYWJvcnQgdGhlIHNwZWN1bGF0aW9uIGJlZm9yZSB3ZSBhY2NlcHQgaXRcbiAgICAgICAgICBsb2dPdXRjb21lQXRTdWJtaXNzaW9uKHN1Z2dlc3Rpb25UZXh0LCB7IHNraXBSZXNldDogdHJ1ZSB9KVxuXG4gICAgICAgICAgdm9pZCBvblN1Ym1pdFByb3AoXG4gICAgICAgICAgICBzdWdnZXN0aW9uVGV4dCxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0LFxuICAgICAgICAgICAgICBjbGVhckJ1ZmZlcixcbiAgICAgICAgICAgICAgcmVzZXRIaXN0b3J5LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgc3RhdGU6IHNwZWN1bGF0aW9uLFxuICAgICAgICAgICAgICBzcGVjdWxhdGlvblNlc3Npb25UaW1lU2F2ZWRNczogc3BlY3VsYXRpb25TZXNzaW9uVGltZVNhdmVkTXMsXG4gICAgICAgICAgICAgIHNldEFwcFN0YXRlLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICApXG4gICAgICAgICAgcmV0dXJuIC8vIFNraXAgbm9ybWFsIHF1ZXJ5IC0gc3BlY3VsYXRpb24gaGFuZGxlZCBpdFxuICAgICAgICB9XG5cbiAgICAgICAgLy8gUmVndWxhciBzdWdnZXN0aW9uIGFjY2VwdGFuY2UgKHJlcXVpcmVzIHNob3duQXQgPiAwKVxuICAgICAgICBpZiAocHJvbXB0U3VnZ2VzdGlvblN0YXRlLnNob3duQXQgPiAwKSB7XG4gICAgICAgICAgbWFya0FjY2VwdGVkKClcbiAgICAgICAgICBpbnB1dFBhcmFtID0gc3VnZ2VzdGlvblRleHRcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBIYW5kbGUgQG5hbWUgZGlyZWN0IG1lc3NhZ2VcbiAgICAgIGlmIChpc0FnZW50U3dhcm1zRW5hYmxlZCgpKSB7XG4gICAgICAgIGNvbnN0IGRpcmVjdE1lc3NhZ2UgPSBwYXJzZURpcmVjdE1lbWJlck1lc3NhZ2UoaW5wdXRQYXJhbSlcbiAgICAgICAgaWYgKGRpcmVjdE1lc3NhZ2UpIHtcbiAgICAgICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzZW5kRGlyZWN0TWVtYmVyTWVzc2FnZShcbiAgICAgICAgICAgIGRpcmVjdE1lc3NhZ2UucmVjaXBpZW50TmFtZSxcbiAgICAgICAgICAgIGRpcmVjdE1lc3NhZ2UubWVzc2FnZSxcbiAgICAgICAgICAgIHRlYW1Db250ZXh0LFxuICAgICAgICAgICAgd3JpdGVUb01haWxib3gsXG4gICAgICAgICAgKVxuXG4gICAgICAgICAgaWYgKHJlc3VsdC5zdWNjZXNzKSB7XG4gICAgICAgICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICAgICAgICBrZXk6ICdkaXJlY3QtbWVzc2FnZS1zZW50JyxcbiAgICAgICAgICAgICAgdGV4dDogYFNlbnQgdG8gQCR7cmVzdWx0LnJlY2lwaWVudE5hbWV9YCxcbiAgICAgICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgICAgICB0aW1lb3V0TXM6IDMwMDAsXG4gICAgICAgICAgICB9KVxuICAgICAgICAgICAgdHJhY2tBbmRTZXRJbnB1dCgnJylcbiAgICAgICAgICAgIHNldEN1cnNvck9mZnNldCgwKVxuICAgICAgICAgICAgY2xlYXJCdWZmZXIoKVxuICAgICAgICAgICAgcmVzZXRIaXN0b3J5KClcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH0gZWxzZSBpZiAocmVzdWx0LmVycm9yID09PSAnbm9fdGVhbV9jb250ZXh0Jykge1xuICAgICAgICAgICAgLy8gTm8gdGVhbSBjb250ZXh0IC0gZmFsbCB0aHJvdWdoIHRvIG5vcm1hbCBwcm9tcHQgc3VibWlzc2lvblxuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBVbmtub3duIHJlY2lwaWVudCAtIGZhbGwgdGhyb3VnaCB0byBub3JtYWwgcHJvbXB0IHN1Ym1pc3Npb25cbiAgICAgICAgICAgIC8vIFRoaXMgYWxsb3dzIGUuZy4gXCJAdXRpbHMgZXhwbGFpbiB0aGlzIGNvZGVcIiB0byBiZSBzZW50IGFzIGEgcHJvbXB0XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEFsbG93IHN1Ym1pc3Npb24gaWYgdGhlcmUgYXJlIGltYWdlcyBhdHRhY2hlZCwgZXZlbiB3aXRob3V0IHRleHRcbiAgICAgIGlmIChpbnB1dFBhcmFtLnRyaW0oKSA9PT0gJycgJiYgIWhhc0ltYWdlcykge1xuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gUHJvbXB0SW5wdXQgVVg6IENoZWNrIGlmIHN1Z2dlc3Rpb25zIGRyb3Bkb3duIGlzIHNob3dpbmdcbiAgICAgIC8vIEZvciBkaXJlY3Rvcnkgc3VnZ2VzdGlvbnMsIGFsbG93IHN1Ym1pc3Npb24gKFRhYiBpcyB1c2VkIGZvciBjb21wbGV0aW9uKVxuICAgICAgY29uc3QgaGFzRGlyZWN0b3J5U3VnZ2VzdGlvbnMgPVxuICAgICAgICBzdWdnZXN0aW9uc1N0YXRlLnN1Z2dlc3Rpb25zLmxlbmd0aCA+IDAgJiZcbiAgICAgICAgc3VnZ2VzdGlvbnNTdGF0ZS5zdWdnZXN0aW9ucy5ldmVyeShzID0+IHMuZGVzY3JpcHRpb24gPT09ICdkaXJlY3RvcnknKVxuXG4gICAgICBpZiAoXG4gICAgICAgIHN1Z2dlc3Rpb25zU3RhdGUuc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCAmJlxuICAgICAgICAhaXNTdWJtaXR0aW5nU2xhc2hDb21tYW5kICYmXG4gICAgICAgICFoYXNEaXJlY3RvcnlTdWdnZXN0aW9uc1xuICAgICAgKSB7XG4gICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgICBgW29uU3VibWl0XSBlYXJseSByZXR1cm46IHN1Z2dlc3Rpb25zIHNob3dpbmcgKGNvdW50PSR7c3VnZ2VzdGlvbnNTdGF0ZS5zdWdnZXN0aW9ucy5sZW5ndGh9KWAsXG4gICAgICAgIClcbiAgICAgICAgcmV0dXJuIC8vIERvbid0IHN1Ym1pdCwgdXNlciBuZWVkcyB0byBjbGVhciBzdWdnZXN0aW9ucyBmaXJzdFxuICAgICAgfVxuXG4gICAgICAvLyBMb2cgc3VnZ2VzdGlvbiBvdXRjb21lIGlmIG9uZSBleGlzdHNcbiAgICAgIGlmIChwcm9tcHRTdWdnZXN0aW9uU3RhdGUudGV4dCAmJiBwcm9tcHRTdWdnZXN0aW9uU3RhdGUuc2hvd25BdCA+IDApIHtcbiAgICAgICAgbG9nT3V0Y29tZUF0U3VibWlzc2lvbihpbnB1dFBhcmFtKVxuICAgICAgfVxuXG4gICAgICAvLyBDbGVhciBzdGFzaCBoaW50IG5vdGlmaWNhdGlvbiBvbiBzdWJtaXRcbiAgICAgIHJlbW92ZU5vdGlmaWNhdGlvbignc3Rhc2gtaGludCcpXG5cbiAgICAgIC8vIFJvdXRlIGlucHV0IHRvIHZpZXdlZCBhZ2VudCAoaW4tcHJvY2VzcyB0ZWFtbWF0ZSBvciBuYW1lZCBsb2NhbF9hZ2VudCkuXG4gICAgICBjb25zdCBhY3RpdmVBZ2VudCA9IGdldEFjdGl2ZUFnZW50Rm9ySW5wdXQoc3RvcmUuZ2V0U3RhdGUoKSlcbiAgICAgIGlmIChhY3RpdmVBZ2VudC50eXBlICE9PSAnbGVhZGVyJyAmJiBvbkFnZW50U3VibWl0KSB7XG4gICAgICAgIGxvZ0V2ZW50KCd0ZW5ndV90cmFuc2NyaXB0X2lucHV0X3RvX3RlYW1tYXRlJywge30pXG4gICAgICAgIGF3YWl0IG9uQWdlbnRTdWJtaXQoaW5wdXRQYXJhbSwgYWN0aXZlQWdlbnQudGFzaywge1xuICAgICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgICBjbGVhckJ1ZmZlcixcbiAgICAgICAgICByZXNldEhpc3RvcnksXG4gICAgICAgIH0pXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBOb3JtYWwgbGVhZGVyIHN1Ym1pc3Npb25cbiAgICAgIGF3YWl0IG9uU3VibWl0UHJvcChpbnB1dFBhcmFtLCB7XG4gICAgICAgIHNldEN1cnNvck9mZnNldCxcbiAgICAgICAgY2xlYXJCdWZmZXIsXG4gICAgICAgIHJlc2V0SGlzdG9yeSxcbiAgICAgIH0pXG4gICAgfSxcbiAgICBbXG4gICAgICBwcm9tcHRTdWdnZXN0aW9uU3RhdGUsXG4gICAgICBzcGVjdWxhdGlvbixcbiAgICAgIHNwZWN1bGF0aW9uU2Vzc2lvblRpbWVTYXZlZE1zLFxuICAgICAgdGVhbUNvbnRleHQsXG4gICAgICBzdG9yZSxcbiAgICAgIGZvb3Rlckl0ZW1zLFxuICAgICAgc3VnZ2VzdGlvbnNTdGF0ZS5zdWdnZXN0aW9ucyxcbiAgICAgIG9uU3VibWl0UHJvcCxcbiAgICAgIG9uQWdlbnRTdWJtaXQsXG4gICAgICBjbGVhckJ1ZmZlcixcbiAgICAgIHJlc2V0SGlzdG9yeSxcbiAgICAgIGxvZ091dGNvbWVBdFN1Ym1pc3Npb24sXG4gICAgICBzZXRBcHBTdGF0ZSxcbiAgICAgIG1hcmtBY2NlcHRlZCxcbiAgICAgIHBhc3RlZENvbnRlbnRzLFxuICAgICAgcmVtb3ZlTm90aWZpY2F0aW9uLFxuICAgIF0sXG4gIClcblxuICBjb25zdCB7XG4gICAgc3VnZ2VzdGlvbnMsXG4gICAgc2VsZWN0ZWRTdWdnZXN0aW9uLFxuICAgIGNvbW1hbmRBcmd1bWVudEhpbnQsXG4gICAgaW5saW5lR2hvc3RUZXh0LFxuICAgIG1heENvbHVtbldpZHRoLFxuICB9ID0gdXNlVHlwZWFoZWFkKHtcbiAgICBjb21tYW5kcyxcbiAgICBvbklucHV0Q2hhbmdlOiB0cmFja0FuZFNldElucHV0LFxuICAgIG9uU3VibWl0LFxuICAgIHNldEN1cnNvck9mZnNldCxcbiAgICBpbnB1dCxcbiAgICBjdXJzb3JPZmZzZXQsXG4gICAgbW9kZSxcbiAgICBhZ2VudHMsXG4gICAgc2V0U3VnZ2VzdGlvbnNTdGF0ZSxcbiAgICBzdWdnZXN0aW9uc1N0YXRlLFxuICAgIHN1cHByZXNzU3VnZ2VzdGlvbnM6IGlzU2VhcmNoaW5nSGlzdG9yeSB8fCBoaXN0b3J5SW5kZXggPiAwLFxuICAgIG1hcmtBY2NlcHRlZCxcbiAgICBvbk1vZGVDaGFuZ2UsXG4gIH0pXG5cbiAgLy8gVHJhY2sgaWYgcHJvbXB0IHN1Z2dlc3Rpb24gc2hvdWxkIGJlIHNob3duIChjb21wdXRlZCBsYXRlciB3aXRoIHRlcm1pbmFsIHdpZHRoKS5cbiAgLy8gSGlkZGVuIGluIHRlYW1tYXRlIHZpZXcg4oCUIHN1Z2dlc3Rpb24gaXMgbGVhZGVyLWNvbnRleHQgb25seS5cbiAgY29uc3Qgc2hvd1Byb21wdFN1Z2dlc3Rpb24gPVxuICAgIG1vZGUgPT09ICdwcm9tcHQnICYmXG4gICAgc3VnZ2VzdGlvbnMubGVuZ3RoID09PSAwICYmXG4gICAgcHJvbXB0U3VnZ2VzdGlvbiAmJlxuICAgICF2aWV3aW5nQWdlbnRUYXNrSWRcbiAgaWYgKHNob3dQcm9tcHRTdWdnZXN0aW9uKSB7XG4gICAgbWFya1Nob3duKClcbiAgfVxuXG4gIC8vIElmIHN1Z2dlc3Rpb24gd2FzIGdlbmVyYXRlZCBidXQgY2FuJ3QgYmUgc2hvd24gZHVlIHRvIHRpbWluZywgbG9nIHN1cHByZXNzaW9uLlxuICAvLyBFeGNsdWRlIHRlYW1tYXRlIHZpZXc6IG1hcmtTaG93bigpIGlzIGdhdGVkIGFib3ZlLCBzbyBzaG93bkF0IHN0YXlzIDAgdGhlcmUg4oCUXG4gIC8vIGJ1dCB0aGF0J3Mgbm90IGEgdGltaW5nIGZhaWx1cmUsIHRoZSBzdWdnZXN0aW9uIGlzIHZhbGlkIHdoZW4gcmV0dXJuaW5nIHRvIGxlYWRlci5cbiAgaWYgKFxuICAgIHByb21wdFN1Z2dlc3Rpb25TdGF0ZS50ZXh0ICYmXG4gICAgIXByb21wdFN1Z2dlc3Rpb24gJiZcbiAgICBwcm9tcHRTdWdnZXN0aW9uU3RhdGUuc2hvd25BdCA9PT0gMCAmJlxuICAgICF2aWV3aW5nQWdlbnRUYXNrSWRcbiAgKSB7XG4gICAgbG9nU3VnZ2VzdGlvblN1cHByZXNzZWQoJ3RpbWluZycsIHByb21wdFN1Z2dlc3Rpb25TdGF0ZS50ZXh0KVxuICAgIHNldEFwcFN0YXRlKHByZXYgPT4gKHtcbiAgICAgIC4uLnByZXYsXG4gICAgICBwcm9tcHRTdWdnZXN0aW9uOiB7XG4gICAgICAgIHRleHQ6IG51bGwsXG4gICAgICAgIHByb21wdElkOiBudWxsLFxuICAgICAgICBzaG93bkF0OiAwLFxuICAgICAgICBhY2NlcHRlZEF0OiAwLFxuICAgICAgICBnZW5lcmF0aW9uUmVxdWVzdElkOiBudWxsLFxuICAgICAgfSxcbiAgICB9KSlcbiAgfVxuXG4gIGZ1bmN0aW9uIG9uSW1hZ2VQYXN0ZShcbiAgICBpbWFnZTogc3RyaW5nLFxuICAgIG1lZGlhVHlwZT86IHN0cmluZyxcbiAgICBmaWxlbmFtZT86IHN0cmluZyxcbiAgICBkaW1lbnNpb25zPzogSW1hZ2VEaW1lbnNpb25zLFxuICAgIHNvdXJjZVBhdGg/OiBzdHJpbmcsXG4gICkge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV9wYXN0ZV9pbWFnZScsIHt9KVxuICAgIG9uTW9kZUNoYW5nZSgncHJvbXB0JylcblxuICAgIGNvbnN0IHBhc3RlSWQgPSBuZXh0UGFzdGVJZFJlZi5jdXJyZW50KytcblxuICAgIGNvbnN0IG5ld0NvbnRlbnQ6IFBhc3RlZENvbnRlbnQgPSB7XG4gICAgICBpZDogcGFzdGVJZCxcbiAgICAgIHR5cGU6ICdpbWFnZScsXG4gICAgICBjb250ZW50OiBpbWFnZSxcbiAgICAgIG1lZGlhVHlwZTogbWVkaWFUeXBlIHx8ICdpbWFnZS9wbmcnLCAvLyBkZWZhdWx0IHRvIFBORyBpZiBub3QgcHJvdmlkZWRcbiAgICAgIGZpbGVuYW1lOiBmaWxlbmFtZSB8fCAnUGFzdGVkIGltYWdlJyxcbiAgICAgIGRpbWVuc2lvbnMsXG4gICAgICBzb3VyY2VQYXRoLFxuICAgIH1cblxuICAgIC8vIENhY2hlIHBhdGggaW1tZWRpYXRlbHkgKGZhc3QpIHNvIGxpbmtzIHdvcmsgb24gcmVuZGVyXG4gICAgY2FjaGVJbWFnZVBhdGgobmV3Q29udGVudClcblxuICAgIC8vIFN0b3JlIGltYWdlIHRvIGRpc2sgaW4gYmFja2dyb3VuZFxuICAgIHZvaWQgc3RvcmVJbWFnZShuZXdDb250ZW50KVxuXG4gICAgLy8gVXBkYXRlIFVJXG4gICAgc2V0UGFzdGVkQ29udGVudHMocHJldiA9PiAoeyAuLi5wcmV2LCBbcGFzdGVJZF06IG5ld0NvbnRlbnQgfSkpXG4gICAgLy8gTXVsdGktaW1hZ2UgcGFzdGUgY2FsbHMgb25JbWFnZVBhc3RlIGluIGEgbG9vcC4gSWYgdGhlIHJlZiBpcyBhbHJlYWR5XG4gICAgLy8gYXJtZWQsIHRoZSBwcmV2aW91cyBwaWxsJ3MgbGF6eSBzcGFjZSBmaXJlcyBub3cgKGJlZm9yZSB0aGlzIHBpbGwpXG4gICAgLy8gcmF0aGVyIHRoYW4gYmVpbmcgbG9zdC5cbiAgICBjb25zdCBwcmVmaXggPSBwZW5kaW5nU3BhY2VBZnRlclBpbGxSZWYuY3VycmVudCA/ICcgJyA6ICcnXG4gICAgaW5zZXJ0VGV4dEF0Q3Vyc29yKHByZWZpeCArIGZvcm1hdEltYWdlUmVmKHBhc3RlSWQpKVxuICAgIHBlbmRpbmdTcGFjZUFmdGVyUGlsbFJlZi5jdXJyZW50ID0gdHJ1ZVxuICB9XG5cbiAgLy8gUHJ1bmUgaW1hZ2VzIHdob3NlIFtJbWFnZSAjTl0gcGxhY2Vob2xkZXIgaXMgbm8gbG9uZ2VyIGluIHRoZSBpbnB1dCB0ZXh0LlxuICAvLyBDb3ZlcnMgcGlsbCBiYWNrc3BhY2UsIEN0cmwrVSwgY2hhci1ieS1jaGFyIGRlbGV0aW9uIOKAlCBhbnkgZWRpdCB0aGF0IGRyb3BzXG4gIC8vIHRoZSByZWYuIG9uSW1hZ2VQYXN0ZSBiYXRjaGVzIHNldFBhc3RlZENvbnRlbnRzICsgaW5zZXJ0VGV4dEF0Q3Vyc29yIGluIHRoZVxuICAvLyBzYW1lIGV2ZW50LCBzbyB0aGlzIGVmZmVjdCBzZWVzIHRoZSBwbGFjZWhvbGRlciBhbHJlYWR5IHByZXNlbnQuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc3QgcmVmZXJlbmNlZElkcyA9IG5ldyBTZXQocGFyc2VSZWZlcmVuY2VzKGlucHV0KS5tYXAociA9PiByLmlkKSlcbiAgICBzZXRQYXN0ZWRDb250ZW50cyhwcmV2ID0+IHtcbiAgICAgIGNvbnN0IG9ycGhhbmVkID0gT2JqZWN0LnZhbHVlcyhwcmV2KS5maWx0ZXIoXG4gICAgICAgIGMgPT4gYy50eXBlID09PSAnaW1hZ2UnICYmICFyZWZlcmVuY2VkSWRzLmhhcyhjLmlkKSxcbiAgICAgIClcbiAgICAgIGlmIChvcnBoYW5lZC5sZW5ndGggPT09IDApIHJldHVybiBwcmV2XG4gICAgICBjb25zdCBuZXh0ID0geyAuLi5wcmV2IH1cbiAgICAgIGZvciAoY29uc3QgaW1nIG9mIG9ycGhhbmVkKSBkZWxldGUgbmV4dFtpbWcuaWRdXG4gICAgICByZXR1cm4gbmV4dFxuICAgIH0pXG4gIH0sIFtpbnB1dCwgc2V0UGFzdGVkQ29udGVudHNdKVxuXG4gIGZ1bmN0aW9uIG9uVGV4dFBhc3RlKHJhd1RleHQ6IHN0cmluZykge1xuICAgIHBlbmRpbmdTcGFjZUFmdGVyUGlsbFJlZi5jdXJyZW50ID0gZmFsc2VcbiAgICAvLyBDbGVhbiB1cCBwYXN0ZWQgdGV4dCAtIHN0cmlwIEFOU0kgZXNjYXBlIGNvZGVzIGFuZCBub3JtYWxpemUgbGluZSBlbmRpbmdzIGFuZCB0YWJzXG4gICAgbGV0IHRleHQgPSBzdHJpcEFuc2kocmF3VGV4dCkucmVwbGFjZSgvXFxyL2csICdcXG4nKS5yZXBsYWNlQWxsKCdcXHQnLCAnICAgICcpXG5cbiAgICAvLyBNYXRjaCB0eXBlZC9hdXRvLXN1Z2dlc3Q6IGAhY21kYCBwYXN0ZWQgaW50byBlbXB0eSBpbnB1dCBlbnRlcnMgYmFzaCBtb2RlLlxuICAgIGlmIChpbnB1dC5sZW5ndGggPT09IDApIHtcbiAgICAgIGNvbnN0IHBhc3RlZE1vZGUgPSBnZXRNb2RlRnJvbUlucHV0KHRleHQpXG4gICAgICBpZiAocGFzdGVkTW9kZSAhPT0gJ3Byb21wdCcpIHtcbiAgICAgICAgb25Nb2RlQ2hhbmdlKHBhc3RlZE1vZGUpXG4gICAgICAgIHRleHQgPSBnZXRWYWx1ZUZyb21JbnB1dCh0ZXh0KVxuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IG51bUxpbmVzID0gZ2V0UGFzdGVkVGV4dFJlZk51bUxpbmVzKHRleHQpXG4gICAgLy8gTGltaXQgdGhlIG51bWJlciBvZiBsaW5lcyB0byBzaG93IGluIHRoZSBpbnB1dFxuICAgIC8vIElmIHRoZSBvdmVyYWxsIGxheW91dCBpcyB0b28gaGlnaCB0aGVuIEluayB3aWxsIHJlcGFpbnRcbiAgICAvLyB0aGUgZW50aXJlIHRlcm1pbmFsLlxuICAgIC8vIFRoZSBhY3R1YWwgcmVxdWlyZWQgaGVpZ2h0IGlzIGRlcGVuZGVudCBvbiB0aGUgY29udGVudCwgdGhpc1xuICAgIC8vIGlzIGp1c3QgYW4gZXN0aW1hdGUuXG4gICAgY29uc3QgbWF4TGluZXMgPSBNYXRoLm1pbihyb3dzIC0gMTAsIDIpXG5cbiAgICAvLyBVc2Ugc3BlY2lhbCBoYW5kbGluZyBmb3IgbG9uZyBwYXN0ZWQgdGV4dCAoPlBBU1RFX1RIUkVTSE9MRCBjaGFycylcbiAgICAvLyBvciBpZiBpdCBleGNlZWRzIHRoZSBudW1iZXIgb2YgbGluZXMgd2Ugd2FudCB0byBzaG93XG4gICAgaWYgKHRleHQubGVuZ3RoID4gUEFTVEVfVEhSRVNIT0xEIHx8IG51bUxpbmVzID4gbWF4TGluZXMpIHtcbiAgICAgIGNvbnN0IHBhc3RlSWQgPSBuZXh0UGFzdGVJZFJlZi5jdXJyZW50KytcblxuICAgICAgY29uc3QgbmV3Q29udGVudDogUGFzdGVkQ29udGVudCA9IHtcbiAgICAgICAgaWQ6IHBhc3RlSWQsXG4gICAgICAgIHR5cGU6ICd0ZXh0JyxcbiAgICAgICAgY29udGVudDogdGV4dCxcbiAgICAgIH1cblxuICAgICAgc2V0UGFzdGVkQ29udGVudHMocHJldiA9PiAoeyAuLi5wcmV2LCBbcGFzdGVJZF06IG5ld0NvbnRlbnQgfSkpXG5cbiAgICAgIGluc2VydFRleHRBdEN1cnNvcihmb3JtYXRQYXN0ZWRUZXh0UmVmKHBhc3RlSWQsIG51bUxpbmVzKSlcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gRm9yIHNob3J0ZXIgcGFzdGVzLCBqdXN0IGluc2VydCB0aGUgdGV4dCBub3JtYWxseVxuICAgICAgaW5zZXJ0VGV4dEF0Q3Vyc29yKHRleHQpXG4gICAgfVxuICB9XG5cbiAgY29uc3QgbGF6eVNwYWNlSW5wdXRGaWx0ZXIgPSB1c2VDYWxsYmFjayhcbiAgICAoaW5wdXQ6IHN0cmluZywga2V5OiBLZXkpOiBzdHJpbmcgPT4ge1xuICAgICAgaWYgKCFwZW5kaW5nU3BhY2VBZnRlclBpbGxSZWYuY3VycmVudCkgcmV0dXJuIGlucHV0XG4gICAgICBwZW5kaW5nU3BhY2VBZnRlclBpbGxSZWYuY3VycmVudCA9IGZhbHNlXG4gICAgICBpZiAoaXNOb25TcGFjZVByaW50YWJsZShpbnB1dCwga2V5KSkgcmV0dXJuICcgJyArIGlucHV0XG4gICAgICByZXR1cm4gaW5wdXRcbiAgICB9LFxuICAgIFtdLFxuICApXG5cbiAgZnVuY3Rpb24gaW5zZXJ0VGV4dEF0Q3Vyc29yKHRleHQ6IHN0cmluZykge1xuICAgIC8vIFB1c2ggY3VycmVudCBzdGF0ZSB0byBidWZmZXIgYmVmb3JlIGluc2VydGluZ1xuICAgIHB1c2hUb0J1ZmZlcihpbnB1dCwgY3Vyc29yT2Zmc2V0LCBwYXN0ZWRDb250ZW50cylcblxuICAgIGNvbnN0IG5ld0lucHV0ID1cbiAgICAgIGlucHV0LnNsaWNlKDAsIGN1cnNvck9mZnNldCkgKyB0ZXh0ICsgaW5wdXQuc2xpY2UoY3Vyc29yT2Zmc2V0KVxuICAgIHRyYWNrQW5kU2V0SW5wdXQobmV3SW5wdXQpXG4gICAgc2V0Q3Vyc29yT2Zmc2V0KGN1cnNvck9mZnNldCArIHRleHQubGVuZ3RoKVxuICB9XG5cbiAgY29uc3QgZG91YmxlUHJlc3NFc2NGcm9tRW1wdHkgPSB1c2VEb3VibGVQcmVzcyhcbiAgICAoKSA9PiB7fSxcbiAgICAoKSA9PiBvblNob3dNZXNzYWdlU2VsZWN0b3IoKSxcbiAgKVxuXG4gIC8vIEZ1bmN0aW9uIHRvIGdldCB0aGUgcXVldWVkIGNvbW1hbmQgZm9yIGVkaXRpbmcuIFJldHVybnMgdHJ1ZSBpZiBjb21tYW5kcyB3ZXJlIHBvcHBlZC5cbiAgY29uc3QgcG9wQWxsQ29tbWFuZHNGcm9tUXVldWUgPSB1c2VDYWxsYmFjaygoKTogYm9vbGVhbiA9PiB7XG4gICAgY29uc3QgcmVzdWx0ID0gcG9wQWxsRWRpdGFibGUoaW5wdXQsIGN1cnNvck9mZnNldClcbiAgICBpZiAoIXJlc3VsdCkge1xuICAgICAgcmV0dXJuIGZhbHNlXG4gICAgfVxuXG4gICAgdHJhY2tBbmRTZXRJbnB1dChyZXN1bHQudGV4dClcbiAgICBvbk1vZGVDaGFuZ2UoJ3Byb21wdCcpIC8vIEFsd2F5cyBwcm9tcHQgbW9kZSBmb3IgcXVldWVkIGNvbW1hbmRzXG4gICAgc2V0Q3Vyc29yT2Zmc2V0KHJlc3VsdC5jdXJzb3JPZmZzZXQpXG5cbiAgICAvLyBSZXN0b3JlIGltYWdlcyBmcm9tIHF1ZXVlZCBjb21tYW5kcyB0byBwYXN0ZWRDb250ZW50c1xuICAgIGlmIChyZXN1bHQuaW1hZ2VzLmxlbmd0aCA+IDApIHtcbiAgICAgIHNldFBhc3RlZENvbnRlbnRzKHByZXYgPT4ge1xuICAgICAgICBjb25zdCBuZXdDb250ZW50cyA9IHsgLi4ucHJldiB9XG4gICAgICAgIGZvciAoY29uc3QgaW1hZ2Ugb2YgcmVzdWx0LmltYWdlcykge1xuICAgICAgICAgIG5ld0NvbnRlbnRzW2ltYWdlLmlkXSA9IGltYWdlXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5ld0NvbnRlbnRzXG4gICAgICB9KVxuICAgIH1cblxuICAgIHJldHVybiB0cnVlXG4gIH0sIFt0cmFja0FuZFNldElucHV0LCBvbk1vZGVDaGFuZ2UsIGlucHV0LCBjdXJzb3JPZmZzZXQsIHNldFBhc3RlZENvbnRlbnRzXSlcblxuICAvLyBJbnNlcnQgdGhlIGF0LW1lbnRpb25lZCByZWZlcmVuY2UgKHRoZSBmaWxlIGFuZCwgb3B0aW9uYWxseSwgYSBsaW5lIHJhbmdlKSB3aGVuXG4gIC8vIHdlIHJlY2VpdmUgYW4gYXQtbWVudGlvbmVkIG5vdGlmaWNhdGlvbiB0aGUgSURFLlxuICBjb25zdCBvbklkZUF0TWVudGlvbmVkID0gZnVuY3Rpb24gKGF0TWVudGlvbmVkOiBJREVBdE1lbnRpb25lZCkge1xuICAgIGxvZ0V2ZW50KCd0ZW5ndV9leHRfYXRfbWVudGlvbmVkJywge30pXG4gICAgbGV0IGF0TWVudGlvbmVkVGV4dDogc3RyaW5nXG4gICAgY29uc3QgcmVsYXRpdmVQYXRoID0gcGF0aC5yZWxhdGl2ZShnZXRDd2QoKSwgYXRNZW50aW9uZWQuZmlsZVBhdGgpXG4gICAgaWYgKGF0TWVudGlvbmVkLmxpbmVTdGFydCAmJiBhdE1lbnRpb25lZC5saW5lRW5kKSB7XG4gICAgICBhdE1lbnRpb25lZFRleHQgPVxuICAgICAgICBhdE1lbnRpb25lZC5saW5lU3RhcnQgPT09IGF0TWVudGlvbmVkLmxpbmVFbmRcbiAgICAgICAgICA/IGBAJHtyZWxhdGl2ZVBhdGh9I0wke2F0TWVudGlvbmVkLmxpbmVTdGFydH0gYFxuICAgICAgICAgIDogYEAke3JlbGF0aXZlUGF0aH0jTCR7YXRNZW50aW9uZWQubGluZVN0YXJ0fS0ke2F0TWVudGlvbmVkLmxpbmVFbmR9IGBcbiAgICB9IGVsc2Uge1xuICAgICAgYXRNZW50aW9uZWRUZXh0ID0gYEAke3JlbGF0aXZlUGF0aH0gYFxuICAgIH1cbiAgICBjb25zdCBjdXJzb3JDaGFyID0gaW5wdXRbY3Vyc29yT2Zmc2V0IC0gMV0gPz8gJyAnXG4gICAgaWYgKCEvXFxzLy50ZXN0KGN1cnNvckNoYXIpKSB7XG4gICAgICBhdE1lbnRpb25lZFRleHQgPSBgICR7YXRNZW50aW9uZWRUZXh0fWBcbiAgICB9XG4gICAgaW5zZXJ0VGV4dEF0Q3Vyc29yKGF0TWVudGlvbmVkVGV4dClcbiAgfVxuICB1c2VJZGVBdE1lbnRpb25lZChtY3BDbGllbnRzLCBvbklkZUF0TWVudGlvbmVkKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6dW5kbyAtIHVuZG8gbGFzdCBlZGl0XG4gIGNvbnN0IGhhbmRsZVVuZG8gPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgaWYgKGNhblVuZG8pIHtcbiAgICAgIGNvbnN0IHByZXZpb3VzU3RhdGUgPSB1bmRvKClcbiAgICAgIGlmIChwcmV2aW91c1N0YXRlKSB7XG4gICAgICAgIHRyYWNrQW5kU2V0SW5wdXQocHJldmlvdXNTdGF0ZS50ZXh0KVxuICAgICAgICBzZXRDdXJzb3JPZmZzZXQocHJldmlvdXNTdGF0ZS5jdXJzb3JPZmZzZXQpXG4gICAgICAgIHNldFBhc3RlZENvbnRlbnRzKHByZXZpb3VzU3RhdGUucGFzdGVkQ29udGVudHMpXG4gICAgICB9XG4gICAgfVxuICB9LCBbY2FuVW5kbywgdW5kbywgdHJhY2tBbmRTZXRJbnB1dCwgc2V0UGFzdGVkQ29udGVudHNdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6bmV3bGluZSAtIGluc2VydCBhIG5ld2xpbmUgYXQgdGhlIGN1cnNvciBwb3NpdGlvblxuICBjb25zdCBoYW5kbGVOZXdsaW5lID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHB1c2hUb0J1ZmZlcihpbnB1dCwgY3Vyc29yT2Zmc2V0LCBwYXN0ZWRDb250ZW50cylcbiAgICBjb25zdCBuZXdJbnB1dCA9XG4gICAgICBpbnB1dC5zbGljZSgwLCBjdXJzb3JPZmZzZXQpICsgJ1xcbicgKyBpbnB1dC5zbGljZShjdXJzb3JPZmZzZXQpXG4gICAgdHJhY2tBbmRTZXRJbnB1dChuZXdJbnB1dClcbiAgICBzZXRDdXJzb3JPZmZzZXQoY3Vyc29yT2Zmc2V0ICsgMSlcbiAgfSwgW1xuICAgIGlucHV0LFxuICAgIGN1cnNvck9mZnNldCxcbiAgICB0cmFja0FuZFNldElucHV0LFxuICAgIHNldEN1cnNvck9mZnNldCxcbiAgICBwdXNoVG9CdWZmZXIsXG4gICAgcGFzdGVkQ29udGVudHMsXG4gIF0pXG5cbiAgLy8gSGFuZGxlciBmb3IgY2hhdDpleHRlcm5hbEVkaXRvciAtIGVkaXQgaW4gJEVESVRPUlxuICBjb25zdCBoYW5kbGVFeHRlcm5hbEVkaXRvciA9IHVzZUNhbGxiYWNrKGFzeW5jICgpID0+IHtcbiAgICBsb2dFdmVudCgndGVuZ3VfZXh0ZXJuYWxfZWRpdG9yX3VzZWQnLCB7fSlcbiAgICBzZXRJc0V4dGVybmFsRWRpdG9yQWN0aXZlKHRydWUpXG5cbiAgICB0cnkge1xuICAgICAgLy8gUGFzcyBwYXN0ZWRDb250ZW50cyB0byBleHBhbmQgY29sbGFwc2VkIHRleHQgcmVmZXJlbmNlc1xuICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZWRpdFByb21wdEluRWRpdG9yKGlucHV0LCBwYXN0ZWRDb250ZW50cylcblxuICAgICAgaWYgKHJlc3VsdC5lcnJvcikge1xuICAgICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICAgIGtleTogJ2V4dGVybmFsLWVkaXRvci1lcnJvcicsXG4gICAgICAgICAgdGV4dDogcmVzdWx0LmVycm9yLFxuICAgICAgICAgIGNvbG9yOiAnd2FybmluZycsXG4gICAgICAgICAgcHJpb3JpdHk6ICdoaWdoJyxcbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgaWYgKHJlc3VsdC5jb250ZW50ICE9PSBudWxsICYmIHJlc3VsdC5jb250ZW50ICE9PSBpbnB1dCkge1xuICAgICAgICAvLyBQdXNoIGN1cnJlbnQgc3RhdGUgdG8gYnVmZmVyIGJlZm9yZSBtYWtpbmcgY2hhbmdlc1xuICAgICAgICBwdXNoVG9CdWZmZXIoaW5wdXQsIGN1cnNvck9mZnNldCwgcGFzdGVkQ29udGVudHMpXG5cbiAgICAgICAgdHJhY2tBbmRTZXRJbnB1dChyZXN1bHQuY29udGVudClcbiAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KHJlc3VsdC5jb250ZW50Lmxlbmd0aClcbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBFcnJvcikge1xuICAgICAgICBsb2dFcnJvcihlcnIpXG4gICAgICB9XG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICdleHRlcm5hbC1lZGl0b3ItZXJyb3InLFxuICAgICAgICB0ZXh0OiBgRXh0ZXJuYWwgZWRpdG9yIGZhaWxlZDogJHtlcnJvck1lc3NhZ2UoZXJyKX1gLFxuICAgICAgICBjb2xvcjogJ3dhcm5pbmcnLFxuICAgICAgICBwcmlvcml0eTogJ2hpZ2gnLFxuICAgICAgfSlcbiAgICB9IGZpbmFsbHkge1xuICAgICAgc2V0SXNFeHRlcm5hbEVkaXRvckFjdGl2ZShmYWxzZSlcbiAgICB9XG4gIH0sIFtcbiAgICBpbnB1dCxcbiAgICBjdXJzb3JPZmZzZXQsXG4gICAgcGFzdGVkQ29udGVudHMsXG4gICAgcHVzaFRvQnVmZmVyLFxuICAgIHRyYWNrQW5kU2V0SW5wdXQsXG4gICAgYWRkTm90aWZpY2F0aW9uLFxuICBdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6c3Rhc2ggLSBzdGFzaC91bnN0YXNoIHByb21wdFxuICBjb25zdCBoYW5kbGVTdGFzaCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoaW5wdXQudHJpbSgpID09PSAnJyAmJiBzdGFzaGVkUHJvbXB0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIFBvcCBzdGFzaCB3aGVuIGlucHV0IGlzIGVtcHR5XG4gICAgICB0cmFja0FuZFNldElucHV0KHN0YXNoZWRQcm9tcHQudGV4dClcbiAgICAgIHNldEN1cnNvck9mZnNldChzdGFzaGVkUHJvbXB0LmN1cnNvck9mZnNldClcbiAgICAgIHNldFBhc3RlZENvbnRlbnRzKHN0YXNoZWRQcm9tcHQucGFzdGVkQ29udGVudHMpXG4gICAgICBzZXRTdGFzaGVkUHJvbXB0KHVuZGVmaW5lZClcbiAgICB9IGVsc2UgaWYgKGlucHV0LnRyaW0oKSAhPT0gJycpIHtcbiAgICAgIC8vIFB1c2ggdG8gc3Rhc2ggKHNhdmUgdGV4dCwgY3Vyc29yIHBvc2l0aW9uLCBhbmQgcGFzdGVkIGNvbnRlbnRzKVxuICAgICAgc2V0U3Rhc2hlZFByb21wdCh7IHRleHQ6IGlucHV0LCBjdXJzb3JPZmZzZXQsIHBhc3RlZENvbnRlbnRzIH0pXG4gICAgICB0cmFja0FuZFNldElucHV0KCcnKVxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KDApXG4gICAgICBzZXRQYXN0ZWRDb250ZW50cyh7fSlcbiAgICAgIC8vIFRyYWNrIHVzYWdlIGZvciAvZGlzY292ZXIgYW5kIHN0b3Agc2hvd2luZyBoaW50XG4gICAgICBzYXZlR2xvYmFsQ29uZmlnKGMgPT4ge1xuICAgICAgICBpZiAoYy5oYXNVc2VkU3Rhc2gpIHJldHVybiBjXG4gICAgICAgIHJldHVybiB7IC4uLmMsIGhhc1VzZWRTdGFzaDogdHJ1ZSB9XG4gICAgICB9KVxuICAgIH1cbiAgfSwgW1xuICAgIGlucHV0LFxuICAgIGN1cnNvck9mZnNldCxcbiAgICBzdGFzaGVkUHJvbXB0LFxuICAgIHRyYWNrQW5kU2V0SW5wdXQsXG4gICAgc2V0U3Rhc2hlZFByb21wdCxcbiAgICBwYXN0ZWRDb250ZW50cyxcbiAgICBzZXRQYXN0ZWRDb250ZW50cyxcbiAgXSlcblxuICAvLyBIYW5kbGVyIGZvciBjaGF0Om1vZGVsUGlja2VyIC0gdG9nZ2xlIG1vZGVsIHBpY2tlclxuICBjb25zdCBoYW5kbGVNb2RlbFBpY2tlciA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTaG93TW9kZWxQaWNrZXIocHJldiA9PiAhcHJldilcbiAgICBpZiAoaGVscE9wZW4pIHtcbiAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgIH1cbiAgfSwgW2hlbHBPcGVuXSlcblxuICAvLyBIYW5kbGVyIGZvciBjaGF0OmZhc3RNb2RlIC0gdG9nZ2xlIGZhc3QgbW9kZSBwaWNrZXJcbiAgY29uc3QgaGFuZGxlRmFzdE1vZGVQaWNrZXIgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgc2V0U2hvd0Zhc3RNb2RlUGlja2VyKHByZXYgPT4gIXByZXYpXG4gICAgaWYgKGhlbHBPcGVuKSB7XG4gICAgICBzZXRIZWxwT3BlbihmYWxzZSlcbiAgICB9XG4gIH0sIFtoZWxwT3Blbl0pXG5cbiAgLy8gSGFuZGxlciBmb3IgY2hhdDp0aGlua2luZ1RvZ2dsZSAtIHRvZ2dsZSB0aGlua2luZyBtb2RlXG4gIGNvbnN0IGhhbmRsZVRoaW5raW5nVG9nZ2xlID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHNldFNob3dUaGlua2luZ1RvZ2dsZShwcmV2ID0+ICFwcmV2KVxuICAgIGlmIChoZWxwT3Blbikge1xuICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgfVxuICB9LCBbaGVscE9wZW5dKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6Y3ljbGVNb2RlIC0gY3ljbGUgdGhyb3VnaCBwZXJtaXNzaW9uIG1vZGVzXG4gIGNvbnN0IGhhbmRsZUN5Y2xlTW9kZSA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICAvLyBXaGVuIHZpZXdpbmcgYSB0ZWFtbWF0ZSwgY3ljbGUgdGhlaXIgbW9kZSBpbnN0ZWFkIG9mIHRoZSBsZWFkZXInc1xuICAgIGlmIChpc0FnZW50U3dhcm1zRW5hYmxlZCgpICYmIHZpZXdlZFRlYW1tYXRlICYmIHZpZXdpbmdBZ2VudFRhc2tJZCkge1xuICAgICAgY29uc3QgdGVhbW1hdGVDb250ZXh0OiBUb29sUGVybWlzc2lvbkNvbnRleHQgPSB7XG4gICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgbW9kZTogdmlld2VkVGVhbW1hdGUucGVybWlzc2lvbk1vZGUsXG4gICAgICB9XG4gICAgICAvLyBQYXNzIHVuZGVmaW5lZCBmb3IgdGVhbUNvbnRleHQgKHVudXNlZCBidXQga2VwdCBmb3IgQVBJIGNvbXBhdGliaWxpdHkpXG4gICAgICBjb25zdCBuZXh0TW9kZSA9IGdldE5leHRQZXJtaXNzaW9uTW9kZSh0ZWFtbWF0ZUNvbnRleHQsIHVuZGVmaW5lZClcblxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X21vZGVfY3ljbGUnLCB7XG4gICAgICAgIHRvOiBuZXh0TW9kZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcblxuICAgICAgY29uc3QgdGVhbW1hdGVUYXNrSWQgPSB2aWV3aW5nQWdlbnRUYXNrSWRcbiAgICAgIHNldEFwcFN0YXRlKHByZXYgPT4ge1xuICAgICAgICBjb25zdCB0YXNrID0gcHJldi50YXNrc1t0ZWFtbWF0ZVRhc2tJZF1cbiAgICAgICAgaWYgKCF0YXNrIHx8IHRhc2sudHlwZSAhPT0gJ2luX3Byb2Nlc3NfdGVhbW1hdGUnKSB7XG4gICAgICAgICAgcmV0dXJuIHByZXZcbiAgICAgICAgfVxuICAgICAgICBpZiAodGFzay5wZXJtaXNzaW9uTW9kZSA9PT0gbmV4dE1vZGUpIHtcbiAgICAgICAgICByZXR1cm4gcHJldlxuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICB0YXNrczoge1xuICAgICAgICAgICAgLi4ucHJldi50YXNrcyxcbiAgICAgICAgICAgIFt0ZWFtbWF0ZVRhc2tJZF06IHtcbiAgICAgICAgICAgICAgLi4udGFzayxcbiAgICAgICAgICAgICAgcGVybWlzc2lvbk1vZGU6IG5leHRNb2RlLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICBpZiAoaGVscE9wZW4pIHtcbiAgICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgICB9XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBDb21wdXRlIHRoZSBuZXh0IG1vZGUgd2l0aG91dCB0cmlnZ2VyaW5nIHNpZGUgZWZmZWN0cyBmaXJzdFxuICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgIGBbYXV0by1tb2RlXSBoYW5kbGVDeWNsZU1vZGU6IGN1cnJlbnRNb2RlPSR7dG9vbFBlcm1pc3Npb25Db250ZXh0Lm1vZGV9IGlzQXV0b01vZGVBdmFpbGFibGU9JHt0b29sUGVybWlzc2lvbkNvbnRleHQuaXNBdXRvTW9kZUF2YWlsYWJsZX0gc2hvd0F1dG9Nb2RlT3B0SW49JHtzaG93QXV0b01vZGVPcHRJbn0gdGltZW91dFBlbmRpbmc9JHshIWF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnR9YCxcbiAgICApXG4gICAgY29uc3QgbmV4dE1vZGUgPSBnZXROZXh0UGVybWlzc2lvbk1vZGUodG9vbFBlcm1pc3Npb25Db250ZXh0LCB0ZWFtQ29udGV4dClcblxuICAgIC8vIENoZWNrIGlmIHVzZXIgaXMgZW50ZXJpbmcgYXV0byBtb2RlIGZvciB0aGUgZmlyc3QgdGltZS4gR2F0ZWQgb24gdGhlXG4gICAgLy8gcGVyc2lzdGVudCBzZXR0aW5ncyBmbGFnIChoYXNBdXRvTW9kZU9wdEluKSByYXRoZXIgdGhhbiB0aGUgYnJvYWRlclxuICAgIC8vIGhhc0F1dG9Nb2RlT3B0SW5BbnlTb3VyY2Ugc28gdGhhdCAtLWVuYWJsZS1hdXRvLW1vZGUgdXNlcnMgc3RpbGwgc2VlXG4gICAgLy8gdGhlIHdhcm5pbmcgZGlhbG9nIG9uY2Ug4oCUIHRoZSBDTEkgZmxhZyBzaG91bGQgZ3JhbnQgY2Fyb3VzZWwgYWNjZXNzLFxuICAgIC8vIG5vdCBieXBhc3MgdGhlIHNhZmV0eSB0ZXh0LlxuICAgIGxldCBpc0VudGVyaW5nQXV0b01vZGVGaXJzdFRpbWUgPSBmYWxzZVxuICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgICAgaXNFbnRlcmluZ0F1dG9Nb2RlRmlyc3RUaW1lID1cbiAgICAgICAgbmV4dE1vZGUgPT09ICdhdXRvJyAmJlxuICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSAhPT0gJ2F1dG8nICYmXG4gICAgICAgICFoYXNBdXRvTW9kZU9wdEluKCkgJiZcbiAgICAgICAgIXZpZXdpbmdBZ2VudFRhc2tJZCAvLyBPbmx5IHNob3cgZm9yIHByaW1hcnkgYWdlbnQsIG5vdCBzdWJhZ2VudHNcbiAgICB9XG5cbiAgICBpZiAoZmVhdHVyZSgnVFJBTlNDUklQVF9DTEFTU0lGSUVSJykpIHtcbiAgICAgIGlmIChpc0VudGVyaW5nQXV0b01vZGVGaXJzdFRpbWUpIHtcbiAgICAgICAgLy8gU3RvcmUgcHJldmlvdXMgbW9kZSBzbyB3ZSBjYW4gcmV2ZXJ0IGlmIHVzZXIgZGVjbGluZXNcbiAgICAgICAgc2V0UHJldmlvdXNNb2RlQmVmb3JlQXV0byh0b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSlcblxuICAgICAgICAvLyBPbmx5IHVwZGF0ZSB0aGUgVUkgbW9kZSBsYWJlbCDigJQgZG8gTk9UIGNhbGwgdHJhbnNpdGlvblBlcm1pc3Npb25Nb2RlXG4gICAgICAgIC8vIG9yIGN5Y2xlUGVybWlzc2lvbk1vZGUgeWV0OyB3ZSBoYXZlbid0IGNvbmZpcm1lZCB3aXRoIHRoZSB1c2VyLlxuICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQ6IHtcbiAgICAgICAgICAgIC4uLnByZXYudG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICAgICAgICAgICAgbW9kZTogJ2F1dG8nLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pKVxuICAgICAgICBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQoe1xuICAgICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgICBtb2RlOiAnYXV0bycsXG4gICAgICAgIH0pXG5cbiAgICAgICAgLy8gU2hvdyBvcHQtaW4gZGlhbG9nIGFmdGVyIDQwMG1zIGRlYm91bmNlXG4gICAgICAgIGlmIChhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50KSB7XG4gICAgICAgICAgY2xlYXJUaW1lb3V0KGF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnQpXG4gICAgICAgIH1cbiAgICAgICAgYXV0b01vZGVPcHRJblRpbWVvdXRSZWYuY3VycmVudCA9IHNldFRpbWVvdXQoXG4gICAgICAgICAgKHNldFNob3dBdXRvTW9kZU9wdEluLCBhdXRvTW9kZU9wdEluVGltZW91dFJlZikgPT4ge1xuICAgICAgICAgICAgc2V0U2hvd0F1dG9Nb2RlT3B0SW4odHJ1ZSlcbiAgICAgICAgICAgIGF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnQgPSBudWxsXG4gICAgICAgICAgfSxcbiAgICAgICAgICA0MDAsXG4gICAgICAgICAgc2V0U2hvd0F1dG9Nb2RlT3B0SW4sXG4gICAgICAgICAgYXV0b01vZGVPcHRJblRpbWVvdXRSZWYsXG4gICAgICAgIClcblxuICAgICAgICBpZiAoaGVscE9wZW4pIHtcbiAgICAgICAgICBzZXRIZWxwT3BlbihmYWxzZSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBEaXNtaXNzIGF1dG8gbW9kZSBvcHQtaW4gZGlhbG9nIGlmIHNob3dpbmcgb3IgcGVuZGluZyAodXNlciBpcyBjeWNsaW5nIGF3YXkpLlxuICAgIC8vIERvIE5PVCByZXZlcnQgdG8gcHJldmlvdXNNb2RlQmVmb3JlQXV0byBoZXJlIOKAlCBzaGlmdCt0YWIgbWVhbnMgXCJhZHZhbmNlIHRoZVxuICAgIC8vIGNhcm91c2VsXCIsIG5vdCBcImRlY2xpbmVcIi4gUmV2ZXJ0aW5nIGNhdXNlcyBhIHBpbmctcG9uZyBsb29wOiBhdXRvIHJldmVydHMgdG9cbiAgICAvLyB0aGUgcHJpb3IgbW9kZSwgd2hvc2UgbmV4dCBtb2RlIGlzIGF1dG8gYWdhaW4sIGZvcmV2ZXIuXG4gICAgLy8gVGhlIGRpYWxvZydzIG93biBkZWNsaW5lIGJ1dHRvbiAoaGFuZGxlQXV0b01vZGVPcHRJbkRlY2xpbmUpIGhhbmRsZXMgcmV2ZXJ0LlxuICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgICAgaWYgKHNob3dBdXRvTW9kZU9wdEluIHx8IGF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnQpIHtcbiAgICAgICAgaWYgKHNob3dBdXRvTW9kZU9wdEluKSB7XG4gICAgICAgICAgbG9nRXZlbnQoJ3Rlbmd1X2F1dG9fbW9kZV9vcHRfaW5fZGlhbG9nX2RlY2xpbmUnLCB7fSlcbiAgICAgICAgfVxuICAgICAgICBzZXRTaG93QXV0b01vZGVPcHRJbihmYWxzZSlcbiAgICAgICAgaWYgKGF1dG9Nb2RlT3B0SW5UaW1lb3V0UmVmLmN1cnJlbnQpIHtcbiAgICAgICAgICBjbGVhclRpbWVvdXQoYXV0b01vZGVPcHRJblRpbWVvdXRSZWYuY3VycmVudClcbiAgICAgICAgICBhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50ID0gbnVsbFxuICAgICAgICB9XG4gICAgICAgIHNldFByZXZpb3VzTW9kZUJlZm9yZUF1dG8obnVsbClcbiAgICAgICAgLy8gRmFsbCB0aHJvdWdoIOKAlCBtb2RlIGlzICdhdXRvJywgY3ljbGVQZXJtaXNzaW9uTW9kZSBiZWxvdyBnb2VzIHRvICdkZWZhdWx0Jy5cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBOb3cgdGhhdCB3ZSBrbm93IHRoaXMgaXMgTk9UIHRoZSBmaXJzdC10aW1lIGF1dG8gbW9kZSBwYXRoLFxuICAgIC8vIGNhbGwgY3ljbGVQZXJtaXNzaW9uTW9kZSB0byBhcHBseSBzaWRlIGVmZmVjdHMgKGUuZy4gc3RyaXBcbiAgICAvLyBkYW5nZXJvdXMgcGVybWlzc2lvbnMsIGFjdGl2YXRlIGNsYXNzaWZpZXIpXG4gICAgY29uc3QgeyBjb250ZXh0OiBwcmVwYXJlZENvbnRleHQgfSA9IGN5Y2xlUGVybWlzc2lvbk1vZGUoXG4gICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICB0ZWFtQ29udGV4dCxcbiAgICApXG5cbiAgICBsb2dFdmVudCgndGVuZ3VfbW9kZV9jeWNsZScsIHtcbiAgICAgIHRvOiBuZXh0TW9kZSBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgIH0pXG5cbiAgICAvLyBUcmFjayB3aGVuIHVzZXIgZW50ZXJzIHBsYW4gbW9kZVxuICAgIGlmIChuZXh0TW9kZSA9PT0gJ3BsYW4nKSB7XG4gICAgICBzYXZlR2xvYmFsQ29uZmlnKGN1cnJlbnQgPT4gKHtcbiAgICAgICAgLi4uY3VycmVudCxcbiAgICAgICAgbGFzdFBsYW5Nb2RlVXNlOiBEYXRlLm5vdygpLFxuICAgICAgfSkpXG4gICAgfVxuXG4gICAgLy8gU2V0IHRoZSBtb2RlIHZpYSBzZXRBcHBTdGF0ZSBkaXJlY3RseSBiZWNhdXNlIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dFxuICAgIC8vIGludGVudGlvbmFsbHkgcHJlc2VydmVzIHRoZSBleGlzdGluZyBtb2RlICh0byBwcmV2ZW50IGNvb3JkaW5hdG9yIG1vZGVcbiAgICAvLyBjb3JydXB0aW9uIGZyb20gd29ya2VycykuIFRoZW4gY2FsbCBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQgdG8gdHJpZ2dlclxuICAgIC8vIHJlY2hlY2sgb2YgcXVldWVkIHBlcm1pc3Npb24gcHJvbXB0cy5cbiAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAuLi5wcmV2LFxuICAgICAgdG9vbFBlcm1pc3Npb25Db250ZXh0OiB7XG4gICAgICAgIC4uLnByZXBhcmVkQ29udGV4dCxcbiAgICAgICAgbW9kZTogbmV4dE1vZGUsXG4gICAgICB9LFxuICAgIH0pKVxuICAgIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCh7XG4gICAgICAuLi5wcmVwYXJlZENvbnRleHQsXG4gICAgICBtb2RlOiBuZXh0TW9kZSxcbiAgICB9KVxuXG4gICAgLy8gSWYgdGhpcyBpcyBhIHRlYW1tYXRlLCB1cGRhdGUgY29uZmlnLmpzb24gc28gdGVhbSBsZWFkIHNlZXMgdGhlIGNoYW5nZVxuICAgIHN5bmNUZWFtbWF0ZU1vZGUobmV4dE1vZGUsIHRlYW1Db250ZXh0Py50ZWFtTmFtZSlcblxuICAgIC8vIENsb3NlIGhlbHAgdGlwcyBpZiB0aGV5J3JlIG9wZW4gd2hlbiBtb2RlIGlzIGN5Y2xlZFxuICAgIGlmIChoZWxwT3Blbikge1xuICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgfVxuICB9LCBbXG4gICAgdG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICAgIHRlYW1Db250ZXh0LFxuICAgIHZpZXdpbmdBZ2VudFRhc2tJZCxcbiAgICB2aWV3ZWRUZWFtbWF0ZSxcbiAgICBzZXRBcHBTdGF0ZSxcbiAgICBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgaGVscE9wZW4sXG4gICAgc2hvd0F1dG9Nb2RlT3B0SW4sXG4gIF0pXG5cbiAgLy8gSGFuZGxlciBmb3IgYXV0byBtb2RlIG9wdC1pbiBkaWFsb2cgYWNjZXB0YW5jZVxuICBjb25zdCBoYW5kbGVBdXRvTW9kZU9wdEluQWNjZXB0ID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGlmIChmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSkge1xuICAgICAgc2V0U2hvd0F1dG9Nb2RlT3B0SW4oZmFsc2UpXG4gICAgICBzZXRQcmV2aW91c01vZGVCZWZvcmVBdXRvKG51bGwpXG5cbiAgICAgIC8vIE5vdyB0aGF0IHRoZSB1c2VyIGFjY2VwdGVkLCBhcHBseSB0aGUgZnVsbCB0cmFuc2l0aW9uOiBhY3RpdmF0ZSB0aGVcbiAgICAgIC8vIGF1dG8gbW9kZSBiYWNrZW5kIChjbGFzc2lmaWVyLCBiZXRhIGhlYWRlcnMpIGFuZCBzdHJpcCBkYW5nZXJvdXNcbiAgICAgIC8vIHBlcm1pc3Npb25zIChlLmcuIEJhc2goKikgYWx3YXlzLWFsbG93IHJ1bGVzKS5cbiAgICAgIGNvbnN0IHN0cmlwcGVkQ29udGV4dCA9IHRyYW5zaXRpb25QZXJtaXNzaW9uTW9kZShcbiAgICAgICAgcHJldmlvdXNNb2RlQmVmb3JlQXV0byA/PyB0b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSxcbiAgICAgICAgJ2F1dG8nLFxuICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgICApXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgIC4uLnByZXYsXG4gICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dDoge1xuICAgICAgICAgIC4uLnN0cmlwcGVkQ29udGV4dCxcbiAgICAgICAgICBtb2RlOiAnYXV0bycsXG4gICAgICAgIH0sXG4gICAgICB9KSlcbiAgICAgIHNldFRvb2xQZXJtaXNzaW9uQ29udGV4dCh7XG4gICAgICAgIC4uLnN0cmlwcGVkQ29udGV4dCxcbiAgICAgICAgbW9kZTogJ2F1dG8nLFxuICAgICAgfSlcblxuICAgICAgLy8gQ2xvc2UgaGVscCB0aXBzIGlmIHRoZXkncmUgb3BlbiB3aGVuIGF1dG8gbW9kZSBpcyBlbmFibGVkXG4gICAgICBpZiAoaGVscE9wZW4pIHtcbiAgICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgICB9XG4gICAgfVxuICB9LCBbXG4gICAgaGVscE9wZW4sXG4gICAgc2V0SGVscE9wZW4sXG4gICAgcHJldmlvdXNNb2RlQmVmb3JlQXV0byxcbiAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgc2V0QXBwU3RhdGUsXG4gICAgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICBdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGF1dG8gbW9kZSBvcHQtaW4gZGlhbG9nIGRlY2xpbmVcbiAgY29uc3QgaGFuZGxlQXV0b01vZGVPcHRJbkRlY2xpbmUgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgaWYgKGZlYXR1cmUoJ1RSQU5TQ1JJUFRfQ0xBU1NJRklFUicpKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgIGBbYXV0by1tb2RlXSBoYW5kbGVBdXRvTW9kZU9wdEluRGVjbGluZTogcmV2ZXJ0aW5nIHRvICR7cHJldmlvdXNNb2RlQmVmb3JlQXV0b30sIHNldHRpbmcgaXNBdXRvTW9kZUF2YWlsYWJsZT1mYWxzZWAsXG4gICAgICApXG4gICAgICBzZXRTaG93QXV0b01vZGVPcHRJbihmYWxzZSlcbiAgICAgIGlmIChhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50KSB7XG4gICAgICAgIGNsZWFyVGltZW91dChhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50KVxuICAgICAgICBhdXRvTW9kZU9wdEluVGltZW91dFJlZi5jdXJyZW50ID0gbnVsbFxuICAgICAgfVxuXG4gICAgICAvLyBSZXZlcnQgdG8gcHJldmlvdXMgbW9kZSBhbmQgcmVtb3ZlIGF1dG8gZnJvbSB0aGUgY2Fyb3VzZWxcbiAgICAgIC8vIGZvciB0aGUgcmVzdCBvZiB0aGlzIHNlc3Npb25cbiAgICAgIGlmIChwcmV2aW91c01vZGVCZWZvcmVBdXRvKSB7XG4gICAgICAgIHNldEF1dG9Nb2RlQWN0aXZlKGZhbHNlKVxuICAgICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgICAgLi4ucHJldixcbiAgICAgICAgICB0b29sUGVybWlzc2lvbkNvbnRleHQ6IHtcbiAgICAgICAgICAgIC4uLnByZXYudG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICAgICAgICAgICAgbW9kZTogcHJldmlvdXNNb2RlQmVmb3JlQXV0byxcbiAgICAgICAgICAgIGlzQXV0b01vZGVBdmFpbGFibGU6IGZhbHNlLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pKVxuICAgICAgICBzZXRUb29sUGVybWlzc2lvbkNvbnRleHQoe1xuICAgICAgICAgIC4uLnRvb2xQZXJtaXNzaW9uQ29udGV4dCxcbiAgICAgICAgICBtb2RlOiBwcmV2aW91c01vZGVCZWZvcmVBdXRvLFxuICAgICAgICAgIGlzQXV0b01vZGVBdmFpbGFibGU6IGZhbHNlLFxuICAgICAgICB9KVxuICAgICAgICBzZXRQcmV2aW91c01vZGVCZWZvcmVBdXRvKG51bGwpXG4gICAgICB9XG4gICAgfVxuICB9LCBbXG4gICAgcHJldmlvdXNNb2RlQmVmb3JlQXV0byxcbiAgICB0b29sUGVybWlzc2lvbkNvbnRleHQsXG4gICAgc2V0QXBwU3RhdGUsXG4gICAgc2V0VG9vbFBlcm1pc3Npb25Db250ZXh0LFxuICBdKVxuXG4gIC8vIEhhbmRsZXIgZm9yIGNoYXQ6aW1hZ2VQYXN0ZSAtIHBhc3RlIGltYWdlIGZyb20gY2xpcGJvYXJkXG4gIGNvbnN0IGhhbmRsZUltYWdlUGFzdGUgPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgdm9pZCBnZXRJbWFnZUZyb21DbGlwYm9hcmQoKS50aGVuKGltYWdlRGF0YSA9PiB7XG4gICAgICBpZiAoaW1hZ2VEYXRhKSB7XG4gICAgICAgIG9uSW1hZ2VQYXN0ZShpbWFnZURhdGEuYmFzZTY0LCBpbWFnZURhdGEubWVkaWFUeXBlKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3Qgc2hvcnRjdXREaXNwbGF5ID0gZ2V0U2hvcnRjdXREaXNwbGF5KFxuICAgICAgICAgICdjaGF0OmltYWdlUGFzdGUnLFxuICAgICAgICAgICdDaGF0JyxcbiAgICAgICAgICAnY3RybCt2JyxcbiAgICAgICAgKVxuICAgICAgICBjb25zdCBtZXNzYWdlID0gZW52LmlzU1NIKClcbiAgICAgICAgICA/IFwiTm8gaW1hZ2UgZm91bmQgaW4gY2xpcGJvYXJkLiBZb3UncmUgU1NIJ2Q7IHRyeSBzY3A/XCJcbiAgICAgICAgICA6IGBObyBpbWFnZSBmb3VuZCBpbiBjbGlwYm9hcmQuIFVzZSAke3Nob3J0Y3V0RGlzcGxheX0gdG8gcGFzdGUgaW1hZ2VzLmBcbiAgICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAgICBrZXk6ICduby1pbWFnZS1pbi1jbGlwYm9hcmQnLFxuICAgICAgICAgIHRleHQ6IG1lc3NhZ2UsXG4gICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgIHRpbWVvdXRNczogMTAwMCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9KVxuICB9LCBbYWRkTm90aWZpY2F0aW9uLCBvbkltYWdlUGFzdGVdKVxuXG4gIC8vIFJlZ2lzdGVyIGNoYXQ6c3VibWl0IGhhbmRsZXIgZGlyZWN0bHkgaW4gdGhlIGhhbmRsZXIgcmVnaXN0cnkgKG5vdCB2aWFcbiAgLy8gdXNlS2V5YmluZGluZ3MpIHNvIHRoYXQgb25seSB0aGUgQ2hvcmRJbnRlcmNlcHRvciBjYW4gaW52b2tlIGl0IGZvciBjaG9yZFxuICAvLyBjb21wbGV0aW9ucyAoZS5nLiwgXCJjdHJsK2Ugc1wiKS4gVGhlIGRlZmF1bHQgRW50ZXIgYmluZGluZyBmb3Igc3VibWl0IGlzXG4gIC8vIGhhbmRsZWQgYnkgVGV4dElucHV0IGRpcmVjdGx5ICh2aWEgb25TdWJtaXQgcHJvcCkgYW5kIHVzZVR5cGVhaGVhZCAoZm9yXG4gIC8vIGF1dG9jb21wbGV0ZSBhY2NlcHRhbmNlKS4gVXNpbmcgdXNlS2V5YmluZGluZ3Mgd291bGQgY2F1c2VcbiAgLy8gc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uIG9uIEVudGVyLCBibG9ja2luZyBhdXRvY29tcGxldGUgZnJvbSBzZWVpbmcgdGhlIGtleS5cbiAgY29uc3Qga2V5YmluZGluZ0NvbnRleHQgPSB1c2VPcHRpb25hbEtleWJpbmRpbmdDb250ZXh0KClcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoIWtleWJpbmRpbmdDb250ZXh0IHx8IGlzTW9kYWxPdmVybGF5QWN0aXZlKSByZXR1cm5cbiAgICByZXR1cm4ga2V5YmluZGluZ0NvbnRleHQucmVnaXN0ZXJIYW5kbGVyKHtcbiAgICAgIGFjdGlvbjogJ2NoYXQ6c3VibWl0JyxcbiAgICAgIGNvbnRleHQ6ICdDaGF0JyxcbiAgICAgIGhhbmRsZXI6ICgpID0+IHtcbiAgICAgICAgdm9pZCBvblN1Ym1pdChpbnB1dClcbiAgICAgIH0sXG4gICAgfSlcbiAgfSwgW2tleWJpbmRpbmdDb250ZXh0LCBpc01vZGFsT3ZlcmxheUFjdGl2ZSwgb25TdWJtaXQsIGlucHV0XSlcblxuICAvLyBDaGF0IGNvbnRleHQga2V5YmluZGluZ3MgZm9yIGVkaXRpbmcgc2hvcnRjdXRzXG4gIC8vIE5vdGU6IGhpc3Rvcnk6cHJldmlvdXMvaGlzdG9yeTpuZXh0IGFyZSBOT1QgaGFuZGxlZCBoZXJlLiBUaGV5IGFyZSBwYXNzZWQgYXNcbiAgLy8gb25IaXN0b3J5VXAvb25IaXN0b3J5RG93biBwcm9wcyB0byBUZXh0SW5wdXQsIHNvIHRoYXQgdXNlVGV4dElucHV0J3NcbiAgLy8gdXBPckhpc3RvcnlVcC9kb3duT3JIaXN0b3J5RG93biBjYW4gdHJ5IGN1cnNvciBtb3ZlbWVudCBmaXJzdCBhbmQgb25seVxuICAvLyBmYWxsIHRocm91Z2ggdG8gaGlzdG9yeSB3aGVuIHRoZSBjdXJzb3IgY2FuJ3QgbW92ZSBmdXJ0aGVyLlxuICBjb25zdCBjaGF0SGFuZGxlcnMgPSB1c2VNZW1vKFxuICAgICgpID0+ICh7XG4gICAgICAnY2hhdDp1bmRvJzogaGFuZGxlVW5kbyxcbiAgICAgICdjaGF0Om5ld2xpbmUnOiBoYW5kbGVOZXdsaW5lLFxuICAgICAgJ2NoYXQ6ZXh0ZXJuYWxFZGl0b3InOiBoYW5kbGVFeHRlcm5hbEVkaXRvcixcbiAgICAgICdjaGF0OnN0YXNoJzogaGFuZGxlU3Rhc2gsXG4gICAgICAnY2hhdDptb2RlbFBpY2tlcic6IGhhbmRsZU1vZGVsUGlja2VyLFxuICAgICAgJ2NoYXQ6dGhpbmtpbmdUb2dnbGUnOiBoYW5kbGVUaGlua2luZ1RvZ2dsZSxcbiAgICAgICdjaGF0OmN5Y2xlTW9kZSc6IGhhbmRsZUN5Y2xlTW9kZSxcbiAgICAgICdjaGF0OmltYWdlUGFzdGUnOiBoYW5kbGVJbWFnZVBhc3RlLFxuICAgIH0pLFxuICAgIFtcbiAgICAgIGhhbmRsZVVuZG8sXG4gICAgICBoYW5kbGVOZXdsaW5lLFxuICAgICAgaGFuZGxlRXh0ZXJuYWxFZGl0b3IsXG4gICAgICBoYW5kbGVTdGFzaCxcbiAgICAgIGhhbmRsZU1vZGVsUGlja2VyLFxuICAgICAgaGFuZGxlVGhpbmtpbmdUb2dnbGUsXG4gICAgICBoYW5kbGVDeWNsZU1vZGUsXG4gICAgICBoYW5kbGVJbWFnZVBhc3RlLFxuICAgIF0sXG4gIClcblxuICB1c2VLZXliaW5kaW5ncyhjaGF0SGFuZGxlcnMsIHtcbiAgICBjb250ZXh0OiAnQ2hhdCcsXG4gICAgaXNBY3RpdmU6ICFpc01vZGFsT3ZlcmxheUFjdGl2ZSxcbiAgfSlcblxuICAvLyBTaGlmdCvihpEgZW50ZXJzIG1lc3NhZ2UtYWN0aW9ucyBjdXJzb3IuIFNlcGFyYXRlIGlzQWN0aXZlIHNvIGN0cmwrciBzZWFyY2hcbiAgLy8gZG9lc24ndCBsZWF2ZSBzdGFsZSBpc1NlYXJjaGluZ0hpc3Rvcnkgb24gY3Vyc29yLWV4aXQgcmVtb3VudC5cbiAgdXNlS2V5YmluZGluZygnY2hhdDptZXNzYWdlQWN0aW9ucycsICgpID0+IG9uTWVzc2FnZUFjdGlvbnNFbnRlcj8uKCksIHtcbiAgICBjb250ZXh0OiAnQ2hhdCcsXG4gICAgaXNBY3RpdmU6ICFpc01vZGFsT3ZlcmxheUFjdGl2ZSAmJiAhaXNTZWFyY2hpbmdIaXN0b3J5LFxuICB9KVxuXG4gIC8vIEZhc3QgbW9kZSBrZXliaW5kaW5nIGlzIG9ubHkgYWN0aXZlIHdoZW4gZmFzdCBtb2RlIGlzIGVuYWJsZWQgYW5kIGF2YWlsYWJsZVxuICB1c2VLZXliaW5kaW5nKCdjaGF0OmZhc3RNb2RlJywgaGFuZGxlRmFzdE1vZGVQaWNrZXIsIHtcbiAgICBjb250ZXh0OiAnQ2hhdCcsXG4gICAgaXNBY3RpdmU6XG4gICAgICAhaXNNb2RhbE92ZXJsYXlBY3RpdmUgJiYgaXNGYXN0TW9kZUVuYWJsZWQoKSAmJiBpc0Zhc3RNb2RlQXZhaWxhYmxlKCksXG4gIH0pXG5cbiAgLy8gSGFuZGxlIGhlbHA6ZGlzbWlzcyBrZXliaW5kaW5nIChFU0MgY2xvc2VzIGhlbHAgbWVudSlcbiAgLy8gVGhpcyBpcyByZWdpc3RlcmVkIHNlcGFyYXRlbHkgZnJvbSBDaGF0IGNvbnRleHQgc28gaXQgaGFzIHByaW9yaXR5IG92ZXJcbiAgLy8gQ2FuY2VsUmVxdWVzdEhhbmRsZXIgd2hlbiBoZWxwIG1lbnUgaXMgb3BlblxuICB1c2VLZXliaW5kaW5nKFxuICAgICdoZWxwOmRpc21pc3MnLFxuICAgICgpID0+IHtcbiAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnSGVscCcsIGlzQWN0aXZlOiBoZWxwT3BlbiB9LFxuICApXG5cbiAgLy8gUXVpY2sgT3BlbiAvIEdsb2JhbCBTZWFyY2guIEhvb2sgY2FsbHMgYXJlIHVuY29uZGl0aW9uYWwgKFJ1bGVzIG9mIEhvb2tzKTtcbiAgLy8gdGhlIGhhbmRsZXIgYm9keSBpcyBmZWF0dXJlKCktZ2F0ZWQgc28gdGhlIHNldFN0YXRlIGNhbGxzIGFuZCBjb21wb25lbnRcbiAgLy8gcmVmZXJlbmNlcyBnZXQgdHJlZS1zaGFrZW4gaW4gZXh0ZXJuYWwgYnVpbGRzLlxuICBjb25zdCBxdWlja1NlYXJjaEFjdGl2ZSA9IGZlYXR1cmUoJ1FVSUNLX1NFQVJDSCcpXG4gICAgPyAhaXNNb2RhbE92ZXJsYXlBY3RpdmVcbiAgICA6IGZhbHNlXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2FwcDpxdWlja09wZW4nLFxuICAgICgpID0+IHtcbiAgICAgIGlmIChmZWF0dXJlKCdRVUlDS19TRUFSQ0gnKSkge1xuICAgICAgICBzZXRTaG93UXVpY2tPcGVuKHRydWUpXG4gICAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgICAgfVxuICAgIH0sXG4gICAgeyBjb250ZXh0OiAnR2xvYmFsJywgaXNBY3RpdmU6IHF1aWNrU2VhcmNoQWN0aXZlIH0sXG4gIClcbiAgdXNlS2V5YmluZGluZyhcbiAgICAnYXBwOmdsb2JhbFNlYXJjaCcsXG4gICAgKCkgPT4ge1xuICAgICAgaWYgKGZlYXR1cmUoJ1FVSUNLX1NFQVJDSCcpKSB7XG4gICAgICAgIHNldFNob3dHbG9iYWxTZWFyY2godHJ1ZSlcbiAgICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgICB9XG4gICAgfSxcbiAgICB7IGNvbnRleHQ6ICdHbG9iYWwnLCBpc0FjdGl2ZTogcXVpY2tTZWFyY2hBY3RpdmUgfSxcbiAgKVxuXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2hpc3Rvcnk6c2VhcmNoJyxcbiAgICAoKSA9PiB7XG4gICAgICBpZiAoZmVhdHVyZSgnSElTVE9SWV9QSUNLRVInKSkge1xuICAgICAgICBzZXRTaG93SGlzdG9yeVBpY2tlcih0cnVlKVxuICAgICAgICBzZXRIZWxwT3BlbihmYWxzZSlcbiAgICAgIH1cbiAgICB9LFxuICAgIHtcbiAgICAgIGNvbnRleHQ6ICdHbG9iYWwnLFxuICAgICAgaXNBY3RpdmU6IGZlYXR1cmUoJ0hJU1RPUllfUElDS0VSJykgPyAhaXNNb2RhbE92ZXJsYXlBY3RpdmUgOiBmYWxzZSxcbiAgICB9LFxuICApXG5cbiAgLy8gSGFuZGxlIEN0cmwrQyB0byBhYm9ydCBzcGVjdWxhdGlvbiB3aGVuIGlkbGUgKG5vdCBsb2FkaW5nKVxuICAvLyBDYW5jZWxSZXF1ZXN0SGFuZGxlciBvbmx5IGhhbmRsZXMgQ3RybCtDIGR1cmluZyBhY3RpdmUgdGFza3NcbiAgdXNlS2V5YmluZGluZyhcbiAgICAnYXBwOmludGVycnVwdCcsXG4gICAgKCkgPT4ge1xuICAgICAgYWJvcnRTcGVjdWxhdGlvbihzZXRBcHBTdGF0ZSlcbiAgICB9LFxuICAgIHtcbiAgICAgIGNvbnRleHQ6ICdHbG9iYWwnLFxuICAgICAgaXNBY3RpdmU6ICFpc0xvYWRpbmcgJiYgc3BlY3VsYXRpb24uc3RhdHVzID09PSAnYWN0aXZlJyxcbiAgICB9LFxuICApXG5cbiAgLy8gRm9vdGVyIGluZGljYXRvciBuYXZpZ2F0aW9uIGtleWJpbmRpbmdzLiDihpEv4oaTIGxpdmUgaGVyZSAobm90IGluXG4gIC8vIGhhbmRsZUhpc3RvcnlVcC9Eb3duKSBiZWNhdXNlIFRleHRJbnB1dCBmb2N1cz1mYWxzZSB3aGVuIGEgcGlsbCBpc1xuICAvLyBzZWxlY3RlZCDigJQgaXRzIHVzZUlucHV0IGlzIGluYWN0aXZlLCBzbyB0aGlzIGlzIHRoZSBvbmx5IHBhdGguXG4gIHVzZUtleWJpbmRpbmdzKFxuICAgIHtcbiAgICAgICdmb290ZXI6dXAnOiAoKSA9PiB7XG4gICAgICAgIC8vIOKGkSBzY3JvbGxzIHdpdGhpbiB0aGUgY29vcmRpbmF0b3IgdGFzayBsaXN0IGJlZm9yZSBsZWF2aW5nIHRoZSBwaWxsXG4gICAgICAgIGlmIChcbiAgICAgICAgICB0YXNrc1NlbGVjdGVkICYmXG4gICAgICAgICAgXCJleHRlcm5hbFwiID09PSAnYW50JyAmJlxuICAgICAgICAgIGNvb3JkaW5hdG9yVGFza0NvdW50ID4gMCAmJlxuICAgICAgICAgIGNvb3JkaW5hdG9yVGFza0luZGV4ID4gbWluQ29vcmRpbmF0b3JJbmRleFxuICAgICAgICApIHtcbiAgICAgICAgICBzZXRDb29yZGluYXRvclRhc2tJbmRleChwcmV2ID0+IHByZXYgLSAxKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIG5hdmlnYXRlRm9vdGVyKC0xLCB0cnVlKVxuICAgICAgfSxcbiAgICAgICdmb290ZXI6ZG93bic6ICgpID0+IHtcbiAgICAgICAgLy8g4oaTIHNjcm9sbHMgd2l0aGluIHRoZSBjb29yZGluYXRvciB0YXNrIGxpc3QsIG5ldmVyIGxlYXZlcyB0aGUgcGlsbFxuICAgICAgICBpZiAoXG4gICAgICAgICAgdGFza3NTZWxlY3RlZCAmJlxuICAgICAgICAgIFwiZXh0ZXJuYWxcIiA9PT0gJ2FudCcgJiZcbiAgICAgICAgICBjb29yZGluYXRvclRhc2tDb3VudCA+IDBcbiAgICAgICAgKSB7XG4gICAgICAgICAgaWYgKGNvb3JkaW5hdG9yVGFza0luZGV4IDwgY29vcmRpbmF0b3JUYXNrQ291bnQgLSAxKSB7XG4gICAgICAgICAgICBzZXRDb29yZGluYXRvclRhc2tJbmRleChwcmV2ID0+IHByZXYgKyAxKVxuICAgICAgICAgIH1cbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBpZiAodGFza3NTZWxlY3RlZCAmJiAhaXNUZWFtbWF0ZU1vZGUpIHtcbiAgICAgICAgICBzZXRTaG93QmFzaGVzRGlhbG9nKHRydWUpXG4gICAgICAgICAgc2VsZWN0Rm9vdGVySXRlbShudWxsKVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIG5hdmlnYXRlRm9vdGVyKDEpXG4gICAgICB9LFxuICAgICAgJ2Zvb3RlcjpuZXh0JzogKCkgPT4ge1xuICAgICAgICAvLyBUZWFtbWF0ZSBtb2RlOiDihpAv4oaSIGN5Y2xlcyB3aXRoaW4gdGhlIHRlYW0gbWVtYmVyIGxpc3RcbiAgICAgICAgaWYgKHRhc2tzU2VsZWN0ZWQgJiYgaXNUZWFtbWF0ZU1vZGUpIHtcbiAgICAgICAgICBjb25zdCB0b3RhbEFnZW50cyA9IDEgKyBpblByb2Nlc3NUZWFtbWF0ZXMubGVuZ3RoXG4gICAgICAgICAgc2V0VGVhbW1hdGVGb290ZXJJbmRleChwcmV2ID0+IChwcmV2ICsgMSkgJSB0b3RhbEFnZW50cylcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBuYXZpZ2F0ZUZvb3RlcigxKVxuICAgICAgfSxcbiAgICAgICdmb290ZXI6cHJldmlvdXMnOiAoKSA9PiB7XG4gICAgICAgIGlmICh0YXNrc1NlbGVjdGVkICYmIGlzVGVhbW1hdGVNb2RlKSB7XG4gICAgICAgICAgY29uc3QgdG90YWxBZ2VudHMgPSAxICsgaW5Qcm9jZXNzVGVhbW1hdGVzLmxlbmd0aFxuICAgICAgICAgIHNldFRlYW1tYXRlRm9vdGVySW5kZXgocHJldiA9PiAocHJldiAtIDEgKyB0b3RhbEFnZW50cykgJSB0b3RhbEFnZW50cylcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuICAgICAgICBuYXZpZ2F0ZUZvb3RlcigtMSlcbiAgICAgIH0sXG4gICAgICAnZm9vdGVyOm9wZW5TZWxlY3RlZCc6ICgpID0+IHtcbiAgICAgICAgaWYgKHZpZXdTZWxlY3Rpb25Nb2RlID09PSAnc2VsZWN0aW5nLWFnZW50Jykge1xuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIHN3aXRjaCAoZm9vdGVySXRlbVNlbGVjdGVkKSB7XG4gICAgICAgICAgY2FzZSAnY29tcGFuaW9uJzpcbiAgICAgICAgICAgIGlmIChmZWF0dXJlKCdCVUREWScpKSB7XG4gICAgICAgICAgICAgIHNlbGVjdEZvb3Rlckl0ZW0obnVsbClcbiAgICAgICAgICAgICAgdm9pZCBvblN1Ym1pdCgnL2J1ZGR5JylcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSAndGFza3MnOlxuICAgICAgICAgICAgaWYgKGlzVGVhbW1hdGVNb2RlKSB7XG4gICAgICAgICAgICAgIC8vIEVudGVyIHN3aXRjaGVzIHRvIHRoZSBzZWxlY3RlZCBhZ2VudCdzIHZpZXdcbiAgICAgICAgICAgICAgaWYgKHRlYW1tYXRlRm9vdGVySW5kZXggPT09IDApIHtcbiAgICAgICAgICAgICAgICBleGl0VGVhbW1hdGVWaWV3KHNldEFwcFN0YXRlKVxuICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGNvbnN0IHRlYW1tYXRlID0gaW5Qcm9jZXNzVGVhbW1hdGVzW3RlYW1tYXRlRm9vdGVySW5kZXggLSAxXVxuICAgICAgICAgICAgICAgIGlmICh0ZWFtbWF0ZSkgZW50ZXJUZWFtbWF0ZVZpZXcodGVhbW1hdGUuaWQsIHNldEFwcFN0YXRlKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGNvb3JkaW5hdG9yVGFza0luZGV4ID09PSAwICYmIGNvb3JkaW5hdG9yVGFza0NvdW50ID4gMCkge1xuICAgICAgICAgICAgICBleGl0VGVhbW1hdGVWaWV3KHNldEFwcFN0YXRlKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgY29uc3Qgc2VsZWN0ZWRUYXNrSWQgPVxuICAgICAgICAgICAgICAgIGdldFZpc2libGVBZ2VudFRhc2tzKHRhc2tzKVtjb29yZGluYXRvclRhc2tJbmRleCAtIDFdPy5pZFxuICAgICAgICAgICAgICBpZiAoc2VsZWN0ZWRUYXNrSWQpIHtcbiAgICAgICAgICAgICAgICBlbnRlclRlYW1tYXRlVmlldyhzZWxlY3RlZFRhc2tJZCwgc2V0QXBwU3RhdGUpXG4gICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgc2V0U2hvd0Jhc2hlc0RpYWxvZyh0cnVlKVxuICAgICAgICAgICAgICAgIHNlbGVjdEZvb3Rlckl0ZW0obnVsbClcbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnJlYWtcbiAgICAgICAgICBjYXNlICd0bXV4JzpcbiAgICAgICAgICAgIGlmIChcImV4dGVybmFsXCIgPT09ICdhbnQnKSB7XG4gICAgICAgICAgICAgIHNldEFwcFN0YXRlKHByZXYgPT5cbiAgICAgICAgICAgICAgICBwcmV2LnR1bmdzdGVuUGFuZWxBdXRvSGlkZGVuXG4gICAgICAgICAgICAgICAgICA/IHsgLi4ucHJldiwgdHVuZ3N0ZW5QYW5lbEF1dG9IaWRkZW46IGZhbHNlIH1cbiAgICAgICAgICAgICAgICAgIDoge1xuICAgICAgICAgICAgICAgICAgICAgIC4uLnByZXYsXG4gICAgICAgICAgICAgICAgICAgICAgdHVuZ3N0ZW5QYW5lbFZpc2libGU6ICEoXG4gICAgICAgICAgICAgICAgICAgICAgICBwcmV2LnR1bmdzdGVuUGFuZWxWaXNpYmxlID8/IHRydWVcbiAgICAgICAgICAgICAgICAgICAgICApLFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICApXG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgJ2JhZ2VsJzpcbiAgICAgICAgICAgIGJyZWFrXG4gICAgICAgICAgY2FzZSAndGVhbXMnOlxuICAgICAgICAgICAgc2V0U2hvd1RlYW1zRGlhbG9nKHRydWUpXG4gICAgICAgICAgICBzZWxlY3RGb290ZXJJdGVtKG51bGwpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICAgIGNhc2UgJ2JyaWRnZSc6XG4gICAgICAgICAgICBzZXRTaG93QnJpZGdlRGlhbG9nKHRydWUpXG4gICAgICAgICAgICBzZWxlY3RGb290ZXJJdGVtKG51bGwpXG4gICAgICAgICAgICBicmVha1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgJ2Zvb3RlcjpjbGVhclNlbGVjdGlvbic6ICgpID0+IHtcbiAgICAgICAgc2VsZWN0Rm9vdGVySXRlbShudWxsKVxuICAgICAgfSxcbiAgICAgICdmb290ZXI6Y2xvc2UnOiAoKSA9PiB7XG4gICAgICAgIGlmICh0YXNrc1NlbGVjdGVkICYmIGNvb3JkaW5hdG9yVGFza0luZGV4ID49IDEpIHtcbiAgICAgICAgICBjb25zdCB0YXNrID0gZ2V0VmlzaWJsZUFnZW50VGFza3ModGFza3MpW2Nvb3JkaW5hdG9yVGFza0luZGV4IC0gMV1cbiAgICAgICAgICBpZiAoIXRhc2spIHJldHVybiBmYWxzZVxuICAgICAgICAgIC8vIFdoZW4gdGhlIHNlbGVjdGVkIHJvdyBJUyB0aGUgdmlld2VkIGFnZW50LCAneCcgdHlwZXMgaW50byB0aGVcbiAgICAgICAgICAvLyBzdGVlcmluZyBpbnB1dC4gQW55IG90aGVyIHJvdyDigJQgZGlzbWlzcyBpdC5cbiAgICAgICAgICBpZiAoXG4gICAgICAgICAgICB2aWV3U2VsZWN0aW9uTW9kZSA9PT0gJ3ZpZXdpbmctYWdlbnQnICYmXG4gICAgICAgICAgICB0YXNrLmlkID09PSB2aWV3aW5nQWdlbnRUYXNrSWRcbiAgICAgICAgICApIHtcbiAgICAgICAgICAgIG9uQ2hhbmdlKFxuICAgICAgICAgICAgICBpbnB1dC5zbGljZSgwLCBjdXJzb3JPZmZzZXQpICsgJ3gnICsgaW5wdXQuc2xpY2UoY3Vyc29yT2Zmc2V0KSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIHNldEN1cnNvck9mZnNldChjdXJzb3JPZmZzZXQgKyAxKVxuICAgICAgICAgICAgcmV0dXJuXG4gICAgICAgICAgfVxuICAgICAgICAgIHN0b3BPckRpc21pc3NBZ2VudCh0YXNrLmlkLCBzZXRBcHBTdGF0ZSlcbiAgICAgICAgICBpZiAodGFzay5zdGF0dXMgIT09ICdydW5uaW5nJykge1xuICAgICAgICAgICAgc2V0Q29vcmRpbmF0b3JUYXNrSW5kZXgoaSA9PiBNYXRoLm1heChtaW5Db29yZGluYXRvckluZGV4LCBpIC0gMSkpXG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVyblxuICAgICAgICB9XG4gICAgICAgIC8vIE5vdCBoYW5kbGVkIOKAlCBsZXQgJ3gnIGZhbGwgdGhyb3VnaCB0byB0eXBlLXRvLWV4aXRcbiAgICAgICAgcmV0dXJuIGZhbHNlXG4gICAgICB9LFxuICAgIH0sXG4gICAge1xuICAgICAgY29udGV4dDogJ0Zvb3RlcicsXG4gICAgICBpc0FjdGl2ZTogISFmb290ZXJJdGVtU2VsZWN0ZWQgJiYgIWlzTW9kYWxPdmVybGF5QWN0aXZlLFxuICAgIH0sXG4gIClcblxuICB1c2VJbnB1dCgoY2hhciwga2V5KSA9PiB7XG4gICAgLy8gU2tpcCBhbGwgaW5wdXQgaGFuZGxpbmcgd2hlbiBhIGZ1bGwtc2NyZWVuIGRpYWxvZyBpcyBvcGVuLiBUaGVzZSBkaWFsb2dzXG4gICAgLy8gcmVuZGVyIHZpYSBlYXJseSByZXR1cm4sIGJ1dCBob29rcyBydW4gdW5jb25kaXRpb25hbGx5IOKAlCBzbyB3aXRob3V0IHRoaXNcbiAgICAvLyBndWFyZCwgRXNjYXBlIGluc2lkZSBhIGRpYWxvZyBsZWFrcyB0byB0aGUgZG91YmxlLXByZXNzIG1lc3NhZ2Utc2VsZWN0b3IuXG4gICAgaWYgKFxuICAgICAgc2hvd1RlYW1zRGlhbG9nIHx8XG4gICAgICBzaG93UXVpY2tPcGVuIHx8XG4gICAgICBzaG93R2xvYmFsU2VhcmNoIHx8XG4gICAgICBzaG93SGlzdG9yeVBpY2tlclxuICAgICkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gRGV0ZWN0IGZhaWxlZCBBbHQgc2hvcnRjdXRzIG9uIG1hY09TIChPcHRpb24ga2V5IHByb2R1Y2VzIHNwZWNpYWwgY2hhcmFjdGVycylcbiAgICBpZiAoZ2V0UGxhdGZvcm0oKSA9PT0gJ21hY29zJyAmJiBpc01hY29zT3B0aW9uQ2hhcihjaGFyKSkge1xuICAgICAgY29uc3Qgc2hvcnRjdXQgPSBNQUNPU19PUFRJT05fU1BFQ0lBTF9DSEFSU1tjaGFyXVxuICAgICAgY29uc3QgdGVybWluYWxOYW1lID0gZ2V0TmF0aXZlQ1NJdVRlcm1pbmFsRGlzcGxheU5hbWUoKVxuICAgICAgY29uc3QganN4ID0gdGVybWluYWxOYW1lID8gKFxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICBUbyBlbmFibGUge3Nob3J0Y3V0fSwgc2V0IDxUZXh0IGJvbGQ+T3B0aW9uIGFzIE1ldGE8L1RleHQ+IGlueycgJ31cbiAgICAgICAgICB7dGVybWluYWxOYW1lfSBwcmVmZXJlbmNlcyAo4oyYLClcbiAgICAgICAgPC9UZXh0PlxuICAgICAgKSA6IChcbiAgICAgICAgPFRleHQgZGltQ29sb3I+VG8gZW5hYmxlIHtzaG9ydGN1dH0sIHJ1biAvdGVybWluYWwtc2V0dXA8L1RleHQ+XG4gICAgICApXG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICdvcHRpb24tbWV0YS1oaW50JyxcbiAgICAgICAganN4LFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogNTAwMCxcbiAgICAgIH0pXG4gICAgICAvLyBEb24ndCByZXR1cm4gLSBsZXQgdGhlIGNoYXJhY3RlciBiZSB0eXBlZCBzbyB1c2VyIHNlZXMgdGhlIGlzc3VlXG4gICAgfVxuXG4gICAgLy8gRm9vdGVyIG5hdmlnYXRpb24gaXMgaGFuZGxlZCB2aWEgdXNlS2V5YmluZGluZ3MgYWJvdmUgKEZvb3RlciBjb250ZXh0KVxuXG4gICAgLy8gTk9URTogY3RybCtfLCBjdHJsK2csIGN0cmwrcyBhcmUgaGFuZGxlZCB2aWEgQ2hhdCBjb250ZXh0IGtleWJpbmRpbmdzIGFib3ZlXG5cbiAgICAvLyBUeXBlLXRvLWV4aXQgZm9vdGVyOiBwcmludGFibGUgY2hhcnMgd2hpbGUgYSBwaWxsIGlzIHNlbGVjdGVkIHJlZm9jdXNcbiAgICAvLyB0aGUgaW5wdXQgYW5kIHR5cGUgdGhlIGNoYXIuIE5hdiBrZXlzIGFyZSBjYXB0dXJlZCBieSB1c2VLZXliaW5kaW5nc1xuICAgIC8vIGFib3ZlLCBzbyBhbnl0aGluZyByZWFjaGluZyBoZXJlIGlzIGdlbnVpbmVseSBub3QgYSBmb290ZXIgYWN0aW9uLlxuICAgIC8vIG9uQ2hhbmdlIGNsZWFycyBmb290ZXJTZWxlY3Rpb24sIHNvIG5vIGV4cGxpY2l0IGRlc2VsZWN0LlxuICAgIGlmIChcbiAgICAgIGZvb3Rlckl0ZW1TZWxlY3RlZCAmJlxuICAgICAgY2hhciAmJlxuICAgICAgIWtleS5jdHJsICYmXG4gICAgICAha2V5Lm1ldGEgJiZcbiAgICAgICFrZXkuZXNjYXBlICYmXG4gICAgICAha2V5LnJldHVyblxuICAgICkge1xuICAgICAgb25DaGFuZ2UoaW5wdXQuc2xpY2UoMCwgY3Vyc29yT2Zmc2V0KSArIGNoYXIgKyBpbnB1dC5zbGljZShjdXJzb3JPZmZzZXQpKVxuICAgICAgc2V0Q3Vyc29yT2Zmc2V0KGN1cnNvck9mZnNldCArIGNoYXIubGVuZ3RoKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gRXhpdCBzcGVjaWFsIG1vZGVzIHdoZW4gYmFja3NwYWNlL2VzY2FwZS9kZWxldGUvY3RybCt1IGlzIHByZXNzZWQgYXQgY3Vyc29yIHBvc2l0aW9uIDBcbiAgICBpZiAoXG4gICAgICBjdXJzb3JPZmZzZXQgPT09IDAgJiZcbiAgICAgIChrZXkuZXNjYXBlIHx8IGtleS5iYWNrc3BhY2UgfHwga2V5LmRlbGV0ZSB8fCAoa2V5LmN0cmwgJiYgY2hhciA9PT0gJ3UnKSlcbiAgICApIHtcbiAgICAgIG9uTW9kZUNoYW5nZSgncHJvbXB0JylcbiAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgIH1cblxuICAgIC8vIEV4aXQgaGVscCBtb2RlIHdoZW4gYmFja3NwYWNlIGlzIHByZXNzZWQgYW5kIGlucHV0IGlzIGVtcHR5XG4gICAgaWYgKGhlbHBPcGVuICYmIGlucHV0ID09PSAnJyAmJiAoa2V5LmJhY2tzcGFjZSB8fCBrZXkuZGVsZXRlKSkge1xuICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgfVxuXG4gICAgLy8gZXNjIGlzIGEgbGl0dGxlIG92ZXJsb2FkZWQ6XG4gICAgLy8gLSB3aGVuIHdlJ3JlIGxvYWRpbmcgYSByZXNwb25zZSwgaXQncyB1c2VkIHRvIGNhbmNlbCB0aGUgcmVxdWVzdFxuICAgIC8vIC0gb3RoZXJ3aXNlLCBpdCdzIHVzZWQgdG8gc2hvdyB0aGUgbWVzc2FnZSBzZWxlY3RvclxuICAgIC8vIC0gd2hlbiBkb3VibGUgcHJlc3NlZCwgaXQncyB1c2VkIHRvIGNsZWFyIHRoZSBpbnB1dFxuICAgIC8vIC0gd2hlbiBpbnB1dCBpcyBlbXB0eSwgcG9wIGZyb20gY29tbWFuZCBxdWV1ZVxuXG4gICAgLy8gSGFuZGxlIEVTQyBrZXkgcHJlc3NcbiAgICBpZiAoa2V5LmVzY2FwZSkge1xuICAgICAgLy8gQWJvcnQgYWN0aXZlIHNwZWN1bGF0aW9uXG4gICAgICBpZiAoc3BlY3VsYXRpb24uc3RhdHVzID09PSAnYWN0aXZlJykge1xuICAgICAgICBhYm9ydFNwZWN1bGF0aW9uKHNldEFwcFN0YXRlKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gRGlzbWlzcyBzaWRlIHF1ZXN0aW9uIHJlc3BvbnNlIGlmIHZpc2libGVcbiAgICAgIGlmIChpc1NpZGVRdWVzdGlvblZpc2libGUgJiYgb25EaXNtaXNzU2lkZVF1ZXN0aW9uKSB7XG4gICAgICAgIG9uRGlzbWlzc1NpZGVRdWVzdGlvbigpXG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBDbG9zZSBoZWxwIG1lbnUgaWYgb3BlblxuICAgICAgaWYgKGhlbHBPcGVuKSB7XG4gICAgICAgIHNldEhlbHBPcGVuKGZhbHNlKVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgLy8gRm9vdGVyIHNlbGVjdGlvbiBjbGVhcmluZyBpcyBub3cgaGFuZGxlZCB2aWEgRm9vdGVyIGNvbnRleHQga2V5YmluZGluZ3NcbiAgICAgIC8vIChmb290ZXI6Y2xlYXJTZWxlY3Rpb24gYWN0aW9uIGJvdW5kIHRvIGVzY2FwZSlcbiAgICAgIC8vIElmIGEgZm9vdGVyIGl0ZW0gaXMgc2VsZWN0ZWQsIGxldCB0aGUgRm9vdGVyIGtleWJpbmRpbmcgaGFuZGxlIGl0XG4gICAgICBpZiAoZm9vdGVySXRlbVNlbGVjdGVkKSB7XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICAvLyBJZiB0aGVyZSdzIGFuIGVkaXRhYmxlIHF1ZXVlZCBjb21tYW5kLCBtb3ZlIGl0IHRvIHRoZSBpbnB1dCBmb3IgZWRpdGluZyB3aGVuIEVTQyBpcyBwcmVzc2VkXG4gICAgICBjb25zdCBoYXNFZGl0YWJsZUNvbW1hbmQgPSBxdWV1ZWRDb21tYW5kcy5zb21lKGlzUXVldWVkQ29tbWFuZEVkaXRhYmxlKVxuICAgICAgaWYgKGhhc0VkaXRhYmxlQ29tbWFuZCkge1xuICAgICAgICB2b2lkIHBvcEFsbENvbW1hbmRzRnJvbVF1ZXVlKClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGlmIChtZXNzYWdlcy5sZW5ndGggPiAwICYmICFpbnB1dCAmJiAhaXNMb2FkaW5nKSB7XG4gICAgICAgIGRvdWJsZVByZXNzRXNjRnJvbUVtcHR5KClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoa2V5LnJldHVybiAmJiBoZWxwT3Blbikge1xuICAgICAgc2V0SGVscE9wZW4oZmFsc2UpXG4gICAgfVxuICB9KVxuXG4gIGNvbnN0IHN3YXJtQmFubmVyID0gdXNlU3dhcm1CYW5uZXIoKVxuXG4gIGNvbnN0IGZhc3RNb2RlQ29vbGRvd24gPSBpc0Zhc3RNb2RlRW5hYmxlZCgpID8gaXNGYXN0TW9kZUNvb2xkb3duKCkgOiBmYWxzZVxuICBjb25zdCBzaG93RmFzdEljb24gPSBpc0Zhc3RNb2RlRW5hYmxlZCgpXG4gICAgPyBpc0Zhc3RNb2RlICYmIChpc0Zhc3RNb2RlQXZhaWxhYmxlKCkgfHwgZmFzdE1vZGVDb29sZG93bilcbiAgICA6IGZhbHNlXG5cbiAgY29uc3Qgc2hvd0Zhc3RJY29uSGludCA9IHVzZVNob3dGYXN0SWNvbkhpbnQoc2hvd0Zhc3RJY29uID8/IGZhbHNlKVxuXG4gIC8vIFNob3cgZWZmb3J0IG5vdGlmaWNhdGlvbiBvbiBzdGFydHVwIGFuZCB3aGVuIGVmZm9ydCBjaGFuZ2VzLlxuICAvLyBTdXBwcmVzc2VkIGluIGJyaWVmL2Fzc2lzdGFudCBtb2RlIOKAlCB0aGUgdmFsdWUgcmVmbGVjdHMgdGhlIGxvY2FsXG4gIC8vIGNsaWVudCdzIGVmZm9ydCwgbm90IHRoZSBjb25uZWN0ZWQgYWdlbnQncy5cbiAgY29uc3QgZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCA9IGJyaWVmT3duc0dhcFxuICAgID8gdW5kZWZpbmVkXG4gICAgOiBnZXRFZmZvcnROb3RpZmljYXRpb25UZXh0KGVmZm9ydFZhbHVlLCBtYWluTG9vcE1vZGVsKVxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGlmICghZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCkge1xuICAgICAgcmVtb3ZlTm90aWZpY2F0aW9uKCdlZmZvcnQtbGV2ZWwnKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICBrZXk6ICdlZmZvcnQtbGV2ZWwnLFxuICAgICAgdGV4dDogZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCxcbiAgICAgIHByaW9yaXR5OiAnaGlnaCcsXG4gICAgICB0aW1lb3V0TXM6IDEyXzAwMCxcbiAgICB9KVxuICB9LCBbZWZmb3J0Tm90aWZpY2F0aW9uVGV4dCwgYWRkTm90aWZpY2F0aW9uLCByZW1vdmVOb3RpZmljYXRpb25dKVxuXG4gIHVzZUJ1ZGR5Tm90aWZpY2F0aW9uKClcblxuICBjb25zdCBjb21wYW5pb25TcGVha2luZyA9IGZlYXR1cmUoJ0JVRERZJylcbiAgICA/IC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgICAgIHVzZUFwcFN0YXRlKHMgPT4gcy5jb21wYW5pb25SZWFjdGlvbiAhPT0gdW5kZWZpbmVkKVxuICAgIDogZmFsc2VcbiAgY29uc3QgeyBjb2x1bW5zLCByb3dzIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuICBjb25zdCB0ZXh0SW5wdXRDb2x1bW5zID1cbiAgICBjb2x1bW5zIC0gMyAtIGNvbXBhbmlvblJlc2VydmVkQ29sdW1ucyhjb2x1bW5zLCBjb21wYW5pb25TcGVha2luZylcblxuICAvLyBQT0M6IGNsaWNrLXRvLXBvc2l0aW9uLWN1cnNvci4gTW91c2UgdHJhY2tpbmcgaXMgb25seSBlbmFibGVkIGluc2lkZVxuICAvLyA8QWx0ZXJuYXRlU2NyZWVuPiwgc28gdGhpcyBpcyBkb3JtYW50IGluIHRoZSBub3JtYWwgbWFpbi1zY3JlZW4gUkVQTC5cbiAgLy8gbG9jYWxDb2wvbG9jYWxSb3cgYXJlIHJlbGF0aXZlIHRvIHRoZSBvbkNsaWNrIEJveCdzIHRvcC1sZWZ0OyB0aGUgQm94XG4gIC8vIHRpZ2h0bHkgd3JhcHMgdGhlIHRleHQgaW5wdXQgc28gdGhleSBtYXAgZGlyZWN0bHkgdG8gKGNvbHVtbiwgbGluZSlcbiAgLy8gaW4gdGhlIEN1cnNvciB3cmFwIG1vZGVsLiBNZWFzdXJlZFRleHQuZ2V0T2Zmc2V0RnJvbVBvc2l0aW9uIGhhbmRsZXNcbiAgLy8gd2lkZSBjaGFycywgd3JhcHBlZCBsaW5lcywgYW5kIGNsYW1wcyBwYXN0LWVuZCBjbGlja3MgdG8gbGluZSBlbmQuXG4gIGNvbnN0IG1heFZpc2libGVMaW5lcyA9IGlzRnVsbHNjcmVlbkVudkVuYWJsZWQoKVxuICAgID8gTWF0aC5tYXgoXG4gICAgICAgIE1JTl9JTlBVVF9WSUVXUE9SVF9MSU5FUyxcbiAgICAgICAgTWF0aC5mbG9vcihyb3dzIC8gMikgLSBQUk9NUFRfRk9PVEVSX0xJTkVTLFxuICAgICAgKVxuICAgIDogdW5kZWZpbmVkXG5cbiAgY29uc3QgaGFuZGxlSW5wdXRDbGljayA9IHVzZUNhbGxiYWNrKFxuICAgIChlOiBDbGlja0V2ZW50KSA9PiB7XG4gICAgICAvLyBEdXJpbmcgaGlzdG9yeSBzZWFyY2ggdGhlIGRpc3BsYXllZCB0ZXh0IGlzIGhpc3RvcnlNYXRjaCwgbm90XG4gICAgICAvLyBpbnB1dCwgYW5kIHNob3dDdXJzb3IgaXMgZmFsc2UgYW55d2F5IOKAlCBza2lwIHJhdGhlciB0aGFuXG4gICAgICAvLyBjb21wdXRlIGFuIG9mZnNldCBhZ2FpbnN0IHRoZSB3cm9uZyBzdHJpbmcuXG4gICAgICBpZiAoIWlucHV0IHx8IGlzU2VhcmNoaW5nSGlzdG9yeSkgcmV0dXJuXG4gICAgICBjb25zdCBjID0gQ3Vyc29yLmZyb21UZXh0KGlucHV0LCB0ZXh0SW5wdXRDb2x1bW5zLCBjdXJzb3JPZmZzZXQpXG4gICAgICBjb25zdCB2aWV3cG9ydFN0YXJ0ID0gYy5nZXRWaWV3cG9ydFN0YXJ0TGluZShtYXhWaXNpYmxlTGluZXMpXG4gICAgICBjb25zdCBvZmZzZXQgPSBjLm1lYXN1cmVkVGV4dC5nZXRPZmZzZXRGcm9tUG9zaXRpb24oe1xuICAgICAgICBsaW5lOiBlLmxvY2FsUm93ICsgdmlld3BvcnRTdGFydCxcbiAgICAgICAgY29sdW1uOiBlLmxvY2FsQ29sLFxuICAgICAgfSlcbiAgICAgIHNldEN1cnNvck9mZnNldChvZmZzZXQpXG4gICAgfSxcbiAgICBbXG4gICAgICBpbnB1dCxcbiAgICAgIHRleHRJbnB1dENvbHVtbnMsXG4gICAgICBpc1NlYXJjaGluZ0hpc3RvcnksXG4gICAgICBjdXJzb3JPZmZzZXQsXG4gICAgICBtYXhWaXNpYmxlTGluZXMsXG4gICAgXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZU9wZW5UYXNrc0RpYWxvZyA9IHVzZUNhbGxiYWNrKFxuICAgICh0YXNrSWQ/OiBzdHJpbmcpID0+IHNldFNob3dCYXNoZXNEaWFsb2codGFza0lkID8/IHRydWUpLFxuICAgIFtzZXRTaG93QmFzaGVzRGlhbG9nXSxcbiAgKVxuXG4gIGNvbnN0IHBsYWNlaG9sZGVyID1cbiAgICBzaG93UHJvbXB0U3VnZ2VzdGlvbiAmJiBwcm9tcHRTdWdnZXN0aW9uXG4gICAgICA/IHByb21wdFN1Z2dlc3Rpb25cbiAgICAgIDogZGVmYXVsdFBsYWNlaG9sZGVyXG5cbiAgLy8gQ2FsY3VsYXRlIGlmIGlucHV0IGhhcyBtdWx0aXBsZSBsaW5lc1xuICBjb25zdCBpc0lucHV0V3JhcHBlZCA9IHVzZU1lbW8oKCkgPT4gaW5wdXQuaW5jbHVkZXMoJ1xcbicpLCBbaW5wdXRdKVxuXG4gIC8vIE1lbW9pemVkIGNhbGxiYWNrcyBmb3IgbW9kZWwgcGlja2VyIHRvIHByZXZlbnQgcmUtcmVuZGVycyB3aGVuIHVucmVsYXRlZFxuICAvLyBzdGF0ZSAobGlrZSBub3RpZmljYXRpb25zKSBjaGFuZ2VzLiBUaGlzIHByZXZlbnRzIHRoZSBpbmxpbmUgbW9kZWwgcGlja2VyXG4gIC8vIGZyb20gdmlzdWFsbHkgXCJqdW1waW5nXCIgd2hlbiBub3RpZmljYXRpb25zIGFycml2ZS5cbiAgY29uc3QgaGFuZGxlTW9kZWxTZWxlY3QgPSB1c2VDYWxsYmFjayhcbiAgICAobW9kZWw6IHN0cmluZyB8IG51bGwsIF9lZmZvcnQ6IEVmZm9ydExldmVsIHwgdW5kZWZpbmVkKSA9PiB7XG4gICAgICBsZXQgd2FzRmFzdE1vZGVEaXNhYmxlZCA9IGZhbHNlXG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+IHtcbiAgICAgICAgd2FzRmFzdE1vZGVEaXNhYmxlZCA9XG4gICAgICAgICAgaXNGYXN0TW9kZUVuYWJsZWQoKSAmJlxuICAgICAgICAgICFpc0Zhc3RNb2RlU3VwcG9ydGVkQnlNb2RlbChtb2RlbCkgJiZcbiAgICAgICAgICAhIXByZXYuZmFzdE1vZGVcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgIG1haW5Mb29wTW9kZWw6IG1vZGVsLFxuICAgICAgICAgIG1haW5Mb29wTW9kZWxGb3JTZXNzaW9uOiBudWxsLFxuICAgICAgICAgIC8vIFR1cm4gb2ZmIGZhc3QgbW9kZSBpZiBzd2l0Y2hpbmcgdG8gYSBtb2RlbCB0aGF0IGRvZXNuJ3Qgc3VwcG9ydCBpdFxuICAgICAgICAgIC4uLih3YXNGYXN0TW9kZURpc2FibGVkICYmIHsgZmFzdE1vZGU6IGZhbHNlIH0pLFxuICAgICAgICB9XG4gICAgICB9KVxuICAgICAgc2V0U2hvd01vZGVsUGlja2VyKGZhbHNlKVxuICAgICAgY29uc3QgZWZmZWN0aXZlRmFzdE1vZGUgPSAoaXNGYXN0TW9kZSA/PyBmYWxzZSkgJiYgIXdhc0Zhc3RNb2RlRGlzYWJsZWRcbiAgICAgIGxldCBtZXNzYWdlID0gYE1vZGVsIHNldCB0byAke21vZGVsRGlzcGxheVN0cmluZyhtb2RlbCl9YFxuICAgICAgaWYgKFxuICAgICAgICBpc0JpbGxlZEFzRXh0cmFVc2FnZShtb2RlbCwgZWZmZWN0aXZlRmFzdE1vZGUsIGlzT3B1czFtTWVyZ2VFbmFibGVkKCkpXG4gICAgICApIHtcbiAgICAgICAgbWVzc2FnZSArPSAnIMK3IEJpbGxlZCBhcyBleHRyYSB1c2FnZSdcbiAgICAgIH1cbiAgICAgIGlmICh3YXNGYXN0TW9kZURpc2FibGVkKSB7XG4gICAgICAgIG1lc3NhZ2UgKz0gJyDCtyBGYXN0IG1vZGUgT0ZGJ1xuICAgICAgfVxuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAnbW9kZWwtc3dpdGNoZWQnLFxuICAgICAgICBqc3g6IDxUZXh0PnttZXNzYWdlfTwvVGV4dD4sXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgdGltZW91dE1zOiAzMDAwLFxuICAgICAgfSlcbiAgICAgIGxvZ0V2ZW50KCd0ZW5ndV9tb2RlbF9waWNrZXJfaG90a2V5Jywge1xuICAgICAgICBtb2RlbDpcbiAgICAgICAgICBtb2RlbCBhcyBBbmFseXRpY3NNZXRhZGF0YV9JX1ZFUklGSUVEX1RISVNfSVNfTk9UX0NPREVfT1JfRklMRVBBVEhTLFxuICAgICAgfSlcbiAgICB9LFxuICAgIFtzZXRBcHBTdGF0ZSwgYWRkTm90aWZpY2F0aW9uLCBpc0Zhc3RNb2RlXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZU1vZGVsQ2FuY2VsID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIHNldFNob3dNb2RlbFBpY2tlcihmYWxzZSlcbiAgfSwgW10pXG5cbiAgLy8gTWVtb2l6ZSB0aGUgbW9kZWwgcGlja2VyIGVsZW1lbnQgdG8gcHJldmVudCB1bm5lY2Vzc2FyeSByZS1yZW5kZXJzXG4gIC8vIHdoZW4gQXBwU3RhdGUgY2hhbmdlcyBmb3IgdW5yZWxhdGVkIHJlYXNvbnMgKGUuZy4sIG5vdGlmaWNhdGlvbnMgYXJyaXZpbmcpXG4gIGNvbnN0IG1vZGVsUGlja2VyRWxlbWVudCA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmICghc2hvd01vZGVsUGlja2VyKSByZXR1cm4gbnVsbFxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8TW9kZWxQaWNrZXJcbiAgICAgICAgICBpbml0aWFsPXttYWluTG9vcE1vZGVsX31cbiAgICAgICAgICBzZXNzaW9uTW9kZWw9e21haW5Mb29wTW9kZWxGb3JTZXNzaW9ufVxuICAgICAgICAgIG9uU2VsZWN0PXtoYW5kbGVNb2RlbFNlbGVjdH1cbiAgICAgICAgICBvbkNhbmNlbD17aGFuZGxlTW9kZWxDYW5jZWx9XG4gICAgICAgICAgaXNTdGFuZGFsb25lQ29tbWFuZFxuICAgICAgICAgIHNob3dGYXN0TW9kZU5vdGljZT17XG4gICAgICAgICAgICBpc0Zhc3RNb2RlRW5hYmxlZCgpICYmXG4gICAgICAgICAgICBpc0Zhc3RNb2RlICYmXG4gICAgICAgICAgICBpc0Zhc3RNb2RlU3VwcG9ydGVkQnlNb2RlbChtYWluTG9vcE1vZGVsXykgJiZcbiAgICAgICAgICAgIGlzRmFzdE1vZGVBdmFpbGFibGUoKVxuICAgICAgICAgIH1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfSwgW1xuICAgIHNob3dNb2RlbFBpY2tlcixcbiAgICBtYWluTG9vcE1vZGVsXyxcbiAgICBtYWluTG9vcE1vZGVsRm9yU2Vzc2lvbixcbiAgICBoYW5kbGVNb2RlbFNlbGVjdCxcbiAgICBoYW5kbGVNb2RlbENhbmNlbCxcbiAgXSlcblxuICBjb25zdCBoYW5kbGVGYXN0TW9kZVNlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgIChyZXN1bHQ/OiBzdHJpbmcpID0+IHtcbiAgICAgIHNldFNob3dGYXN0TW9kZVBpY2tlcihmYWxzZSlcbiAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAgICBrZXk6ICdmYXN0LW1vZGUtdG9nZ2xlZCcsXG4gICAgICAgICAganN4OiA8VGV4dD57cmVzdWx0fTwvVGV4dD4sXG4gICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgIHRpbWVvdXRNczogMzAwMCxcbiAgICAgICAgfSlcbiAgICAgIH1cbiAgICB9LFxuICAgIFthZGROb3RpZmljYXRpb25dLFxuICApXG5cbiAgLy8gTWVtb2l6ZSB0aGUgZmFzdCBtb2RlIHBpY2tlciBlbGVtZW50XG4gIGNvbnN0IGZhc3RNb2RlUGlja2VyRWxlbWVudCA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmICghc2hvd0Zhc3RNb2RlUGlja2VyKSByZXR1cm4gbnVsbFxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8RmFzdE1vZGVQaWNrZXJcbiAgICAgICAgICBvbkRvbmU9e2hhbmRsZUZhc3RNb2RlU2VsZWN0fVxuICAgICAgICAgIHVuYXZhaWxhYmxlUmVhc29uPXtnZXRGYXN0TW9kZVVuYXZhaWxhYmxlUmVhc29uKCl9XG4gICAgICAgIC8+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH0sIFtzaG93RmFzdE1vZGVQaWNrZXIsIGhhbmRsZUZhc3RNb2RlU2VsZWN0XSlcblxuICAvLyBNZW1vaXplZCBjYWxsYmFja3MgZm9yIHRoaW5raW5nIHRvZ2dsZVxuICBjb25zdCBoYW5kbGVUaGlua2luZ1NlbGVjdCA9IHVzZUNhbGxiYWNrKFxuICAgIChlbmFibGVkOiBib29sZWFuKSA9PiB7XG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7XG4gICAgICAgIC4uLnByZXYsXG4gICAgICAgIHRoaW5raW5nRW5hYmxlZDogZW5hYmxlZCxcbiAgICAgIH0pKVxuICAgICAgc2V0U2hvd1RoaW5raW5nVG9nZ2xlKGZhbHNlKVxuICAgICAgbG9nRXZlbnQoJ3Rlbmd1X3RoaW5raW5nX3RvZ2dsZWRfaG90a2V5JywgeyBlbmFibGVkIH0pXG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6ICd0aGlua2luZy10b2dnbGVkLWhvdGtleScsXG4gICAgICAgIGpzeDogKFxuICAgICAgICAgIDxUZXh0IGNvbG9yPXtlbmFibGVkID8gJ3N1Z2dlc3Rpb24nIDogdW5kZWZpbmVkfSBkaW1Db2xvcj17IWVuYWJsZWR9PlxuICAgICAgICAgICAgVGhpbmtpbmcge2VuYWJsZWQgPyAnb24nIDogJ29mZid9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICApLFxuICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgIHRpbWVvdXRNczogMzAwMCxcbiAgICAgIH0pXG4gICAgfSxcbiAgICBbc2V0QXBwU3RhdGUsIGFkZE5vdGlmaWNhdGlvbl0sXG4gIClcblxuICBjb25zdCBoYW5kbGVUaGlua2luZ0NhbmNlbCA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRTaG93VGhpbmtpbmdUb2dnbGUoZmFsc2UpXG4gIH0sIFtdKVxuXG4gIC8vIE1lbW9pemUgdGhlIHRoaW5raW5nIHRvZ2dsZSBlbGVtZW50XG4gIGNvbnN0IHRoaW5raW5nVG9nZ2xlRWxlbWVudCA9IHVzZU1lbW8oKCkgPT4ge1xuICAgIGlmICghc2hvd1RoaW5raW5nVG9nZ2xlKSByZXR1cm4gbnVsbFxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8VGhpbmtpbmdUb2dnbGVcbiAgICAgICAgICBjdXJyZW50VmFsdWU9e3RoaW5raW5nRW5hYmxlZCA/PyB0cnVlfVxuICAgICAgICAgIG9uU2VsZWN0PXtoYW5kbGVUaGlua2luZ1NlbGVjdH1cbiAgICAgICAgICBvbkNhbmNlbD17aGFuZGxlVGhpbmtpbmdDYW5jZWx9XG4gICAgICAgICAgaXNNaWRDb252ZXJzYXRpb249e21lc3NhZ2VzLnNvbWUobSA9PiBtLnR5cGUgPT09ICdhc3Npc3RhbnQnKX1cbiAgICAgICAgLz5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfSwgW1xuICAgIHNob3dUaGlua2luZ1RvZ2dsZSxcbiAgICB0aGlua2luZ0VuYWJsZWQsXG4gICAgaGFuZGxlVGhpbmtpbmdTZWxlY3QsXG4gICAgaGFuZGxlVGhpbmtpbmdDYW5jZWwsXG4gICAgbWVzc2FnZXMubGVuZ3RoLFxuICBdKVxuXG4gIC8vIFBvcnRhbCBkaWFsb2cgdG8gRGlhbG9nT3ZlcmxheSBpbiBmdWxsc2NyZWVuIHNvIGl0IGVzY2FwZXMgdGhlIGJvdHRvbVxuICAvLyBzbG90J3Mgb3ZlcmZsb3dZOmhpZGRlbiBjbGlwIChzYW1lIHBhdHRlcm4gYXMgU3VnZ2VzdGlvbnNPdmVybGF5KS5cbiAgLy8gTXVzdCBiZSBjYWxsZWQgYmVmb3JlIGVhcmx5IHJldHVybnMgYmVsb3cgdG8gc2F0aXNmeSBydWxlcy1vZi1ob29rcy5cbiAgLy8gTWVtb2l6ZWQgc28gdGhlIHBvcnRhbCB1c2VFZmZlY3QgZG9lc24ndCBjaHVybiBvbiBldmVyeSBQcm9tcHRJbnB1dCByZW5kZXIuXG4gIGNvbnN0IGF1dG9Nb2RlT3B0SW5EaWFsb2cgPSB1c2VNZW1vKFxuICAgICgpID0+XG4gICAgICBmZWF0dXJlKCdUUkFOU0NSSVBUX0NMQVNTSUZJRVInKSAmJiBzaG93QXV0b01vZGVPcHRJbiA/IChcbiAgICAgICAgPEF1dG9Nb2RlT3B0SW5EaWFsb2dcbiAgICAgICAgICBvbkFjY2VwdD17aGFuZGxlQXV0b01vZGVPcHRJbkFjY2VwdH1cbiAgICAgICAgICBvbkRlY2xpbmU9e2hhbmRsZUF1dG9Nb2RlT3B0SW5EZWNsaW5lfVxuICAgICAgICAvPlxuICAgICAgKSA6IG51bGwsXG4gICAgW3Nob3dBdXRvTW9kZU9wdEluLCBoYW5kbGVBdXRvTW9kZU9wdEluQWNjZXB0LCBoYW5kbGVBdXRvTW9kZU9wdEluRGVjbGluZV0sXG4gIClcbiAgdXNlU2V0UHJvbXB0T3ZlcmxheURpYWxvZyhcbiAgICBpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgPyBhdXRvTW9kZU9wdEluRGlhbG9nIDogbnVsbCxcbiAgKVxuXG4gIGlmIChzaG93QmFzaGVzRGlhbG9nKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCYWNrZ3JvdW5kVGFza3NEaWFsb2dcbiAgICAgICAgb25Eb25lPXsoKSA9PiBzZXRTaG93QmFzaGVzRGlhbG9nKGZhbHNlKX1cbiAgICAgICAgdG9vbFVzZUNvbnRleHQ9e2dldFRvb2xVc2VDb250ZXh0KFxuICAgICAgICAgIG1lc3NhZ2VzLFxuICAgICAgICAgIFtdLFxuICAgICAgICAgIG5ldyBBYm9ydENvbnRyb2xsZXIoKSxcbiAgICAgICAgICBtYWluTG9vcE1vZGVsLFxuICAgICAgICApfVxuICAgICAgICBpbml0aWFsRGV0YWlsVGFza0lkPXtcbiAgICAgICAgICB0eXBlb2Ygc2hvd0Jhc2hlc0RpYWxvZyA9PT0gJ3N0cmluZycgPyBzaG93QmFzaGVzRGlhbG9nIDogdW5kZWZpbmVkXG4gICAgICAgIH1cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgaWYgKGlzQWdlbnRTd2FybXNFbmFibGVkKCkgJiYgc2hvd1RlYW1zRGlhbG9nKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxUZWFtc0RpYWxvZ1xuICAgICAgICBpbml0aWFsVGVhbXM9e2NhY2hlZFRlYW1zfVxuICAgICAgICBvbkRvbmU9eygpID0+IHtcbiAgICAgICAgICBzZXRTaG93VGVhbXNEaWFsb2coZmFsc2UpXG4gICAgICAgIH19XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIGlmIChmZWF0dXJlKCdRVUlDS19TRUFSQ0gnKSkge1xuICAgIGNvbnN0IGluc2VydFdpdGhTcGFjaW5nID0gKHRleHQ6IHN0cmluZykgPT4ge1xuICAgICAgY29uc3QgY3Vyc29yQ2hhciA9IGlucHV0W2N1cnNvck9mZnNldCAtIDFdID8/ICcgJ1xuICAgICAgaW5zZXJ0VGV4dEF0Q3Vyc29yKC9cXHMvLnRlc3QoY3Vyc29yQ2hhcikgPyB0ZXh0IDogYCAke3RleHR9YClcbiAgICB9XG4gICAgaWYgKHNob3dRdWlja09wZW4pIHtcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxRdWlja09wZW5EaWFsb2dcbiAgICAgICAgICBvbkRvbmU9eygpID0+IHNldFNob3dRdWlja09wZW4oZmFsc2UpfVxuICAgICAgICAgIG9uSW5zZXJ0PXtpbnNlcnRXaXRoU3BhY2luZ31cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICB9XG4gICAgaWYgKHNob3dHbG9iYWxTZWFyY2gpIHtcbiAgICAgIHJldHVybiAoXG4gICAgICAgIDxHbG9iYWxTZWFyY2hEaWFsb2dcbiAgICAgICAgICBvbkRvbmU9eygpID0+IHNldFNob3dHbG9iYWxTZWFyY2goZmFsc2UpfVxuICAgICAgICAgIG9uSW5zZXJ0PXtpbnNlcnRXaXRoU3BhY2luZ31cbiAgICAgICAgLz5cbiAgICAgIClcbiAgICB9XG4gIH1cblxuICBpZiAoZmVhdHVyZSgnSElTVE9SWV9QSUNLRVInKSAmJiBzaG93SGlzdG9yeVBpY2tlcikge1xuICAgIHJldHVybiAoXG4gICAgICA8SGlzdG9yeVNlYXJjaERpYWxvZ1xuICAgICAgICBpbml0aWFsUXVlcnk9e2lucHV0fVxuICAgICAgICBvblNlbGVjdD17ZW50cnkgPT4ge1xuICAgICAgICAgIGNvbnN0IGVudHJ5TW9kZSA9IGdldE1vZGVGcm9tSW5wdXQoZW50cnkuZGlzcGxheSlcbiAgICAgICAgICBjb25zdCB2YWx1ZSA9IGdldFZhbHVlRnJvbUlucHV0KGVudHJ5LmRpc3BsYXkpXG4gICAgICAgICAgb25Nb2RlQ2hhbmdlKGVudHJ5TW9kZSlcbiAgICAgICAgICB0cmFja0FuZFNldElucHV0KHZhbHVlKVxuICAgICAgICAgIHNldFBhc3RlZENvbnRlbnRzKGVudHJ5LnBhc3RlZENvbnRlbnRzKVxuICAgICAgICAgIHNldEN1cnNvck9mZnNldCh2YWx1ZS5sZW5ndGgpXG4gICAgICAgICAgc2V0U2hvd0hpc3RvcnlQaWNrZXIoZmFsc2UpXG4gICAgICAgIH19XG4gICAgICAgIG9uQ2FuY2VsPXsoKSA9PiBzZXRTaG93SGlzdG9yeVBpY2tlcihmYWxzZSl9XG4gICAgICAvPlxuICAgIClcbiAgfVxuXG4gIC8vIFNob3cgbG9vcCBtb2RlIG1lbnUgd2hlbiByZXF1ZXN0ZWQgKGFudC1vbmx5LCBlbGltaW5hdGVkIGZyb20gZXh0ZXJuYWwgYnVpbGRzKVxuICBpZiAobW9kZWxQaWNrZXJFbGVtZW50KSB7XG4gICAgcmV0dXJuIG1vZGVsUGlja2VyRWxlbWVudFxuICB9XG5cbiAgaWYgKGZhc3RNb2RlUGlja2VyRWxlbWVudCkge1xuICAgIHJldHVybiBmYXN0TW9kZVBpY2tlckVsZW1lbnRcbiAgfVxuXG4gIGlmICh0aGlua2luZ1RvZ2dsZUVsZW1lbnQpIHtcbiAgICByZXR1cm4gdGhpbmtpbmdUb2dnbGVFbGVtZW50XG4gIH1cblxuICBpZiAoc2hvd0JyaWRnZURpYWxvZykge1xuICAgIHJldHVybiAoXG4gICAgICA8QnJpZGdlRGlhbG9nXG4gICAgICAgIG9uRG9uZT17KCkgPT4ge1xuICAgICAgICAgIHNldFNob3dCcmlkZ2VEaWFsb2coZmFsc2UpXG4gICAgICAgICAgc2VsZWN0Rm9vdGVySXRlbShudWxsKVxuICAgICAgICB9fVxuICAgICAgLz5cbiAgICApXG4gIH1cblxuICBjb25zdCBiYXNlUHJvcHM6IEJhc2VUZXh0SW5wdXRQcm9wcyA9IHtcbiAgICBtdWx0aWxpbmU6IHRydWUsXG4gICAgb25TdWJtaXQsXG4gICAgb25DaGFuZ2UsXG4gICAgdmFsdWU6IGhpc3RvcnlNYXRjaFxuICAgICAgPyBnZXRWYWx1ZUZyb21JbnB1dChcbiAgICAgICAgICB0eXBlb2YgaGlzdG9yeU1hdGNoID09PSAnc3RyaW5nJ1xuICAgICAgICAgICAgPyBoaXN0b3J5TWF0Y2hcbiAgICAgICAgICAgIDogaGlzdG9yeU1hdGNoLmRpc3BsYXksXG4gICAgICAgIClcbiAgICAgIDogaW5wdXQsXG4gICAgLy8gSGlzdG9yeSBuYXZpZ2F0aW9uIGlzIGhhbmRsZWQgdmlhIFRleHRJbnB1dCBwcm9wcyAob25IaXN0b3J5VXAvb25IaXN0b3J5RG93biksXG4gICAgLy8gTk9UIHZpYSB1c2VLZXliaW5kaW5ncy4gVGhpcyBhbGxvd3MgdXNlVGV4dElucHV0J3MgdXBPckhpc3RvcnlVcC9kb3duT3JIaXN0b3J5RG93blxuICAgIC8vIHRvIHRyeSBjdXJzb3IgbW92ZW1lbnQgZmlyc3QgYW5kIG9ubHkgZmFsbCB0aHJvdWdoIHRvIGhpc3RvcnkgbmF2aWdhdGlvbiB3aGVuIHRoZVxuICAgIC8vIGN1cnNvciBjYW4ndCBtb3ZlIGZ1cnRoZXIgKGltcG9ydGFudCBmb3Igd3JhcHBlZCB0ZXh0IGFuZCBtdWx0aS1saW5lIGlucHV0KS5cbiAgICBvbkhpc3RvcnlVcDogaGFuZGxlSGlzdG9yeVVwLFxuICAgIG9uSGlzdG9yeURvd246IGhhbmRsZUhpc3RvcnlEb3duLFxuICAgIG9uSGlzdG9yeVJlc2V0OiByZXNldEhpc3RvcnksXG4gICAgcGxhY2Vob2xkZXIsXG4gICAgb25FeGl0LFxuICAgIG9uRXhpdE1lc3NhZ2U6IChzaG93LCBrZXkpID0+IHNldEV4aXRNZXNzYWdlKHsgc2hvdywga2V5IH0pLFxuICAgIG9uSW1hZ2VQYXN0ZSxcbiAgICBjb2x1bW5zOiB0ZXh0SW5wdXRDb2x1bW5zLFxuICAgIG1heFZpc2libGVMaW5lcyxcbiAgICBkaXNhYmxlQ3Vyc29yTW92ZW1lbnRGb3JVcERvd25LZXlzOlxuICAgICAgc3VnZ2VzdGlvbnMubGVuZ3RoID4gMCB8fCAhIWZvb3Rlckl0ZW1TZWxlY3RlZCxcbiAgICBkaXNhYmxlRXNjYXBlRG91YmxlUHJlc3M6IHN1Z2dlc3Rpb25zLmxlbmd0aCA+IDAsXG4gICAgY3Vyc29yT2Zmc2V0LFxuICAgIG9uQ2hhbmdlQ3Vyc29yT2Zmc2V0OiBzZXRDdXJzb3JPZmZzZXQsXG4gICAgb25QYXN0ZTogb25UZXh0UGFzdGUsXG4gICAgb25Jc1Bhc3RpbmdDaGFuZ2U6IHNldElzUGFzdGluZyxcbiAgICBmb2N1czogIWlzU2VhcmNoaW5nSGlzdG9yeSAmJiAhaXNNb2RhbE92ZXJsYXlBY3RpdmUgJiYgIWZvb3Rlckl0ZW1TZWxlY3RlZCxcbiAgICBzaG93Q3Vyc29yOlxuICAgICAgIWZvb3Rlckl0ZW1TZWxlY3RlZCAmJiAhaXNTZWFyY2hpbmdIaXN0b3J5ICYmICFjdXJzb3JBdEltYWdlQ2hpcCxcbiAgICBhcmd1bWVudEhpbnQ6IGNvbW1hbmRBcmd1bWVudEhpbnQsXG4gICAgb25VbmRvOiBjYW5VbmRvXG4gICAgICA/ICgpID0+IHtcbiAgICAgICAgICBjb25zdCBwcmV2aW91c1N0YXRlID0gdW5kbygpXG4gICAgICAgICAgaWYgKHByZXZpb3VzU3RhdGUpIHtcbiAgICAgICAgICAgIHRyYWNrQW5kU2V0SW5wdXQocHJldmlvdXNTdGF0ZS50ZXh0KVxuICAgICAgICAgICAgc2V0Q3Vyc29yT2Zmc2V0KHByZXZpb3VzU3RhdGUuY3Vyc29yT2Zmc2V0KVxuICAgICAgICAgICAgc2V0UGFzdGVkQ29udGVudHMocHJldmlvdXNTdGF0ZS5wYXN0ZWRDb250ZW50cylcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIDogdW5kZWZpbmVkLFxuICAgIGhpZ2hsaWdodHM6IGNvbWJpbmVkSGlnaGxpZ2h0cyxcbiAgICBpbmxpbmVHaG9zdFRleHQsXG4gICAgaW5wdXRGaWx0ZXI6IGxhenlTcGFjZUlucHV0RmlsdGVyLFxuICB9XG5cbiAgY29uc3QgZ2V0Qm9yZGVyQ29sb3IgPSAoKToga2V5b2YgVGhlbWUgPT4ge1xuICAgIGNvbnN0IG1vZGVDb2xvcnM6IFJlY29yZDxzdHJpbmcsIGtleW9mIFRoZW1lPiA9IHtcbiAgICAgIGJhc2g6ICdiYXNoQm9yZGVyJyxcbiAgICB9XG5cbiAgICAvLyBNb2RlIGNvbG9ycyB0YWtlIHByaW9yaXR5LCB0aGVuIHRlYW1tYXRlIGNvbG9yLCB0aGVuIGRlZmF1bHRcbiAgICBpZiAobW9kZUNvbG9yc1ttb2RlXSkge1xuICAgICAgcmV0dXJuIG1vZGVDb2xvcnNbbW9kZV1cbiAgICB9XG5cbiAgICAvLyBJbi1wcm9jZXNzIHRlYW1tYXRlcyBydW4gaGVhZGxlc3MgLSBkb24ndCBhcHBseSB0ZWFtbWF0ZSBjb2xvcnMgdG8gbGVhZGVyIFVJXG4gICAgaWYgKGlzSW5Qcm9jZXNzVGVhbW1hdGUoKSkge1xuICAgICAgcmV0dXJuICdwcm9tcHRCb3JkZXInXG4gICAgfVxuXG4gICAgLy8gQ2hlY2sgZm9yIHRlYW1tYXRlIGNvbG9yIGZyb20gZW52aXJvbm1lbnRcbiAgICBjb25zdCB0ZWFtbWF0ZUNvbG9yTmFtZSA9IGdldFRlYW1tYXRlQ29sb3IoKVxuICAgIGlmIChcbiAgICAgIHRlYW1tYXRlQ29sb3JOYW1lICYmXG4gICAgICBBR0VOVF9DT0xPUlMuaW5jbHVkZXModGVhbW1hdGVDb2xvck5hbWUgYXMgQWdlbnRDb2xvck5hbWUpXG4gICAgKSB7XG4gICAgICByZXR1cm4gQUdFTlRfQ09MT1JfVE9fVEhFTUVfQ09MT1JbdGVhbW1hdGVDb2xvck5hbWUgYXMgQWdlbnRDb2xvck5hbWVdXG4gICAgfVxuXG4gICAgcmV0dXJuICdwcm9tcHRCb3JkZXInXG4gIH1cblxuICBpZiAoaXNFeHRlcm5hbEVkaXRvckFjdGl2ZSkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94XG4gICAgICAgIGZsZXhEaXJlY3Rpb249XCJyb3dcIlxuICAgICAgICBhbGlnbkl0ZW1zPVwiY2VudGVyXCJcbiAgICAgICAganVzdGlmeUNvbnRlbnQ9XCJjZW50ZXJcIlxuICAgICAgICBib3JkZXJDb2xvcj17Z2V0Qm9yZGVyQ29sb3IoKX1cbiAgICAgICAgYm9yZGVyU3R5bGU9XCJyb3VuZFwiXG4gICAgICAgIGJvcmRlckxlZnQ9e2ZhbHNlfVxuICAgICAgICBib3JkZXJSaWdodD17ZmFsc2V9XG4gICAgICAgIGJvcmRlckJvdHRvbVxuICAgICAgICB3aWR0aD1cIjEwMCVcIlxuICAgICAgPlxuICAgICAgICA8VGV4dCBkaW1Db2xvciBpdGFsaWM+XG4gICAgICAgICAgU2F2ZSBhbmQgY2xvc2UgZWRpdG9yIHRvIGNvbnRpbnVlLi4uXG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGNvbnN0IHRleHRJbnB1dEVsZW1lbnQgPSBpc1ZpbU1vZGVFbmFibGVkKCkgPyAoXG4gICAgPFZpbVRleHRJbnB1dFxuICAgICAgey4uLmJhc2VQcm9wc31cbiAgICAgIGluaXRpYWxNb2RlPXt2aW1Nb2RlfVxuICAgICAgb25Nb2RlQ2hhbmdlPXtzZXRWaW1Nb2RlfVxuICAgIC8+XG4gICkgOiAoXG4gICAgPFRleHRJbnB1dCB7Li4uYmFzZVByb3BzfSAvPlxuICApXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBtYXJnaW5Ub3A9e2JyaWVmT3duc0dhcCA/IDAgOiAxfT5cbiAgICAgIHshaXNGdWxsc2NyZWVuRW52RW5hYmxlZCgpICYmIDxQcm9tcHRJbnB1dFF1ZXVlZENvbW1hbmRzIC8+fVxuICAgICAge2hhc1N1cHByZXNzZWREaWFsb2dzICYmIChcbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9IG1hcmdpbkxlZnQ9ezJ9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPldhaXRpbmcgZm9yIHBlcm1pc3Npb27igKY8L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICAgIDxQcm9tcHRJbnB1dFN0YXNoTm90aWNlIGhhc1N0YXNoPXtzdGFzaGVkUHJvbXB0ICE9PSB1bmRlZmluZWR9IC8+XG4gICAgICB7c3dhcm1CYW5uZXIgPyAoXG4gICAgICAgIDw+XG4gICAgICAgICAgPFRleHQgY29sb3I9e3N3YXJtQmFubmVyLmJnQ29sb3J9PlxuICAgICAgICAgICAge3N3YXJtQmFubmVyLnRleHQgPyAoXG4gICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgeyfilIAnLnJlcGVhdChcbiAgICAgICAgICAgICAgICAgIE1hdGgubWF4KDAsIGNvbHVtbnMgLSBzdHJpbmdXaWR0aChzd2FybUJhbm5lci50ZXh0KSAtIDQpLFxuICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgICAgPFRleHQgYmFja2dyb3VuZENvbG9yPXtzd2FybUJhbm5lci5iZ0NvbG9yfSBjb2xvcj1cImludmVyc2VUZXh0XCI+XG4gICAgICAgICAgICAgICAgICB7JyAnfVxuICAgICAgICAgICAgICAgICAge3N3YXJtQmFubmVyLnRleHR9eycgJ31cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgeyfilIDilIAnfVxuICAgICAgICAgICAgICA8Lz5cbiAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICfilIAnLnJlcGVhdChjb2x1bW5zKVxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwicm93XCIgd2lkdGg9XCIxMDAlXCI+XG4gICAgICAgICAgICA8UHJvbXB0SW5wdXRNb2RlSW5kaWNhdG9yXG4gICAgICAgICAgICAgIG1vZGU9e21vZGV9XG4gICAgICAgICAgICAgIGlzTG9hZGluZz17aXNMb2FkaW5nfVxuICAgICAgICAgICAgICB2aWV3aW5nQWdlbnROYW1lPXt2aWV3aW5nQWdlbnROYW1lfVxuICAgICAgICAgICAgICB2aWV3aW5nQWdlbnRDb2xvcj17dmlld2luZ0FnZW50Q29sb3J9XG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAgPEJveCBmbGV4R3Jvdz17MX0gZmxleFNocmluaz17MX0gb25DbGljaz17aGFuZGxlSW5wdXRDbGlja30+XG4gICAgICAgICAgICAgIHt0ZXh0SW5wdXRFbGVtZW50fVxuICAgICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgPFRleHQgY29sb3I9e3N3YXJtQmFubmVyLmJnQ29sb3J9Pnsn4pSAJy5yZXBlYXQoY29sdW1ucyl9PC9UZXh0PlxuICAgICAgICA8Lz5cbiAgICAgICkgOiAoXG4gICAgICAgIDxCb3hcbiAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwicm93XCJcbiAgICAgICAgICBhbGlnbkl0ZW1zPVwiZmxleC1zdGFydFwiXG4gICAgICAgICAganVzdGlmeUNvbnRlbnQ9XCJmbGV4LXN0YXJ0XCJcbiAgICAgICAgICBib3JkZXJDb2xvcj17Z2V0Qm9yZGVyQ29sb3IoKX1cbiAgICAgICAgICBib3JkZXJTdHlsZT1cInJvdW5kXCJcbiAgICAgICAgICBib3JkZXJMZWZ0PXtmYWxzZX1cbiAgICAgICAgICBib3JkZXJSaWdodD17ZmFsc2V9XG4gICAgICAgICAgYm9yZGVyQm90dG9tXG4gICAgICAgICAgd2lkdGg9XCIxMDAlXCJcbiAgICAgICAgICBib3JkZXJUZXh0PXtidWlsZEJvcmRlclRleHQoXG4gICAgICAgICAgICBzaG93RmFzdEljb24gPz8gZmFsc2UsXG4gICAgICAgICAgICBzaG93RmFzdEljb25IaW50LFxuICAgICAgICAgICAgZmFzdE1vZGVDb29sZG93bixcbiAgICAgICAgICApfVxuICAgICAgICA+XG4gICAgICAgICAgPFByb21wdElucHV0TW9kZUluZGljYXRvclxuICAgICAgICAgICAgbW9kZT17bW9kZX1cbiAgICAgICAgICAgIGlzTG9hZGluZz17aXNMb2FkaW5nfVxuICAgICAgICAgICAgdmlld2luZ0FnZW50TmFtZT17dmlld2luZ0FnZW50TmFtZX1cbiAgICAgICAgICAgIHZpZXdpbmdBZ2VudENvbG9yPXt2aWV3aW5nQWdlbnRDb2xvcn1cbiAgICAgICAgICAvPlxuICAgICAgICAgIDxCb3ggZmxleEdyb3c9ezF9IGZsZXhTaHJpbms9ezF9IG9uQ2xpY2s9e2hhbmRsZUlucHV0Q2xpY2t9PlxuICAgICAgICAgICAge3RleHRJbnB1dEVsZW1lbnR9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgIDwvQm94PlxuICAgICAgKX1cbiAgICAgIDxQcm9tcHRJbnB1dEZvb3RlclxuICAgICAgICBhcGlLZXlTdGF0dXM9e2FwaUtleVN0YXR1c31cbiAgICAgICAgZGVidWc9e2RlYnVnfVxuICAgICAgICBleGl0TWVzc2FnZT17ZXhpdE1lc3NhZ2V9XG4gICAgICAgIHZpbU1vZGU9e2lzVmltTW9kZUVuYWJsZWQoKSA/IHZpbU1vZGUgOiB1bmRlZmluZWR9XG4gICAgICAgIG1vZGU9e21vZGV9XG4gICAgICAgIGF1dG9VcGRhdGVyUmVzdWx0PXthdXRvVXBkYXRlclJlc3VsdH1cbiAgICAgICAgaXNBdXRvVXBkYXRpbmc9e2lzQXV0b1VwZGF0aW5nfVxuICAgICAgICB2ZXJib3NlPXt2ZXJib3NlfVxuICAgICAgICBvbkF1dG9VcGRhdGVyUmVzdWx0PXtvbkF1dG9VcGRhdGVyUmVzdWx0fVxuICAgICAgICBvbkNoYW5nZUlzVXBkYXRpbmc9e3NldElzQXV0b1VwZGF0aW5nfVxuICAgICAgICBzdWdnZXN0aW9ucz17c3VnZ2VzdGlvbnN9XG4gICAgICAgIHNlbGVjdGVkU3VnZ2VzdGlvbj17c2VsZWN0ZWRTdWdnZXN0aW9ufVxuICAgICAgICBtYXhDb2x1bW5XaWR0aD17bWF4Q29sdW1uV2lkdGh9XG4gICAgICAgIHRvb2xQZXJtaXNzaW9uQ29udGV4dD17ZWZmZWN0aXZlVG9vbFBlcm1pc3Npb25Db250ZXh0fVxuICAgICAgICBoZWxwT3Blbj17aGVscE9wZW59XG4gICAgICAgIHN1cHByZXNzSGludD17aW5wdXQubGVuZ3RoID4gMH1cbiAgICAgICAgaXNMb2FkaW5nPXtpc0xvYWRpbmd9XG4gICAgICAgIHRhc2tzU2VsZWN0ZWQ9e3Rhc2tzU2VsZWN0ZWR9XG4gICAgICAgIHRlYW1zU2VsZWN0ZWQ9e3RlYW1zU2VsZWN0ZWR9XG4gICAgICAgIGJyaWRnZVNlbGVjdGVkPXticmlkZ2VTZWxlY3RlZH1cbiAgICAgICAgdG11eFNlbGVjdGVkPXt0bXV4U2VsZWN0ZWR9XG4gICAgICAgIHRlYW1tYXRlRm9vdGVySW5kZXg9e3RlYW1tYXRlRm9vdGVySW5kZXh9XG4gICAgICAgIGlkZVNlbGVjdGlvbj17aWRlU2VsZWN0aW9ufVxuICAgICAgICBtY3BDbGllbnRzPXttY3BDbGllbnRzfVxuICAgICAgICBpc1Bhc3Rpbmc9e2lzUGFzdGluZ31cbiAgICAgICAgaXNJbnB1dFdyYXBwZWQ9e2lzSW5wdXRXcmFwcGVkfVxuICAgICAgICBtZXNzYWdlcz17bWVzc2FnZXN9XG4gICAgICAgIGlzU2VhcmNoaW5nPXtpc1NlYXJjaGluZ0hpc3Rvcnl9XG4gICAgICAgIGhpc3RvcnlRdWVyeT17aGlzdG9yeVF1ZXJ5fVxuICAgICAgICBzZXRIaXN0b3J5UXVlcnk9e3NldEhpc3RvcnlRdWVyeX1cbiAgICAgICAgaGlzdG9yeUZhaWxlZE1hdGNoPXtoaXN0b3J5RmFpbGVkTWF0Y2h9XG4gICAgICAgIG9uT3BlblRhc2tzRGlhbG9nPXtcbiAgICAgICAgICBpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgPyBoYW5kbGVPcGVuVGFza3NEaWFsb2cgOiB1bmRlZmluZWRcbiAgICAgICAgfVxuICAgICAgLz5cbiAgICAgIHtpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgPyBudWxsIDogYXV0b01vZGVPcHRJbkRpYWxvZ31cbiAgICAgIHtpc0Z1bGxzY3JlZW5FbnZFbmFibGVkKCkgPyAoXG4gICAgICAgIC8vIHBvc2l0aW9uPWFic29sdXRlIHRha2VzIHplcm8gbGF5b3V0IGhlaWdodCBzbyB0aGUgc3Bpbm5lclxuICAgICAgICAvLyBkb2Vzbid0IHNoaWZ0IHdoZW4gYSBub3RpZmljYXRpb24gYXBwZWFycy9kaXNhcHBlYXJzLiBZb2dhXG4gICAgICAgIC8vIGFuY2hvcnMgYWJzb2x1dGUgY2hpbGRyZW4gYXQgdGhlIHBhcmVudCdzIGNvbnRlbnQtYm94IG9yaWdpbjtcbiAgICAgICAgLy8gbWFyZ2luVG9wPS0xIHB1bGxzIGl0IGludG8gdGhlIG1hcmdpblRvcD0xIGdhcCByb3cgYWJvdmUgdGhlXG4gICAgICAgIC8vIHByb21wdCBib3JkZXIuIEluIGJyaWVmIG1vZGUgdGhlcmUgaXMgbm8gc3VjaCBnYXAgKGJyaWVmT3duc0dhcFxuICAgICAgICAvLyBzdHJpcHMgb3VyIG1hcmdpblRvcCkgYW5kIEJyaWVmU3Bpbm5lciBzaXRzIGZsdXNoIGFnYWluc3QgdGhlXG4gICAgICAgIC8vIGJvcmRlciDigJQgbWFyZ2luVG9wPS0yIHNraXBzIG92ZXIgdGhlIHNwaW5uZXIgY29udGVudCBpbnRvXG4gICAgICAgIC8vIEJyaWVmU3Bpbm5lcidzIG93biBtYXJnaW5Ub3A9MSBibGFuayByb3cuIGhlaWdodD0xICtcbiAgICAgICAgLy8gb3ZlcmZsb3c9aGlkZGVuIGNsaXBzIG11bHRpLWxpbmUgbm90aWZpY2F0aW9ucyB0byBhIHNpbmdsZSByb3cuXG4gICAgICAgIC8vIGZsZXgtZW5kIGFuY2hvcnMgdGhlIGJvdHRvbSBsaW5lIHNvIHRoZSB2aXNpYmxlIHJvdyBpcyBhbHdheXNcbiAgICAgICAgLy8gdGhlIG1vc3QgcmVjZW50LiBTdXBwcmVzc2VkIHdoaWxlIHRoZSBzbGFzaCBvdmVybGF5IG9yXG4gICAgICAgIC8vIGF1dG8tbW9kZSBvcHQtaW4gZGlhbG9nIGlzIHVwIGJ5IGhlaWdodD0wIChOT1QgdW5tb3VudCkg4oCUIHRoaXNcbiAgICAgICAgLy8gQm94IHJlbmRlcnMgbGF0ZXIgaW4gdHJlZSBvcmRlciBzbyBpdCB3b3VsZCBwYWludCBvdmVyIHRoZWlyXG4gICAgICAgIC8vIGJvdHRvbSByb3cuIEtlZXBpbmcgTm90aWZpY2F0aW9ucyBtb3VudGVkIHByZXZlbnRzIEF1dG9VcGRhdGVyJ3NcbiAgICAgICAgLy8gaW5pdGlhbC1jaGVjayBlZmZlY3QgZnJvbSByZS1maXJpbmcgb24gZXZlcnkgc2xhc2gtY29tcGxldGlvblxuICAgICAgICAvLyB0b2dnbGUgKFBSIzIyNDEzKS5cbiAgICAgICAgPEJveFxuICAgICAgICAgIHBvc2l0aW9uPVwiYWJzb2x1dGVcIlxuICAgICAgICAgIG1hcmdpblRvcD17YnJpZWZPd25zR2FwID8gLTIgOiAtMX1cbiAgICAgICAgICBoZWlnaHQ9e3N1Z2dlc3Rpb25zLmxlbmd0aCA9PT0gMCAmJiAhc2hvd0F1dG9Nb2RlT3B0SW4gPyAxIDogMH1cbiAgICAgICAgICB3aWR0aD1cIjEwMCVcIlxuICAgICAgICAgIHBhZGRpbmdMZWZ0PXsyfVxuICAgICAgICAgIHBhZGRpbmdSaWdodD17MX1cbiAgICAgICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgICAgICBqdXN0aWZ5Q29udGVudD1cImZsZXgtZW5kXCJcbiAgICAgICAgICBvdmVyZmxvdz1cImhpZGRlblwiXG4gICAgICAgID5cbiAgICAgICAgICA8Tm90aWZpY2F0aW9uc1xuICAgICAgICAgICAgYXBpS2V5U3RhdHVzPXthcGlLZXlTdGF0dXN9XG4gICAgICAgICAgICBhdXRvVXBkYXRlclJlc3VsdD17YXV0b1VwZGF0ZXJSZXN1bHR9XG4gICAgICAgICAgICBkZWJ1Zz17ZGVidWd9XG4gICAgICAgICAgICBpc0F1dG9VcGRhdGluZz17aXNBdXRvVXBkYXRpbmd9XG4gICAgICAgICAgICB2ZXJib3NlPXt2ZXJib3NlfVxuICAgICAgICAgICAgbWVzc2FnZXM9e21lc3NhZ2VzfVxuICAgICAgICAgICAgb25BdXRvVXBkYXRlclJlc3VsdD17b25BdXRvVXBkYXRlclJlc3VsdH1cbiAgICAgICAgICAgIG9uQ2hhbmdlSXNVcGRhdGluZz17c2V0SXNBdXRvVXBkYXRpbmd9XG4gICAgICAgICAgICBpZGVTZWxlY3Rpb249e2lkZVNlbGVjdGlvbn1cbiAgICAgICAgICAgIG1jcENsaWVudHM9e21jcENsaWVudHN9XG4gICAgICAgICAgICBpc0lucHV0V3JhcHBlZD17aXNJbnB1dFdyYXBwZWR9XG4gICAgICAgICAgLz5cbiAgICAgICAgPC9Cb3g+XG4gICAgICApIDogbnVsbH1cbiAgICA8L0JveD5cbiAgKVxufVxuXG4vKipcbiAqIENvbXB1dGUgdGhlIGluaXRpYWwgcGFzdGUgSUQgYnkgZmluZGluZyB0aGUgbWF4IElEIHVzZWQgaW4gZXhpc3RpbmcgbWVzc2FnZXMuXG4gKiBUaGlzIGhhbmRsZXMgLS1jb250aW51ZS8tLXJlc3VtZSBzY2VuYXJpb3Mgd2hlcmUgd2UgbmVlZCB0byBhdm9pZCBJRCBjb2xsaXNpb25zLlxuICovXG5mdW5jdGlvbiBnZXRJbml0aWFsUGFzdGVJZChtZXNzYWdlczogTWVzc2FnZVtdKTogbnVtYmVyIHtcbiAgbGV0IG1heElkID0gMFxuICBmb3IgKGNvbnN0IG1lc3NhZ2Ugb2YgbWVzc2FnZXMpIHtcbiAgICBpZiAobWVzc2FnZS50eXBlID09PSAndXNlcicpIHtcbiAgICAgIC8vIENoZWNrIGltYWdlIHBhc3RlIElEc1xuICAgICAgaWYgKG1lc3NhZ2UuaW1hZ2VQYXN0ZUlkcykge1xuICAgICAgICBmb3IgKGNvbnN0IGlkIG9mIG1lc3NhZ2UuaW1hZ2VQYXN0ZUlkcykge1xuICAgICAgICAgIGlmIChpZCA+IG1heElkKSBtYXhJZCA9IGlkXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIENoZWNrIHRleHQgcGFzdGUgcmVmZXJlbmNlcyBpbiBtZXNzYWdlIGNvbnRlbnRcbiAgICAgIGlmIChBcnJheS5pc0FycmF5KG1lc3NhZ2UubWVzc2FnZS5jb250ZW50KSkge1xuICAgICAgICBmb3IgKGNvbnN0IGJsb2NrIG9mIG1lc3NhZ2UubWVzc2FnZS5jb250ZW50KSB7XG4gICAgICAgICAgaWYgKGJsb2NrLnR5cGUgPT09ICd0ZXh0Jykge1xuICAgICAgICAgICAgY29uc3QgcmVmcyA9IHBhcnNlUmVmZXJlbmNlcyhibG9jay50ZXh0KVxuICAgICAgICAgICAgZm9yIChjb25zdCByZWYgb2YgcmVmcykge1xuICAgICAgICAgICAgICBpZiAocmVmLmlkID4gbWF4SWQpIG1heElkID0gcmVmLmlkXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG4gIHJldHVybiBtYXhJZCArIDFcbn1cblxuZnVuY3Rpb24gYnVpbGRCb3JkZXJUZXh0KFxuICBzaG93RmFzdEljb246IGJvb2xlYW4sXG4gIHNob3dGYXN0SWNvbkhpbnQ6IGJvb2xlYW4sXG4gIGZhc3RNb2RlQ29vbGRvd246IGJvb2xlYW4sXG4pOiBCb3JkZXJUZXh0T3B0aW9ucyB8IHVuZGVmaW5lZCB7XG4gIGlmICghc2hvd0Zhc3RJY29uKSByZXR1cm4gdW5kZWZpbmVkXG4gIGNvbnN0IGZhc3RTZWcgPSBzaG93RmFzdEljb25IaW50XG4gICAgPyBgJHtnZXRGYXN0SWNvblN0cmluZyh0cnVlLCBmYXN0TW9kZUNvb2xkb3duKX0gJHtjaGFsay5kaW0oJy9mYXN0Jyl9YFxuICAgIDogZ2V0RmFzdEljb25TdHJpbmcodHJ1ZSwgZmFzdE1vZGVDb29sZG93bilcbiAgcmV0dXJuIHtcbiAgICBjb250ZW50OiBgICR7ZmFzdFNlZ30gYCxcbiAgICBwb3NpdGlvbjogJ3RvcCcsXG4gICAgYWxpZ246ICdlbmQnLFxuICAgIG9mZnNldDogMCxcbiAgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBSZWFjdC5tZW1vKFByb21wdElucHV0KVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxPQUFPLFFBQVEsWUFBWTtBQUNwQyxPQUFPQyxLQUFLLE1BQU0sT0FBTztBQUN6QixPQUFPLEtBQUtDLElBQUksTUFBTSxNQUFNO0FBQzVCLE9BQU8sS0FBS0MsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FDRUMsV0FBVyxFQUNYQyxTQUFTLEVBQ1RDLE9BQU8sRUFDUEMsTUFBTSxFQUNOQyxRQUFRLEVBQ1JDLG9CQUFvQixRQUNmLE9BQU87QUFDZCxTQUFTQyxnQkFBZ0IsUUFBUSw4QkFBOEI7QUFDL0QsU0FBU0MsZUFBZSxRQUFRLDhCQUE4QjtBQUM5RCxTQUNFLEtBQUtDLGNBQWMsRUFDbkJDLGlCQUFpQixRQUNaLGdDQUFnQztBQUN2QyxTQUNFLEtBQUtDLDBEQUEwRCxFQUMvREMsUUFBUSxRQUNILGlDQUFpQztBQUN4QyxTQUNFLEtBQUtDLFFBQVEsRUFDYkMsV0FBVyxFQUNYQyxnQkFBZ0IsRUFDaEJDLGNBQWMsUUFDVCx1QkFBdUI7QUFDOUIsY0FBY0MsVUFBVSxRQUFRLDRCQUE0QjtBQUM1RCxTQUFTQyxNQUFNLFFBQVEsa0JBQWtCO0FBQ3pDLFNBQ0VDLHVCQUF1QixFQUN2QkMsY0FBYyxRQUNULGtDQUFrQztBQUN6QyxPQUFPQyxTQUFTLE1BQU0sWUFBWTtBQUNsQyxTQUFTQyx3QkFBd0IsUUFBUSxnQ0FBZ0M7QUFDekUsU0FDRUMseUJBQXlCLEVBQ3pCQyxvQkFBb0IsUUFDZixxQ0FBcUM7QUFDNUMsU0FBU0MsY0FBYyxRQUFRLDZCQUE2QjtBQUM1RCxTQUFTQyxvQkFBb0IsUUFBUSw2Q0FBNkM7QUFDbEYsU0FBU0MsZ0NBQWdDLFFBQVEsK0NBQStDO0FBQ2hHLFNBQVMsS0FBS0MsT0FBTyxFQUFFQyxVQUFVLFFBQVEsbUJBQW1CO0FBQzVELFNBQVNDLHVCQUF1QixRQUFRLGlDQUFpQztBQUN6RSxTQUFTQyx5QkFBeUIs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 \ No newline at end of file diff --git a/src/components/PromptInput/ShimmeredInput.tsx b/src/components/PromptInput/ShimmeredInput.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b6890e5971dd8176ee243a0135427a67595a5007 --- /dev/null +++ b/src/components/PromptInput/ShimmeredInput.tsx @@ -0,0 +1,143 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Ansi, Box, Text, useAnimationFrame } from '../../ink.js'; +import { segmentTextByHighlights, type TextHighlight } from '../../utils/textHighlighting.js'; +import { ShimmerChar } from '../Spinner/ShimmerChar.js'; +type Props = { + text: string; + highlights: TextHighlight[]; +}; +type LinePart = { + text: string; + highlight: TextHighlight | undefined; + start: number; +}; +export function HighlightedInput(t0) { + const $ = _c(23); + const { + text, + highlights + } = t0; + let lines; + if ($[0] !== highlights || $[1] !== text) { + const segments = segmentTextByHighlights(text, highlights); + lines = [[]]; + let pos = 0; + for (const segment of segments) { + const parts = segment.text.split("\n"); + for (let i = 0; i < parts.length; i++) { + if (i > 0) { + lines.push([]); + pos = pos + 1; + } + const part = parts[i]; + if (part.length > 0) { + lines[lines.length - 1].push({ + text: part, + highlight: segment.highlight, + start: pos + }); + } + pos = pos + part.length; + } + } + $[0] = highlights; + $[1] = text; + $[2] = lines; + } else { + lines = $[2]; + } + let t1; + if ($[3] !== highlights) { + t1 = highlights.some(_temp); + $[3] = highlights; + $[4] = t1; + } else { + t1 = $[4]; + } + const hasShimmer = t1; + let sweepStart = 0; + let cycleLength = 1; + if (hasShimmer) { + let lo = Infinity; + let hi = -Infinity; + if ($[5] !== hi || $[6] !== highlights || $[7] !== lo) { + for (const h_0 of highlights) { + if (h_0.shimmerColor) { + lo = Math.min(lo, h_0.start); + hi = Math.max(hi, h_0.end); + } + } + $[5] = hi; + $[6] = highlights; + $[7] = lo; + $[8] = lo; + $[9] = hi; + } else { + lo = $[8]; + hi = $[9]; + } + sweepStart = lo - 10; + cycleLength = hi - lo + 20; + } + let t2; + if ($[10] !== cycleLength || $[11] !== hasShimmer || $[12] !== lines || $[13] !== sweepStart) { + t2 = { + lines, + hasShimmer, + sweepStart, + cycleLength + }; + $[10] = cycleLength; + $[11] = hasShimmer; + $[12] = lines; + $[13] = sweepStart; + $[14] = t2; + } else { + t2 = $[14]; + } + const { + lines: lines_0, + hasShimmer: hasShimmer_0, + sweepStart: sweepStart_0, + cycleLength: cycleLength_0 + } = t2; + const [ref, time] = useAnimationFrame(hasShimmer_0 ? 50 : null); + const glimmerIndex = hasShimmer_0 ? sweepStart_0 + Math.floor(time / 50) % cycleLength_0 : -100; + let t3; + if ($[15] !== glimmerIndex || $[16] !== lines_0) { + let t4; + if ($[18] !== glimmerIndex) { + t4 = (lineParts, lineIndex) => {lineParts.length === 0 ? : lineParts.map((part_0, partIndex) => { + if (part_0.highlight?.shimmerColor && part_0.highlight.color) { + return {part_0.text.split("").map((char, charIndex) => )}; + } + return {part_0.text}; + })}; + $[18] = glimmerIndex; + $[19] = t4; + } else { + t4 = $[19]; + } + t3 = lines_0.map(t4); + $[15] = glimmerIndex; + $[16] = lines_0; + $[17] = t3; + } else { + t3 = $[17]; + } + let t4; + if ($[20] !== ref || $[21] !== t3) { + t4 = {t3}; + $[20] = ref; + $[21] = t3; + $[22] = t4; + } else { + t4 = $[22]; + } + return t4; +} +function _temp(h) { + return h.shimmerColor; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkFuc2kiLCJCb3giLCJUZXh0IiwidXNlQW5pbWF0aW9uRnJhbWUiLCJzZWdtZW50VGV4dEJ5SGlnaGxpZ2h0cyIsIlRleHRIaWdobGlnaHQiLCJTaGltbWVyQ2hhciIsIlByb3BzIiwidGV4dCIsImhpZ2hsaWdodHMiLCJMaW5lUGFydCIsImhpZ2hsaWdodCIsInN0YXJ0IiwiSGlnaGxpZ2h0ZWRJbnB1dCIsInQwIiwiJCIsIl9jIiwibGluZXMiLCJzZWdtZW50cyIsInBvcyIsInNlZ21lbnQiLCJwYXJ0cyIsInNwbGl0IiwiaSIsImxlbmd0aCIsInB1c2giLCJwYXJ0IiwidDEiLCJzb21lIiwiX3RlbXAiLCJoYXNTaGltbWVyIiwic3dlZXBTdGFydCIsImN5Y2xlTGVuZ3RoIiwibG8iLCJJbmZpbml0eSIsImhpIiwiaF8wIiwiaCIsInNoaW1tZXJDb2xvciIsIk1hdGgiLCJtaW4iLCJtYXgiLCJlbmQiLCJ0MiIsImxpbmVzXzAiLCJoYXNTaGltbWVyXzAiLCJzd2VlcFN0YXJ0XzAiLCJjeWNsZUxlbmd0aF8wIiwicmVmIiwidGltZSIsImdsaW1tZXJJbmRleCIsImZsb29yIiwidDMiLCJ0NCIsImxpbmVQYXJ0cyIsImxpbmVJbmRleCIsIm1hcCIsInBhcnRfMCIsInBhcnRJbmRleCIsImNvbG9yIiwiY2hhciIsImNoYXJJbmRleCIsImRpbUNvbG9yIiwiaW52ZXJzZSJdLCJzb3VyY2VzIjpbIlNoaW1tZXJlZElucHV0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEFuc2ksIEJveCwgVGV4dCwgdXNlQW5pbWF0aW9uRnJhbWUgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQge1xuICBzZWdtZW50VGV4dEJ5SGlnaGxpZ2h0cyxcbiAgdHlwZSBUZXh0SGlnaGxpZ2h0LFxufSBmcm9tICcuLi8uLi91dGlscy90ZXh0SGlnaGxpZ2h0aW5nLmpzJ1xuaW1wb3J0IHsgU2hpbW1lckNoYXIgfSBmcm9tICcuLi9TcGlubmVyL1NoaW1tZXJDaGFyLmpzJ1xuXG50eXBlIFByb3BzID0ge1xuICB0ZXh0OiBzdHJpbmdcbiAgaGlnaGxpZ2h0czogVGV4dEhpZ2hsaWdodFtdXG59XG5cbnR5cGUgTGluZVBhcnQgPSB7XG4gIHRleHQ6IHN0cmluZ1xuICBoaWdobGlnaHQ6IFRleHRIaWdobGlnaHQgfCB1bmRlZmluZWRcbiAgc3RhcnQ6IG51bWJlclxufVxuXG5leHBvcnQgZnVuY3Rpb24gSGlnaGxpZ2h0ZWRJbnB1dCh7IHRleHQsIGhpZ2hsaWdodHMgfTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBUaGUgc2hpbW1lciBhbmltYXRpb24gKGJlbG93KSByZS1yZW5kZXJzIHRoaXMgY29tcG9uZW50IGF0IDIwZnBzIHdoaWxlIHRoZVxuICAvLyB1bHRyYXRoaW5rIGtleXdvcmQgaXMgcHJlc2VudC4gdGV4dC9oaWdobGlnaHRzIGFyZSByZWZlcmVudGlhbGx5IHN0YWJsZVxuICAvLyBhY3Jvc3MgYW5pbWF0aW9uIHRpY2tzIChwYXJlbnQgZG9lc24ndCByZS1yZW5kZXIpLCBzbyBtZW1vaXplIGV2ZXJ5dGhpbmdcbiAgLy8gdGhhdCBkZXJpdmVzIGZyb20gdGhlbTogc2VnbWVudFRleHRCeUhpZ2hsaWdodHMgYWxvbmUgaXMgfjg1wrVzL2NhbGxcbiAgLy8gKHRva2VuaXplICsgc29ydCArIE8obsKyKSBvdmVybGFwKSwgd2hpY2ggYWRkcyB1cCBmYXN0IGF0IDIwZnBzLlxuICBjb25zdCB7IGxpbmVzLCBoYXNTaGltbWVyLCBzd2VlcFN0YXJ0LCBjeWNsZUxlbmd0aCB9ID0gUmVhY3QudXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3Qgc2VnbWVudHMgPSBzZWdtZW50VGV4dEJ5SGlnaGxpZ2h0cyh0ZXh0LCBoaWdobGlnaHRzKVxuXG4gICAgLy8gU3BsaXQgc2VnbWVudHMgYnkgbmV3bGluZXMgaW50byBwZXItbGluZSBncm91cHMuIEluaydzIHJvdy1kaXJlY3Rpb24gQm94XG4gICAgLy8gaW5kZW50cyBjb250aW51YXRpb24gbGluZXMgb2YgYSBtdWx0aS1saW5lIGNoaWxkIHRvIHRoYXQgY2hpbGQncyBYIG9mZnNldC5cbiAgICAvLyBCeSBzcGxpdHRpbmcgYXQgbmV3bGluZXMsIGVhY2ggbGluZSByZW5kZXJzIGFzIGl0cyBvd24gcm93LCBhdm9pZGluZyB0aGVcbiAgICAvLyBpbmNvcnJlY3QgaW5kZW50YXRpb24gd2hlbiBoaWdobGlnaHRlZCB0ZXh0IGlzIGZvbGxvd2VkIGJ5IHdyYXBwZWQgY29udGVudC5cbiAgICBjb25zdCBsaW5lczogTGluZVBhcnRbXVtdID0gW1tdXVxuICAgIGxldCBwb3MgPSAwXG4gICAgZm9yIChjb25zdCBzZWdtZW50IG9mIHNlZ21lbnRzKSB7XG4gICAgICBjb25zdCBwYXJ0cyA9IHNlZ21lbnQudGV4dC5zcGxpdCgnXFxuJylcbiAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgcGFydHMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgaWYgKGkgPiAwKSB7XG4gICAgICAgICAgbGluZXMucHVzaChbXSlcbiAgICAgICAgICBwb3MgKz0gMVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHBhcnQgPSBwYXJ0c1tpXSFcbiAgICAgICAgaWYgKHBhcnQubGVuZ3RoID4gMCkge1xuICAgICAgICAgIGxpbmVzW2xpbmVzLmxlbmd0aCAtIDFdIS5wdXNoKHtcbiAgICAgICAgICAgIHRleHQ6IHBhcnQsXG4gICAgICAgICAgICBoaWdobGlnaHQ6IHNlZ21lbnQuaGlnaGxpZ2h0LFxuICAgICAgICAgICAgc3RhcnQ6IHBvcyxcbiAgICAgICAgICB9KVxuICAgICAgICB9XG4gICAgICAgIHBvcyArPSBwYXJ0Lmxlbmd0aFxuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFNjb3BlIHRoZSBzd2VlcCB0byBzaGltbWVyLWhpZ2hsaWdodGVkIHJhbmdlcyBzbyBjeWNsZSB0aW1lIGRvZXNuJ3QgZ3Jvd1xuICAgIC8vIHdpdGggaW5wdXQgbGVuZ3RoLiBQYWRkaW5nIGNyZWF0ZXMgYW4gb2Zmc2NyZWVuIHBhdXNlIGJldHdlZW4gc3dlZXBzLlxuICAgIGNvbnN0IGhhc1NoaW1tZXIgPSBoaWdobGlnaHRzLnNvbWUoaCA9PiBoLnNoaW1tZXJDb2xvcilcbiAgICBsZXQgc3dlZXBTdGFydCA9IDBcbiAgICBsZXQgY3ljbGVMZW5ndGggPSAxXG4gICAgaWYgKGhhc1NoaW1tZXIpIHtcbiAgICAgIGNvbnN0IHBhZGRpbmcgPSAxMFxuICAgICAgbGV0IGxvID0gSW5maW5pdHlcbiAgICAgIGxldCBoaSA9IC1JbmZpbml0eVxuICAgICAgZm9yIChjb25zdCBoIG9mIGhpZ2hsaWdodHMpIHtcbiAgICAgICAgaWYgKGguc2hpbW1lckNvbG9yKSB7XG4gICAgICAgICAgbG8gPSBNYXRoLm1pbihsbywgaC5zdGFydClcbiAgICAgICAgICBoaSA9IE1hdGgubWF4KGhpLCBoLmVuZClcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgc3dlZXBTdGFydCA9IGxvIC0gcGFkZGluZ1xuICAgICAgY3ljbGVMZW5ndGggPSBoaSAtIGxvICsgcGFkZGluZyAqIDJcbiAgICB9XG5cbiAgICByZXR1cm4geyBsaW5lcywgaGFzU2hpbW1lciwgc3dlZXBTdGFydCwgY3ljbGVMZW5ndGggfVxuICB9LCBbdGV4dCwgaGlnaGxpZ2h0c10pXG5cbiAgY29uc3QgW3JlZiwgdGltZV0gPSB1c2VBbmltYXRpb25GcmFtZShoYXNTaGltbWVyID8gNTAgOiBudWxsKVxuICBjb25zdCBnbGltbWVySW5kZXggPSBoYXNTaGltbWVyXG4gICAgPyBzd2VlcFN0YXJ0ICsgKE1hdGguZmxvb3IodGltZSAvIDUwKSAlIGN5Y2xlTGVuZ3RoKVxuICAgIDogLTEwMFxuXG4gIHJldHVybiAoXG4gICAgPEJveCByZWY9e3JlZn0gZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAge2xpbmVzLm1hcCgobGluZVBhcnRzLCBsaW5lSW5kZXgpID0+IChcbiAgICAgICAgPEJveCBrZXk9e2xpbmVJbmRleH0+XG4gICAgICAgICAge2xpbmVQYXJ0cy5sZW5ndGggPT09IDAgPyAoXG4gICAgICAgICAgICA8VGV4dD4gPC9UZXh0PlxuICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICBsaW5lUGFydHMubWFwKChwYXJ0LCBwYXJ0SW5kZXgpID0+IHtcbiAgICAgICAgICAgICAgaWYgKHBhcnQuaGlnaGxpZ2h0Py5zaGltbWVyQ29sb3IgJiYgcGFydC5oaWdobGlnaHQuY29sb3IpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICAgICAgPFRleHQga2V5PXtwYXJ0SW5kZXh9PlxuICAgICAgICAgICAgICAgICAgICB7cGFydC50ZXh0LnNwbGl0KCcnKS5tYXAoKGNoYXIsIGNoYXJJbmRleCkgPT4gKFxuICAgICAgICAgICAgICAgICAgICAgIDxTaGltbWVyQ2hhclxuICAgICAgICAgICAgICAgICAgICAgICAga2V5PXtjaGFySW5kZXh9XG4gICAgICAgICAgICAgICAgICAgICAgICBjaGFyPXtjaGFyfVxuICAgICAgICAgICAgICAgICAgICAgICAgaW5kZXg9e3BhcnQuc3RhcnQgKyBjaGFySW5kZXh9XG4gICAgICAgICAgICAgICAgICAgICAgICBnbGltbWVySW5kZXg9e2dsaW1tZXJJbmRleH1cbiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2VDb2xvcj17cGFydC5oaWdobGlnaHQhLmNvbG9yIX1cbiAgICAgICAgICAgICAgICAgICAgICAgIHNoaW1tZXJDb2xvcj17cGFydC5oaWdobGlnaHQhLnNoaW1tZXJDb2xvciF9XG4gICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgKSl9XG4gICAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgPFRleHRcbiAgICAgICAgICAgICAgICAgIGtleT17cGFydEluZGV4fVxuICAgICAgICAgICAgICAgICAgY29sb3I9e3BhcnQuaGlnaGxpZ2h0Py5jb2xvcn1cbiAgICAgICAgICAgICAgICAgIGRpbUNvbG9yPXtwYXJ0LmhpZ2hsaWdodD8uZGltQ29sb3J9XG4gICAgICAgICAgICAgICAgICBpbnZlcnNlPXtwYXJ0LmhpZ2hsaWdodD8uaW52ZXJzZX1cbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICA8QW5zaT57cGFydC50ZXh0fTwvQW5zaT5cbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIClcbiAgICAgICAgICAgIH0pXG4gICAgICAgICAgKX1cbiAgICAgICAgPC9Cb3g+XG4gICAgICApKX1cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxJQUFJLEVBQUVDLEdBQUcsRUFBRUMsSUFBSSxFQUFFQyxpQkFBaUIsUUFBUSxjQUFjO0FBQ2pFLFNBQ0VDLHVCQUF1QixFQUN2QixLQUFLQyxhQUFhLFFBQ2IsaUNBQWlDO0FBQ3hDLFNBQVNDLFdBQVcsUUFBUSwyQkFBMkI7QUFFdkQsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLElBQUksRUFBRSxNQUFNO0VBQ1pDLFVBQVUsRUFBRUosYUFBYSxFQUFFO0FBQzdCLENBQUM7QUFFRCxLQUFLSyxRQUFRLEdBQUc7RUFDZEYsSUFBSSxFQUFFLE1BQU07RUFDWkcsU0FBUyxFQUFFTixhQUFhLEdBQUcsU0FBUztFQUNwQ08sS0FBSyxFQUFFLE1BQU07QUFDZixDQUFDO0FBRUQsT0FBTyxTQUFBQyxpQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUEwQjtJQUFBUixJQUFBO0lBQUFDO0VBQUEsSUFBQUssRUFBMkI7RUFBQSxJQUFBRyxLQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBTixVQUFBLElBQUFNLENBQUEsUUFBQVAsSUFBQTtJQU94RCxNQUFBVSxRQUFBLEdBQWlCZCx1QkFB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 \ No newline at end of file diff --git a/src/components/PromptInput/useShowFastIconHint.ts b/src/components/PromptInput/useShowFastIconHint.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a49cd26a20d60ff7279eb1fa3e2989b3af7bc0e --- /dev/null +++ b/src/components/PromptInput/useShowFastIconHint.ts @@ -0,0 +1,31 @@ +import { useEffect, useState } from 'react' + +const HINT_DISPLAY_DURATION_MS = 5000 + +let hasShownThisSession = false + +/** + * Hook to manage the /fast hint display next to the fast icon. + * Shows the hint for 5 seconds once per session. + */ +export function useShowFastIconHint(showFastIcon: boolean): boolean { + const [showHint, setShowHint] = useState(false) + + useEffect(() => { + if (hasShownThisSession || !showFastIcon) { + return + } + + hasShownThisSession = true + setShowHint(true) + + const timer = setTimeout(setShowHint, HINT_DISPLAY_DURATION_MS, false) + + return () => { + clearTimeout(timer) + setShowHint(false) + } + }, [showFastIcon]) + + return showHint +} diff --git a/src/components/Settings/Settings.tsx b/src/components/Settings/Settings.tsx new file mode 100644 index 0000000000000000000000000000000000000000..41b5b26af1e6059c1fd6ba49f682a6346ae08be5 --- /dev/null +++ b/src/components/Settings/Settings.tsx @@ -0,0 +1,137 @@ +import { c as _c } from "react/compiler-runtime"; +// biome-ignore-all assist/source/organizeImports: ANT-ONLY import markers must not be reordered +import * as React from 'react'; +import { Suspense, useState } from 'react'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { useIsInsideModal, useModalOrTerminalSize } from '../../context/modalContext.js'; +import { Pane } from '../design-system/Pane.js'; +import { Tabs, Tab } from '../design-system/Tabs.js'; +import { Status, buildDiagnostics } from './Status.js'; +import { Config } from './Config.js'; +import { Usage } from './Usage.js'; +import type { LocalJSXCommandContext, CommandResultDisplay } from '../../commands.js'; +type Props = { + onClose: (result?: string, options?: { + display?: CommandResultDisplay; + }) => void; + context: LocalJSXCommandContext; + defaultTab: 'Status' | 'Config' | 'Usage' | 'Gates'; +}; +export function Settings(t0) { + const $ = _c(25); + const { + onClose, + context, + defaultTab + } = t0; + const [selectedTab, setSelectedTab] = useState(defaultTab); + const [tabsHidden, setTabsHidden] = useState(false); + const [configOwnsEsc, setConfigOwnsEsc] = useState(false); + const [gatesOwnsEsc, setGatesOwnsEsc] = useState(false); + const insideModal = useIsInsideModal(); + const { + rows + } = useModalOrTerminalSize(useTerminalSize()); + const contentHeight = insideModal ? rows + 1 : Math.max(15, Math.min(Math.floor(rows * 0.8), 30)); + const [diagnosticsPromise] = useState(_temp2); + useExitOnCtrlCDWithKeybindings(); + let t1; + if ($[0] !== onClose || $[1] !== tabsHidden) { + t1 = () => { + if (tabsHidden) { + return; + } + onClose("Status dialog dismissed", { + display: "system" + }); + }; + $[0] = onClose; + $[1] = tabsHidden; + $[2] = t1; + } else { + t1 = $[2]; + } + const handleEscape = t1; + const t2 = !tabsHidden && !(selectedTab === "Config" && configOwnsEsc) && !(selectedTab === "Gates" && gatesOwnsEsc); + let t3; + if ($[3] !== t2) { + t3 = { + context: "Settings", + isActive: t2 + }; + $[3] = t2; + $[4] = t3; + } else { + t3 = $[4]; + } + useKeybinding("confirm:no", handleEscape, t3); + let t4; + if ($[5] !== context || $[6] !== diagnosticsPromise) { + t4 = ; + $[5] = context; + $[6] = diagnosticsPromise; + $[7] = t4; + } else { + t4 = $[7]; + } + let t5; + if ($[8] !== contentHeight || $[9] !== context || $[10] !== onClose) { + t5 = ; + $[8] = contentHeight; + $[9] = context; + $[10] = onClose; + $[11] = t5; + } else { + t5 = $[11]; + } + let t6; + if ($[12] === Symbol.for("react.memo_cache_sentinel")) { + t6 = ; + $[12] = t6; + } else { + t6 = $[12]; + } + let t7; + if ($[13] !== contentHeight) { + t7 = false ? [] : []; + $[13] = contentHeight; + $[14] = t7; + } else { + t7 = $[14]; + } + let t8; + if ($[15] !== t4 || $[16] !== t5 || $[17] !== t7) { + t8 = [t4, t5, t6, ...t7]; + $[15] = t4; + $[16] = t5; + $[17] = t7; + $[18] = t8; + } else { + t8 = $[18]; + } + const tabs = t8; + const t9 = defaultTab !== "Config" && defaultTab !== "Gates"; + const t10 = tabsHidden || insideModal ? undefined : contentHeight; + let t11; + if ($[19] !== selectedTab || $[20] !== t10 || $[21] !== t9 || $[22] !== tabs || $[23] !== tabsHidden) { + t11 = ; + $[19] = selectedTab; + $[20] = t10; + $[21] = t9; + $[22] = tabs; + $[23] = tabsHidden; + $[24] = t11; + } else { + t11 = $[24]; + } + return t11; +} +function _temp2() { + return buildDiagnostics().catch(_temp); +} +function _temp() { + return []; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlN1c3BlbnNlIiwidXNlU3RhdGUiLCJ1c2VLZXliaW5kaW5nIiwidXNlRXhpdE9uQ3RybENEV2l0aEtleWJpbmRpbmdzIiwidXNlVGVybWluYWxTaXplIiwidXNlSXNJbnNpZGVNb2RhbCIsInVzZU1vZGFsT3JUZXJtaW5hbFNpemUiLCJQYW5lIiwiVGFicyIsIlRhYiIsIlN0YXR1cyIsImJ1aWxkRGlhZ25vc3RpY3MiLCJDb25maWciLCJVc2FnZSIsIkxvY2FsSlNYQ29tbWFuZENvbnRleHQiLCJDb21tYW5kUmVzdWx0RGlzcGxheSIsIlByb3BzIiwib25DbG9zZSIsInJlc3VsdCIsIm9wdGlvbnMiLCJkaXNwbGF5IiwiY29udGV4dCIsImRlZmF1bHRUYWIiLCJTZXR0aW5ncyIsInQwIiwiJCIsIl9jIiwic2VsZWN0ZWRUYWIiLCJzZXRTZWxlY3RlZFRhYiIsInRhYnNIaWRkZW4iLCJzZXRUYWJzSGlkZGVuIiwiY29uZmlnT3duc0VzYyIsInNldENvbmZpZ093bnNFc2MiLCJnYXRlc093bnNFc2MiLCJzZXRHYXRlc093bnNFc2MiLCJpbnNpZGVNb2RhbCIsInJvd3MiLCJjb250ZW50SGVpZ2h0IiwiTWF0aCIsIm1heCIsIm1pbiIsImZsb29yIiwiZGlhZ25vc3RpY3NQcm9taXNlIiwiX3RlbXAyIiwidDEiLCJoYW5kbGVFc2NhcGUiLCJ0MiIsInQzIiwiaXNBY3RpdmUiLCJ0NCIsInQ1IiwidDYiLCJTeW1ib2wiLCJmb3IiLCJ0NyIsInQ4IiwidGFicyIsInQ5IiwidDEwIiwidW5kZWZpbmVkIiwidDExIiwiY2F0Y2giLCJfdGVtcCJdLCJzb3VyY2VzIjpbIlNldHRpbmdzLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBiaW9tZS1pZ25vcmUtYWxsIGFzc2lzdC9zb3VyY2Uvb3JnYW5pemVJbXBvcnRzOiBBTlQtT05MWSBpbXBvcnQgbWFya2VycyBtdXN0IG5vdCBiZSByZW9yZGVyZWRcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgU3VzcGVuc2UsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7IHVzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncyB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUV4aXRPbkN0cmxDRFdpdGhLZXliaW5kaW5ncy5qcydcbmltcG9ydCB7IHVzZVRlcm1pbmFsU2l6ZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZVRlcm1pbmFsU2l6ZS5qcydcbmltcG9ydCB7XG4gIHVzZUlzSW5zaWRlTW9kYWwsXG4gIHVzZU1vZGFsT3JUZXJtaW5hbFNpemUsXG59IGZyb20gJy4uLy4uL2NvbnRleHQvbW9kYWxDb250ZXh0LmpzJ1xuaW1wb3J0IHsgUGFuZSB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vUGFuZS5qcydcbmltcG9ydCB7IFRhYnMsIFRhYiB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vVGFicy5qcydcbmltcG9ydCB7IFN0YXR1cywgYnVpbGREaWFnbm9zdGljcyB9IGZyb20gJy4vU3RhdHVzLmpzJ1xuaW1wb3J0IHsgQ29uZmlnIH0gZnJvbSAnLi9Db25maWcuanMnXG5pbXBvcnQgeyBVc2FnZSB9IGZyb20gJy4vVXNhZ2UuanMnXG5pbXBvcnQgdHlwZSB7XG4gIExvY2FsSlNYQ29tbWFuZENvbnRleHQsXG4gIENvbW1hbmRSZXN1bHREaXNwbGF5LFxufSBmcm9tICcuLi8uLi9jb21tYW5kcy5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgb25DbG9zZTogKFxuICAgIHJlc3VsdD86IHN0cmluZyxcbiAgICBvcHRpb25zPzogeyBkaXNwbGF5PzogQ29tbWFuZFJlc3VsdERpc3BsYXkgfSxcbiAgKSA9PiB2b2lkXG4gIGNvbnRleHQ6IExvY2FsSlNYQ29tbWFuZENvbnRleHRcbiAgZGVmYXVsdFRhYjogJ1N0YXR1cycgfCAnQ29uZmlnJyB8ICdVc2FnZScgfCAnR2F0ZXMnXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBTZXR0aW5ncyh7XG4gIG9uQ2xvc2UsXG4gIGNvbnRleHQsXG4gIGRlZmF1bHRUYWIsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtzZWxlY3RlZFRhYiwgc2V0U2VsZWN0ZWRUYWJdID0gdXNlU3RhdGU8c3RyaW5nPihkZWZhdWx0VGFiKVxuICBjb25zdCBbdGFic0hpZGRlbiwgc2V0VGFic0hpZGRlbl0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgLy8gVHJ1ZSB3aGlsZSBDb25maWcncyBvd24gRXNjIGhhbmRsZXIgaXMgYWN0aXZlIChzZWFyY2ggbW9kZSB3aXRoIGNvbnRlbnRcbiAgLy8gZm9jdXNlZCkuIFNldHRpbmdzIG11c3QgY2VkZSBFc2Mgc28gc2VhcmNoIGNhbiBjbGVhci9leGl0IGZpcnN0LlxuICBjb25zdCBbY29uZmlnT3duc0VzYywgc2V0Q29uZmlnT3duc0VzY10gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW2dhdGVzT3duc0VzYywgc2V0R2F0ZXNPd25zRXNjXSA9IHVzZVN0YXRlKGZhbHNlKVxuICAvLyBGaXhlZCBjb250ZW50IGhlaWdodCBzbyBzd2l0Y2hpbmcgdGFicyBkb2Vzbid0IHNoaWZ0IHRoZSBwYW5lIGhlaWdodC5cbiAgLy8gT3V0c2lkZSBtb2RhbHMgY2FwIGF0IG1pbig4MCUgdmlld3BvcnQsIDMwKS4gSW5zaWRlIGEgTW9kYWwgdGhlIG1vZGFsJ3NcbiAgLy8gaW5uZXJTaXplLnJvd3MgSVMgdGhlIFNjcm9sbEJveCB2aWV3cG9ydCDigJQgdGhlIDAuOCBtdWx0aXBsaWVyIG92ZXItXG4gIC8vIHNocmlua3MsIGxlYXZpbmcgZW1wdHkgcm93cyB3aGlsZSBDb25maWcgc2hvd3MgXCLihpMgTiBtb3JlIGJlbG93XCIuXG4gIC8vXG4gIC8vIEluc2lkZS1tb2RhbCBtYXRoOiBDb25maWcncyBwYW5lQ2FwLTEwIGNocm9tZSBlc3RpbWF0ZSB3YXMgdHVuZWQgZm9yXG4gIC8vIG1hcmdpblk9ezF9ICgyIHJvd3MpIHdoaWNoIGlzIHN0cmlwcGVkIGluc2lkZSBtb2RhbHMg4oaSICsyIHRvIHJlY292ZXIuXG4gIC8vIFRoZW4gLTIgZm9yIFRhYnMnIGhlYWRlciByb3cgKyBpdHMgbWFyZ2luVG9wPTEuIFBsdXMgKzEgb2JzZXJ2ZWQgZ2FwXG4gIC8vIGZyb20gdGhlIHBhbmVDYXAtMTAgZXN0aW1hdGUgYmVpbmcgc2xpZ2h0bHkgZ2VuZXJvdXMuIE5ldDogcm93cyArIDEuXG4gIGNvbnN0IGluc2lkZU1vZGFsID0gdXNlSXNJbnNpZGVNb2RhbCgpXG4gIGNvbnN0IHsgcm93cyB9ID0gdXNlTW9kYWxPclRlcm1pbmFsU2l6ZSh1c2VUZXJtaW5hbFNpemUoKSlcbiAgY29uc3QgY29udGVudEhlaWdodCA9IGluc2lkZU1vZGFsXG4gICAgPyByb3dzICsgMVxuICAgIDogTWF0aC5tYXgoMTUsIE1hdGgubWluKE1hdGguZmxvb3Iocm93cyAqIDAuOCksIDMwKSlcbiAgLy8gS2ljayBvZmYgZGlhZ25vc3RpY3Mgb25jZSB3aGVuIHRoZSBwYW5lIG9wZW5zLiBTdGF0dXMgdXNlKClzIHRoaXMgc29cbiAgLy8gaXQgcmVzb2x2ZXMgb25jZSBwZXIgL2NvbmZpZyBpbnZvY2F0aW9uIOKAlCBubyByZS1mZXRjaCBmbGFzaCB3aGVuXG4gIC8vIHRhYmJpbmcgYmFjayB0byBTdGF0dXMgKFRhYiB1bm1vdW50cyBjaGlsZHJlbiB3aGVuIG5vdCBzZWxlY3RlZCkuXG4gIGNvbnN0IFtkaWFnbm9zdGljc1Byb21pc2VdID0gdXNlU3RhdGUoKCkgPT5cbiAgICBidWlsZERpYWdub3N0aWNzKCkuY2F0Y2goKCkgPT4gW10pLFxuICApXG5cbiAgdXNlRXhpdE9uQ3RybENEV2l0aEtleWJpbmRpbmdzKClcblxuICAvLyBIYW5kbGUgZXNjYXBlIHZpYSBrZXliaW5kaW5nIC0gb25seSB3aGVuIG5vdCBpbiBzdWJtZW51XG4gIGNvbnN0IGhhbmRsZUVzY2FwZSA9ICgpID0+IHtcbiAgICAvLyBEb24ndCBoYW5kbGUgZXNjYXBlIHdoZW4gYSBzdWJtZW51IGlzIHNob3dpbmcgKHRhYnNIaWRkZW4gbWVhbnMgc3VibWVudSBpcyBvcGVuKVxuICAgIC8vIExldCB0aGUgc3VibWVudSBoYW5kbGUgZXNjYXBlIHRvIHJldHVybiB0byB0aGUgbWFpbiBtZW51XG4gICAgaWYgKHRhYnNIaWRkZW4pIHtcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICAvLyBUT0RPOiBVcGRhdGUgdG8gXCJTZXR0aW5nc1wiIGRpYWxvZyBvbmNlIHdlIGRlZmluZSAnL3NldHRpbmdzJy5cbiAgICBvbkNsb3NlKCdTdGF0dXMgZGlhbG9nIGRpc21pc3NlZCcsIHsgZGlzcGxheTogJ3N5c3RlbScgfSlcbiAgfVxuXG4gIC8vIERpc2FibGUgd2hlbiBzdWJtZW51IGlzIG9wZW4gc28gdGhlIHN1Ym1lbnUncyBEaWFsb2cgY2FuIGhhbmRsZSBFU0MsXG4gIC8vIGFuZCB3aGVuIENvbmZpZydzIHNlYXJjaCBtb2RlIGlzIGFjdGl2ZSBzbyBpdHMgdXNlSW5wdXQgaGFuZGxlclxuICAvLyAoY2xlYXIgcXVlcnkg4oaSIGV4aXQgc2VhcmNoKSBwcm9jZXNzZXMgRXNjYXBlIGZpcnN0LlxuICB1c2VLZXliaW5kaW5nKCdjb25maXJtOm5vJywgaGFuZGxlRXNjYXBlLCB7XG4gICAgY29udGV4dDogJ1NldHRpbmdzJyxcbiAgICBpc0FjdGl2ZTpcbiAgICAgICF0YWJzSGlkZGVuICYmXG4gICAgICAhKHNlbGVjdGVkVGFiID09PSAnQ29uZmlnJyAmJiBjb25maWdPd25zRXNjKSAmJlxuICAgICAgIShzZWxlY3RlZFRhYiA9PT0gJ0dhdGVzJyAmJiBnYXRlc093bnNFc2MpLFxuICB9KVxuXG4gIGNvbnN0IHRhYnMgPSBbXG4gICAgPFRhYiBrZXk9XCJzdGF0dXNcIiB0aXRsZT1cIlN0YXR1c1wiPlxuICAgICAgPFN0YXR1cyBjb250ZXh0PXtjb250ZXh0fSBkaWFnbm9zdGljc1Byb21pc2U9e2RpYWdub3N0aWNzUHJvbWlzZX0gLz5cbiAgICA8L1RhYj4sXG4gICAgPFRhYiBrZXk9XCJjb25maWdcIiB0aXRsZT1cIkNvbmZpZ1wiPlxuICAgICAgPFN1c3BlbnNlIGZhbGxiYWNrPXtudWxsfT5cbiAgICAgICAgPENvbmZpZ1xuICAgICAgICAgIGNvbnRleHQ9e2NvbnRleHR9XG4gICAgICAgICAgb25DbG9zZT17b25DbG9zZX1cbiAgICAgICAgICBzZXRUYWJzSGlkZGVuPXtzZXRUYWJzSGlkZGVufVxuICAgICAgICAgIG9uSXNTZWFyY2hNb2RlQ2hhbmdlPXtzZXRDb25maWdPd25zRXNjfVxuICAgICAgICAgIGNvbnRlbnRIZWlnaHQ9e2NvbnRlbnRIZWlnaHR9XG4gICAgICAgIC8+XG4gICAgICA8L1N1c3BlbnNlPlxuICAgIDwvVGFiPixcbiAgICA8VGFiIGtleT1cInVzYWdlXCIgdGl0bGU9XCJVc2FnZVwiPlxuICAgICAgPFVzYWdlIC8+XG4gICAgPC9UYWI+LFxuICAgIC4uLihcImV4dGVybmFsXCIgPT09ICdhbnQnXG4gICAgICA/IFtcbiAgICAgICAgICA8VGFiIGtleT1cImdhdGVzXCIgdGl0bGU9XCJHYXRlc1wiPlxuICAgICAgICAgICAgPEdhdGVzXG4gICAgICAgICAgICAgIG9uT3duc0VzY0NoYW5nZT17c2V0R2F0ZXNPd25zRXNjfVxuICAgICAgICAgICAgICBjb250ZW50SGVpZ2h0PXtjb250ZW50SGVpZ2h0fVxuICAgICAgICAgICAgLz5cbiAgICAgICAgICA8L1RhYj4sXG4gICAgICAgIF1cbiAgICAgIDogW10pLFxuICBdXG5cbiAgcmV0dXJuIChcbiAgICA8UGFuZSBjb2xvcj1cInBlcm1pc3Npb25cIj5cbiAgICAgIDxUYWJzXG4gICAgICAgIGNvbG9yPVwicGVybWlzc2lvblwiXG4gICAgICAgIHNlbGVjdGVkVGFiPXtzZWxlY3RlZFRhYn1cbiAgICAgICAgb25UYWJDaGFuZ2U9e3NldFNlbGVjdGVkVGFifVxuICAgICAgICBoaWRkZW49e3RhYnNIaWRkZW59XG4gICAgICAgIC8vIENvbmZpZyBoYXMgaW50ZXJhY3RpdmUgY29udGVudCDigJQgc3RhcnQgd2l0aCBoZWFkZXIgdW5mb2N1c2VkIHNvXG4gICAgICAgIC8vIGxlZnQvcmlnaHQvdGFiIGN5Y2xlIG9wdGlvbiB2YWx1ZXMgaW5zdGVhZCBvZiBzd2l0Y2hpbmcgdGFicy5cbiAgICAgICAgaW5pdGlhbEhlYWRlckZvY3VzZWQ9e2RlZmF1bHRUYWIgIT09ICdDb25maWcnICYmIGRlZmF1bHRUYWIgIT09ICdHYXRlcyd9XG4gICAgICAgIC8vIEluc2lkZSBhIE1vZGFsLCBza2lwIHRoZSBUYWJzLWxldmVsIGNhcCBzbyB0YWxsIHRhYnMgKFN0YXR1cydzXG4gICAgICAgIC8vIE1DUCBsaXN0KSBmbG93IHRvIHRoZWlyIG5hdHVyYWwgaGVpZ2h0IGZvciB0aGUgTW9kYWwncyBTY3JvbGxCb3hcbiAgICAgICAgLy8gdG8gc2Nyb2xsLiBDb25maWcvR2F0ZXMgc3RpbGwgZ2V0IGNvbnRlbnRIZWlnaHQgYWJvdmUg4oCUIHRoZXlcbiAgICAgICAgLy8gcGFnaW5hdGUgaW50ZXJuYWxseSBzbyB0aGlzIG9ubHkgYWZmZWN0cyBTdGF0dXMvVXNhZ2UuXG4gICAgICAgIGNvbnRlbnRIZWlnaHQ9e3RhYnNIaWRkZW4gfHwgaW5zaWRlTW9kYWwgPyB1bmRlZmluZWQgOiBjb250ZW50SGVpZ2h0fVxuICAgICAgPlxuICAgICAgICB7dGFic31cbiAgICAgIDwvVGFicz5cbiAgICA8L1BhbmU+XG4gIClcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBO0FBQ0EsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxRQUFRLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQzFDLFNBQVNDLGFBQWEsUUFBUSxvQ0FBb0M7QUFDbEUsU0FBU0MsOEJBQThCLFFBQVEsK0NBQStDO0FBQzlGLFNBQVNDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDaEUsU0FDRUMsZ0JBQWdCLEVBQ2hCQyxzQkFBc0IsUUFDakIsK0JBQStCO0FBQ3RDLFNBQVNDLElBQUksUUFBUSwwQkFBMEI7QUFDL0MsU0FBU0MsSUFB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 \ No newline at end of file diff --git a/src/components/Settings/Usage.tsx b/src/components/Settings/Usage.tsx new file mode 100644 index 0000000000000000000000000000000000000000..744cb2be5d57327b5c0f6aac26d3a31895017fc4 --- /dev/null +++ b/src/components/Settings/Usage.tsx @@ -0,0 +1,377 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { extraUsage as extraUsageCommand } from 'src/commands/extra-usage/index.js'; +import { formatCost } from 'src/cost-tracker.js'; +import { getSubscriptionType } from 'src/utils/auth.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { Box, Text } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { type ExtraUsage, fetchUtilization, type RateLimit, type Utilization } from '../../services/api/usage.js'; +import { formatResetText } from '../../utils/format.js'; +import { logError } from '../../utils/log.js'; +import { jsonStringify } from '../../utils/slowOperations.js'; +import { ConfigurableShortcutHint } from '../ConfigurableShortcutHint.js'; +import { Byline } from '../design-system/Byline.js'; +import { ProgressBar } from '../design-system/ProgressBar.js'; +import { isEligibleForOverageCreditGrant, OverageCreditUpsell } from '../LogoV2/OverageCreditUpsell.js'; +type LimitBarProps = { + title: string; + limit: RateLimit; + maxWidth: number; + showTimeInReset?: boolean; + extraSubtext?: string; +}; +function LimitBar(t0) { + const $ = _c(34); + const { + title, + limit, + maxWidth, + showTimeInReset: t1, + extraSubtext + } = t0; + const showTimeInReset = t1 === undefined ? true : t1; + const { + utilization, + resets_at + } = limit; + if (utilization === null) { + return null; + } + const usedText = `${Math.floor(utilization)}% used`; + let subtext; + if (resets_at) { + let t2; + if ($[0] !== resets_at || $[1] !== showTimeInReset) { + t2 = formatResetText(resets_at, true, showTimeInReset); + $[0] = resets_at; + $[1] = showTimeInReset; + $[2] = t2; + } else { + t2 = $[2]; + } + subtext = `Resets ${t2}`; + } + if (extraSubtext) { + if (subtext) { + subtext = `${extraSubtext} · ${subtext}`; + } else { + subtext = extraSubtext; + } + } + if (maxWidth >= 62) { + let t2; + if ($[3] !== title) { + t2 = {title}; + $[3] = title; + $[4] = t2; + } else { + t2 = $[4]; + } + const t3 = utilization / 100; + let t4; + if ($[5] !== t3) { + t4 = ; + $[5] = t3; + $[6] = t4; + } else { + t4 = $[6]; + } + let t5; + if ($[7] !== usedText) { + t5 = {usedText}; + $[7] = usedText; + $[8] = t5; + } else { + t5 = $[8]; + } + let t6; + if ($[9] !== t4 || $[10] !== t5) { + t6 = {t4}{t5}; + $[9] = t4; + $[10] = t5; + $[11] = t6; + } else { + t6 = $[11]; + } + let t7; + if ($[12] !== subtext) { + t7 = subtext && {subtext}; + $[12] = subtext; + $[13] = t7; + } else { + t7 = $[13]; + } + let t8; + if ($[14] !== t2 || $[15] !== t6 || $[16] !== t7) { + t8 = {t2}{t6}{t7}; + $[14] = t2; + $[15] = t6; + $[16] = t7; + $[17] = t8; + } else { + t8 = $[17]; + } + return t8; + } else { + let t2; + if ($[18] !== title) { + t2 = {title}; + $[18] = title; + $[19] = t2; + } else { + t2 = $[19]; + } + let t3; + if ($[20] !== subtext) { + t3 = subtext && <> · {subtext}; + $[20] = subtext; + $[21] = t3; + } else { + t3 = $[21]; + } + let t4; + if ($[22] !== t2 || $[23] !== t3) { + t4 = {t2}{t3}; + $[22] = t2; + $[23] = t3; + $[24] = t4; + } else { + t4 = $[24]; + } + const t5 = utilization / 100; + let t6; + if ($[25] !== maxWidth || $[26] !== t5) { + t6 = ; + $[25] = maxWidth; + $[26] = t5; + $[27] = t6; + } else { + t6 = $[27]; + } + let t7; + if ($[28] !== usedText) { + t7 = {usedText}; + $[28] = usedText; + $[29] = t7; + } else { + t7 = $[29]; + } + let t8; + if ($[30] !== t4 || $[31] !== t6 || $[32] !== t7) { + t8 = {t4}{t6}{t7}; + $[30] = t4; + $[31] = t6; + $[32] = t7; + $[33] = t8; + } else { + t8 = $[33]; + } + return t8; + } +} +export function Usage(): React.ReactNode { + const [utilization, setUtilization] = useState(null); + const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const { + columns + } = useTerminalSize(); + const availableWidth = columns - 2; // 2 for screen padding + const maxWidth = Math.min(availableWidth, 80); + const loadUtilization = React.useCallback(async () => { + setIsLoading(true); + setError(null); + try { + const data = await fetchUtilization(); + setUtilization(data); + } catch (err) { + logError(err as Error); + const axiosError = err as { + response?: { + data?: unknown; + }; + }; + const responseBody = axiosError.response?.data ? jsonStringify(axiosError.response.data) : undefined; + setError(responseBody ? `Failed to load usage data: ${responseBody}` : 'Failed to load usage data'); + } finally { + setIsLoading(false); + } + }, []); + useEffect(() => { + void loadUtilization(); + }, [loadUtilization]); + useKeybinding('settings:retry', () => { + void loadUtilization(); + }, { + context: 'Settings', + isActive: !!error && !isLoading + }); + if (error) { + return + Error: {error} + + + + + + + ; + } + if (!utilization) { + return + Loading usage data… + + + + ; + } + + // Only Max and Team plans have a Sonnet limit that differs from the weekly + // limit (see rateLimitMessages.ts). For other plans the bar is redundant. + // Show for null (unknown plan) to stay consistent with rateLimitMessages.ts, + // which labels it "Sonnet limit" in that case. + const subscriptionType = getSubscriptionType(); + const showSonnetBar = subscriptionType === 'max' || subscriptionType === 'team' || subscriptionType === null; + const limits = [{ + title: 'Current session', + limit: utilization.five_hour + }, { + title: 'Current week (all models)', + limit: utilization.seven_day + }, ...(showSonnetBar ? [{ + title: 'Current week (Sonnet only)', + limit: utilization.seven_day_sonnet + }] : [])]; + return + {limits.some(({ + limit + }) => limit) || /usage is only available for subscription plans.} + + {limits.map(({ + title, + limit: limit_0 + }) => limit_0 && )} + + {utilization.extra_usage && } + + {isEligibleForOverageCreditGrant() && } + + + + + ; +} +type ExtraUsageSectionProps = { + extraUsage: ExtraUsage; + maxWidth: number; +}; +const EXTRA_USAGE_SECTION_TITLE = 'Extra usage'; +function ExtraUsageSection(t0) { + const $ = _c(20); + const { + extraUsage, + maxWidth + } = t0; + const subscriptionType = getSubscriptionType(); + const isProOrMax = subscriptionType === "pro" || subscriptionType === "max"; + if (!isProOrMax) { + return false; + } + if (!extraUsage.is_enabled) { + if (extraUsageCommand.isEnabled()) { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {EXTRA_USAGE_SECTION_TITLE}Extra usage not enabled · /extra-usage to enable; + $[0] = t1; + } else { + t1 = $[0]; + } + return t1; + } + return null; + } + if (extraUsage.monthly_limit === null) { + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = {EXTRA_USAGE_SECTION_TITLE}Unlimited; + $[1] = t1; + } else { + t1 = $[1]; + } + return t1; + } + if (typeof extraUsage.used_credits !== "number" || typeof extraUsage.utilization !== "number") { + return null; + } + const t1 = extraUsage.used_credits / 100; + let t2; + if ($[2] !== t1) { + t2 = formatCost(t1, 2); + $[2] = t1; + $[3] = t2; + } else { + t2 = $[3]; + } + const formattedUsedCredits = t2; + const t3 = extraUsage.monthly_limit / 100; + let t4; + if ($[4] !== t3) { + t4 = formatCost(t3, 2); + $[4] = t3; + $[5] = t4; + } else { + t4 = $[5]; + } + const formattedMonthlyLimit = t4; + let T0; + let t5; + let t6; + let t7; + if ($[6] !== extraUsage.utilization) { + const now = new Date(); + const oneMonthReset = new Date(now.getFullYear(), now.getMonth() + 1, 1); + T0 = LimitBar; + t7 = EXTRA_USAGE_SECTION_TITLE; + t5 = extraUsage.utilization; + t6 = oneMonthReset.toISOString(); + $[6] = extraUsage.utilization; + $[7] = T0; + $[8] = t5; + $[9] = t6; + $[10] = t7; + } else { + T0 = $[7]; + t5 = $[8]; + t6 = $[9]; + t7 = $[10]; + } + let t8; + if ($[11] !== t5 || $[12] !== t6) { + t8 = { + utilization: t5, + resets_at: t6 + }; + $[11] = t5; + $[12] = t6; + $[13] = t8; + } else { + t8 = $[13]; + } + const t9 = `${formattedUsedCredits} / ${formattedMonthlyLimit} spent`; + let t10; + if ($[14] !== T0 || $[15] !== maxWidth || $[16] !== t7 || $[17] !== t8 || $[18] !== t9) { + t10 = ; + $[14] = T0; + $[15] = maxWidth; + $[16] = t7; + $[17] = t8; + $[18] = t9; + $[19] = t10; + } else { + t10 = $[19]; + } + return t10; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiZXh0cmFVc2FnZSIsImV4dHJhVXNhZ2VDb21tYW5kIiwiZm9ybWF0Q29zdCIsImdldFN1YnNjcmlwdGlvblR5cGUiLCJ1c2VUZXJtaW5hbFNpemUiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZyIsIkV4dHJhVXNhZ2UiLCJmZXRjaFV0aWxpemF0aW9uIiwiUmF0ZUxpbWl0IiwiVXRpbGl6YXRpb24iLCJmb3JtYXRSZXNldFRleHQiLCJsb2dFcnJvciIsImpzb25TdHJpbmdpZnkiLCJDb25maWd1cmFibGVTaG9ydGN1dEhpbnQiLCJCeWxpbmUiLCJQcm9ncmVzc0JhciIsImlzRWxpZ2libGVGb3JPdmVyYWdlQ3JlZGl0R3JhbnQiLCJPdmVyYWdlQ3JlZGl0VXBzZWxsIiwiTGltaXRCYXJQcm9wcyIsInRpdGxlIiwibGltaXQiLCJtYXhXaWR0aCIsInNob3dUaW1lSW5SZXNldCIsImV4dHJhU3VidGV4dCIsIkxpbWl0QmFyIiwidDAiLCIkIiwiX2MiLCJ0MSIsInVuZGVmaW5lZCIsInV0aWxpemF0aW9uIiwicmVzZXRzX2F0IiwidXNlZFRleHQiLCJNYXRoIiwiZmxvb3IiLCJzdWJ0ZXh0IiwidDIiLCJ0MyIsInQ0IiwibWF4QmFyV2lkdGgiLCJ0NSIsInQ2IiwidDciLCJ0OCIsIlVzYWdlIiwiUmVhY3ROb2RlIiwic2V0VXRpbGl6YXRpb24iLCJlcnJvciIsInNldEVycm9yIiwiaXNMb2FkaW5nIiwic2V0SXNMb2FkaW5nIiwiY29sdW1ucyIsImF2YWlsYWJsZVdpZHRoIiwibWluIiwibG9hZFV0aWxpemF0aW9uIiwidXNlQ2FsbGJhY2siLCJkYXRhIiwiZXJyIiwiRXJyb3IiLCJheGlvc0Vycm9yIiwicmVzcG9uc2UiLCJyZXNwb25zZUJvZHkiLCJjb250ZXh0IiwiaXNBY3RpdmUiLCJzdWJzY3JpcHRpb25UeXBlIiwic2hvd1Nvbm5ldEJhciIsImxpbWl0cyIsImZpdmVfaG91ciIsInNldmVuX2RheSIsInNldmVuX2RheV9zb25uZXQiLCJzb21lIiwibWFwIiwiZXh0cmFfdXNhZ2UiLCJFeHRyYVVzYWdlU2VjdGlvblByb3BzIiwiRVhUUkFfVVNBR0VfU0VDVElPTl9USVRMRSIsIkV4dHJhVXNhZ2VTZWN0aW9uIiwiaXNQcm9Pck1heCIsImlzX2VuYWJsZWQiLCJpc0VuYWJsZWQiLCJTeW1ib2wiLCJmb3IiLCJtb250aGx5X2xpbWl0IiwidXNlZF9jcmVkaXRzIiwiZm9ybWF0dGVkVXNlZENyZWRpdHMiLCJmb3JtYXR0ZWRNb250aGx5TGltaXQiLCJUMCIsIm5vdyIsIkRhdGUiLCJvbmVNb250aFJlc2V0IiwiZ2V0RnVsbFllYXIiLCJnZXRNb250aCIsInRvSVNPU3RyaW5nIiwidDkiLCJ0MTAiXSwic291cmNlcyI6WyJVc2FnZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBleHRyYVVzYWdlIGFzIGV4dHJhVXNhZ2VDb21tYW5kIH0gZnJvbSAnc3JjL2NvbW1hbmRzL2V4dHJhLXVzYWdlL2luZGV4LmpzJ1xuaW1wb3J0IHsgZm9ybWF0Q29zdCB9IGZyb20gJ3NyYy9jb3N0LXRyYWNrZXIuanMnXG5pbXBvcnQgeyBnZXRTdWJzY3JpcHRpb25UeXBlIH0gZnJvbSAnc3JjL3V0aWxzL2F1dGguanMnXG5pbXBvcnQgeyB1c2VUZXJtaW5hbFNpemUgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUZXJtaW5hbFNpemUuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5nIH0gZnJvbSAnLi4vLi4va2V5YmluZGluZ3MvdXNlS2V5YmluZGluZy5qcydcbmltcG9ydCB7XG4gIHR5cGUgRXh0cmFVc2FnZSxcbiAgZmV0Y2hVdGlsaXphdGlvbixcbiAgdHlwZSBSYXRlTGltaXQsXG4gIHR5cGUgVXRpbGl6YXRpb24sXG59IGZyb20gJy4uLy4uL3NlcnZpY2VzL2FwaS91c2FnZS5qcydcbmltcG9ydCB7IGZvcm1hdFJlc2V0VGV4dCB9IGZyb20gJy4uLy4uL3V0aWxzL2Zvcm1hdC5qcydcbmltcG9ydCB7IGxvZ0Vycm9yIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nLmpzJ1xuaW1wb3J0IHsganNvblN0cmluZ2lmeSB9IGZyb20gJy4uLy4uL3V0aWxzL3Nsb3dPcGVyYXRpb25zLmpzJ1xuaW1wb3J0IHsgQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50IH0gZnJvbSAnLi4vQ29uZmlndXJhYmxlU2hvcnRjdXRIaW50LmpzJ1xuaW1wb3J0IHsgQnlsaW5lIH0gZnJvbSAnLi4vZGVzaWduLXN5c3RlbS9CeWxpbmUuanMnXG5pbXBvcnQgeyBQcm9ncmVzc0JhciB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vUHJvZ3Jlc3NCYXIuanMnXG5pbXBvcnQge1xuICBpc0VsaWdpYmxlRm9yT3ZlcmFnZUNyZWRpdEdyYW50LFxuICBPdmVyYWdlQ3JlZGl0VXBzZWxsLFxufSBmcm9tICcuLi9Mb2dvVjIvT3ZlcmFnZUNyZWRpdFVwc2VsbC5qcydcblxudHlwZSBMaW1pdEJhclByb3BzID0ge1xuICB0aXRsZTogc3RyaW5nXG4gIGxpbWl0OiBSYXRlTGltaXRcbiAgbWF4V2lkdGg6IG51bWJlclxuICBzaG93VGltZUluUmVzZXQ/OiBib29sZWFuXG4gIGV4dHJhU3VidGV4dD86IHN0cmluZ1xufVxuXG5mdW5jdGlvbiBMaW1pdEJhcih7XG4gIHRpdGxlLFxuICBsaW1pdCxcbiAgbWF4V2lkdGgsXG4gIHNob3dUaW1lSW5SZXNldCA9IHRydWUsXG4gIGV4dHJhU3VidGV4dCxcbn06IExpbWl0QmFyUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCB7IHV0aWxpemF0aW9uLCByZXNldHNfYXQgfSA9IGxpbWl0XG4gIGlmICh1dGlsaXphdGlvbiA9PT0gbnVsbCkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICAvLyBDYWxjdWxhdGUgdXNhZ2UgcGVyY2VudGFnZVxuICBjb25zdCB1c2VkVGV4dCA9IGAke01hdGguZmxvb3IodXRpbGl6YXRpb24pfSUgdXNlZGBcblxuICBsZXQgc3VidGV4dDogc3RyaW5nIHwgdW5kZWZpbmVkXG4gIGlmIChyZXNldHNfYXQpIHtcbiAgICBzdWJ0ZXh0ID0gYFJlc2V0cyAke2Zvcm1hdFJlc2V0VGV4dChyZXNldHNfYXQsIHRydWUsIHNob3dUaW1lSW5SZXNldCl9YFxuICB9XG5cbiAgaWYgKGV4dHJhU3VidGV4dCkge1xuICAgIGlmIChzdWJ0ZXh0KSB7XG4gICAgICBzdWJ0ZXh0ID0gYCR7ZXh0cmFTdWJ0ZXh0fSDCtyAke3N1YnRleHR9YFxuICAgIH0gZWxzZSB7XG4gICAgICBzdWJ0ZXh0ID0gZXh0cmFTdWJ0ZXh0XG4gICAgfVxuICB9XG5cbiAgY29uc3QgbWF4QmFyV2lkdGggPSA1MFxuICBjb25zdCB1c2VkTGFiZWxTcGFjZSA9IDEyXG4gIGlmIChtYXhXaWR0aCA+PSBtYXhCYXJXaWR0aCArIHVzZWRMYWJlbFNwYWNlKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8VGV4dCBib2xkPnt0aXRsZX08L1RleHQ+XG4gICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cInJvd1wiIGdhcD17MX0+XG4gICAgICAgICAgPFByb2dyZXNzQmFyXG4gICAgICAgICAgICByYXRpbz17dXRpbGl6YXRpb24gLyAxMDB9XG4gICAgICAgICAgICB3aWR0aD17bWF4QmFyV2lkdGh9XG4gICAgICAgICAgICBmaWxsQ29sb3I9XCJyYXRlX2xpbWl0X2ZpbGxcIlxuICAgICAgICAgICAgZW1wdHlDb2xvcj1cInJhdGVfbGltaXRfZW1wdHlcIlxuICAgICAgICAgIC8+XG4gICAgICAgICAgPFRleHQ+e3VzZWRUZXh0fTwvVGV4dD5cbiAgICAgICAgPC9Cb3g+XG4gICAgICAgIHtzdWJ0ZXh0ICYmIDxUZXh0IGRpbUNvbG9yPntzdWJ0ZXh0fTwvVGV4dD59XG4gICAgICA8L0JveD5cbiAgICApXG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICA8VGV4dD5cbiAgICAgICAgICA8VGV4dCBib2xkPnt0aXRsZX08L1RleHQ+XG4gICAgICAgICAge3N1YnRleHQgJiYgKFxuICAgICAgICAgICAgPD5cbiAgICAgICAgICAgICAgPFRleHQ+IDwvVGV4dD5cbiAgICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+wrcge3N1YnRleHR9PC9UZXh0PlxuICAgICAgICAgICAgPC8+XG4gICAgICAgICAgKX1cbiAgICAgICAgPC9UZXh0PlxuICAgICAgICA8UHJvZ3Jlc3NCYXJcbiAgICAgICAgICByYXRpbz17dXRpbGl6YXRpb24gLyAxMDB9XG4gICAgICAgICAgd2lkdGg9e21heFdpZHRofVxuICAgICAgICAgIGZpbGxDb2xvcj1cInJhdGVfbGltaXRfZmlsbFwiXG4gICAgICAgICAgZW1wdHlDb2xvcj1cInJhdGVfbGltaXRfZW1wdHlcIlxuICAgICAgICAvPlxuICAgICAgICA8VGV4dD57dXNlZFRleHR9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBVc2FnZSgpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbdXRpbGl6YXRpb24sIHNldFV0aWxpemF0aW9uXSA9IHVzZVN0YXRlPFV0aWxpemF0aW9uIHwgbnVsbD4obnVsbClcbiAgY29uc3QgW2Vycm9yLCBzZXRFcnJvcl0gPSB1c2VTdGF0ZTxzdHJpbmcgfCBudWxsPihudWxsKVxuICBjb25zdCBbaXNMb2FkaW5nLCBzZXRJc0xvYWRpbmddID0gdXNlU3RhdGUodHJ1ZSlcbiAgY29uc3QgeyBjb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuXG4gIGNvbnN0IGF2YWlsYWJsZVdpZHRoID0gY29sdW1ucyAtIDIgLy8gMiBmb3Igc2NyZWVuIHBhZGRpbmdcbiAgY29uc3QgbWF4V2lkdGggPSBNYXRoLm1pbihhdmFpbGFibGVXaWR0aCwgODApXG5cbiAgY29uc3QgbG9hZFV0aWxpemF0aW9uID0gUmVhY3QudXNlQ2FsbGJhY2soYXN5bmMgKCkgPT4ge1xuICAgIHNldElzTG9hZGluZyh0cnVlKVxuICAgIHNldEVycm9yKG51bGwpXG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGRhdGEgPSBhd2FpdCBmZXRjaFV0aWxpemF0aW9uKClcbiAgICAgIHNldFV0aWxpemF0aW9uKGRhdGEpXG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBsb2dFcnJvcihlcnIgYXMgRXJyb3IpXG4gICAgICBjb25zdCBheGlvc0Vycm9yID0gZXJyIGFzIHsgcmVzcG9uc2U/OiB7IGRhdGE/OiB1bmtub3duIH0gfVxuICAgICAgY29uc3QgcmVzcG9uc2VCb2R5ID0gYXhpb3NFcnJvci5yZXNwb25zZT8uZGF0YVxuICAgICAgICA/IGpzb25TdHJpbmdpZnkoYXhpb3NFcnJvci5yZXNwb25zZS5kYXRhKVxuICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgc2V0RXJyb3IoXG4gICAgICAgIHJlc3BvbnNlQm9keVxuICAgICAgICAgID8gYEZhaWxlZCB0byBsb2FkIHVzYWdlIGRhdGE6ICR7cmVzcG9uc2VCb2R5fWBcbiAgICAgICAgICA6ICdGYWlsZWQgdG8gbG9hZCB1c2FnZSBkYXRhJyxcbiAgICAgIClcbiAgICB9IGZpbmFsbHkge1xuICAgICAgc2V0SXNMb2FkaW5nKGZhbHNlKVxuICAgIH1cbiAgfSwgW10pXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICB2b2lkIGxvYWRVdGlsaXphdGlvbigpXG4gIH0sIFtsb2FkVXRpbGl6YXRpb25dKVxuXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ3NldHRpbmdzOnJldHJ5JyxcbiAgICAoKSA9PiB7XG4gICAgICB2b2lkIGxvYWRVdGlsaXphdGlvbigpXG4gICAgfSxcbiAgICB7IGNvbnRleHQ6ICdTZXR0aW5ncycsIGlzQWN0aXZlOiAhIWVycm9yICYmICFpc0xvYWRpbmcgfSxcbiAgKVxuXG4gIGlmIChlcnJvcikge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9PlxuICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+RXJyb3I6IHtlcnJvcn08L1RleHQ+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgIDxCeWxpbmU+XG4gICAgICAgICAgICA8Q29uZmlndXJhYmxlU2hvcnRjdXRIaW50XG4gICAgICAgICAgICAgIGFjdGlvbj1cInNldHRpbmdzOnJldHJ5XCJcbiAgICAgICAgICAgICAgY29udGV4dD1cIlNldHRpbmdzXCJcbiAgICAgICAgICAgICAgZmFsbGJhY2s9XCJyXCJcbiAgICAgICAgICAgICAgZGVzY3JpcHRpb249XCJyZXRyeVwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgICBhY3Rpb249XCJjb25maXJtOm5vXCJcbiAgICAgICAgICAgICAgY29udGV4dD1cIlNldHRpbmdzXCJcbiAgICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgICBkZXNjcmlwdGlvbj1cImNhbmNlbFwiXG4gICAgICAgICAgICAvPlxuICAgICAgICAgIDwvQnlsaW5lPlxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBpZiAoIXV0aWxpemF0aW9uKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGdhcD17MX0+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPkxvYWRpbmcgdXNhZ2UgZGF0YeKApjwvVGV4dD5cbiAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgICAgYWN0aW9uPVwiY29uZmlybTpub1wiXG4gICAgICAgICAgICBjb250ZXh0PVwiU2V0dGluZ3NcIlxuICAgICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgICAgZGVzY3JpcHRpb249XCJjYW5jZWxcIlxuICAgICAgICAgIC8+XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIC8vIE9ubHkgTWF4IGFuZCBUZWFtIHBsYW5zIGhhdmUgYSBTb25uZXQgbGltaXQgdGhhdCBkaWZmZXJzIGZyb20gdGhlIHdlZWtseVxuICAvLyBsaW1pdCAoc2VlIHJhdGVMaW1pdE1lc3NhZ2VzLnRzKS4gRm9yIG90aGVyIHBsYW5zIHRoZSBiYXIgaXMgcmVkdW5kYW50LlxuICAvLyBTaG93IGZvciBudWxsICh1bmtub3duIHBsYW4pIHRvIHN0YXkgY29uc2lzdGVudCB3aXRoIHJhdGVMaW1pdE1lc3NhZ2VzLnRzLFxuICAvLyB3aGljaCBsYWJlbHMgaXQgXCJTb25uZXQgbGltaXRcIiBpbiB0aGF0IGNhc2UuXG4gIGNvbnN0IHN1YnNjcmlwdGlvblR5cGUgPSBnZXRTdWJzY3JpcHRpb25UeXBlKClcbiAgY29uc3Qgc2hvd1Nvbm5ldEJhciA9XG4gICAgc3Vic2NyaXB0aW9uVHlwZSA9PT0gJ21heCcgfHxcbiAgICBzdWJzY3JpcHRpb25UeXBlID09PSAndGVhbScgfHxcbiAgICBzdWJzY3JpcHRpb25UeXBlID09PSBudWxsXG5cbiAgY29uc3QgbGltaXRzID0gW1xuICAgIHtcbiAgICAgIHRpdGxlOiAnQ3VycmVudCBzZXNzaW9uJyxcbiAgICAgIGxpbWl0OiB1dGlsaXphdGlvbi5maXZlX2hvdXIsXG4gICAgfSxcbiAgICB7XG4gICAgICB0aXRsZTogJ0N1cnJlbnQgd2VlayAoYWxsIG1vZGVscyknLFxuICAgICAgbGltaXQ6IHV0aWxpemF0aW9uLnNldmVuX2RheSxcbiAgICB9LFxuICAgIC4uLihzaG93U29ubmV0QmFyXG4gICAgICA/IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICB0aXRsZTogJ0N1cnJlbnQgd2VlayAoU29ubmV0IG9ubHkpJyxcbiAgICAgICAgICAgIGxpbWl0OiB1dGlsaXphdGlvbi5zZXZlbl9kYXlfc29ubmV0LFxuICAgICAgICAgIH0sXG4gICAgICAgIF1cbiAgICAgIDogW10pLFxuICBdXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezF9IHdpZHRoPVwiMTAwJVwiPlxuICAgICAge2xpbWl0cy5zb21lKCh7IGxpbWl0IH0pID0+IGxpbWl0KSB8fCAoXG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPi91c2FnZSBpcyBvbmx5IGF2YWlsYWJsZSBmb3Igc3Vic2NyaXB0aW9uIHBsYW5zLjwvVGV4dD5cbiAgICAgICl9XG5cbiAgICAgIHtsaW1pdHMubWFwKFxuICAgICAgICAoeyB0aXRsZSwgbGltaXQgfSkgPT5cbiAgICAgICAgICBsaW1pdCAmJiAoXG4gICAgICAgICAgICA8TGltaXRCYXJcbiAgICAgICAgICAgICAga2V5PXt0aXRsZX1cbiAgICAgICAgICAgICAgdGl0bGU9e3RpdGxlfVxuICAgICAgICAgICAgICBsaW1pdD17bGltaXR9XG4gICAgICAgICAgICAgIG1heFdpZHRoPXttYXhXaWR0aH1cbiAgICAgICAgICAgIC8+XG4gICAgICAgICAgKSxcbiAgICAgICl9XG5cbiAgICAgIHt1dGlsaXphdGlvbi5leHRyYV91c2FnZSAmJiAoXG4gICAgICAgIDxFeHRyYVVzYWdlU2VjdGlvblxuICAgICAgICAgIGV4dHJhVXNhZ2U9e3V0aWxpemF0aW9uLmV4dHJhX3VzYWdlfVxuICAgICAgICAgIG1heFdpZHRoPXttYXhXaWR0aH1cbiAgICAgICAgLz5cbiAgICAgICl9XG5cbiAgICAgIHtpc0VsaWdpYmxlRm9yT3ZlcmFnZUNyZWRpdEdyYW50KCkgJiYgKFxuICAgICAgICA8T3ZlcmFnZUNyZWRpdFVwc2VsbCBtYXhXaWR0aD17bWF4V2lkdGh9IC8+XG4gICAgICApfVxuXG4gICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgPENvbmZpZ3VyYWJsZVNob3J0Y3V0SGludFxuICAgICAgICAgIGFjdGlvbj1cImNvbmZpcm06bm9cIlxuICAgICAgICAgIGNvbnRleHQ9XCJTZXR0aW5nc1wiXG4gICAgICAgICAgZmFsbGJhY2s9XCJFc2NcIlxuICAgICAgICAgIGRlc2NyaXB0aW9uPVwiY2FuY2VsXCJcbiAgICAgICAgLz5cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuXG50eXBlIEV4dHJhVXNhZ2VTZWN0aW9uUHJvcHMgPSB7XG4gIGV4dHJhVXNhZ2U6IEV4dHJhVXNhZ2VcbiAgbWF4V2lkdGg6IG51bWJlclxufVxuXG5jb25zdCBFWFRSQV9VU0FHRV9TRUNUSU9OX1RJVExFID0gJ0V4dHJhIHVzYWdlJ1xuXG5mdW5jdGlvbiBFeHRyYVVzYWdlU2VjdGlvbih7XG4gIGV4dHJhVXNhZ2UsXG4gIG1heFdpZHRoLFxufTogRXh0cmFVc2FnZVNlY3Rpb25Qcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IHN1YnNjcmlwdGlvblR5cGUgPSBnZXRTdWJzY3JpcHRpb25UeXBlKClcbiAgY29uc3QgaXNQcm9Pck1heCA9IHN1YnNjcmlwdGlvblR5cGUgPT09ICdwcm8nIHx8IHN1YnNjcmlwdGlvblR5cGUgPT09ICdtYXgnXG4gIGlmICghaXNQcm9Pck1heCkge1xuICAgIC8vIE9ubHkgc2hvdyB0byBQcm8gYW5kIE1heCwgY29uc2lzdGVudCB3aXRoIGNsYXVkZS5haSBub24tYWRtaW4gdXNhZ2Ugc2V0dGluZ3NcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxuXG4gIGlmICghZXh0cmFVc2FnZS5pc19lbmFibGVkKSB7XG4gICAgaWYgKGV4dHJhVXNhZ2VDb21tYW5kLmlzRW5hYmxlZCgpKSB7XG4gICAgICByZXR1cm4gKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgICA8VGV4dCBib2xkPntFWFRSQV9VU0FHRV9TRUNUSU9OX1RJVExFfTwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5FeHRyYSB1c2FnZSBub3QgZW5hYmxlZCDCtyAvZXh0cmEtdXNhZ2UgdG8gZW5hYmxlPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIClcbiAgICB9XG5cbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgaWYgKGV4dHJhVXNhZ2UubW9udGhseV9saW1pdCA9PT0gbnVsbCkge1xuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIj5cbiAgICAgICAgPFRleHQgYm9sZD57RVhUUkFfVVNBR0VfU0VDVElPTl9USVRMRX08L1RleHQ+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlVubGltaXRlZDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIGlmIChcbiAgICB0eXBlb2YgZXh0cmFVc2FnZS51c2VkX2NyZWRpdHMgIT09ICdudW1iZXInIHx8XG4gICAgdHlwZW9mIGV4dHJhVXNhZ2UudXRpbGl6YXRpb24gIT09ICdudW1iZXInXG4gICkge1xuICAgIHJldHVybiBudWxsXG4gIH1cblxuICBjb25zdCBmb3JtYXR0ZWRVc2VkQ3JlZGl0cyA9IGZvcm1hdENvc3QoZXh0cmFVc2FnZS51c2VkX2NyZWRpdHMgLyAxMDAsIDIpXG4gIGNvbnN0IGZvcm1hdHRlZE1vbnRobHlMaW1pdCA9IGZvcm1hdENvc3QoZXh0cmFVc2FnZS5tb250aGx5X2xpbWl0IC8gMTAwLCAyKVxuICBjb25zdCBub3cgPSBuZXcgRGF0ZSgpXG4gIGNvbnN0IG9uZU1vbnRoUmVzZXQgPSBuZXcgRGF0ZShub3cuZ2V0RnVsbFllYXIoKSwgbm93LmdldE1vbnRoKCkgKyAxLCAxKVxuXG4gIHJldHVybiAoXG4gICAgPExpbWl0QmFyXG4gICAgICB0aXRsZT17RVhUUkFfVVNBR0VfU0VDVElPTl9USVRMRX1cbiAgICAgIGxpbWl0PXt7XG4gICAgICAgIHV0aWxpemF0aW9uOiBleHRyYVVzYWdlLnV0aWxpemF0aW9uLFxuICAgICAgICAvLyBOb3QgYXBwbGljYWJsZSBmb3IgZW50ZXJwcmlzZXMsIGJ1dCBmb3Igbm93IHdlIGRvbid0IHJlbmRlciB0aGlzIGZvciB0aGVtXG4gICAgICAgIHJlc2V0c19hdDogb25lTW9udGhSZXNldC50b0lTT1N0cmluZygpLFxuICAgICAgfX1cbiAgICAgIHNob3dUaW1lSW5SZXNldD17ZmFsc2V9XG4gICAgICBleHRyYVN1YnRleHQ9e2Ake2Zvcm1hdHRlZFVzZWRDcmVkaXRzfSAvICR7Zm9ybWF0dGVkTW9udGhseUxpbWl0fSBzcGVudGB9XG4gICAgICBtYXhXaWR0aD17bWF4V2lkdGh9XG4gICAgLz5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxTQUFTLEVBQUVDLFFBQVEsUUFBUSxPQUFPO0FBQzNDLFNBQVNDLFVBQVUsSUFBSUMsaUJBQWlCLFFBQVEsbUNBQW1DO0FBQ25GLFNBQVNDLFVBQVUsUUFBUSxxQkFBcUI7QUFDaEQsU0FBU0MsbUJBQW1CLFFBQVEsbUJBQW1CO0FBQ3ZELFNBQVNDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDaEUsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxhQUFhLFFBQVEsb0NBQW9DO0FBQ2xFLFNBQ0UsS0FBS0MsVUFBVSxFQUNmQyxnQkFBZ0IsRUFDaEIsS0FBS0MsU0FBUyxFQUNkLEtBQUtDLFdBQVcsUUFDWCw2QkFBNkI7QUFDcEMsU0FBU0MsZUFBZSxRQUFRLHVCQUF1QjtBQUN2RCxTQUFTQyxRQUFRLFFBQVEsb0JBQW9CO0FBQzdDLFNBQVNDLGFBQWEsUUFBUSwrQkFBK0I7QUFDN0QsU0FBU0Msd0JBQXdCLFFBQVEsZ0NBQWdDO0FBQ3pFLFNBQVNDLE1BQU0sUUFBUSw0QkFBNEI7QUFDbkQsU0FBU0MsV0FBVyxRQUFRLGlDQUFpQztBQUM3RCxTQUNFQywrQkFBK0IsRUFDL0JDLG1CQUFtQixRQUNkLGtDQUFrQztBQUV6QyxLQUFLQyxhQUFhLEdB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 \ No newline at end of file diff --git a/src/components/Spinner/SpinnerGlyph.tsx b/src/components/Spinner/SpinnerGlyph.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e4d71d4db34381c534de0b8e791d3679e4694d83 --- /dev/null +++ b/src/components/Spinner/SpinnerGlyph.tsx @@ -0,0 +1,80 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { Box, Text, useTheme } from '../../ink.js'; +import { getTheme, type Theme } from '../../utils/theme.js'; +import { getDefaultCharacters, interpolateColor, parseRGB, toRGBColor } from './utils.js'; +const DEFAULT_CHARACTERS = getDefaultCharacters(); +const SPINNER_FRAMES = [...DEFAULT_CHARACTERS, ...[...DEFAULT_CHARACTERS].reverse()]; +const REDUCED_MOTION_DOT = '●'; +const REDUCED_MOTION_CYCLE_MS = 2000; // 2-second cycle: 1s visible, 1s dim +const ERROR_RED = { + r: 171, + g: 43, + b: 63 +}; +type Props = { + frame: number; + messageColor: keyof Theme; + stalledIntensity?: number; + reducedMotion?: boolean; + time?: number; +}; +export function SpinnerGlyph(t0) { + const $ = _c(9); + const { + frame, + messageColor, + stalledIntensity: t1, + reducedMotion: t2, + time: t3 + } = t0; + const stalledIntensity = t1 === undefined ? 0 : t1; + const reducedMotion = t2 === undefined ? false : t2; + const time = t3 === undefined ? 0 : t3; + const [themeName] = useTheme(); + const theme = getTheme(themeName); + if (reducedMotion) { + const isDim = Math.floor(time / (REDUCED_MOTION_CYCLE_MS / 2)) % 2 === 1; + let t4; + if ($[0] !== isDim || $[1] !== messageColor) { + t4 = {REDUCED_MOTION_DOT}; + $[0] = isDim; + $[1] = messageColor; + $[2] = t4; + } else { + t4 = $[2]; + } + return t4; + } + const spinnerChar = SPINNER_FRAMES[frame % SPINNER_FRAMES.length]; + if (stalledIntensity > 0) { + const baseColorStr = theme[messageColor]; + const baseRGB = baseColorStr ? parseRGB(baseColorStr) : null; + if (baseRGB) { + const interpolated = interpolateColor(baseRGB, ERROR_RED, stalledIntensity); + return {spinnerChar}; + } + const color = stalledIntensity > 0.5 ? "error" : messageColor; + let t4; + if ($[3] !== color || $[4] !== spinnerChar) { + t4 = {spinnerChar}; + $[3] = color; + $[4] = spinnerChar; + $[5] = t4; + } else { + t4 = $[5]; + } + return t4; + } + let t4; + if ($[6] !== messageColor || $[7] !== spinnerChar) { + t4 = {spinnerChar}; + $[6] = messageColor; + $[7] = spinnerChar; + $[8] = t4; + } else { + t4 = $[8]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJ1c2VUaGVtZSIsImdldFRoZW1lIiwiVGhlbWUiLCJnZXREZWZhdWx0Q2hhcmFjdGVycyIsImludGVycG9sYXRlQ29sb3IiLCJwYXJzZVJHQiIsInRvUkdCQ29sb3IiLCJERUZBVUxUX0NIQVJBQ1RFUlMiLCJTUElOTkVSX0ZSQU1FUyIsInJldmVyc2UiLCJSRURVQ0VEX01PVElPTl9ET1QiLCJSRURVQ0VEX01PVElPTl9DWUNMRV9NUyIsIkVSUk9SX1JFRCIsInIiLCJnIiwiYiIsIlByb3BzIiwiZnJhbWUiLCJtZXNzYWdlQ29sb3IiLCJzdGFsbGVkSW50ZW5zaXR5IiwicmVkdWNlZE1vdGlvbiIsInRpbWUiLCJTcGlubmVyR2x5cGgiLCJ0MCIsIiQiLCJfYyIsInQxIiwidDIiLCJ0MyIsInVuZGVmaW5lZCIsInRoZW1lTmFtZSIsInRoZW1lIiwiaXNEaW0iLCJNYXRoIiwiZmxvb3IiLCJ0NCIsInNwaW5uZXJDaGFyIiwibGVuZ3RoIiwiYmFzZUNvbG9yU3RyIiwiYmFzZVJHQiIsImludGVycG9sYXRlZCIsImNvbG9yIl0sInNvdXJjZXMiOlsiU3Bpbm5lckdseXBoLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCwgdXNlVGhlbWUgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBnZXRUaGVtZSwgdHlwZSBUaGVtZSB9IGZyb20gJy4uLy4uL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0RGVmYXVsdENoYXJhY3RlcnMsXG4gIGludGVycG9sYXRlQ29sb3IsXG4gIHBhcnNlUkdCLFxuICB0b1JHQkNvbG9yLFxufSBmcm9tICcuL3V0aWxzLmpzJ1xuXG5jb25zdCBERUZBVUxUX0NIQVJBQ1RFUlMgPSBnZXREZWZhdWx0Q2hhcmFjdGVycygpXG5cbmNvbnN0IFNQSU5ORVJfRlJBTUVTID0gW1xuICAuLi5ERUZBVUxUX0NIQVJBQ1RFUlMsXG4gIC4uLlsuLi5ERUZBVUxUX0NIQVJBQ1RFUlNdLnJldmVyc2UoKSxcbl1cblxuY29uc3QgUkVEVUNFRF9NT1RJT05fRE9UID0gJ+KXjydcbmNvbnN0IFJFRFVDRURfTU9USU9OX0NZQ0xFX01TID0gMjAwMCAvLyAyLXNlY29uZCBjeWNsZTogMXMgdmlzaWJsZSwgMXMgZGltXG5jb25zdCBFUlJPUl9SRUQgPSB7IHI6IDE3MSwgZzogNDMsIGI6IDYzIH1cblxudHlwZSBQcm9wcyA9IHtcbiAgZnJhbWU6IG51bWJlclxuICBtZXNzYWdlQ29sb3I6IGtleW9mIFRoZW1lXG4gIHN0YWxsZWRJbnRlbnNpdHk/OiBudW1iZXJcbiAgcmVkdWNlZE1vdGlvbj86IGJvb2xlYW5cbiAgdGltZT86IG51bWJlclxufVxuXG5leHBvcnQgZnVuY3Rpb24gU3Bpbm5lckdseXBoKHtcbiAgZnJhbWUsXG4gIG1lc3NhZ2VDb2xvcixcbiAgc3RhbGxlZEludGVuc2l0eSA9IDAsXG4gIHJlZHVjZWRNb3Rpb24gPSBmYWxzZSxcbiAgdGltZSA9IDAsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZU5hbWVdID0gdXNlVGhlbWUoKVxuICBjb25zdCB0aGVtZSA9IGdldFRoZW1lKHRoZW1lTmFtZSlcblxuICAvLyBSZWR1Y2VkIG1vdGlvbjogc2xvd2x5IGZsYXNoaW5nIG9yYW5nZSBkb3RcbiAgaWYgKHJlZHVjZWRNb3Rpb24pIHtcbiAgICBjb25zdCBpc0RpbSA9IE1hdGguZmxvb3IodGltZSAvIChSRURVQ0VEX01PVElPTl9DWUNMRV9NUyAvIDIpKSAlIDIgPT09IDFcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4V3JhcD1cIndyYXBcIiBoZWlnaHQ9ezF9IHdpZHRoPXsyfT5cbiAgICAgICAgPFRleHQgY29sb3I9e21lc3NhZ2VDb2xvcn0gZGltQ29sb3I9e2lzRGltfT5cbiAgICAgICAgICB7UkVEVUNFRF9NT1RJT05fRE9UfVxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICApXG4gIH1cblxuICBjb25zdCBzcGlubmVyQ2hhciA9IFNQSU5ORVJfRlJBTUVTW2ZyYW1lICUgU1BJTk5FUl9GUkFNRVMubGVuZ3RoXVxuXG4gIC8vIFNtb290aGx5IGludGVycG9sYXRlIGZyb20gY3VycmVudCBjb2xvciB0byByZWQgd2hlbiBzdGFsbGVkXG4gIGlmIChzdGFsbGVkSW50ZW5zaXR5ID4gMCkge1xuICAgIGNvbnN0IGJhc2VDb2xvclN0ciA9IHRoZW1lW21lc3NhZ2VDb2xvcl1cbiAgICBjb25zdCBiYXNlUkdCID0gYmFzZUNvbG9yU3RyID8gcGFyc2VSR0IoYmFzZUNvbG9yU3RyKSA6IG51bGxcblxuICAgIGlmIChiYXNlUkdCKSB7XG4gICAgICBjb25zdCBpbnRlcnBvbGF0ZWQgPSBpbnRlcnBvbGF0ZUNvbG9yKFxuICAgICAgICBiYXNlUkdCLFxuICAgICAgICBFUlJPUl9SRUQsXG4gICAgICAgIHN0YWxsZWRJbnRlbnNpdHksXG4gICAgICApXG4gICAgICByZXR1cm4gKFxuICAgICAgICA8Qm94IGZsZXhXcmFwPVwid3JhcFwiIGhlaWdodD17MX0gd2lkdGg9ezJ9PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPXt0b1JHQkNvbG9yKGludGVycG9sYXRlZCl9PntzcGlubmVyQ2hhcn08L1RleHQ+XG4gICAgICAgIDwvQm94PlxuICAgICAgKVxuICAgIH1cblxuICAgIC8vIEZhbGxiYWNrIGZvciBBTlNJIHRoZW1lc1xuICAgIGNvbnN0IGNvbG9yID0gc3RhbGxlZEludGVuc2l0eSA+IDAuNSA/ICdlcnJvcicgOiBtZXNzYWdlQ29sb3JcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBmbGV4V3JhcD1cIndyYXBcIiBoZWlnaHQ9ezF9IHdpZHRoPXsyfT5cbiAgICAgICAgPFRleHQgY29sb3I9e2NvbG9yfT57c3Bpbm5lckNoYXJ9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IGZsZXhXcmFwPVwid3JhcFwiIGhlaWdodD17MX0gd2lkdGg9ezJ9PlxuICAgICAgPFRleHQgY29sb3I9e21lc3NhZ2VDb2xvcn0+e3NwaW5uZXJDaGFyfTwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxHQUFHLEVBQUVDLElBQUksRUFBRUMsUUFBUSxRQUFRLGNBQWM7QUFDbEQsU0FBU0MsUUFBUSxFQUFFLEtBQUtDLEtBQUssUUFBUSxzQkFBc0I7QUFDM0QsU0FDRUMsb0JBQW9CLEVBQ3BCQyxnQkFBZ0IsRUFDaEJDLFFBQVEsRUFDUkMsVUFBVSxRQUNMLFlBQVk7QUFFbkIsTUFBTUMsa0JBQWtCLEdBQUdKLG9CQUFvQixDQUFDLENBQUM7QUFFakQsTUFBTUssY0FBYyxHQUFHLENBQ3JCLEdBQUdELGtCQUFrQixFQUNyQixHQUFHLENBQUMsR0FBR0Esa0JBQWtCLENBQUMsQ0FBQ0UsT0FBTyxDQUFDLENBQUMsQ0FDckM7QUFFRCxNQUFNQyxrQkFBa0IsR0FBRyxHQUFHO0FBQzlCLE1BQU1DLHVCQUF1QixHQUFHLElBQUksRUFBQztBQUNyQyxNQUFNQyxTQUFTLEdBQUc7RUFBRUMsQ0FBQyxFQUFFLEdBQUc7RUFBRUMsQ0FBQyxFQUFFLEVBQUU7RUFBRUMsQ0FBQyxFQUFFO0FBQUcsQ0FBQztBQUUxQyxLQUFLQyxLQUFLLEdBQUc7RUFDWEMsS0FBSyxFQUFFLE1BQU07RUFDYkMsWUFBWSxFQUFFLE1BQU1oQixLQUFLO0VBQ3pCaUIsZ0JBQWdCLENBQUMsRUFBRSxNQUFNO0VBQ3pCQyxhQUFhLENBQUMsRUFBRSxPQUFPO0VBQ3ZCQyxJQUFJLENBQUMsRUFBRSxNQUFNO0FBQ2YsQ0FBQztBQUVELE9BQU8sU0FBQUMsYUFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFzQjtJQUFBUixLQUFBO0lBQUFDLFlBQUE7SUFBQUMsZ0JBQUEsRUFBQU8sRUFBQTtJQUFBTixhQUFBLEVBQUFPLEVBQUE7SUFBQU4sSUFBQSxFQUFBTztFQUFBLElBQUFMLEVBTXJCO0VBSE4sTUFBQUosZ0JBQUEsR0FBQU8sRUFBb0IsS0FBcEJHLFNBQW9CLEdBQXBCLENBQW9CLEdBQXBCSCxFQUFvQjtFQUNwQixNQUFBTixhQUFBLEdBQUFPLEVBQXFCLEtBQXJCRSxTQUFxQixHQUFyQixLQUFxQixHQUFyQkYsRUFBcUI7RUFDckIsTUFBQU4sSUFBQSxHQUFBTyxFQUFRLEtBQVJDLFNBQVEsR0FBUixDQUFRLEdBQVJELEVBQVE7RUFFUixPQUFBRSxTQUFBLElBQW9COUIsUUFBUSxDQUFDLENBQUM7RUFDOUIsTUFBQStCLEtBQUEsR0FBYzlCLFFBQVEsQ0FBQzZCLFNBQVMsQ0FBQztFQUdqQyxJQUFJVixhQUFhO0lBQ2YsTUFBQVksS0FBQSxHQUFjQyxJQUFJLENBQUFDLEtBQU0sQ0FBQ2IsSUFBSSxJQUFJVix1QkFBdUIsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDO0lBQUEsSUFBQXdCLEVBQUE7SUFBQSxJQUFBWCxDQUFBLFFBQUFRLEtBQUEsSUFBQVIsQ0FBQSxRQUFBTixZQUFBO01BRXRFaUIsRUFBQSxJQUFDLEdBQUcsQ0FBVSxRQUFNLENBQU4sTUFBTSxDQUFTLE1BQUMsQ0FBRCxHQUFDLENBQVMsS0FBQyxDQUFELEdBQUMsQ0FDdEMsQ0FBQyxJQUFJLENBQVFqQixLQUFZLENBQVpBLGFBQVcsQ0FBQyxDQUFZYyxRQUFLLENBQUxBLE1BQUksQ0FBQyxDQUN2Q3RCLG1CQUFpQixDQUNwQixFQUZDLElBQUksQ0FHUCxFQUpDLEdBQUcsQ0FJRTtNQUFBYyxDQUFBLE1BQUFRLEtBQUE7TUFBQVIsQ0FBQSxNQUFBTixZQUFBO01BQUFNLENBQUEsTUFBQVcsRUFBQTtJQUFBO01BQUFBLEVBQUEsR0FBQVgsQ0FBQTtJQUFBO0lBQUEsT0FKTlcsRUFJTTtFQUFBO0VBSVYsTUFBQUMsV0FBQSxHQUFvQjVCLGNBQWMsQ0FBQ1MsS0FBSyxHQUFHVCxjQUFjLENBQUE2QixNQUFPLENBQUM7RUFHakUsSUFBSWxCLGdCQUFnQixHQUFHLENBQUM7SUFDdEIsTUFBQW1CLFlBQUEsR0FBcUJQLEtBQUssQ0FBQ2IsWUFBWSxDQUFDO0lBQ3hDLE1BQUFxQixPQUFBLEdBQWdCRCxZQUFZLEdBQUdqQyxRQUFRLENBQUNpQyxZQUFtQixDQUFDLEdBQTVDLElBQTRDO0lBRTVELElBQUlDLE9BQU87TUFDVCxNQUFBQyxZQUFBLEdBQXFCcEMsZ0JBQWdCLENBQ25DbUMsT0FBTyxFQUNQM0IsU0FBUyxFQUNUTyxnQkFDRixDQUFDO01BQUEsT0FFQyxDQUFDLEdBQUcsQ0FBVSxRQUFNLENBQU4sTUFBTSxDQUFTLE1BQUMsQ0FBRCxHQUFDLENBQVMsS0FBQyxDQUFELEdBQUMsQ0FDdEMsQ0FBQyxJQUFJLENBQVEsS0FBd0IsQ0FBeEIsQ0FBQWIsVUFBVSxDQUFDa0MsWUFBWSxFQUFDLENBQUdKLFlBQVUsQ0FBRSxFQUFuRCxJQUFJLENBQ1AsRUFGQyxHQUFHLENBRUU7SUFBQTtJQUtWLE1BQUFLLEtBQUEsR0FBY3RCLGdCQUFnQixHQUFHLEdBQTRCLEdBQS9DLE9BQStDLEdBQS9DRCxZQUErQztJQUFBLElBQUFpQixFQUFBO0lBQUEsSUFBQVgsQ0FBQSxRQUFBaUIsS0FBQSxJQUFBakIsQ0FBQSxRQUFBWSxXQUFBO01BRTNERCxFQUFBLElBQUMsR0FBRyxDQUFVLFFBQU0sQ0FBTixNQUFNLENBQVMsTUFBQyxDQUFELEdBQUMsQ0FBUyxLQUFDLENBQUQsR0FBQyxDQUN0QyxDQUFDLElBQUksQ0FBUU0sS0FBSyxDQUFMQSxNQUFJLENBQUMsQ0FBR0wsWUFBVSxDQUFFLEVBQWhDLElBQUksQ0FDUCxFQUZDLEdBQUcsQ0FFRTtNQUFBWixDQUFBLE1BQUFpQixLQUFBO01BQUFqQixDQUFBLE1BQUFZLFdBQUE7TUFBQVosQ0FBQSxNQUFBVyxFQUFBO0lBQUE7TUFBQUEsRUFBQSxHQUFBWCxDQUFBO0lBQUE7SUFBQSxPQUZOVyxFQUVNO0VBQUE7RUFFVCxJQUFBQSxFQUFBO0VBQUEsSUFBQVgsQ0FBQSxRQUFBTixZQUFBLElBQUFNLENBQUEsUUFBQVksV0FBQTtJQUdDRCxFQUFBLElBQUMsR0FBRyxDQUFVLFFBQU0sQ0FBTixNQUFNLENBQVMsTUFBQyxDQUFELEdBQUMsQ0FBUyxLQUFDLENBQUQsR0FBQyxDQUN0QyxDQUFDLElBQUksQ0FBUWpCLEtBQVksQ0FBWkEsYUFBVyxDQUFDLENBQUdrQixZQUFVLENBQUUsRUFBdkMsSUFBSSxDQUNQLEVBRkMsR0FBRyxDQUVFO0lBQUFaLENBQUEsTUFBQU4sWUFBQTtJQUFBTSxDQUFBLE1BQUFZLFdBQUE7SUFBQVosQ0FBQSxNQUFBVyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBWCxDQUFBO0VBQUE7RUFBQSxPQUZOVyxFQUVNO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/src/components/Spinner/TeammateSpinnerLine.tsx b/src/components/Spinner/TeammateSpinnerLine.tsx new file mode 100644 index 0000000000000000000000000000000000000000..638667be621f99a612a1449b4bc05c1f16e67108 --- /dev/null +++ b/src/components/Spinner/TeammateSpinnerLine.tsx @@ -0,0 +1,233 @@ +import figures from 'figures'; +import sample from 'lodash-es/sample.js'; +import * as React from 'react'; +import { useRef, useState } from 'react'; +import { getSpinnerVerbs } from '../../constants/spinnerVerbs.js'; +import { TURN_COMPLETION_VERBS } from '../../constants/turnCompletionVerbs.js'; +import { useElapsedTime } from '../../hooks/useElapsedTime.js'; +import { useTerminalSize } from '../../hooks/useTerminalSize.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, Text } from '../../ink.js'; +import type { InProcessTeammateTaskState } from '../../tasks/InProcessTeammateTask/types.js'; +import { summarizeRecentActivities } from '../../utils/collapseReadSearch.js'; +import { formatDuration, formatNumber, truncateToWidth } from '../../utils/format.js'; +import { toInkColor } from '../../utils/ink.js'; +import { TEAMMATE_SELECT_HINT } from './teammateSelectHint.js'; +type Props = { + teammate: InProcessTeammateTaskState; + isLast: boolean; + isSelected?: boolean; + isForegrounded?: boolean; + allIdle?: boolean; + showPreview?: boolean; +}; + +/** + * Extract the last 3 lines of content from a teammate's conversation. + * Shows recent activity from any message type (user or assistant). + */ +function getMessagePreview(messages: InProcessTeammateTaskState['messages']): string[] { + if (!messages?.length) return []; + const allLines: string[] = []; + const maxLineLength = 80; + + // Collect lines from recent messages (newest first) + for (let i = messages.length - 1; i >= 0 && allLines.length < 3; i--) { + const msg = messages[i]; + // Only process messages that have content (user/assistant messages) + if (!msg || msg.type !== 'user' && msg.type !== 'assistant' || !msg.message?.content?.length) { + continue; + } + const content = msg.message.content; + for (const block of content) { + if (allLines.length >= 3) break; + if (!block || typeof block !== 'object') continue; + if ('type' in block && block.type === 'tool_use' && 'name' in block) { + // Try to show meaningful info from tool input + const input = 'input' in block ? block.input as Record : null; + let toolLine = `Using ${block.name}…`; + if (input) { + // Look for common descriptive fields + const desc = input.description as string | undefined || input.prompt as string | undefined || input.command as string | undefined || input.query as string | undefined || input.pattern as string | undefined; + if (desc) { + toolLine = desc.split('\n')[0] ?? toolLine; + } + } + allLines.push(truncateToWidth(toolLine, maxLineLength)); + } else if ('type' in block && block.type === 'text' && 'text' in block) { + const textLines = (block.text as string).split('\n').filter(l => l.trim()); + // Take from end of text (most recent lines) + for (let j = textLines.length - 1; j >= 0 && allLines.length < 3; j--) { + const line = textLines[j]; + if (!line) continue; + allLines.push(truncateToWidth(line, maxLineLength)); + } + } + } + } + + // Reverse so oldest of the 3 is first (reading order) + return allLines.reverse(); +} +export function TeammateSpinnerLine({ + teammate, + isLast, + isSelected, + isForegrounded, + allIdle, + showPreview +}: Props): React.ReactNode { + const [randomVerb] = useState(() => teammate.spinnerVerb ?? sample(getSpinnerVerbs())); + const [pastTenseVerb] = useState(() => teammate.pastTenseVerb ?? sample(TURN_COMPLETION_VERBS)); + const isHighlighted = isSelected || isForegrounded; + const treeChar = isHighlighted ? isLast ? '╘═' : '╞═' : isLast ? '└─' : '├─'; + const nameColor = toInkColor(teammate.identity.color); + const { + columns + } = useTerminalSize(); + + // Track when teammate became idle (for "Idle for X..." display) + const idleStartRef = useRef(null); + // Freeze elapsed time when entering all-idle state + const frozenDurationRef = useRef(null); + + // Track idle start time + if (teammate.isIdle && idleStartRef.current === null) { + idleStartRef.current = Date.now(); + } else if (!teammate.isIdle) { + idleStartRef.current = null; + } + + // Reset frozen duration when leaving all-idle state + if (!allIdle && frozenDurationRef.current !== null) { + frozenDurationRef.current = null; + } + + // Get elapsed idle time (how long they've been idle) - for "Idle for X..." display + const idleElapsedTime = useElapsedTime(idleStartRef.current ?? Date.now(), teammate.isIdle && !allIdle); + + // Freeze the duration when we first detect all idle + // Use the teammate's actual work time (since task started) for the past-tense display + if (allIdle && frozenDurationRef.current === null) { + frozenDurationRef.current = formatDuration(Math.max(0, Date.now() - teammate.startTime - (teammate.totalPausedMs ?? 0))); + } + + // Use frozen work duration when all idle, otherwise use idle elapsed time + const displayTime = allIdle ? frozenDurationRef.current ?? (() => { + throw new Error(`frozenDurationRef is null for idle teammate ${teammate.identity.agentName}`); + })() : idleElapsedTime; + + // Layout: paddingLeft(3) + pointer(1) + space(1) + treeChar(2) + space(1) = 8 fixed chars + // Then optionally: @name + ": " OR just ": " + // Then: activity text + optional extras (stats, hints) + const basePrefix = 8; + const fullAgentName = `@${teammate.identity.agentName}`; + const fullNameWidth = stringWidth(fullAgentName); + + // Get stats from progress + const toolUseCount = teammate.progress?.toolUseCount ?? 0; + const tokenCount = teammate.progress?.tokenCount ?? 0; + const statsText = ` · ${toolUseCount} tool ${toolUseCount === 1 ? 'use' : 'uses'} · ${formatNumber(tokenCount)} tokens`; + const statsWidth = stringWidth(statsText); + const selectHintText = ` · ${TEAMMATE_SELECT_HINT}`; + const selectHintWidth = stringWidth(selectHintText); + const viewHintText = ' · enter to view'; + const viewHintWidth = stringWidth(viewHintText); + + // Progressive responsive layout: + // Wide (80+): full name + activity + stats + hint + // Medium (60-80): full name + activity + // Narrow (<60): hide name, just show activity + const minActivityWidth = 25; + + // Hide name on narrow terminals (< 60 cols) or if there's not enough room + const spaceWithFullName = columns - basePrefix - fullNameWidth - 2; + const showName = columns >= 60 && spaceWithFullName >= minActivityWidth; + const nameWidth = showName ? fullNameWidth + 2 : 0; // +2 for ": " when name shown + const availableForActivity = columns - basePrefix - nameWidth; + + // Progressive hiding: view hint → select hint → stats + // Stats always visible (dimmed when not selected); hints only when highlighted/selected + const showViewHint = isSelected && !isForegrounded && availableForActivity > viewHintWidth + statsWidth + minActivityWidth + 5; + const showSelectHint = isHighlighted && availableForActivity > selectHintWidth + (showViewHint ? viewHintWidth : 0) + statsWidth + minActivityWidth + 5; + const showStats = availableForActivity > statsWidth + minActivityWidth + 5; + + // Activity text gets remaining space + const extrasCost = (showStats ? statsWidth : 0) + (showSelectHint ? selectHintWidth : 0) + (showViewHint ? viewHintWidth : 0); + const activityMaxWidth = Math.max(minActivityWidth, availableForActivity - extrasCost - 1); + + // Format the activity text for active teammates, rolling up search/read ops + const activityText = (() => { + const activities = teammate.progress?.recentActivities; + if (activities && activities.length > 0) { + const summary = summarizeRecentActivities(activities); + if (summary) return truncateToWidth(summary, activityMaxWidth); + } + const desc = teammate.progress?.lastActivity?.activityDescription; + if (desc) return truncateToWidth(desc, activityMaxWidth); + return randomVerb; + })(); + + // Status rendering logic + const renderStatus = (): React.ReactNode => { + if (teammate.shutdownRequested) { + return [stopping]; + } + if (teammate.awaitingPlanApproval) { + return [awaiting approval]; + } + if (teammate.isIdle) { + if (allIdle) { + return + {pastTenseVerb} for {displayTime} + ; + } + return Idle for {idleElapsedTime}; + } + // Active - show spinner glyph + activity description (only when not highlighted; + // when highlighted, the main spinner above already shows the verb) + if (isHighlighted) { + return null; + } + return + {activityText?.endsWith('…') ? activityText : `${activityText}…`} + ; + }; + + // Get preview lines if enabled + const previewLines = showPreview ? getMessagePreview(teammate.messages) : []; + + // Tree continuation character for preview lines + const previewTreeChar = isLast ? ' ' : '│ '; + return + + {/* Selection indicator: pointer when selected, otherwise space */} + + {isSelected ? figures.pointer : ' '} + + {treeChar} + {/* Agent name: hidden on very narrow screens */} + {showName && + @{teammate.identity.agentName} + } + {showName && : } + {renderStatus()} + {/* Stats: only shown when selected and terminal is wide enough */} + {showStats && + {' '} + · {toolUseCount} tool {toolUseCount === 1 ? 'use' : 'uses'} ·{' '} + {formatNumber(tokenCount)} tokens + } + {/* Hints: select hint when highlighted, view hint when selected but not foregrounded */} + {showSelectHint && · {TEAMMATE_SELECT_HINT}} + {showViewHint && · enter to view} + + {/* Preview lines */} + {previewLines.map((line, idx) => + + {previewTreeChar} + {line} + )} + ; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwic2FtcGxlIiwiUmVhY3QiLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsImdldFNwaW5uZXJWZXJicyIsIlRVUk5fQ09NUExFVElPTl9WRVJCUyIsInVzZUVsYXBzZWRUaW1lIiwidXNlVGVybWluYWxTaXplIiwic3RyaW5nV2lkdGgiLCJCb3giLCJUZXh0IiwiSW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGUiLCJzdW1tYXJpemVSZWNlbnRBY3Rpdml0aWVzIiwiZm9ybWF0RHVyYXRpb24iLCJmb3JtYXROdW1iZXIiLCJ0cnVuY2F0ZVRvV2lkdGgiLCJ0b0lua0NvbG9yIiwiVEVBTU1BVEVfU0VMRUNUX0hJTlQiLCJQcm9wcyIsInRlYW1tYXRlIiwiaXNMYXN0IiwiaXNTZWxlY3RlZCIsImlzRm9yZWdyb3VuZGVkIiwiYWxsSWRsZSIsInNob3dQcmV2aWV3IiwiZ2V0TWVzc2FnZVByZXZpZXciLCJtZXNzYWdlcyIsImxlbmd0aCIsImFsbExpbmVzIiwibWF4TGluZUxlbmd0aCIsImkiLCJtc2ciLCJ0eXBlIiwibWVzc2FnZSIsImNvbnRlbnQiLCJibG9jayIsImlucHV0IiwiUmVjb3JkIiwidG9vbExpbmUiLCJuYW1lIiwiZGVzYyIsImRlc2NyaXB0aW9uIiwicHJvbXB0IiwiY29tbWFuZCIsInF1ZXJ5IiwicGF0dGVybiIsInNwbGl0IiwicHVzaCIsInRleHRMaW5lcyIsInRleHQiLCJmaWx0ZXIiLCJsIiwidHJpbSIsImoiLCJsaW5lIiwicmV2ZXJzZSIsIlRlYW1tYXRlU3Bpbm5lckxpbmUiLCJSZWFjdE5vZGUiLCJyYW5kb21WZXJiIiwic3Bpbm5lclZlcmIiLCJwYXN0VGVuc2VWZXJiIiwiaXNIaWdobGlnaHRlZCIsInRyZWVDaGFyIiwibmFtZUNvbG9yIiwiaWRlbnRpdHkiLCJjb2xvciIsImNvbHVtbnMiLCJpZGxlU3RhcnRSZWYiLCJmcm96ZW5EdXJhdGlvblJlZiIsImlzSWRsZSIsImN1cnJlbnQiLCJEYXRlIiwibm93IiwiaWRsZUVsYXBzZWRUaW1lIiwiTWF0aCIsIm1heCIsInN0YXJ0VGltZSIsInRvdGFsUGF1c2VkTXMiLCJkaXNwbGF5VGltZSIsIkVycm9yIiwiYWdlbnROYW1lIiwiYmFzZVByZWZpeCIsImZ1bGxBZ2VudE5hbWUiLCJmdWxsTmFtZVdpZHRoIiwidG9vbFVzZUNvdW50IiwicHJvZ3Jlc3MiLCJ0b2tlbkNvdW50Iiwic3RhdHNUZXh0Iiwic3RhdHNXaWR0aCIsInNlbGVjdEhpbnRUZXh0Iiwic2VsZWN0SGludFdpZHRoIiwidmlld0hpbnRUZXh0Iiwidmlld0hpbnRXaWR0aCIsIm1pbkFjdGl2aXR5V2lkdGgiLCJzcGFjZVdpdGhGdWxsTmFtZSIsInNob3dOYW1lIiwibmFtZVdpZHRoIiwiYXZhaWxhYmxlRm9yQWN0aXZpdHkiLCJzaG93Vmlld0hpbnQiLCJzaG93U2VsZWN0SGludCIsInNob3dTdGF0cyIsImV4dHJhc0Nvc3QiLCJhY3Rpdml0eU1heFdpZHRoIiwiYWN0aXZpdHlUZXh0IiwiYWN0aXZpdGllcyIsInJlY2VudEFjdGl2aXRpZXMiLCJzdW1tYXJ5IiwibGFzdEFjdGl2aXR5IiwiYWN0aXZpdHlEZXNjcmlwdGlvbiIsInJlbmRlclN0YXR1cyIsInNodXRkb3duUmVxdWVzdGVkIiwiYXdhaXRpbmdQbGFuQXBwcm92YWwiLCJlbmRzV2l0aCIsInByZXZpZXdMaW5lcyIsInByZXZpZXdUcmVlQ2hhciIsInVuZGVmaW5lZCIsInBvaW50ZXIiLCJtYXAiLCJpZHgiXSwic291cmNlcyI6WyJUZWFtbWF0ZVNwaW5uZXJMaW5lLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZmlndXJlcyBmcm9tICdmaWd1cmVzJ1xuaW1wb3J0IHNhbXBsZSBmcm9tICdsb2Rhc2gtZXMvc2FtcGxlLmpzJ1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBnZXRTcGlubmVyVmVyYnMgfSBmcm9tICcuLi8uLi9jb25zdGFudHMvc3Bpbm5lclZlcmJzLmpzJ1xuaW1wb3J0IHsgVFVSTl9DT01QTEVUSU9OX1ZFUkJTIH0gZnJvbSAnLi4vLi4vY29uc3RhbnRzL3R1cm5Db21wbGV0aW9uVmVyYnMuanMnXG5pbXBvcnQgeyB1c2VFbGFwc2VkVGltZSB9IGZyb20gJy4uLy4uL2hvb2tzL3VzZUVsYXBzZWRUaW1lLmpzJ1xuaW1wb3J0IHsgdXNlVGVybWluYWxTaXplIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlVGVybWluYWxTaXplLmpzJ1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi8uLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgdHlwZSB7IEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlIH0gZnJvbSAnLi4vLi4vdGFza3MvSW5Qcm9jZXNzVGVhbW1hdGVUYXNrL3R5cGVzLmpzJ1xuaW1wb3J0IHsgc3VtbWFyaXplUmVjZW50QWN0aXZpdGllcyB9IGZyb20gJy4uLy4uL3V0aWxzL2NvbGxhcHNlUmVhZFNlYXJjaC5qcydcbmltcG9ydCB7XG4gIGZvcm1hdER1cmF0aW9uLFxuICBmb3JtYXROdW1iZXIsXG4gIHRydW5jYXRlVG9XaWR0aCxcbn0gZnJvbSAnLi4vLi4vdXRpbHMvZm9ybWF0LmpzJ1xuaW1wb3J0IHsgdG9JbmtDb2xvciB9IGZyb20gJy4uLy4uL3V0aWxzL2luay5qcydcbmltcG9ydCB7IFRFQU1NQVRFX1NFTEVDVF9ISU5UIH0gZnJvbSAnLi90ZWFtbWF0ZVNlbGVjdEhpbnQuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHRlYW1tYXRlOiBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZVxuICBpc0xhc3Q6IGJvb2xlYW5cbiAgaXNTZWxlY3RlZD86IGJvb2xlYW5cbiAgaXNGb3JlZ3JvdW5kZWQ/OiBib29sZWFuXG4gIGFsbElkbGU/OiBib29sZWFuXG4gIHNob3dQcmV2aWV3PzogYm9vbGVhblxufVxuXG4vKipcbiAqIEV4dHJhY3QgdGhlIGxhc3QgMyBsaW5lcyBvZiBjb250ZW50IGZyb20gYSB0ZWFtbWF0ZSdzIGNvbnZlcnNhdGlvbi5cbiAqIFNob3dzIHJlY2VudCBhY3Rpdml0eSBmcm9tIGFueSBtZXNzYWdlIHR5cGUgKHVzZXIgb3IgYXNzaXN0YW50KS5cbiAqL1xuZnVuY3Rpb24gZ2V0TWVzc2FnZVByZXZpZXcoXG4gIG1lc3NhZ2VzOiBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZVsnbWVzc2FnZXMnXSxcbik6IHN0cmluZ1tdIHtcbiAgaWYgKCFtZXNzYWdlcz8ubGVuZ3RoKSByZXR1cm4gW11cblxuICBjb25zdCBhbGxMaW5lczogc3RyaW5nW10gPSBbXVxuICBjb25zdCBtYXhMaW5lTGVuZ3RoID0gODBcblxuICAvLyBDb2xsZWN0IGxpbmVzIGZyb20gcmVjZW50IG1lc3NhZ2VzIChuZXdlc3QgZmlyc3QpXG4gIGZvciAobGV0IGkgPSBtZXNzYWdlcy5sZW5ndGggLSAxOyBpID49IDAgJiYgYWxsTGluZXMubGVuZ3RoIDwgMzsgaS0tKSB7XG4gICAgY29uc3QgbXNnID0gbWVzc2FnZXNbaV1cbiAgICAvLyBPbmx5IHByb2Nlc3MgbWVzc2FnZXMgdGhhdCBoYXZlIGNvbnRlbnQgKHVzZXIvYXNzaXN0YW50IG1lc3NhZ2VzKVxuICAgIGlmIChcbiAgICAgICFtc2cgfHxcbiAgICAgIChtc2cudHlwZSAhPT0gJ3VzZXInICYmIG1zZy50eXBlICE9PSAnYXNzaXN0YW50JykgfHxcbiAgICAgICFtc2cubWVzc2FnZT8uY29udGVudD8ubGVuZ3RoXG4gICAgKSB7XG4gICAgICBjb250aW51ZVxuICAgIH1cbiAgICBjb25zdCBjb250ZW50ID0gbXNnLm1lc3NhZ2UuY29udGVudFxuXG4gICAgZm9yIChjb25zdCBibG9jayBvZiBjb250ZW50KSB7XG4gICAgICBpZiAoYWxsTGluZXMubGVuZ3RoID49IDMpIGJyZWFrXG4gICAgICBpZiAoIWJsb2NrIHx8IHR5cGVvZiBibG9jayAhPT0gJ29iamVjdCcpIGNvbnRpbnVlXG5cbiAgICAgIGlmICgndHlwZScgaW4gYmxvY2sgJiYgYmxvY2sudHlwZSA9PT0gJ3Rvb2xfdXNlJyAmJiAnbmFtZScgaW4gYmxvY2spIHtcbiAgICAgICAgLy8gVHJ5IHRvIHNob3cgbWVhbmluZ2Z1bCBpbmZvIGZyb20gdG9vbCBpbnB1dFxuICAgICAgICBjb25zdCBpbnB1dCA9XG4gICAgICAgICAgJ2lucHV0JyBpbiBibG9jayA/IChibG9jay5pbnB1dCBhcyBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPikgOiBudWxsXG4gICAgICAgIGxldCB0b29sTGluZSA9IGBVc2luZyAke2Jsb2NrLm5hbWV94oCmYFxuICAgICAgICBpZiAoaW5wdXQpIHtcbiAgICAgICAgICAvLyBMb29rIGZvciBjb21tb24gZGVzY3JpcHRpdmUgZmllbGRzXG4gICAgICAgICAgY29uc3QgZGVzYyA9XG4gICAgICAgICAgICAoaW5wdXQuZGVzY3JpcHRpb24gYXMgc3RyaW5nIHwgdW5kZWZpbmVkKSB8fFxuICAgICAgICAgICAgKGlucHV0LnByb21wdCBhcyBzdHJpbmcgfCB1bmRlZmluZWQpIHx8XG4gICAgICAgICAgICAoaW5wdXQuY29tbWFuZCBhcyBzdHJpbmcgfCB1bmRlZmluZWQpIHx8XG4gICAgICAgICAgICAoaW5wdXQucXVlcnkgYXMgc3RyaW5nIHwgdW5kZWZpbmVkKSB8fFxuICAgICAgICAgICAgKGlucHV0LnBhdHRlcm4gYXMgc3RyaW5nIHwgdW5kZWZpbmVkKVxuICAgICAgICAgIGlmIChkZXNjKSB7XG4gICAgICAgICAgICB0b29sTGluZSA9IGRlc2Muc3BsaXQoJ1xcbicpWzBdID8/IHRvb2xMaW5lXG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGFsbExpbmVzLnB1c2godHJ1bmNhdGVUb1dpZHRoKHRvb2xMaW5lLCBtYXhMaW5lTGVuZ3RoKSlcbiAgICAgIH0gZWxzZSBpZiAoJ3R5cGUnIGluIGJsb2NrICYmIGJsb2NrLnR5cGUgPT09ICd0ZXh0JyAmJiAndGV4dCcgaW4gYmxvY2spIHtcbiAgICAgICAgY29uc3QgdGV4dExpbmVzID0gKGJsb2NrLnRleHQgYXMgc3RyaW5nKVxuICAgICAgICAgIC5zcGxpdCgnXFxuJylcbiAgICAgICAgICAuZmlsdGVyKGwgPT4gbC50cmltKCkpXG4gICAgICAgIC8vIFRha2UgZnJvbSBlbmQgb2YgdGV4dCAobW9zdCByZWNlbnQgbGluZXMpXG4gICAgICAgIGZvciAobGV0IGogPSB0ZXh0TGluZXMubGVuZ3RoIC0gMTsgaiA+PSAwICYmIGFsbExpbmVzLmxlbmd0aCA8IDM7IGotLSkge1xuICAgICAgICAgIGNvbnN0IGxpbmUgPSB0ZXh0TGluZXNbal1cbiAgICAgICAgICBpZiAoIWxpbmUpIGNvbnRpbnVlXG4gICAgICAgICAgYWxsTGluZXMucHVzaCh0cnVuY2F0ZVRvV2lkdGgobGluZSwgbWF4TGluZUxlbmd0aCkpXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvLyBSZXZlcnNlIHNvIG9sZGVzdCBvZiB0aGUgMyBpcyBmaXJzdCAocmVhZGluZyBvcmRlcilcbiAgcmV0dXJuIGFsbExpbmVzLnJldmVyc2UoKVxufVxuXG5leHBvcnQgZnVuY3Rpb24gVGVhbW1hdGVTcGlubmVyTGluZSh7XG4gIHRlYW1tYXRlLFxuICBpc0xhc3QsXG4gIGlzU2VsZWN0ZWQsXG4gIGlzRm9yZWdyb3VuZGVkLFxuICBhbGxJZGxlLFxuICBzaG93UHJldmlldyxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3JhbmRvbVZlcmJdID0gdXNlU3RhdGUoXG4gICAgKCkgPT4gdGVhbW1hdGUuc3Bpbm5lclZlcmIgPz8gc2FtcGxlKGdldFNwaW5uZXJWZXJicygpKSxcbiAgKVxuICBjb25zdCBbcGFzdFRlbnNlVmVyYl0gPSB1c2VTdGF0ZShcbiAgICAoKSA9PiB0ZWFtbWF0ZS5wYXN0VGVuc2VWZXJiID8/IHNhbXBsZShUVVJOX0NPTVBMRVRJT05fVkVSQlMpLFxuICApXG4gIGNvbnN0IGlzSGlnaGxpZ2h0ZWQgPSBpc1NlbGVjdGVkIHx8IGlzRm9yZWdyb3VuZGVkXG4gIGNvbnN0IHRyZWVDaGFyID0gaXNIaWdobGlnaHRlZCA/IChpc0xhc3QgPyAn4pWY4pWQJyA6ICfilZ7ilZAnKSA6IGlzTGFzdCA/ICfilJTilIAnIDogJ+KUnOKUgCdcbiAgY29uc3QgbmFtZUNvbG9yID0gdG9JbmtDb2xvcih0ZWFtbWF0ZS5pZGVudGl0eS5jb2xvcilcbiAgY29uc3QgeyBjb2x1bW5zIH0gPSB1c2VUZXJtaW5hbFNpemUoKVxuXG4gIC8vIFRyYWNrIHdoZW4gdGVhbW1hdGUgYmVjYW1lIGlkbGUgKGZvciBcIklkbGUgZm9yIFguLi5cIiBkaXNwbGF5KVxuICBjb25zdCBpZGxlU3RhcnRSZWYgPSB1c2VSZWY8bnVtYmVyIHwgbnVsbD4obnVsbClcbiAgLy8gRnJlZXplIGVsYXBzZWQgdGltZSB3aGVuIGVudGVyaW5nIGFsbC1pZGxlIHN0YXRlXG4gIGNvbnN0IGZyb3plbkR1cmF0aW9uUmVmID0gdXNlUmVmPHN0cmluZyB8IG51bGw+KG51bGwpXG5cbiAgLy8gVHJhY2sgaWRsZSBzdGFydCB0aW1lXG4gIGlmICh0ZWFtbWF0ZS5pc0lkbGUgJiYgaWRsZVN0YXJ0UmVmLmN1cnJlbnQgPT09IG51bGwpIHtcbiAgICBpZGxlU3RhcnRSZWYuY3VycmVudCA9IERhdGUubm93KClcbiAgfSBlbHNlIGlmICghdGVhbW1hdGUuaXNJZGxlKSB7XG4gICAgaWRsZVN0YXJ0UmVmLmN1cnJlbnQgPSBudWxsXG4gIH1cblxuICAvLyBSZXNldCBmcm96ZW4gZHVyYXRpb24gd2hlbiBsZWF2aW5nIGFsbC1pZGxlIHN0YXRlXG4gIGlmICghYWxsSWRsZSAmJiBmcm96ZW5EdXJhdGlvblJlZi5jdXJyZW50ICE9PSBudWxsKSB7XG4gICAgZnJvemVuRHVyYXRpb25SZWYuY3VycmVudCA9IG51bGxcbiAgfVxuXG4gIC8vIEdldCBlbGFwc2VkIGlkbGUgdGltZSAoaG93IGxvbmcgdGhleSd2ZSBiZWVuIGlkbGUpIC0gZm9yIFwiSWRsZSBmb3IgWC4uLlwiIGRpc3BsYXlcbiAgY29uc3QgaWRsZUVsYXBzZWRUaW1lID0gdXNlRWxhcHNlZFRpbWUoXG4gICAgaWRsZVN0YXJ0UmVmLmN1cnJlbnQgPz8gRGF0ZS5ub3coKSxcbiAgICB0ZWFtbWF0ZS5pc0lkbGUgJiYgIWFsbElkbGUsXG4gIClcblxuICAvLyBGcmVlemUgdGhlIGR1cmF0aW9uIHdoZW4gd2UgZmlyc3QgZGV0ZWN0IGFsbCBpZGxlXG4gIC8vIFVzZSB0aGUgdGVhbW1hdGUncyBhY3R1YWwgd29yayB0aW1lIChzaW5jZSB0YXNrIHN0YXJ0ZWQpIGZvciB0aGUgcGFzdC10ZW5zZSBkaXNwbGF5XG4gIGlmIChhbGxJZGxlICYmIGZyb3plbkR1cmF0aW9uUmVmLmN1cnJlbnQgPT09IG51bGwpIHtcbiAgICBmcm96ZW5EdXJhdGlvblJlZi5jdXJyZW50ID0gZm9ybWF0RHVyYXRpb24oXG4gICAgICBNYXRoLm1heChcbiAgICAgICAgMCxcbiAgICAgICAgRGF0ZS5ub3coKSAtIHRlYW1tYXRlLnN0YXJ0VGltZSAtICh0ZWFtbWF0ZS50b3RhbFBhdXNlZE1zID8/IDApLFxuICAgICAgKSxcbiAgICApXG4gIH1cblxuICAvLyBVc2UgZnJvemVuIHdvcmsgZHVyYXRpb24gd2hlbiBhbGwgaWRsZSwgb3RoZXJ3aXNlIHVzZSBpZGxlIGVsYXBzZWQgdGltZVxuICBjb25zdCBkaXNwbGF5VGltZSA9IGFsbElkbGVcbiAgICA/IChmcm96ZW5EdXJhdGlvblJlZi5jdXJyZW50ID8/XG4gICAgICAoKCkgPT4ge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgYGZyb3plbkR1cmF0aW9uUmVmIGlzIG51bGwgZm9yIGlkbGUgdGVhbW1hdGUgJHt0ZWFtbWF0ZS5pZGVudGl0eS5hZ2VudE5hbWV9YCxcbiAgICAgICAgKVxuICAgICAgfSkoKSlcbiAgICA6IGlkbGVFbGFwc2VkVGltZVxuXG4gIC8vIExheW91dDogcGFkZGluZ0xlZnQoMykgKyBwb2ludGVyKDEpICsgc3BhY2UoMSkgKyB0cmVlQ2hhcigyKSArIHNwYWNlKDEpID0gOCBmaXhlZCBjaGFyc1xuICAvLyBUaGVuIG9wdGlvbmFsbHk6IEBuYW1lICsgXCI6IFwiIE9SIGp1c3QgXCI6IFwiXG4gIC8vIFRoZW46IGFjdGl2aXR5IHRleHQgKyBvcHRpb25hbCBleHRyYXMgKHN0YXRzLCBoaW50cylcbiAgY29uc3QgYmFzZVByZWZpeCA9IDhcbiAgY29uc3QgZnVsbEFnZW50TmFtZSA9IGBAJHt0ZWFtbWF0ZS5pZGVudGl0eS5hZ2VudE5hbWV9YFxuICBjb25zdCBmdWxsTmFtZVdpZHRoID0gc3RyaW5nV2lkdGgoZnVsbEFnZW50TmFtZSlcblxuICAvLyBHZXQgc3RhdHMgZnJvbSBwcm9ncmVzc1xuICBjb25zdCB0b29sVXNlQ291bnQgPSB0ZWFtbWF0ZS5wcm9ncmVzcz8udG9vbFVzZUNvdW50ID8/IDBcbiAgY29uc3QgdG9rZW5Db3VudCA9IHRlYW1tYXRlLnByb2dyZXNzPy50b2tlbkNvdW50ID8/IDBcbiAgY29uc3Qgc3RhdHNUZXh0ID0gYCDCtyAke3Rvb2xVc2VDb3VudH0gdG9vbCAke3Rvb2xVc2VDb3VudCA9PT0gMSA/ICd1c2UnIDogJ3VzZXMnfSDCtyAke2Zvcm1hdE51bWJlcih0b2tlbkNvdW50KX0gdG9rZW5zYFxuICBjb25zdCBzdGF0c1dpZHRoID0gc3RyaW5nV2lkdGgoc3RhdHNUZXh0KVxuICBjb25zdCBzZWxlY3RIaW50VGV4dCA9IGAgwrcgJHtURUFNTUFURV9TRUxFQ1RfSElOVH1gXG4gIGNvbnN0IHNlbGVjdEhpbnRXaWR0aCA9IHN0cmluZ1dpZHRoKHNlbGVjdEhpbnRUZXh0KVxuICBjb25zdCB2aWV3SGludFRleHQgPSAnIMK3IGVudGVyIHRvIHZpZXcnXG4gIGNvbnN0IHZpZXdIaW50V2lkdGggPSBzdHJpbmdXaWR0aCh2aWV3SGludFRleHQpXG5cbiAgLy8gUHJvZ3Jlc3NpdmUgcmVzcG9uc2l2ZSBsYXlvdXQ6XG4gIC8vIFdpZGUgKDgwKyk6IGZ1bGwgbmFtZSArIGFjdGl2aXR5ICsgc3RhdHMgKyBoaW50XG4gIC8vIE1lZGl1bSAoNjAtODApOiBmdWxsIG5hbWUgKyBhY3Rpdml0eVxuICAvLyBOYXJyb3cgKDw2MCk6IGhpZGUgbmFtZSwganVzdCBzaG93IGFjdGl2aXR5XG4gIGNvbnN0IG1pbkFjdGl2aXR5V2lkdGggPSAyNVxuXG4gIC8vIEhpZGUgbmFtZSBvbiBuYXJyb3cgdGVybWluYWxzICg8IDYwIGNvbHMpIG9yIGlmIHRoZXJlJ3Mgbm90IGVub3VnaCByb29tXG4gIGNvbnN0IHNwYWNlV2l0aEZ1bGxOYW1lID0gY29sdW1ucyAtIGJhc2VQcmVmaXggLSBmdWxsTmFtZVdpZHRoIC0gMlxuICBjb25zdCBzaG93TmFtZSA9IGNvbHVtbnMgPj0gNjAgJiYgc3BhY2VXaXRoRnVsbE5hbWUgPj0gbWluQWN0aXZpdHlXaWR0aFxuICBjb25zdCBuYW1lV2lkdGggPSBzaG93TmFtZSA/IGZ1bGxOYW1lV2lkdGggKyAyIDogMCAvLyArMiBmb3IgXCI6IFwiIHdoZW4gbmFtZSBzaG93blxuICBjb25zdCBhdmFpbGFibGVGb3JBY3Rpdml0eSA9IGNvbHVtbnMgLSBiYXNlUHJlZml4IC0gbmFtZVdpZHRoXG5cbiAgLy8gUHJvZ3Jlc3NpdmUgaGlkaW5nOiB2aWV3IGhpbnQg4oaSIHNlbGVjdCBoaW50IOKGkiBzdGF0c1xuICAvLyBTdGF0cyBhbHdheXMgdmlzaWJsZSAoZGltbWVkIHdoZW4gbm90IHNlbGVjdGVkKTsgaGludHMgb25seSB3aGVuIGhpZ2hsaWdodGVkL3NlbGVjdGVkXG4gIGNvbnN0IHNob3dWaWV3SGludCA9XG4gICAgaXNTZWxlY3RlZCAmJlxuICAgICFpc0ZvcmVncm91bmRlZCAmJlxuICAgIGF2YWlsYWJsZUZvckFjdGl2aXR5ID4gdmlld0hpbnRXaWR0aCArIHN0YXRzV2lkdGggKyBtaW5BY3Rpdml0eVdpZHRoICsgNVxuICBjb25zdCBzaG93U2VsZWN0SGludCA9XG4gICAgaXNIaWdobGlnaHRlZCAmJlxuICAgIGF2YWlsYWJsZUZvckFjdGl2aXR5ID5cbiAgICAgIHNlbGVjdEhpbnRXaWR0aCArXG4gICAgICAgIChzaG93Vmlld0hpbnQgPyB2aWV3SGludFdpZHRoIDogMCkgK1xuICAgICAgICBzdGF0c1dpZHRoICtcbiAgICAgICAgbWluQWN0aXZpdHlXaWR0aCArXG4gICAgICAgIDVcbiAgY29uc3Qgc2hvd1N0YXRzID0gYXZhaWxhYmxlRm9yQWN0aXZpdHkgPiBzdGF0c1dpZHRoICsgbWluQWN0aXZpdHlXaWR0aCArIDVcblxuICAvLyBBY3Rpdml0eSB0ZXh0IGdldHMgcmVtYWluaW5nIHNwYWNlXG4gIGNvbnN0IGV4dHJhc0Nvc3QgPVxuICAgIChzaG93U3RhdHMgPyBzdGF0c1dpZHRoIDogMCkgK1xuICAgIChzaG93U2VsZWN0SGludCA/IHNlbGVjdEhpbnRXaWR0aCA6IDApICtcbiAgICAoc2hvd1ZpZXdIaW50ID8gdmlld0hpbnRXaWR0aCA6IDApXG4gIGNvbnN0IGFjdGl2aXR5TWF4V2lkdGggPSBNYXRoLm1heChcbiAgICBtaW5BY3Rpdml0eVdpZHRoLFxuICAgIGF2YWlsYWJsZUZvckFjdGl2aXR5IC0gZXh0cmFzQ29zdCAtIDEsXG4gIClcblxuICAvLyBGb3JtYXQgdGhlIGFjdGl2aXR5IHRleHQgZm9yIGFjdGl2ZSB0ZWFtbWF0ZXMsIHJvbGxpbmcgdXAgc2VhcmNoL3JlYWQgb3BzXG4gIGNvbnN0IGFjdGl2aXR5VGV4dCA9ICgoKSA9PiB7XG4gICAgY29uc3QgYWN0aXZpdGllcyA9IHRlYW1tYXRlLnByb2dyZXNzPy5yZWNlbnRBY3Rpdml0aWVzXG4gICAgaWYgKGFjdGl2aXRpZXMgJiYgYWN0aXZpdGllcy5sZW5ndGggPiAwKSB7XG4gICAgICBjb25zdCBzdW1tYXJ5ID0gc3VtbWFyaXplUmVjZW50QWN0aXZpdGllcyhhY3Rpdml0aWVzKVxuICAgICAgaWYgKHN1bW1hcnkpIHJldHVybiB0cnVuY2F0ZVRvV2lkdGgoc3VtbWFyeSwgYWN0aXZpdHlNYXhXaWR0aClcbiAgICB9XG4gICAgY29uc3QgZGVzYyA9IHRlYW1tYXRlLnByb2dyZXNzPy5sYXN0QWN0aXZpdHk/LmFjdGl2aXR5RGVzY3JpcHRpb25cbiAgICBpZiAoZGVzYykgcmV0dXJuIHRydW5jYXRlVG9XaWR0aChkZXNjLCBhY3Rpdml0eU1heFdpZHRoKVxuICAgIHJldHVybiByYW5kb21WZXJiXG4gIH0pKClcblxuICAvLyBTdGF0dXMgcmVuZGVyaW5nIGxvZ2ljXG4gIGNvbnN0IHJlbmRlclN0YXR1cyA9ICgpOiBSZWFjdC5SZWFjdE5vZGUgPT4ge1xuICAgIGlmICh0ZWFtbWF0ZS5zaHV0ZG93blJlcXVlc3RlZCkge1xuICAgICAgcmV0dXJuIDxUZXh0IGRpbUNvbG9yPltzdG9wcGluZ108L1RleHQ+XG4gICAgfVxuICAgIGlmICh0ZWFtbWF0ZS5hd2FpdGluZ1BsYW5BcHByb3ZhbCkge1xuICAgICAgcmV0dXJuIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPlthd2FpdGluZyBhcHByb3ZhbF08L1RleHQ+XG4gICAgfVxuICAgIGlmICh0ZWFtbWF0ZS5pc0lkbGUpIHtcbiAgICAgIGlmIChhbGxJZGxlKSB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgICAgICB7cGFzdFRlbnNlVmVyYn0gZm9yIHtkaXNwbGF5VGltZX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIClcbiAgICAgIH1cbiAgICAgIHJldHVybiA8VGV4dCBkaW1Db2xvcj5JZGxlIGZvciB7aWRsZUVsYXBzZWRUaW1lfTwvVGV4dD5cbiAgICB9XG4gICAgLy8gQWN0aXZlIC0gc2hvdyBzcGlubmVyIGdseXBoICsgYWN0aXZpdHkgZGVzY3JpcHRpb24gKG9ubHkgd2hlbiBub3QgaGlnaGxpZ2h0ZWQ7XG4gICAgLy8gd2hlbiBoaWdobGlnaHRlZCwgdGhlIG1haW4gc3Bpbm5lciBhYm92ZSBhbHJlYWR5IHNob3dzIHRoZSB2ZXJiKVxuICAgIGlmIChpc0hpZ2hsaWdodGVkKSB7XG4gICAgICByZXR1cm4gbnVsbFxuICAgIH1cbiAgICByZXR1cm4gKFxuICAgICAgPFRleHQgZGltQ29sb3I+XG4gICAgICAgIHthY3Rpdml0eVRleHQ/LmVuZHNXaXRoKCfigKYnKSA/IGFjdGl2aXR5VGV4dCA6IGAke2FjdGl2aXR5VGV4dH3igKZgfVxuICAgICAgPC9UZXh0PlxuICAgIClcbiAgfVxuXG4gIC8vIEdldCBwcmV2aWV3IGxpbmVzIGlmIGVuYWJsZWRcbiAgY29uc3QgcHJldmlld0xpbmVzID0gc2hvd1ByZXZpZXcgPyBnZXRNZXNzYWdlUHJldmlldyh0ZWFtbWF0ZS5tZXNzYWdlcykgOiBbXVxuXG4gIC8vIFRyZWUgY29udGludWF0aW9uIGNoYXJhY3RlciBmb3IgcHJldmlldyBsaW5lc1xuICBjb25zdCBwcmV2aWV3VHJlZUNoYXIgPSBpc0xhc3QgPyAnICAgJyA6ICfilIIgICdcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgPEJveCBwYWRkaW5nTGVmdD17M30+XG4gICAgICAgIHsvKiBTZWxlY3Rpb24gaW5kaWNhdG9yOiBwb2ludGVyIHdoZW4gc2VsZWN0ZWQsIG90aGVyd2lzZSBzcGFjZSAqL31cbiAgICAgICAgPFRleHQgY29sb3I9e2lzU2VsZWN0ZWQgPyAnc3VnZ2VzdGlvbicgOiB1bmRlZmluZWR9IGJvbGQ9e2lzU2VsZWN0ZWR9PlxuICAgICAgICAgIHtpc1NlbGVjdGVkID8gZmlndXJlcy5wb2ludGVyIDogJyAnfVxuICAgICAgICA8L1RleHQ+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPXshaXNTZWxlY3RlZH0+e3RyZWVDaGFyfSA8L1RleHQ+XG4gICAgICAgIHsvKiBBZ2VudCBuYW1lOiBoaWRkZW4gb24gdmVyeSBuYXJyb3cgc2NyZWVucyAqL31cbiAgICAgICAge3Nob3dOYW1lICYmIChcbiAgICAgICAgICA8VGV4dCBjb2xvcj17aXNTZWxlY3RlZCA/ICdzdWdnZXN0aW9uJyA6IG5hbWVDb2xvcn0+XG4gICAgICAgICAgICBAe3RlYW1tYXRlLmlkZW50aXR5LmFnZW50TmFtZX1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIHtzaG93TmFtZSAmJiA8VGV4dCBkaW1Db2xvcj17IWlzU2VsZWN0ZWR9PjogPC9UZXh0Pn1cbiAgICAgICAge3JlbmRlclN0YXR1cygpfVxuICAgICAgICB7LyogU3RhdHM6IG9ubHkgc2hvd24gd2hlbiBzZWxlY3RlZCBhbmQgdGVybWluYWwgaXMgd2lkZSBlbm91Z2ggKi99XG4gICAgICAgIHtzaG93U3RhdHMgJiYgKFxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgICAgeycgJ31cbiAgICAgICAgICAgIMK3IHt0b29sVXNlQ291bnR9IHRvb2wge3Rvb2xVc2VDb3VudCA9PT0gMSA/ICd1c2UnIDogJ3VzZXMnfSDCt3snICd9XG4gICAgICAgICAgICB7Zm9ybWF0TnVtYmVyKHRva2VuQ291bnQpfSB0b2tlbnNcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICl9XG4gICAgICAgIHsvKiBIaW50czogc2VsZWN0IGhpbnQgd2hlbiBoaWdobGlnaHRlZCwgdmlldyBoaW50IHdoZW4gc2VsZWN0ZWQgYnV0IG5vdCBmb3JlZ3JvdW5kZWQgKi99XG4gICAgICAgIHtzaG93U2VsZWN0SGludCAmJiA8VGV4dCBkaW1Db2xvcj4gwrcge1RFQU1NQVRFX1NFTEVDVF9ISU5UfTwvVGV4dD59XG4gICAgICAgIHtzaG93Vmlld0hpbnQgJiYgPFRleHQgZGltQ29sb3I+IMK3IGVudGVyIHRvIHZpZXc8L1RleHQ+fVxuICAgICAgPC9Cb3g+XG4gICAgICB7LyogUHJldmlldyBsaW5lcyAqL31cbiAgICAgIHtwcmV2aWV3TGluZXMubWFwKChsaW5lLCBpZHgpID0+IChcbiAgICAgICAgPEJveCBrZXk9e2lkeH0gcGFkZGluZ0xlZnQ9ezN9PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+e3ByZXZpZXdUcmVlQ2hhcn0gPC9UZXh0PlxuICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPntsaW5lfTwvVGV4dD5cbiAgICAg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 \ No newline at end of file diff --git a/src/components/Spinner/index.ts b/src/components/Spinner/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..25d9fe0531ecb3f3aea15b00ab8e1d8b4e24a2bc --- /dev/null +++ b/src/components/Spinner/index.ts @@ -0,0 +1,10 @@ +export { FlashingChar } from './FlashingChar.js' +export { GlimmerMessage } from './GlimmerMessage.js' +export { ShimmerChar } from './ShimmerChar.js' +export { SpinnerGlyph } from './SpinnerGlyph.js' +export type { SpinnerMode } from './types.js' +export { useShimmerAnimation } from './useShimmerAnimation.js' +export { useStalledAnimation } from './useStalledAnimation.js' +export { getDefaultCharacters, interpolateColor } from './utils.js' +// Teammate components are NOT exported here - use dynamic require() to enable dead code elimination +// See REPL.tsx and Spinner.tsx for the correct import pattern diff --git a/src/components/Spinner/useShimmerAnimation.ts b/src/components/Spinner/useShimmerAnimation.ts new file mode 100644 index 0000000000000000000000000000000000000000..d1d4ea94784bd4c9210716d094d370126f547144 --- /dev/null +++ b/src/components/Spinner/useShimmerAnimation.ts @@ -0,0 +1,31 @@ +import { useMemo } from 'react' +import { stringWidth } from '../../ink/stringWidth.js' +import { type DOMElement, useAnimationFrame } from '../../ink.js' +import type { SpinnerMode } from './types.js' + +export function useShimmerAnimation( + mode: SpinnerMode, + message: string, + isStalled: boolean, +): [ref: (element: DOMElement | null) => void, glimmerIndex: number] { + const glimmerSpeed = mode === 'requesting' ? 50 : 200 + // Pass null when stalled to unsubscribe from the clock — otherwise the + // setInterval keeps firing at 20fps even when the shimmer isn't visible. + // Notably, if the caller never attaches `ref` (e.g. conditional JSX), + // useTerminalViewport stays at its initial isVisible:true and the + // viewport-pause never kicks in, so this is the only stop mechanism. + const [ref, time] = useAnimationFrame(isStalled ? null : glimmerSpeed) + const messageWidth = useMemo(() => stringWidth(message), [message]) + + if (isStalled) { + return [ref, -100] + } + + const cyclePosition = Math.floor(time / glimmerSpeed) + const cycleLength = messageWidth + 20 + + if (mode === 'requesting') { + return [ref, (cyclePosition % cycleLength) - 10] + } + return [ref, messageWidth + 10 - (cyclePosition % cycleLength)] +} diff --git a/src/components/Spinner/utils.ts b/src/components/Spinner/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..7c0c54d0031fb9d94bd975935dce187b1a1112da --- /dev/null +++ b/src/components/Spinner/utils.ts @@ -0,0 +1,84 @@ +import type { RGBColor as RGBColorString } from '../../ink/styles.js' +import type { RGBColor as RGBColorType } from './types.js' + +export function getDefaultCharacters(): string[] { + if (process.env.TERM === 'xterm-ghostty') { + return ['·', '✢', '✳', '✶', '✻', '*'] // Use * instead of ✽ for Ghostty because the latter renders in a way that's slightly offset + } + return process.platform === 'darwin' + ? ['·', '✢', '✳', '✶', '✻', '✽'] + : ['·', '✢', '*', '✶', '✻', '✽'] +} + +// Interpolate between two RGB colors +export function interpolateColor( + color1: RGBColorType, + color2: RGBColorType, + t: number, // 0 to 1 +): RGBColorType { + return { + r: Math.round(color1.r + (color2.r - color1.r) * t), + g: Math.round(color1.g + (color2.g - color1.g) * t), + b: Math.round(color1.b + (color2.b - color1.b) * t), + } +} + +// Convert RGB object to rgb() color string for Text component +export function toRGBColor(color: RGBColorType): RGBColorString { + return `rgb(${color.r},${color.g},${color.b})` +} + +// HSL hue (0-360) to RGB, using voice-mode waveform parameters (s=0.7, l=0.6). +export function hueToRgb(hue: number): RGBColorType { + const h = ((hue % 360) + 360) % 360 + const s = 0.7 + const l = 0.6 + const c = (1 - Math.abs(2 * l - 1)) * s + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)) + const m = l - c / 2 + let r = 0 + let g = 0 + let b = 0 + if (h < 60) { + r = c + g = x + } else if (h < 120) { + r = x + g = c + } else if (h < 180) { + g = c + b = x + } else if (h < 240) { + g = x + b = c + } else if (h < 300) { + r = x + b = c + } else { + r = c + b = x + } + return { + r: Math.round((r + m) * 255), + g: Math.round((g + m) * 255), + b: Math.round((b + m) * 255), + } +} + +const RGB_CACHE = new Map() + +export function parseRGB(colorStr: string): RGBColorType | null { + const cached = RGB_CACHE.get(colorStr) + if (cached !== undefined) return cached + + const match = colorStr.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/) + const result = match + ? { + r: parseInt(match[1]!, 10), + g: parseInt(match[2]!, 10), + b: parseInt(match[3]!, 10), + } + : null + RGB_CACHE.set(colorStr, result) + return result +} diff --git a/src/components/StructuredDiff/Fallback.tsx b/src/components/StructuredDiff/Fallback.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8948d768845ae6992b415c02a4b03d73cae01120 --- /dev/null +++ b/src/components/StructuredDiff/Fallback.tsx @@ -0,0 +1,487 @@ +import { c as _c } from "react/compiler-runtime"; +import { diffWordsWithSpace, type StructuredPatchHunk } from 'diff'; +import * as React from 'react'; +import { useMemo } from 'react'; +import type { ThemeName } from 'src/utils/theme.js'; +import { stringWidth } from '../../ink/stringWidth.js'; +import { Box, NoSelect, Text, useTheme, wrapText } from '../../ink.js'; + +/* + * StructuredDiffFallback Component: Word-Level Diff Highlighting Example + * + * This component shows diff changes with word-level highlighting. Here's a walkthrough: + * + * Example: + * ``` + * // Original code + * function oldName(param) { + * return param.oldProperty; + * } + * + * // Changed code + * function newName(param) { + * return param.newProperty; + * } + * ``` + * + * Processing flow: + * 1. Component receives a patch with lines including '+' and '-' prefixes + * 2. Lines are transformed into objects with type (add/remove/nochange) + * 3. Related add/remove lines are paired (e.g., oldName with newName) + * 4. Word-level diffing identifies specific changed parts: + * [ + * { value: 'function ', added: undefined, removed: undefined }, // Common + * { value: 'oldName', removed: true }, // Removed + * { value: 'newName', added: true }, // Added + * { value: '(param) {', added: undefined, removed: undefined } // Common + * ] + * 5. Renders with enhanced highlighting: + * - Common parts are shown normally + * - Removed words get a darker red background + * - Added words get a darker green background + * + * This produces a visually clear diff where users can see exactly which words + * changed rather than just which lines were modified. + */ + +// Define DiffLine interface to be used throughout the file +interface DiffLine { + code: string; + type: 'add' | 'remove' | 'nochange'; + i: number; + originalCode: string; + wordDiff?: boolean; // Flag for word-level diffing + matchedLine?: DiffLine; +} + +// Line object type for internal functions +export interface LineObject { + code: string; + i: number; + type: 'add' | 'remove' | 'nochange'; + originalCode: string; + wordDiff?: boolean; + matchedLine?: LineObject; +} + +// Type for word-level diff parts +interface DiffPart { + added?: boolean; + removed?: boolean; + value: string; +} +type Props = { + patch: StructuredPatchHunk; + dim: boolean; + width: number; +}; + +// Threshold for when we show a full-line diff instead of word-level diffing +const CHANGE_THRESHOLD = 0.4; +export function StructuredDiffFallback(t0) { + const $ = _c(10); + const { + patch, + dim, + width + } = t0; + const [theme] = useTheme(); + let t1; + if ($[0] !== dim || $[1] !== patch.lines || $[2] !== patch.oldStart || $[3] !== theme || $[4] !== width) { + t1 = formatDiff(patch.lines, patch.oldStart, width, dim, theme); + $[0] = dim; + $[1] = patch.lines; + $[2] = patch.oldStart; + $[3] = theme; + $[4] = width; + $[5] = t1; + } else { + t1 = $[5]; + } + const diff = t1; + let t2; + if ($[6] !== diff) { + t2 = diff.map(_temp); + $[6] = diff; + $[7] = t2; + } else { + t2 = $[7]; + } + let t3; + if ($[8] !== t2) { + t3 = {t2}; + $[8] = t2; + $[9] = t3; + } else { + t3 = $[9]; + } + return t3; +} + +// Transform lines to line objects with type information +function _temp(node, i) { + return {node}; +} +export function transformLinesToObjects(lines: string[]): LineObject[] { + return lines.map(code => { + if (code.startsWith('+')) { + return { + code: code.slice(1), + i: 0, + type: 'add', + originalCode: code.slice(1) + }; + } + if (code.startsWith('-')) { + return { + code: code.slice(1), + i: 0, + type: 'remove', + originalCode: code.slice(1) + }; + } + return { + code: code.slice(1), + i: 0, + type: 'nochange', + originalCode: code.slice(1) + }; + }); +} + +// Group adjacent add/remove lines for word-level diffing +export function processAdjacentLines(lineObjects: LineObject[]): LineObject[] { + const processedLines: LineObject[] = []; + let i = 0; + while (i < lineObjects.length) { + const current = lineObjects[i]; + if (!current) { + i++; + continue; + } + + // Find a sequence of remove followed by add (possible word-level diff candidates) + if (current.type === 'remove') { + const removeLines: LineObject[] = [current]; + let j = i + 1; + + // Collect consecutive remove lines + while (j < lineObjects.length && lineObjects[j]?.type === 'remove') { + const line = lineObjects[j]; + if (line) { + removeLines.push(line); + } + j++; + } + + // Check if there are add lines following the remove lines + const addLines: LineObject[] = []; + while (j < lineObjects.length && lineObjects[j]?.type === 'add') { + const line = lineObjects[j]; + if (line) { + addLines.push(line); + } + j++; + } + + // If we have both remove and add lines, perform word-level diffing + if (removeLines.length > 0 && addLines.length > 0) { + // For word diffing, we'll compare each pair of lines or the closest available match + const pairCount = Math.min(removeLines.length, addLines.length); + + // Add paired lines with word diff info + for (let k = 0; k < pairCount; k++) { + const removeLine = removeLines[k]; + const addLine = addLines[k]; + if (removeLine && addLine) { + removeLine.wordDiff = true; + addLine.wordDiff = true; + + // Store the matched pair for later word diffing + removeLine.matchedLine = addLine; + addLine.matchedLine = removeLine; + } + } + + // Add all remove lines (both paired and unpaired) + processedLines.push(...removeLines.filter(Boolean)); + + // Then add all add lines (both paired and unpaired) + processedLines.push(...addLines.filter(Boolean)); + i = j; // Skip all the lines we've processed + } else { + // No matching add lines, just add the current remove line + processedLines.push(current); + i++; + } + } else { + // Not a remove line, just add it + processedLines.push(current); + i++; + } + } + return processedLines; +} + +// Calculate word-level diffs between two text strings +export function calculateWordDiffs(oldText: string, newText: string): DiffPart[] { + // Use diffWordsWithSpace instead of diffWords to preserve whitespace + // This ensures spaces between tokens like > and { are preserved + const result = diffWordsWithSpace(oldText, newText, { + ignoreCase: false + }); + return result; +} + +// Process word-level diffs with manual wrapping support +function generateWordDiffElements(item: DiffLine, width: number, maxWidth: number, dim: boolean, overrideTheme?: ThemeName): React.ReactNode[] | null { + const { + type, + i, + wordDiff, + matchedLine, + originalCode + } = item; + if (!wordDiff || !matchedLine) { + return null; // This function only handles word-level diff rendering + } + const removedLineText = type === 'remove' ? originalCode : matchedLine.originalCode; + const addedLineText = type === 'remove' ? matchedLine.originalCode : originalCode; + const wordDiffs = calculateWordDiffs(removedLineText, addedLineText); + + // Check if we should use word-level diffing + const totalLength = removedLineText.length + addedLineText.length; + const changedLength = wordDiffs.filter(part => part.added || part.removed).reduce((sum, part) => sum + part.value.length, 0); + const changeRatio = changedLength / totalLength; + if (changeRatio > CHANGE_THRESHOLD || dim) { + return null; // Fall back to standard rendering for major changes + } + + // Calculate available width for content + const diffPrefix = type === 'add' ? '+' : '-'; + const diffPrefixWidth = diffPrefix.length; + const availableContentWidth = Math.max(1, width - maxWidth - 1 - diffPrefixWidth); + + // Manually wrap the word diff parts with better space efficiency + const wrappedLines: { + content: React.ReactNode[]; + contentWidth: number; + }[] = []; + let currentLine: React.ReactNode[] = []; + let currentLineWidth = 0; + wordDiffs.forEach((part, partIndex) => { + // Determine if this part should be shown for this line type + let shouldShow = false; + let partBgColor: 'diffAddedWord' | 'diffRemovedWord' | undefined; + if (type === 'add') { + if (part.added) { + shouldShow = true; + partBgColor = 'diffAddedWord'; + } else if (!part.removed) { + shouldShow = true; + } + } else if (type === 'remove') { + if (part.removed) { + shouldShow = true; + partBgColor = 'diffRemovedWord'; + } else if (!part.added) { + shouldShow = true; + } + } + if (!shouldShow) return; + + // Use wrapText to wrap this individual part if it's long + const partWrapped = wrapText(part.value, availableContentWidth, 'wrap'); + const partLines = partWrapped.split('\n'); + partLines.forEach((partLine, lineIdx) => { + if (!partLine) return; + + // Check if we need to start a new line + if (lineIdx > 0 || currentLineWidth + stringWidth(partLine) > availableContentWidth) { + if (currentLine.length > 0) { + wrappedLines.push({ + content: [...currentLine], + contentWidth: currentLineWidth + }); + currentLine = []; + currentLineWidth = 0; + } + } + currentLine.push( + {partLine} + ); + currentLineWidth += stringWidth(partLine); + }); + }); + if (currentLine.length > 0) { + wrappedLines.push({ + content: currentLine, + contentWidth: currentLineWidth + }); + } + + // Render each wrapped line as a separate Text element + return wrappedLines.map(({ + content, + contentWidth + }, lineIndex) => { + const key = `${type}-${i}-${lineIndex}`; + const lineBgColor = type === 'add' ? dim ? 'diffAddedDimmed' : 'diffAdded' : dim ? 'diffRemovedDimmed' : 'diffRemoved'; + const lineNum = lineIndex === 0 ? i : undefined; + const lineNumStr = (lineNum !== undefined ? lineNum.toString().padStart(maxWidth) : ' '.repeat(maxWidth)) + ' '; + // Calculate padding to fill the entire terminal width + const usedWidth = lineNumStr.length + diffPrefixWidth + contentWidth; + const padding = Math.max(0, width - usedWidth); + return + + + {lineNumStr} + {diffPrefix} + + + + {content} + {' '.repeat(padding)} + + ; + }); +} +function formatDiff(lines: string[], startingLineNumber: number, width: number, dim: boolean, overrideTheme?: ThemeName): React.ReactNode[] { + // Ensure width is at least 1 to prevent rendering issues with very narrow terminals + const safeWidth = Math.max(1, Math.floor(width)); + + // Step 1: Transform lines to line objects with type information + const lineObjects = transformLinesToObjects(lines); + + // Step 2: Group adjacent add/remove lines for word-level diffing + const processedLines = processAdjacentLines(lineObjects); + + // Step 3: Number the diff lines + const ls = numberDiffLines(processedLines, startingLineNumber); + + // Find max line number width for alignment + const maxLineNumber = Math.max(...ls.map(({ + i + }) => i), 0); + const maxWidth = Math.max(maxLineNumber.toString().length + 1, 0); + + // Step 4: Render formatting + return ls.flatMap((item): React.ReactNode[] => { + const { + type, + code, + i, + wordDiff, + matchedLine + } = item; + + // Handle word-level diffing for add/remove pairs + if (wordDiff && matchedLine) { + const wordDiffElements = generateWordDiffElements(item, safeWidth, maxWidth, dim, overrideTheme); + + // word-diff might refuse (e.g. due to lines being substantially different) in which + // case we'll fall through to normal renderin gbelow + if (wordDiffElements !== null) { + return wordDiffElements; + } + } + + // Standard rendering for lines without word diffing or as fallback + // Calculate available width accounting for line number + space + diff prefix + const diffPrefixWidth = 2; // " " for unchanged, "+ " or "- " for changes + const availableContentWidth = Math.max(1, safeWidth - maxWidth - 1 - diffPrefixWidth); // -1 for space after line number + const wrappedText = wrapText(code, availableContentWidth, 'wrap'); + const wrappedLines = wrappedText.split('\n'); + return wrappedLines.map((line, lineIndex) => { + const key = `${type}-${i}-${lineIndex}`; + const lineNum = lineIndex === 0 ? i : undefined; + const lineNumStr = (lineNum !== undefined ? lineNum.toString().padStart(maxWidth) : ' '.repeat(maxWidth)) + ' '; + const sigil = type === 'add' ? '+' : type === 'remove' ? '-' : ' '; + // Calculate padding to fill the entire terminal width + const contentWidth = lineNumStr.length + 1 + stringWidth(line); // lineNum + sigil + code + const padding = Math.max(0, safeWidth - contentWidth); + const bgColor = type === 'add' ? dim ? 'diffAddedDimmed' : 'diffAdded' : type === 'remove' ? dim ? 'diffRemovedDimmed' : 'diffRemoved' : undefined; + + // Gutter (line number + sigil) is wrapped in so fullscreen + // text selection yields clean code. bgColor carries across both boxes + // so the visual continuity (solid red/green bar) is unchanged. + return + + + {lineNumStr} + {sigil} + + + + {line} + {' '.repeat(padding)} + + ; + }); + }); +} +export function numberDiffLines(diff: LineObject[], startLine: number): DiffLine[] { + let i = startLine; + const result: DiffLine[] = []; + const queue = [...diff]; + while (queue.length > 0) { + const current = queue.shift()!; + const { + code, + type, + originalCode, + wordDiff, + matchedLine + } = current; + const line = { + code, + type, + i, + originalCode, + wordDiff, + matchedLine + }; + + // Update counters based on change type + switch (type) { + case 'nochange': + i++; + result.push(line); + break; + case 'add': + i++; + result.push(line); + break; + case 'remove': + { + result.push(line); + let numRemoved = 0; + while (queue[0]?.type === 'remove') { + i++; + const current = queue.shift()!; + const { + code, + type, + originalCode, + wordDiff, + matchedLine + } = current; + const line = { + code, + type, + i, + originalCode, + wordDiff, + matchedLine + }; + result.push(line); + numRemoved++; + } + i -= numRemoved; + break; + } + } + } + return result; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJkaWZmV29yZHNXaXRoU3BhY2UiLCJTdHJ1Y3R1cmVkUGF0Y2hIdW5rIiwiUmVhY3QiLCJ1c2VNZW1vIiwiVGhlbWVOYW1lIiwic3RyaW5nV2lkdGgiLCJCb3giLCJOb1NlbGVjdCIsIlRleHQiLCJ1c2VUaGVtZSIsIndyYXBUZXh0IiwiRGlmZkxpbmUiLCJjb2RlIiwidHlwZSIsImkiLCJvcmlnaW5hbENvZGUiLCJ3b3JkRGlmZiIsIm1hdGNoZWRMaW5lIiwiTGluZU9iamVjdCIsIkRpZmZQYXJ0IiwiYWRkZWQiLCJyZW1vdmVkIiwidmFsdWUiLCJQcm9wcyIsInBhdGNoIiwiZGltIiwid2lkdGgiLCJDSEFOR0VfVEhSRVNIT0xEIiwiU3RydWN0dXJlZERpZmZGYWxsYmFjayIsInQwIiwiJCIsIl9jIiwidGhlbWUiLCJ0MSIsImxpbmVzIiwib2xkU3RhcnQiLCJmb3JtYXREaWZmIiwiZGlmZiIsInQyIiwibWFwIiwiX3RlbXAiLCJ0MyIsIm5vZGUiLCJ0cmFuc2Zvcm1MaW5lc1RvT2JqZWN0cyIsInN0YXJ0c1dpdGgiLCJzbGljZSIsInByb2Nlc3NBZGphY2VudExpbmVzIiwibGluZU9iamVjdHMiLCJwcm9jZXNzZWRMaW5lcyIsImxlbmd0aCIsImN1cnJlbnQiLCJyZW1vdmVMaW5lcyIsImoiLCJsaW5lIiwicHVzaCIsImFkZExpbmVzIiwicGFpckNvdW50IiwiTWF0aCIsIm1pbiIsImsiLCJyZW1vdmVMaW5lIiwiYWRkTGluZSIsImZpbHRlciIsIkJvb2xlYW4iLCJjYWxjdWxhdGVXb3JkRGlmZnMiLCJvbGRUZXh0IiwibmV3VGV4dCIsInJlc3VsdCIsImlnbm9yZUNhc2UiLCJnZW5lcmF0ZVdvcmREaWZmRWxlbWVudHMiLCJpdGVtIiwibWF4V2lkdGgiLCJvdmVycmlkZVRoZW1lIiwiUmVhY3ROb2RlIiwicmVtb3ZlZExpbmVUZXh0IiwiYWRkZWRMaW5lVGV4dCIsIndvcmREaWZmcyIsInRvdGFsTGVuZ3RoIiwiY2hhbmdlZExlbmd0aCIsInBhcnQiLCJyZWR1Y2UiLCJzdW0iLCJjaGFuZ2VSYXRpbyIsImRpZmZQcmVmaXgiLCJkaWZmUHJlZml4V2lkdGgiLCJhdmFpbGFibGVDb250ZW50V2lkdGgiLCJtYXgiLCJ3cmFwcGVkTGluZXMiLCJjb250ZW50IiwiY29udGVudFdpZHRoIiwiY3VycmVudExpbmUiLCJjdXJyZW50TGluZVdpZHRoIiwiZm9yRWFjaCIsInBhcnRJbmRleCIsInNob3VsZFNob3ciLCJwYXJ0QmdDb2xvciIsInBhcnRXcmFwcGVkIiwicGFydExpbmVzIiwic3BsaXQiLCJwYXJ0TGluZSIsImxpbmVJZHgiLCJsaW5lSW5kZXgiLCJrZXkiLCJsaW5lQmdDb2xvciIsImxpbmVOdW0iLCJ1bmRlZmluZWQiLCJsaW5lTnVtU3RyIiwidG9TdHJpbmciLCJwYWRTdGFydCIsInJlcGVhdCIsInVzZWRXaWR0aCIsInBhZGRpbmciLCJzdGFydGluZ0xpbmVOdW1iZXIiLCJzYWZlV2lkdGgiLCJmbG9vciIsImxzIiwibnVtYmVyRGlmZkxpbmVzIiwibWF4TGluZU51bWJlciIsImZsYXRNYXAiLCJ3b3JkRGlmZkVsZW1lbnRzIiwid3JhcHBlZFRleHQiLCJzaWdpbCIsImJnQ29sb3IiLCJzdGFydExpbmUiLCJxdWV1ZSIsInNoaWZ0IiwibnVtUmVtb3ZlZCJdLCJzb3VyY2VzIjpbIkZhbGxiYWNrLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBkaWZmV29yZHNXaXRoU3BhY2UsIHR5cGUgU3RydWN0dXJlZFBhdGNoSHVuayB9IGZyb20gJ2RpZmYnXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZU1lbW8gfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgVGhlbWVOYW1lIH0gZnJvbSAnc3JjL3V0aWxzL3RoZW1lLmpzJ1xuaW1wb3J0IHsgc3RyaW5nV2lkdGggfSBmcm9tICcuLi8uLi9pbmsvc3RyaW5nV2lkdGguanMnXG5pbXBvcnQgeyBCb3gsIE5vU2VsZWN0LCBUZXh0LCB1c2VUaGVtZSwgd3JhcFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5cbi8qXG4gKiBTdHJ1Y3R1cmVkRGlmZkZhbGxiYWNrIENvbXBvbmVudDogV29yZC1MZXZlbCBEaWZmIEhpZ2hsaWdodGluZyBFeGFtcGxlXG4gKlxuICogVGhpcyBjb21wb25lbnQgc2hvd3MgZGlmZiBjaGFuZ2VzIHdpdGggd29yZC1sZXZlbCBoaWdobGlnaHRpbmcuIEhlcmUncyBhIHdhbGt0aHJvdWdoOlxuICpcbiAqIEV4YW1wbGU6XG4gKiBgYGBcbiAqIC8vIE9yaWdpbmFsIGNvZGVcbiAqIGZ1bmN0aW9uIG9sZE5hbWUocGFyYW0pIHtcbiAqICAgcmV0dXJuIHBhcmFtLm9sZFByb3BlcnR5O1xuICogfVxuICpcbiAqIC8vIENoYW5nZWQgY29kZVxuICogZnVuY3Rpb24gbmV3TmFtZShwYXJhbSkge1xuICogICByZXR1cm4gcGFyYW0ubmV3UHJvcGVydHk7XG4gKiB9XG4gKiBgYGBcbiAqXG4gKiBQcm9jZXNzaW5nIGZsb3c6XG4gKiAxLiBDb21wb25lbnQgcmVjZWl2ZXMgYSBwYXRjaCB3aXRoIGxpbmVzIGluY2x1ZGluZyAnKycgYW5kICctJyBwcmVmaXhlc1xuICogMi4gTGluZXMgYXJlIHRyYW5zZm9ybWVkIGludG8gb2JqZWN0cyB3aXRoIHR5cGUgKGFkZC9yZW1vdmUvbm9jaGFuZ2UpXG4gKiAzLiBSZWxhdGVkIGFkZC9yZW1vdmUgbGluZXMgYXJlIHBhaXJlZCAoZS5nLiwgb2xkTmFtZSB3aXRoIG5ld05hbWUpXG4gKiA0LiBXb3JkLWxldmVsIGRpZmZpbmcgaWRlbnRpZmllcyBzcGVjaWZpYyBjaGFuZ2VkIHBhcnRzOlxuICogICAgW1xuICogICAgICB7IHZhbHVlOiAnZnVuY3Rpb24gJywgYWRkZWQ6IHVuZGVmaW5lZCwgcmVtb3ZlZDogdW5kZWZpbmVkIH0sICAvLyBDb21tb25cbiAqICAgICAgeyB2YWx1ZTogJ29sZE5hbWUnLCByZW1vdmVkOiB0cnVlIH0sICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gUmVtb3ZlZFxuICogICAgICB7IHZhbHVlOiAnbmV3TmFtZScsIGFkZGVkOiB0cnVlIH0sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBBZGRlZFxuICogICAgICB7IHZhbHVlOiAnKHBhcmFtKSB7JywgYWRkZWQ6IHVuZGVmaW5lZCwgcmVtb3ZlZDogdW5kZWZpbmVkIH0gICAvLyBDb21tb25cbiAqICAgIF1cbiAqIDUuIFJlbmRlcnMgd2l0aCBlbmhhbmNlZCBoaWdobGlnaHRpbmc6XG4gKiAgICAtIENvbW1vbiBwYXJ0cyBhcmUgc2hvd24gbm9ybWFsbHlcbiAqICAgIC0gUmVtb3ZlZCB3b3JkcyBnZXQgYSBkYXJrZXIgcmVkIGJhY2tncm91bmRcbiAqICAgIC0gQWRkZWQgd29yZHMgZ2V0IGEgZGFya2VyIGdyZWVuIGJhY2tncm91bmRcbiAqXG4gKiBUaGlzIHByb2R1Y2VzIGEgdmlzdWFsbHkgY2xlYXIgZGlmZiB3aGVyZSB1c2VycyBjYW4gc2VlIGV4YWN0bHkgd2hpY2ggd29yZHNcbiAqIGNoYW5nZWQgcmF0aGVyIHRoYW4ganVzdCB3aGljaCBsaW5lcyB3ZXJlIG1vZGlmaWVkLlxuICovXG5cbi8vIERlZmluZSBEaWZmTGluZSBpbnRlcmZhY2UgdG8gYmUgdXNlZCB0aHJvdWdob3V0IHRoZSBmaWxlXG5pbnRlcmZhY2UgRGlmZkxpbmUge1xuICBjb2RlOiBzdHJpbmdcbiAgdHlwZTogJ2FkZCcgfCAncmVtb3ZlJyB8ICdub2NoYW5nZSdcbiAgaTogbnVtYmVyXG4gIG9yaWdpbmFsQ29kZTogc3RyaW5nXG4gIHdvcmREaWZmPzogYm9vbGVhbiAvLyBGbGFnIGZvciB3b3JkLWxldmVsIGRpZmZpbmdcbiAgbWF0Y2hlZExpbmU/OiBEaWZmTGluZVxufVxuXG4vLyBMaW5lIG9iamVjdCB0eXBlIGZvciBpbnRlcm5hbCBmdW5jdGlvbnNcbmV4cG9ydCBpbnRlcmZhY2UgTGluZU9iamVjdCB7XG4gIGNvZGU6IHN0cmluZ1xuICBpOiBudW1iZXJcbiAgdHlwZTogJ2FkZCcgfCAncmVtb3ZlJyB8ICdub2NoYW5nZSdcbiAgb3JpZ2luYWxDb2RlOiBzdHJpbmdcbiAgd29yZERpZmY/OiBib29sZWFuXG4gIG1hdGNoZWRMaW5lPzogTGluZU9iamVjdFxufVxuXG4vLyBUeXBlIGZvciB3b3JkLWxldmVsIGRpZmYgcGFydHNcbmludGVyZmFjZSBEaWZmUGFydCB7XG4gIGFkZGVkPzogYm9vbGVhblxuICByZW1vdmVkPzogYm9vbGVhblxuICB2YWx1ZTogc3RyaW5nXG59XG5cbnR5cGUgUHJvcHMgPSB7XG4gIHBhdGNoOiBTdHJ1Y3R1cmVkUGF0Y2hIdW5rXG4gIGRpbTogYm9vbGVhblxuICB3aWR0aDogbnVtYmVyXG59XG5cbi8vIFRocmVzaG9sZCBmb3Igd2hlbiB3ZSBzaG93IGEgZnVsbC1saW5lIGRpZmYgaW5zdGVhZCBvZiB3b3JkLWxldmVsIGRpZmZpbmdcbmNvbnN0IENIQU5HRV9USFJFU0hPTEQgPSAwLjRcblxuZXhwb3J0IGZ1bmN0aW9uIFN0cnVjdHVyZWREaWZmRmFsbGJhY2soe1xuICBwYXRjaCxcbiAgZGltLFxuICB3aWR0aCxcbn06IFByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgW3RoZW1lXSA9IHVzZVRoZW1lKClcbiAgY29uc3QgZGlmZiA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZm9ybWF0RGlmZihwYXRjaC5saW5lcywgcGF0Y2gub2xkU3RhcnQsIHdpZHRoLCBkaW0sIHRoZW1lKSxcbiAgICBbcGF0Y2gubGluZXMsIHBhdGNoLm9sZFN0YXJ0LCB3aWR0aCwgZGltLCB0aGVtZV0sXG4gIClcblxuICByZXR1cm4gKFxuICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiIGZsZXhHcm93PXsxfT5cbiAgICAgIHtkaWZmLm1hcCgobm9kZSwgaSkgPT4gKFxuICAgICAgICA8Qm94IGtleT17aX0+e25vZGV9PC9Cb3g+XG4gICAgICApKX1cbiAgICA8L0JveD5cbiAgKVxufVxuXG4vLyBUcmFuc2Zvcm0gbGluZXMgdG8gbGluZSBvYmplY3RzIHdpdGggdHlwZSBpbmZvcm1hdGlvblxuZXhwb3J0IGZ1bmN0aW9uIHRyYW5zZm9ybUxpbmVzVG9PYmplY3RzKGxpbmVzOiBzdHJpbmdbXSk6IExpbmVPYmplY3RbXSB7XG4gIHJldHVybiBsaW5lcy5tYXAoY29kZSA9PiB7XG4gICAgaWYgKGNvZGUuc3RhcnRzV2l0aCgnKycpKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBjb2RlOiBjb2RlLnNsaWNlKDEpLFxuICAgICAgICBpOiAwLFxuICAgICAgICB0eXBlOiAnYWRkJyxcbiAgICAgICAgb3JpZ2luYWxDb2RlOiBjb2RlLnNsaWNlKDEpLFxuICAgICAgfVxuICAgIH1cbiAgICBpZiAoY29kZS5zdGFydHNXaXRoKCctJykpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIGNvZGU6IGNvZGUuc2xpY2UoMSksXG4gICAgICAgIGk6IDAsXG4gICAgICAgIHR5cGU6ICdyZW1vdmUnLFxuICAgICAgICBvcmlnaW5hbENvZGU6IGNvZGUuc2xpY2UoMSksXG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB7XG4gICAgICBjb2RlOiBjb2RlLnNsaWNlKDEpLFxuICAgICAgaTogMCxcbiAgICAgIHR5cGU6ICdub2NoYW5nZScsXG4gICAgICBvcmlnaW5hbENvZGU6IGNvZGUuc2xpY2UoMSksXG4gICAgfVxuICB9KVxufVxuXG4vLyBHcm91cCBhZGphY2VudCBhZGQvcmVtb3ZlIGxpbmVzIGZvciB3b3JkLWxldmVsIGRpZmZpbmdcbmV4cG9ydCBmdW5jdGlvbiBwcm9jZXNzQWRqYWNlbnRMaW5lcyhsaW5lT2JqZWN0czogTGluZU9iamVjdFtdKTogTGluZU9iamVjdFtdIHtcbiAgY29uc3QgcHJvY2Vzc2VkTGluZXM6IExpbmVPYmplY3RbXSA9IFtdXG4gIGxldCBpID0gMFxuXG4gIHdoaWxlIChpIDwgbGluZU9iamVjdHMubGVuZ3RoKSB7XG4gICAgY29uc3QgY3VycmVudCA9IGxpbmVPYmplY3RzW2ldXG4gICAgaWYgKCFjdXJyZW50KSB7XG4gICAgICBpKytcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgLy8gRmluZCBhIHNlcXVlbmNlIG9mIHJlbW92ZSBmb2xsb3dlZCBieSBhZGQgKHBvc3NpYmxlIHdvcmQtbGV2ZWwgZGlmZiBjYW5kaWRhdGVzKVxuICAgIGlmIChjdXJyZW50LnR5cGUgPT09ICdyZW1vdmUnKSB7XG4gICAgICBjb25zdCByZW1vdmVMaW5lczogTGluZU9iamVjdFtdID0gW2N1cnJlbnRdXG4gICAgICBsZXQgaiA9IGkgKyAxXG5cbiAgICAgIC8vIENvbGxlY3QgY29uc2VjdXRpdmUgcmVtb3ZlIGxpbmVzXG4gICAgICB3aGlsZSAoaiA8IGxpbmVPYmplY3RzLmxlbmd0aCAmJiBsaW5lT2JqZWN0c1tqXT8udHlwZSA9PT0gJ3JlbW92ZScpIHtcbiAgICAgICAgY29uc3QgbGluZSA9IGxpbmVPYmplY3RzW2pdXG4gICAgICAgIGlmIChsaW5lKSB7XG4gICAgICAgICAgcmVtb3ZlTGluZXMucHVzaChsaW5lKVxuICAgICAgICB9XG4gICAgICAgIGorK1xuICAgICAgfVxuXG4gICAgICAvLyBDaGVjayBpZiB0aGVyZSBhcmUgYWRkIGxpbmVzIGZvbGxvd2luZyB0aGUgcmVtb3ZlIGxpbmVzXG4gICAgICBjb25zdCBhZGRMaW5lczogTGluZU9iamVjdFtdID0gW11cbiAgICAgIHdoaWxlIChqIDwgbGluZU9iamVjdHMubGVuZ3RoICYmIGxpbmVPYmplY3RzW2pdPy50eXBlID09PSAnYWRkJykge1xuICAgICAgICBjb25zdCBsaW5lID0gbGluZU9iamVjdHNbal1cbiAgICAgICAgaWYgKGxpbmUpIHtcbiAgICAgICAgICBhZGRMaW5lcy5wdXNoKGxpbmUpXG4gICAgICAgIH1cbiAgICAgICAgaisrXG4gICAgICB9XG5cbiAgICAgIC8vIElmIHdlIGhhdmUgYm90aCByZW1vdmUgYW5kIGFkZCBsaW5lcywgcGVyZm9ybSB3b3JkLWxldmVsIGRpZmZpbmdcbiAgICAgIGlmIChyZW1vdmVMaW5lcy5sZW5ndGggPiAwICYmIGFkZExpbmVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgLy8gRm9yIHdvcmQgZGlmZmluZywgd2UnbGwgY29tcGFyZSBlYWNoIHBhaXIgb2YgbGluZXMgb3IgdGhlIGNsb3Nlc3QgYXZhaWxhYmxlIG1hdGNoXG4gICAgICAgIGNvbnN0IHBhaXJDb3VudCA9IE1hdGgubWluKHJlbW92ZUxpbmVzLmxlbmd0aCwgYWRkTGluZXMubGVuZ3RoKVxuXG4gICAgICAgIC8vIEFkZCBwYWlyZWQgbGluZXMgd2l0aCB3b3JkIGRpZmYgaW5mb1xuICAgICAgICBmb3IgKGxldCBrID0gMDsgayA8IHBhaXJDb3VudDsgaysrKSB7XG4gICAgICAgICAgY29uc3QgcmVtb3ZlTGluZSA9IHJlbW92ZUxpbmVzW2tdXG4gICAgICAgICAgY29uc3QgYWRkTGluZSA9IGFkZExpbmVzW2tdXG5cbiAgICAgICAgICBpZiAocmVtb3ZlTGluZSAmJiBhZGRMaW5lKSB7XG4gICAgICAgICAgICByZW1vdmVMaW5lLndvcmREaWZmID0gdHJ1ZVxuICAgICAgICAgICAgYWRkTGluZS53b3JkRGlmZiA9IHRydWVcblxuICAgICAgICAgICAgLy8gU3RvcmUgdGhlIG1hdGNoZWQgcGFpciBmb3IgbGF0ZXIgd29yZCBkaWZmaW5nXG4gICAgICAgICAgICByZW1vdmVMaW5lLm1hdGNoZWRMaW5lID0gYWRkTGluZVxuICAgICAgICAgICAgYWRkTGluZS5tYXRjaGVkTGluZSA9IHJlbW92ZUxpbmVcbiAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBBZGQgYWxsIHJlbW92ZSBsaW5lcyAoYm90aCBwYWlyZWQgYW5kIHVucGFpcmVkKVxuICAgICAgICBwcm9jZXNzZWRMaW5lcy5wdXNoKC4uLnJlbW92ZUxpbmVzLmZpbHRlcihCb29sZWFuKSlcblxuICAgICAgICAvLyBUaGVuIGFkZCBhbGwgYWRkIGxpbmVzIChib3RoIHBhaXJlZCBhbmQgdW5wYWlyZWQpXG4gICAgICAgIHByb2Nlc3NlZExpbmVzLnB1c2goLi4uYWRkTGluZXMuZmlsdGVyKEJvb2xlYW4pKVxuXG4gICAgICAgIGkgPSBqIC8vIFNraXAgYWxsIHRoZSBsaW5lcyB3ZSd2ZSBwcm9jZXNzZWRcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIE5vIG1hdGNoaW5nIGFkZCBsaW5lcywganVzdCBhZGQgdGhlIGN1cnJlbnQgcmVtb3ZlIGxpbmVcbiAgICAgICAgcHJvY2Vzc2VkTGluZXMucHVzaChjdXJyZW50KVxuICAgICAgICBpKytcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgLy8gTm90IGEgcmVtb3ZlIGxpbmUsIGp1c3QgYWRkIGl0XG4gICAgICBwcm9jZXNzZWRMaW5lcy5wdXNoKGN1cnJlbnQpXG4gICAgICBpKytcbiAgICB9XG4gIH1cblxuICByZXR1cm4gcHJvY2Vzc2VkTGluZXNcbn1cblxuLy8gQ2FsY3VsYXRlIHdvcmQtbGV2ZWwgZGlmZnMgYmV0d2VlbiB0d28gdGV4dCBzdHJpbmdzXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlV29yZERpZmZzKFxuICBvbGRUZXh0OiBzdHJpbmcsXG4gIG5ld1RleHQ6IHN0cmluZyxcbik6IERpZmZQYXJ0W10ge1xuICAvLyBVc2UgZGlmZldvcmRzV2l0aFNwYWNlIGluc3RlYWQgb2YgZGlmZldvcmRzIHRvIHByZXNlcnZlIHdoaXRlc3BhY2VcbiAgLy8gVGhpcyBlbnN1cmVzIHNwYWNlcyBiZXR3ZWVuIHRva2VucyBsaWtlID4gYW5kIHsgYXJlIHByZXNlcnZlZFxuICBjb25zdCByZXN1bHQgPSBkaWZmV29yZHNXaXRoU3BhY2Uob2xkVGV4dCwgbmV3VGV4dCwgeyBpZ25vcmVDYXNlOiBmYWxzZSB9KVxuXG4gIHJldHVybiByZXN1bHRcbn1cblxuLy8gUHJvY2VzcyB3b3JkLWxldmVsIGRpZmZzIHdpdGggbWFudWFsIHdyYXBwaW5nIHN1cHBvcnRcbmZ1bmN0aW9uIGdlbmVyYXRlV29yZERpZmZFbGVtZW50cyhcbiAgaXRlbTogRGlmZkxpbmUsXG4gIHdpZHRoOiBudW1iZXIsXG4gIG1heFdpZHRoOiBudW1iZXIsXG4gIGRpbTogYm9vbGVhbixcbiAgb3ZlcnJpZGVUaGVtZT86IFRoZW1lTmFtZSxcbik6IFJlYWN0LlJlYWN0Tm9kZVtdIHwgbnVsbCB7XG4gIGNvbnN0IHsgdHlwZSwgaSwgd29yZERpZmYsIG1hdGNoZWRMaW5lLCBvcmlnaW5hbENvZGUgfSA9IGl0ZW1cblxuICBpZiAoIXdvcmREaWZmIHx8ICFtYXRjaGVkTGluZSkge1xuICAgIHJldHVybiBudWxsIC8vIFRoaXMgZnVuY3Rpb24gb25seSBoYW5kbGVzIHdvcmQtbGV2ZWwgZGlmZiByZW5kZXJpbmdcbiAgfVxuXG4gIGNvbnN0IHJlbW92ZWRMaW5lVGV4dCA9XG4gICAgdHlwZSA9PT0gJ3JlbW92ZScgPyBvcmlnaW5hbENvZGUgOiBtYXRjaGVkTGluZS5vcmlnaW5hbENvZGVcbiAgY29uc3QgYWRkZWRMaW5lVGV4dCA9XG4gICAgdHlwZSA9PT0gJ3JlbW92ZScgPyBtYXRjaGVkTGluZS5vcmlnaW5hbENvZGUgOiBvcmlnaW5hbENvZGVcblxuICBjb25zdCB3b3JkRGlmZnMgPSBjYWxjdWxhdGVXb3JkRGlmZnMocmVtb3ZlZExpbmVUZXh0LCBhZGRlZExpbmVUZXh0KVxuXG4gIC8vIENoZWNrIGlmIHdlIHNob3VsZCB1c2Ugd29yZC1sZXZlbCBkaWZmaW5nXG4gIGNvbnN0IHRvdGFsTGVuZ3RoID0gcmVtb3ZlZExpbmVUZXh0Lmxlbmd0aCArIGFkZGVkTGluZVRleHQubGVuZ3RoXG4gIGNvbnN0IGNoYW5nZWRMZW5ndGggPSB3b3JkRGlmZnNcbiAgICAuZmlsdGVyKHBhcnQgPT4gcGFydC5hZGRlZCB8fCBwYXJ0LnJlbW92ZWQpXG4gICAgLnJlZHVjZSgoc3VtLCBwYXJ0KSA9PiBzdW0gKyBwYXJ0LnZhbHVlLmxlbmd0aCwgMClcbiAgY29uc3QgY2hhbmdlUmF0aW8gPSBjaGFuZ2VkTGVuZ3RoIC8gdG90YWxMZW5ndGhcblxuICBpZiAoY2hhbmdlUmF0aW8gPiBDSEFOR0VfVEhSRVNIT0xEIHx8IGRpbSkge1xuICAgIHJldHVybiBudWxsIC8vIEZhbGwgYmFjayB0byBzdGFuZGFyZCByZW5kZXJpbmcgZm9yIG1ham9yIGNoYW5nZXNcbiAgfVxuXG4gIC8vIENhbGN1bGF0ZSBhdmFpbGFibGUgd2lkdGggZm9yIGNvbnRlbnRcbiAgY29uc3QgZGlmZlByZWZpeCA9IHR5cGUgPT09ICdhZGQnID8gJysnIDogJy0nXG4gIGNvbnN0IGRpZmZQcmVmaXhXaWR0aCA9IGRpZmZQcmVmaXgubGVuZ3RoXG4gIGNvbnN0IGF2YWlsYWJsZUNvbnRlbnRXaWR0aCA9IE1hdGgubWF4KFxuICAgIDEsXG4gICAgd2lkdGggLSBtYXhXaWR0aCAtIDEgLSBkaWZmUHJlZml4V2lkdGgsXG4gIClcblxuICAvLyBNYW51YWxseSB3cmFwIHRoZSB3b3JkIGRpZmYgcGFydHMgd2l0aCBiZXR0ZXIgc3BhY2UgZWZmaWNpZW5jeVxuICBjb25zdCB3cmFwcGVkTGluZXM6IHsgY29udGVudDogUmVhY3QuUmVhY3ROb2RlW107IGNvbnRlbnRXaWR0aDogbnVtYmVyIH1bXSA9XG4gICAgW11cbiAgbGV0IGN1cnJlbnRMaW5lOiBSZWFjdC5SZWFjdE5vZGVbXSA9IFtdXG4gIGxldCBjdXJyZW50TGluZVdpZHRoID0gMFxuXG4gIHdvcmREaWZmcy5mb3JFYWNoKChwYXJ0LCBwYXJ0SW5kZXgpID0+IHtcbiAgICAvLyBEZXRlcm1pbmUgaWYgdGhpcyBwYXJ0IHNob3VsZCBiZSBzaG93biBmb3IgdGhpcyBsaW5lIHR5cGVcbiAgICBsZXQgc2hvdWxkU2hvdyA9IGZhbHNlXG4gICAgbGV0IHBhcnRCZ0NvbG9yOiAnZGlmZkFkZGVkV29yZCcgfCAnZGlmZlJlbW92ZWRXb3JkJyB8IHVuZGVmaW5lZFxuXG4gICAgaWYgKHR5cGUgPT09ICdhZGQnKSB7XG4gICAgICBpZiAocGFydC5hZGRlZCkge1xuICAgICAgICBzaG91bGRTaG93ID0gdHJ1ZVxuICAgICAgICBwYXJ0QmdDb2xvciA9ICdkaWZmQWRkZWRXb3JkJ1xuICAgICAgfSBlbHNlIGlmICghcGFydC5yZW1vdmVkKSB7XG4gICAgICAgIHNob3VsZFNob3cgPSB0cnVlXG4gICAgICB9XG4gICAgfSBlbHNlIGlmICh0eXBlID09PSAncmVtb3ZlJykge1xuICAgICAgaWYgKHBhcnQucmVtb3ZlZCkge1xuICAgICAgICBzaG91bGRTaG93ID0gdHJ1ZVxuICAgICAgICBwYXJ0QmdDb2xvciA9ICdkaWZmUmVtb3ZlZFdvcmQnXG4gICAgICB9IGVsc2UgaWYgKCFwYXJ0LmFkZGVkKSB7XG4gICAgICAgIHNob3VsZFNob3cgPSB0cnVlXG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFzaG91bGRTaG93KSByZXR1cm5cblxuICAgIC8vIFVzZSB3cmFwVGV4dCB0byB3cmFwIHRoaXMgaW5kaXZpZHVhbCBwYXJ0IGlmIGl0J3MgbG9uZ1xuICAgIGNvbnN0IHBhcnRXcmFwcGVkID0gd3JhcFRleHQocGFydC52YWx1ZSwgYXZhaWxhYmxlQ29udGVudFdpZHRoLCAnd3JhcCcpXG4gICAgY29uc3QgcGFydExpbmVzID0gcGFydFdyYXBwZWQuc3BsaXQoJ1xcbicpXG5cbiAgICBwYXJ0TGluZXMuZm9yRWFjaCgocGFydExpbmUsIGxpbmVJZHgpID0+IHtcbiAgICAgIGlmICghcGFydExpbmUpIHJldHVyblxuXG4gICAgICAvLyBDaGVjayBpZiB3ZSBuZWVkIHRvIHN0YXJ0IGEgbmV3IGxpbmVcbiAgICAgIGlmIChcbiAgICAgICAgbGluZUlkeCA+IDAgfHxcbiAgICAgICAgY3VycmVudExpbmVXaWR0aCArIHN0cmluZ1dpZHRoKHBhcnRMaW5lKSA+IGF2YWlsYWJsZUNvbnRlbnRXaWR0aFxuICAgICAgKSB7XG4gICAgICAgIGlmIChjdXJyZW50TGluZS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgd3JhcHBlZExpbmVzLnB1c2goe1xuICAgICAgICAgICAgY29udGVudDogWy4uLmN1cnJlbnRMaW5lXSxcbiAgICAgICAgICAgIGNvbnRlbnRXaWR0aDogY3VycmVudExpbmVXaWR0aCxcbiAgICAgICAgICB9KVxuICAgICAgICAgIGN1cnJlbnRMaW5lID0gW11cbiAgICAgICAgICBjdXJyZW50TGluZVdpZHRoID0gMFxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIGN1cnJlbnRMaW5lLnB1c2goXG4gICAgICAgIDxUZXh0XG4gICAgICAgICAga2V5PXtgcGFydC0ke3BhcnRJbmRleH0tJHtsaW5lSWR4fWB9XG4gICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtwYXJ0QmdDb2xvcn1cbiAgICAgICAgPlxuICAgICAgICAgIHtwYXJ0TGluZX1cbiAgICAgICAgPC9UZXh0PixcbiAgICAgIClcblxuICAgICAgY3VycmVudExpbmVXaWR0aCArPSBzdHJpbmdXaWR0aChwYXJ0TGluZSlcbiAgICB9KVxuICB9KVxuXG4gIGlmIChjdXJyZW50TGluZS5sZW5ndGggPiAwKSB7XG4gICAgd3JhcHBlZExpbmVzLnB1c2goeyBjb250ZW50OiBjdXJyZW50TGluZSwgY29udGVudFdpZHRoOiBjdXJyZW50TGluZVdpZHRoIH0pXG4gIH1cblxuICAvLyBSZW5kZXIgZWFjaCB3cmFwcGVkIGxpbmUgYXMgYSBzZXBhcmF0ZSBUZXh0IGVsZW1lbnRcbiAgcmV0dXJuIHdyYXBwZWRMaW5lcy5tYXAoKHsgY29udGVudCwgY29udGVudFdpZHRoIH0sIGxpbmVJbmRleCkgPT4ge1xuICAgIGNvbnN0IGtleSA9IGAke3R5cGV9LSR7aX0tJHtsaW5lSW5kZXh9YFxuICAgIGNvbnN0IGxpbmVCZ0NvbG9yID1cbiAgICAgIHR5cGUgPT09ICdhZGQnXG4gICAgICAgID8gZGltXG4gICAgICAgICAgPyAnZGlmZkFkZGVkRGltbWVkJ1xuICAgICAgICAgIDogJ2RpZmZBZGRlZCdcbiAgICAgICAgOiBkaW1cbiAgICAgICAgICA/ICdkaWZmUmVtb3ZlZERpbW1lZCdcbiAgICAgICAgICA6ICdkaWZmUmVtb3ZlZCdcbiAgICBjb25zdCBsaW5lTnVtID0gbGluZUluZGV4ID09PSAwID8gaSA6IHVuZGVmaW5lZFxuICAgIGNvbnN0IGxpbmVOdW1TdHIgPVxuICAgICAgKGxpbmVOdW0gIT09IHVuZGVmaW5lZFxuICAgICAgICA/IGxpbmVOdW0udG9TdHJpbmcoKS5wYWRTdGFydChtYXhXaWR0aClcbiAgICAgICAgOiAnICcucmVwZWF0KG1heFdpZHRoKSkgKyAnICdcbiAgICAvLyBDYWxjdWxhdGUgcGFkZGluZyB0byBmaWxsIHRoZSBlbnRpcmUgdGVybWluYWwgd2lkdGhcbiAgICBjb25zdCB1c2VkV2lkdGggPSBsaW5lTnVtU3RyLmxlbmd0aCArIGRpZmZQcmVmaXhXaWR0aCArIGNvbnRlbnRXaWR0aFxuICAgIGNvbnN0IHBhZGRpbmcgPSBNYXRoLm1heCgwLCB3aWR0aCAtIHVzZWRXaWR0aClcblxuICAgIHJldHVybiAoXG4gICAgICA8Qm94IGtleT17a2V5fSBmbGV4RGlyZWN0aW9uPVwicm93XCI+XG4gICAgICAgIDxOb1NlbGVjdCBmcm9tTGVmdEVkZ2U+XG4gICAgICAgICAgPFRleHRcbiAgICAgICAgICAgIGNvbG9yPXtvdmVycmlkZVRoZW1lID8gJ3RleHQnIDogdW5kZWZpbmVkfVxuICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtsaW5lQmdDb2xvcn1cbiAgICAgICAgICAgIGRpbUNvbG9yPXtkaW19XG4gICAgICAgICAgPlxuICAgICAgICAgICAge2xpbmVOdW1TdHJ9XG4gICAgICAgICAgICB7ZGlmZlByZWZpeH1cbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgIDwvTm9TZWxlY3Q+XG4gICAgICAgIDxUZXh0XG4gICAgICAgICAgY29sb3I9e292ZXJyaWRlVGhlbWUgPyAndGV4dCcgOiB1bmRlZmluZWR9XG4gICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtsaW5lQmdDb2xvcn1cbiAgICAgICAgICBkaW1Db2xvcj17ZGltfVxuICAgICAgICA+XG4gICAgICAgICAge2NvbnRlbnR9XG4gICAgICAgICAgeycgJy5yZXBlYXQocGFkZGluZyl9XG4gICAgICAgIDwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfSlcbn1cblxuZnVuY3Rpb24gZm9ybWF0RGlmZihcbiAgbGluZXM6IHN0cmluZ1tdLFxuICBzdGFydGluZ0xpbmVOdW1iZXI6IG51bWJlcixcbiAgd2lkdGg6IG51bWJlcixcbiAgZGltOiBib29sZWFuLFxuICBvdmVycmlkZVRoZW1lPzogVGhlbWVOYW1lLFxuKTogUmVhY3QuUmVhY3ROb2RlW10ge1xuICAvLyBFbnN1cmUgd2lkdGggaXMgYXQgbGVhc3QgMSB0byBwcmV2ZW50IHJlbmRlcmluZyBpc3N1ZXMgd2l0aCB2ZXJ5IG5hcnJvdyB0ZXJtaW5hbHNcbiAgY29uc3Qgc2FmZVdpZHRoID0gTWF0aC5tYXgoMSwgTWF0aC5mbG9vcih3aWR0aCkpXG5cbiAgLy8gU3RlcCAxOiBUcmFuc2Zvcm0gbGluZXMgdG8gbGluZSBvYmplY3RzIHdpdGggdHlwZSBpbmZvcm1hdGlvblxuICBjb25zdCBsaW5lT2JqZWN0cyA9IHRyYW5zZm9ybUxpbmVzVG9PYmplY3RzKGxpbmVzKVxuXG4gIC8vIFN0ZXAgMjogR3JvdXAgYWRqYWNlbnQgYWRkL3JlbW92ZSBsaW5lcyBmb3Igd29yZC1sZXZlbCBkaWZmaW5nXG4gIGNvbnN0IHByb2Nlc3NlZExpbmVzID0gcHJvY2Vzc0FkamFjZW50TGluZXMobGluZU9iamVjdHMpXG5cbiAgLy8gU3RlcCAzOiBOdW1iZXIgdGhlIGRpZmYgbGluZXNcbiAgY29uc3QgbHMgPSBudW1iZXJEaWZmTGluZXMocHJvY2Vzc2VkTGluZXMsIHN0YXJ0aW5nTGluZU51bWJlcilcblxuICAvLyBGaW5kIG1heCBsaW5lIG51bWJlciB3aWR0aCBmb3IgYWxpZ25tZW50XG4gIGNvbnN0IG1heExpbmVOdW1iZXIgPSBNYXRoLm1heCguLi5scy5tYXAoKHsgaSB9KSA9PiBpKSwgMClcbiAgY29uc3QgbWF4V2lkdGggPSBNYXRoLm1heChtYXhMaW5lTnVtYmVyLnRvU3RyaW5nKCkubGVuZ3RoICsgMSwgMClcblxuICAvLyBTdGVwIDQ6IFJlbmRlciBmb3JtYXR0aW5nXG4gIHJldHVybiBscy5mbGF0TWFwKChpdGVtKTogUmVhY3QuUmVhY3ROb2RlW10gPT4ge1xuICAgIGNvbnN0IHsgdHlwZSwgY29kZSwgaSwgd29yZERpZmYsIG1hdGNoZWRMaW5lIH0gPSBpdGVtXG5cbiAgICAvLyBIYW5kbGUgd29yZC1sZXZlbCBkaWZmaW5nIGZvciBhZGQvcmVtb3ZlIHBhaXJzXG4gICAgaWYgKHdvcmREaWZmICYmIG1hdGNoZWRMaW5lKSB7XG4gICAgICBjb25zdCB3b3JkRGlmZkVsZW1lbnRzID0gZ2VuZXJhdGVXb3JkRGlmZkVsZW1lbnRzKFxuICAgICAgICBpdGVtLFxuICAgICAgICBzYWZlV2lkdGgsXG4gICAgICAgIG1heFdpZHRoLFxuICAgICAgICBkaW0sXG4gICAgICAgIG92ZXJyaWRlVGhlbWUsXG4gICAgICApXG5cbiAgICAgIC8vIHdvcmQtZGlmZiBtaWdodCByZWZ1c2UgKGUuZy4gZHVlIHRvIGxpbmVzIGJlaW5nIHN1YnN0YW50aWFsbHkgZGlmZmVyZW50KSBpbiB3aGljaFxuICAgICAgLy8gY2FzZSB3ZSdsbCBmYWxsIHRocm91Z2ggdG8gbm9ybWFsIHJlbmRlcmluIGdiZWxvd1xuICAgICAgaWYgKHdvcmREaWZmRWxlbWVudHMgIT09IG51bGwpIHtcbiAgICAgICAgcmV0dXJuIHdvcmREaWZmRWxlbWVudHNcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBTdGFuZGFyZCByZW5kZXJpbmcgZm9yIGxpbmVzIHdpdGhvdXQgd29yZCBkaWZmaW5nIG9yIGFzIGZhbGxiYWNrXG4gICAgLy8gQ2FsY3VsYXRlIGF2YWlsYWJsZSB3aWR0aCBhY2NvdW50aW5nIGZvciBsaW5lIG51bWJlciArIHNwYWNlICsgZGlmZiBwcmVmaXhcbiAgICBjb25zdCBkaWZmUHJlZml4V2lkdGggPSAyIC8vIFwiICBcIiBmb3IgdW5jaGFuZ2VkLCBcIisgXCIgb3IgXCItIFwiIGZvciBjaGFuZ2VzXG4gICAgY29uc3QgYXZhaWxhYmxlQ29udGVudFdpZHRoID0gTWF0aC5tYXgoXG4gICAgICAxLFxuICAgICAgc2FmZVdpZHRoIC0gbWF4V2lkdGggLSAxIC0gZGlmZlByZWZpeFdpZHRoLFxuICAgICkgLy8gLTEgZm9yIHNwYWNlIGFmdGVyIGxpbmUgbnVtYmVyXG4gICAgY29uc3Qgd3JhcHBlZFRleHQgPSB3cmFwVGV4dChjb2RlLCBhdmFpbGFibGVDb250ZW50V2lkdGgsICd3cmFwJylcbiAgICBjb25zdCB3cmFwcGVkTGluZXMgPSB3cmFwcGVkVGV4dC5zcGxpdCgnXFxuJylcblxuICAgIHJldHVybiB3cmFwcGVkTGluZXMubWFwKChsaW5lLCBsaW5lSW5kZXgpID0+IHtcbiAgICAgIGNvbnN0IGtleSA9IGAke3R5cGV9LSR7aX0tJHtsaW5lSW5kZXh9YFxuICAgICAgY29uc3QgbGluZU51bSA9IGxpbmVJbmRleCA9PT0gMCA/IGkgOiB1bmRlZmluZWRcbiAgICAgIGNvbnN0IGxpbmVOdW1TdHIgPVxuICAgICAgICAobGluZU51bSAhPT0gdW5kZWZpbmVkXG4gICAgICAgICAgPyBsaW5lTnVtLnRvU3RyaW5nKCkucGFkU3RhcnQobWF4V2lkdGgpXG4gICAgICAgICAgOiAnICcucmVwZWF0KG1heFdpZHRoKSkgKyAnICdcbiAgICAgIGNvbnN0IHNpZ2lsID0gdHlwZSA9PT0gJ2FkZCcgPyAnKycgOiB0eXBlID09PSAncmVtb3ZlJyA/ICctJyA6ICcgJ1xuICAgICAgLy8gQ2FsY3VsYXRlIHBhZGRpbmcgdG8gZmlsbCB0aGUgZW50aXJlIHRlcm1pbmFsIHdpZHRoXG4gICAgICBjb25zdCBjb250ZW50V2lkdGggPSBsaW5lTnVtU3RyLmxlbmd0aCArIDEgKyBzdHJpbmdXaWR0aChsaW5lKSAvLyBsaW5lTnVtICsgc2lnaWwgKyBjb2RlXG4gICAgICBjb25zdCBwYWRkaW5nID0gTWF0aC5tYXgoMCwgc2FmZVdpZHRoIC0gY29udGVudFdpZHRoKVxuXG4gICAgICBjb25zdCBiZ0NvbG9yID1cbiAgICAgICAgdHlwZSA9PT0gJ2FkZCdcbiAgICAgICAgICA/IGRpbVxuICAgICAgICAgICAgPyAnZGlmZkFkZGVkRGltbWVkJ1xuICAgICAgICAgICAgOiAnZGlmZkFkZGVkJ1xuICAgICAgICAgIDogdHlwZSA9PT0gJ3JlbW92ZSdcbiAgICAgICAgICAgID8gZGltXG4gICAgICAgICAgICAgID8gJ2RpZmZSZW1vdmVkRGltbWVkJ1xuICAgICAgICAgICAgICA6ICdkaWZmUmVtb3ZlZCdcbiAgICAgICAgICAgIDogdW5kZWZpbmVkXG5cbiAgICAgIC8vIEd1dHRlciAobGluZSBudW1iZXIgKyBzaWdpbCkgaXMgd3JhcHBlZCBpbiA8Tm9TZWxlY3Q+IHNvIGZ1bGxzY3JlZW5cbiAgICAgIC8vIHRleHQgc2VsZWN0aW9uIHlpZWxkcyBjbGVhbiBjb2RlLiBiZ0NvbG9yIGNhcnJpZXMgYWNyb3NzIGJvdGggYm94ZXNcbiAgICAgIC8vIHNvIHRoZSB2aXN1YWwgY29udGludWl0eSAoc29saWQgcmVkL2dyZWVuIGJhcikgaXMgdW5jaGFuZ2VkLlxuICAgICAgcmV0dXJuIChcbiAgICAgICAgPEJveCBrZXk9e2tleX0gZmxleERpcmVjdGlvbj1cInJvd1wiPlxuICAgICAgICAgIDxOb1NlbGVjdCBmcm9tTGVmdEVkZ2U+XG4gICAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgICBjb2xvcj17b3ZlcnJpZGVUaGVtZSA/ICd0ZXh0JyA6IHVuZGVmaW5lZH1cbiAgICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yPXtiZ0NvbG9yfVxuICAgICAgICAgICAgICBkaW1Db2xvcj17ZGltIHx8IHR5cGUgPT09ICdub2NoYW5nZSd9XG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgIHtsaW5lTnVtU3RyfVxuICAgICAgICAgICAgICB7c2lnaWx9XG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPC9Ob1NlbGVjdD5cbiAgICAgICAgICA8VGV4dFxuICAgICAgICAgICAgY29sb3I9e292ZXJyaWRlVGhlbWUgPyAndGV4dCcgOiB1bmRlZmluZWR9XG4gICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3I9e2JnQ29sb3J9XG4gICAgICAgICAgICBkaW1Db2xvcj17ZGltfVxuICAgICAgICAgID5cbiAgICAgICAgICAgIHtsaW5lfVxuICAgICAgICAgICAgeycgJy5yZXBlYXQocGFkZGluZyl9XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgIClcbiAgICB9KVxuICB9KVxufVxuXG5leHBvcnQgZnVuY3Rpb24gbnVtYmVyRGlmZkxpbmVzKFxuICBkaWZmOiBMaW5lT2JqZWN0W10sXG4gIHN0YXJ0TGluZTogbnVtYmVyLFxuKTogRGlmZkxpbmVbXSB7XG4gIGxldCBpID0gc3RhcnRMaW5lXG4gIGNvbnN0IHJlc3VsdDogRGlmZkxpbmVbXSA9IFtdXG4gIGNvbnN0IHF1ZXVlID0gWy4uLmRpZmZdXG5cbiAgd2hpbGUgKHF1ZXVlLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBjdXJyZW50ID0gcXVldWUuc2hpZnQoKSFcbiAgICBjb25zdCB7IGNvZGUsIHR5cGUsIG9yaWdpbmFsQ29kZSwgd29yZERpZmYsIG1hdGNoZWRMaW5lIH0gPSBjdXJyZW50XG4gICAgY29uc3QgbGluZSA9IHtcbiAgICAgIGNvZGUsXG4gICAgICB0eXBlLFxuICAgICAgaSxcbiAgICAgIG9yaWdpbmFsQ29kZSxcbiAgICAgIHdvcmREaWZmLFxuICAgICAgbWF0Y2hlZExpbmUsXG4gICAgfVxuXG4gICAgLy8gVXBkYXRlIGNvdW50ZXJzIGJhc2VkIG9uIGNoYW5nZSB0eXBlXG4gICAgc3dpdGNoICh0eXBlKSB7XG4gICAgICBjYXNlICdub2NoYW5nZSc6XG4gICAgICAgIGkrK1xuICAgICAgICByZXN1bHQucHVzaChsaW5lKVxuICAgICAgICBicmVha1xuICAgICAgY2FzZSAnYWRkJzpcbiAgICAgICAgaSsrXG4gICAgICAgIHJlc3VsdC5wdXNoKGxpbmUpXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdyZW1vdmUnOiB7XG4gICAgICAgIHJlc3VsdC5wdXNoKGxpbmUpXG4gICAgICAgIGxldCBudW1SZW1vdmVkID0gMFxuICAgICAgICB3aGlsZSAocXVldWVbMF0/LnR5cGUgPT09ICdyZW1vdmUnKSB7XG4gICAgICAgICAgaSsrXG4gICAgICAgICAgY29uc3QgY3VycmVudCA9IHF1ZXVlLnNoaWZ0KCkhXG4gICAgICAgICAgY29uc3QgeyBjb2RlLCB0eXBlLCBvcmlnaW5hbENvZGUsIHdvcmREaWZmLCBtYXRjaGVkTGluZSB9ID0gY3VycmVudFxuICAgICAgICAgIGNvbnN0IGxpbmUgPSB7XG4gICAgICAgICAgICBjb2RlLFxuICAgICAgICAgICAgdHlwZSxcbiAgICAgICAgICAgIGksXG4gICAgICAgICAgICBvcmlnaW5hbENvZGUsXG4gICAgICAgICAgICB3b3JkRGlmZixcbiAgICAgICAgICAgIG1hdGNoZWRMaW5lLFxuICAgICAgICAgIH1cbiAgICAgICAgICByZXN1bHQucHVzaChsaW5lKVxuICAgICAgICAgIG51bVJlbW92ZWQrK1xuICAgICAgICB9XG4gICAgICAgIGkgLT0gbnVtUmVtb3ZlZFxuICAgICAgICBicmVha1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZXN1bHRcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLFNBQVNBLGtCQUFrQixFQUFFLEtBQUtDLG1CQUFtQixRQUFRLE1BQU07QUFDbkUsT0FBTyxLQUFLQyxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxPQUFPLFFBQVEsT0FBTztBQUMvQixjQUFjQyxTQUFTLFFBQVEsb0JBQW9CO0FBQ25ELFNBQVNDLFdBQVcsUUFBUSwwQkFBMEI7QUFDdEQsU0FBU0MsR0FBRyxFQUFFQyxRQUFRLEVBQUVDLElBQUksRUFBRUMsUUFBUSxFQUFFQyxRQUFRLFFBQVEsY0FBYzs7QUFFdEU7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxVQUFVQyxRQUFRLENBQUM7RUFDakJDLElBQUksRUFBRSxNQUFNO0VBQ1pDLElBQUksRUFBRSxLQUFLLEdBQUcsUUFBUSxHQUFHLFVBQVU7RUFDbkNDLENBQUMsRUFBRSxNQUFNO0VBQ1RDLFlBQVksRUFBRSxNQUFNO0VBQ3BCQyxRQUFRLENBQUMsRUFBRSxPQUFPLEVBQUM7RUFDbkJDLFdBQVcsQ0FBQyxFQUFFTixRQUFRO0FBQ3hCOztBQUVBO0FBQ0EsT0FBTyxVQUFVTyxVQUFVLENBQUM7RUFDMUJOLElBQUksRUFBRSxNQUFNO0VBQ1pFLENBQUMsRUFBRSxNQUFNO0VBQ1RELElBQUksRUFBRSxLQUFLLEdBQUcsUUFBUSxHQUFHLFVBQVU7RUFDbkNFLFlBQVksRUFBRSxNQUFNO0VBQ3BCQyxRQUFRLENBQUMsRUFBRSxPQUFPO0VBQ2xCQyxXQUFXLENBQUMsRUFBRUMsVUFBVTtBQUMxQjs7QUFFQTtBQUNBLFVBQVVDLFFBQVEsQ0FBQztFQUNqQkMsS0FBSyxDQUFDLEVBQUUsT0FBTztFQUNmQyxPQUFPLENBQUMsRUFBRSxPQUFPO0VBQ2pCQyxLQUFLLEVBQUUsTUFBTTtBQUNmO0FBRUEsS0FBS0MsS0FBSyxHQUFHO0VBQ1hDLEtBQUssRUFBRXZCLG1CQUFtQjtFQUMxQndCLEdBQUcsRUFBRSxPQUFPO0VBQ1pDLEtBQUssRUFBRSxNQUFNO0FBQ2YsQ0FBQzs7QUFFRDtBQUNBLE1BQU1DLGdCQUFnQixHQUFHLEdBQUc7QUFFNUIsT0FBTyxTQUFBQyx1QkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFnQztJQUFBUCxLQUFBO0lBQUFDLEdBQUE7SUFBQUM7RUFBQSxJQUFBRyxFQUkvQjtFQUNOLE9BQUFHLEtBQUEsSUFBZ0J2QixRQUFRLENBQUMsQ0FBQztFQUFBLElBQUF3QixFQUFBO0VBQUEsSUFBQUgsQ0FBQSxRQUFBTCxHQUFBLElBQUFLLENBQUEsUUFBQU4sS0FBQSxDQUFBVSxLQUFBLElBQUFKLENBQUEsUUFBQU4sS0FBQSxDQUFBVyxRQUFBLElBQUFMLENBQUEsUUFBQUUsS0FBQSxJQUFBRixDQUFBLFFBQUFKLEtBQUE7SUFFbEJPLEVBQUEsR0FBQUcsVUFBVSxDQUFDWixLQUFLLENBQUFVLEtBQU0sRUFBRVYsS0FBSyxDQUFBVyxRQUFTLEVBQUVULEtBQUssRUFBRUQsR0FBRyxFQUFFTyxLQUFLLENBQUM7SUFBQUYsQ0FBQSxNQUFBTCxHQUFBO0lBQUFLLENBQUEsTUFBQU4sS0FBQSxDQUFBVSxLQUFBO0lBQUFKLENBQUEsTUFBQU4sS0FBQSxDQUFBVyxRQUFBO0lBQUFMLENBQUEsTUFBQUUsS0FBQTtJQUFBRixDQUFBLE1BQUFKLEtBQUE7SUFBQUksQ0FBQSxNQUFBRyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSCxDQUFBO0VBQUE7RUFEbEUsTUFBQU8sSUFBQSxHQUNRSixFQUEwRDtFQUVqRSxJQUFBSyxFQUFBO0VBQUEsSUFBQVIsQ0FBQSxRQUFBTyxJQUFBO0lBSUlDLEVBQUEsR0FBQUQsSUFBSSxDQUFBRSxHQUFJLENBQUNDLEtBRVQsQ0FBQztJQUFBVixDQUFBLE1BQUFPLElBQUE7SUFBQVAsQ0FBQSxNQUFBUSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBUixDQUFBO0VBQUE7RUFBQSxJQUFBVyxFQUFBO0VBQUEsSUFBQVgsQ0FBQSxRQUFBUSxFQUFBO0lBSEpHLEVBQUEsSUFBQyxHQUFHLENBQWUsYUFBUSxDQUFSLFFBQVEsQ0FBVyxRQUFDLENBQUQsR0FBQyxDQUNwQyxDQUFBSCxFQUVBLENBQ0gsRUFKQyxHQUFHLENBSUU7SUFBQVIsQ0FBQSxNQUFBUSxFQUFBO0lBQUFSLENBQUEsTUFBQVcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQVgsQ0FBQTtFQUFBO0VBQUEsT0FKTlcsRUFJTTtBQUFBOztBQUlWO0FBcEJPLFNBQUFELE1BQUFFLElBQUEsRUFBQTVCLENBQUE7RUFBQSxPQWNDLENBQUMsR0FBRyxDQUFNQSxHQUFDLENBQURBLEVBQUEsQ0FBQyxDQUFHNEIsS0FBRyxDQUFFLEVBQWxCLEdBQUcsQ0FBcUI7QUFBQTtBQU9qQyxPQUFPLFNBQVNDLHVCQUF1QkEsQ0FBQ1QsS0FBSyxFQUFFLE1BQU0sRUFBRSxDQUFDLEVBQUVoQixVQUFVLEVBQUUsQ0FBQztFQUNyRSxPQUFPZ0IsS0FBSyxDQUFDSyxHQUFHLENBQUMzQixJQUFJLElBQUk7SUFDdkIsSUFBSUEsSUFBSSxDQUFDZ0MsVUFBVSxDQUFDLEdBQUcsQ0FBQyxFQUFFO01BQ3hCLE9BQU87UUFDTGhDLElBQUksRUFBRUEsSUFBSSxDQUFDaUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNuQi9CLENBQUMsRUFBRSxDQUFDO1FBQ0pELElBQUksRUFBRSxLQUFLO1FBQ1hFLFlBQVksRUFBRUgsSUFBSSxDQUFDaUMsS0FBSyxDQUFDLENBQUM7TUFDNUIsQ0FBQztJQUNIO0lBQ0EsSUFBSWpDLElBQUksQ0FBQ2dDLFVBQVUsQ0FBQyxHQUFHLENBQUMsRUFBRTtNQUN4QixPQUFPO1FBQ0xoQyxJQUFJLEVBQUVBLElBQUksQ0FBQ2lDLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDbkIvQixDQUFDLEVBQUUsQ0FBQztRQUNKRCxJQUFJLEVBQUUsUUFBUTtRQUNkRSxZQUFZLEVBQUVILElBQUksQ0FBQ2lDLEtBQUssQ0FBQyxDQUFDO01BQzVCLENBQUM7SUFDSDtJQUNBLE9BQU87TUFDTGpDLElBQUksRUFBRUEsSUFBSSxDQUFDaUMsS0FBSyxDQUFDLENBQUMsQ0FBQztNQUNuQi9CLENBQUMsRUFBRSxDQUFDO01BQ0pELElBQUksRUFBRSxVQUFVO01BQ2hCRSxZQUFZLEVBQUVILElBQUksQ0FBQ2lDLEtBQUssQ0FBQyxDQUFDO0lBQzVCLENBQUM7RUFDSCxDQUFDLENBQUM7QUFDSjs7QUFFQTtBQUNBLE9BQU8sU0FBU0Msb0JBQW9CQSxDQUFDQyxXQUFXLEVBQUU3QixVQUFVLEVBQUUsQ0FBQyxFQUFFQSxVQUFVLEVBQUUsQ0FBQztFQUM1RSxNQUFNOEIsY0FBYyxFQUFFOUIsVUFBVSxFQUFFLEdBQUcsRUFBRTtFQUN2QyxJQUFJSixDQUFDLEdBQUcsQ0FBQztFQUVULE9BQU9BLENBQUMsR0FBR2lDLFdBQVcsQ0FBQ0UsTUFBTSxFQUFFO0lBQzdCLE1BQU1DLE9BQU8sR0FBR0gsV0FBVyxDQUFDakMsQ0FBQyxDQUFDO0lBQzlCLElBQUksQ0FBQ29DLE9BQU8sRUFBRTtNQUNacEMsQ0FBQyxFQUFFO01BQ0g7SUFDRjs7SUFFQTtJQUNBLElBQUlvQyxPQUFPLENBQUNyQyxJQUFJLEtBQUssUUFBUSxFQUFFO01BQzdCLE1BQU1zQyxXQUFXLEVBQUVqQyxVQUFVLEVBQUUsR0FBRyxDQUFDZ0MsT0FBTyxDQUFDO01BQzNDLElBQUlFLENBQUMsR0FBR3RDLENBQUMsR0FBRyxDQUFDOztNQUViO01BQ0EsT0FBT3NDLENBQUMsR0FBR0wsV0FBVyxDQUFDRSxNQUFNLElBQUlGLFdBQVcsQ0FBQ0ssQ0FBQyxDQUFDLEVBQUV2QyxJQUFJLEtBQUssUUFBUSxFQUFFO1FBQ2xFLE1BQU13QyxJQUFJLEdBQUdOLFdBQVcsQ0FBQ0ssQ0FBQyxDQUFDO1FBQzNCLElBQUlDLElBQUksRUFBRTtVQUNSRixXQUFXLENBQUNHLElBQUksQ0FBQ0QsSUFBSSxDQUFDO1FBQ3hCO1FBQ0FELENBQUMsRUFBRTtNQUNMOztNQUVBO01BQ0EsTUFBTUcsUUFBUSxFQUFFckMsVUFBVSxFQUFFLEdBQUcsRUFBRTtNQUNqQyxPQUFPa0MsQ0FBQyxHQUFHTCxXQUFXLENBQUNFLE1BQU0sSUFBSUYsV0FBVyxDQUFDSyxDQUFDLENBQUMsRUFBRXZDLElBQUksS0FBSyxLQUFLLEVBQUU7UUFDL0QsTUFBTXdDLElBQUksR0FBR04sV0FBVyxDQUFDSyxDQUFDLENBQUM7UUFDM0IsSUFBSUMsSUFBSSxFQUFFO1VBQ1JFLFFBQVEsQ0FBQ0QsSUFBSSxDQUFDRCxJQUFJLENBQUM7UUFDckI7UUFDQUQsQ0FBQyxFQUFFO01BQ0w7O01BRUE7TUFDQSxJQUFJRCxXQUFXLENBQUNGLE1BQU0sR0FBRyxDQUFDLElBQUlNLFFBQVEsQ0FBQ04sTUFBTSxHQUFHLENBQUMsRUFBRTtRQUNqRDtRQUNBLE1BQU1PLFNBQVMsR0FBR0MsSUFBSSxDQUFDQyxHQUFHLENBQUNQLFdBQVcsQ0FBQ0YsTUFBTSxFQUFFTSxRQUFRLENBQUNOLE1BQU0sQ0FBQzs7UUFFL0Q7UUFDQSxLQUFLLElBQUlVLENBQUMsR0FBRyxDQUFDLEVBQUVBLENBQUMsR0FBR0gsU0FBUyxFQUFFRyxDQUFDLEVBQUUsRUFBRTtVQUNsQyxNQUFNQyxVQUFVLEdBQUdULFdBQVcsQ0FBQ1EsQ0FBQyxDQUFDO1VBQ2pDLE1BQU1FLE9BQU8sR0FBR04sUUFBUSxDQUFDSSxDQUFDLENBQUM7VUFFM0IsSUFBSUMsVUFBVSxJQUFJQyxPQUFPLEVBQUU7WUFDekJELFVBQVUsQ0FBQzVDLFFBQVEsR0FBRyxJQUFJO1lBQzFCNkMsT0FBTyxDQUFDN0MsUUFBUSxHQUFHLElBQUk7O1lBRXZCO1lBQ0E0QyxVQUFVLENBQUMzQyxXQUFXLEdBQUc0QyxPQUFPO1lBQ2hDQSxPQUFPLENBQUM1QyxXQUFXLEdBQUcyQyxVQUFVO1VBQ2xDO1FBQ0Y7O1FBRUE7UUFDQVosY0FBYyxDQUFDTSxJQUFJLENBQUMsR0FBR0gsV0FBVyxDQUFDVyxNQUFNLENBQUNDLE9BQU8sQ0FBQyxDQUFDOztRQUVuRDtRQUNBZixjQUFjLENBQUNNLElBQUksQ0FBQyxHQUFHQyxRQUFRLENBQUNPLE1BQU0sQ0FBQ0MsT0FBTyxDQUFDLENBQUM7UUFFaERqRCxDQUFDLEdBQUdzQyxDQUFDLEVBQUM7TUFDUixDQUFDLE1BQU07UUFDTDtRQUNBSixjQUFjLENBQUNNLElBQUksQ0FBQ0osT0FBTyxDQUFDO1FBQzVCcEMsQ0FBQyxFQUFFO01BQ0w7SUFDRixDQUFDLE1BQU07TUFDTDtNQUNBa0MsY0FBYyxDQUFDTSxJQUFJLENBQUNKLE9BQU8sQ0FBQztNQUM1QnBDLENBQUMsRUFBRTtJQUNMO0VBQ0Y7RUFFQSxPQUFPa0MsY0FBYztBQUN2Qjs7QUFFQTtBQUNBLE9BQU8sU0FBU2dCLGtCQUFrQkEsQ0FDaENDLE9BQU8sRUFBRSxNQUFNLEVBQ2ZDLE9BQU8sRUFBRSxNQUFNLENBQ2hCLEVBQUUvQyxRQUFRLEVBQUUsQ0FBQztFQUNaO0VBQ0E7RUFDQSxNQUFNZ0QsTUFBTSxHQUFHbkUsa0JBQWtCLENBQUNpRSxPQUFPLEVBQUVDLE9BQU8sRUFBRTtJQUFFRSxVQUFVLEVBQUU7RUFBTSxDQUFDLENBQUM7RUFFMUUsT0FBT0QsTUFBTTtBQUNmOztBQUVBO0FBQ0EsU0FBU0Usd0JBQXdCQSxDQUMvQkMsSUFBSSxFQUFFM0QsUUFBUSxFQUNkZSxLQUFLLEVBQUUsTUFBTSxFQUNiNkMsUUFBUSxFQUFFLE1BQU0sRUFDaEI5QyxHQUFHLEVBQUUsT0FBTyxFQUNaK0MsYUFBeUIsQ0FBWCxFQUFFcEUsU0FBUyxDQUMxQixFQUFFRixLQUFLLENBQUN1RSxTQUFTLEVBQUUsR0FBRyxJQUFJLENBQUM7RUFDMUIsTUFBTTtJQUFFNUQsSUFBSTtJQUFFQyxDQUFDO0lBQUVFLFFBQVE7SUFBRUMsV0FBVztJQUFFRjtFQUFhLENBQUMsR0FBR3VELElBQUk7RUFFN0QsSUFBSSxDQUFDdEQsUUFBUSxJQUFJLENBQUNDLFdBQVcsRUFBRTtJQUM3QixPQUFPLElBQUksRUFBQztFQUNkO0VBRUEsTUFBTXlELGVBQWUsR0FDbkI3RCxJQUFJLEtBQUssUUFBUSxHQUFHRSxZQUFZLEdBQUdFLFdBQVcsQ0FBQ0YsWUFBWTtFQUM3RCxNQUFNNEQsYUFBYSxHQUNqQjlELElBQUksS0FBSyxRQUFRLEdBQUdJLFdBQVcsQ0FBQ0YsWUFBWSxHQUFHQSxZQUFZO0VBRTdELE1BQU02RCxTQUFTLEdBQUdaLGtCQUFrQixDQUFDVSxlQUFlLEVBQUVDLGFBQWEsQ0FBQzs7RUFFcEU7RUFDQSxNQUFNRSxXQUFXLEdBQUdILGVBQWUsQ0FBQ3pCLE1BQU0sR0FBRzBCLGFBQWEsQ0FBQzFCLE1BQU07RUFDakUsTUFBTTZCLGFBQWEsR0FBR0YsU0FBUyxDQUM1QmQsTUFBTSxDQUFDaUIsSUFBSSxJQUFJQSxJQUFJLENBQUMzRCxLQUFLLElBQUkyRCxJQUFJLENBQUMxRCxPQUFPLENBQUMsQ0FDMUMyRCxNQUFNLENBQUMsQ0FBQ0MsR0FBRyxFQUFFRixJQUFJLEtBQUtFLEdBQUcsR0FBR0YsSUFBSSxDQUFDekQsS0FBSyxDQUFDMkIsTUFBTSxFQUFFLENBQUMsQ0FBQztFQUNwRCxNQUFNaUMsV0FBVyxHQUFHSixhQUFhLEdBQUdELFdBQVc7RUFFL0MsSUFBSUssV0FBVyxHQUFHdkQsZ0JBQWdCLElBQUlGLEdBQUcsRUFBRTtJQUN6QyxPQUFPLElBQUksRUFBQztFQUNkOztFQUVBO0VBQ0EsTUFBTTBELFVBQVUsR0FBR3RFLElBQUksS0FBSyxLQUFLLEdBQUcsR0FBRyxHQUFHLEdBQUc7RUFDN0MsTUFBTXVFLGVBQWUsR0FBR0QsVUFBVSxDQUFDbEMsTUFBTTtFQUN6QyxNQUFNb0MscUJBQXFCLEdBQUc1QixJQUFJLENBQUM2QixHQUFHLENBQ3BDLENBQUMsRUFDRDVELEtBQUssR0FBRzZDLFFBQVEsR0FBRyxDQUFDLEdBQUdhLGVBQ3pCLENBQUM7O0VBRUQ7RUFDQSxNQUFNRyxZQUFZLEVBQUU7SUFBRUMsT0FBTyxFQUFFdEYsS0FBSyxDQUFDdUUsU0FBUyxFQUFFO0lBQUVnQixZQUFZLEVBQUUsTUFBTTtFQUFDLENBQUMsRUFBRSxHQUN4RSxFQUFFO0VBQ0osSUFBSUMsV0FBVyxFQUFFeEYsS0FBSyxDQUFDdUUsU0FBUyxFQUFFLEdBQUcsRUFBRTtFQUN2QyxJQUFJa0IsZ0JBQWdCLEdBQUcsQ0FBQztFQUV4QmYsU0FBUyxDQUFDZ0IsT0FBTyxDQUFDLENBQUNiLElBQUksRUFBRWMsU0FBUyxLQUFLO0lBQ3JDO0lBQ0EsSUFBSUMsVUFBVSxHQUFHLEtBQUs7SUFDdEIsSUFBSUMsV0FBVyxFQUFFLGVBQWUsR0FBRyxpQkFBaUIsR0FBRyxTQUFTO0lBRWhFLElBQUlsRixJQUFJLEtBQUssS0FBSyxFQUFFO01BQ2xCLElBQUlrRSxJQUFJLENBQUMzRCxLQUFLLEVBQUU7UUFDZDBFLFVBQVUsR0FBRyxJQUFJO1FBQ2pCQyxXQUFXLEdBQUcsZUFBZTtNQUMvQixDQUFDLE1BQU0sSUFBSSxDQUFDaEIsSUFBSSxDQUFDMUQsT0FBTyxFQUFFO1FBQ3hCeUUsVUFBVSxHQUFHLElBQUk7TUFDbkI7SUFDRixDQUFDLE1BQU0sSUFBSWpGLElBQUksS0FBSyxRQUFRLEVBQUU7TUFDNUIsSUFBSWtFLElBQUksQ0FBQzFELE9BQU8sRUFBRTtRQUNoQnlFLFVBQVUsR0FBRyxJQUFJO1FBQ2pCQyxXQUFXLEdBQUcsaUJBQWlCO01BQ2pDLENBQUMsTUFBTSxJQUFJLENBQUNoQixJQUFJLENBQUMzRCxLQUFLLEVBQUU7UUFDdEIwRSxVQUFVLEdBQUcsSUFBSTtNQUNuQjtJQUNGO0lBRUEsSUFBSSxDQUFDQSxVQUFVLEVBQUU7O0lBRWpCO0lBQ0EsTUFBTUUsV0FBVyxHQUFHdEYsUUFBUSxDQUFDcUUsSUFBSSxDQUFDekQsS0FBSyxFQUFFK0QscUJBQXFCLEVBQUUsTUFBTSxDQUFDO0lBQ3ZFLE1BQU1ZLFNBQVMsR0FBR0QsV0FBVyxDQUFDRSxLQUFLLENBQUMsSUFBSSxDQUFDO0lBRXpDRCxTQUFTLENBQUNMLE9BQU8sQ0FBQyxDQUFDTyxRQUFRLEVBQUVDLE9BQU8sS0FBSztNQUN2QyxJQUFJLENBQUNELFFBQVEsRUFBRTs7TUFFZjtNQUNBLElBQ0VDLE9BQU8sR0FBRyxDQUFDLElBQ1hULGdCQUFnQixHQUFHdEYsV0FBVyxDQUFDOEYsUUFBUSxDQUFDLEdBQUdkLHFCQUFxQixFQUNoRTtRQUNBLElBQUlLLFdBQVcsQ0FBQ3pDLE1BQU0sR0FBRyxDQUFDLEVBQUU7VUFDMUJzQyxZQUFZLENBQUNqQyxJQUFJLENBQUM7WUFDaEJrQyxPQUFPLEVBQUUsQ0FBQyxHQUFHRSxXQUFXLENBQUM7WUFDekJELFlBQVksRUFBRUU7VUFDaEIsQ0FBQyxDQUFDO1VBQ0ZELFdBQVcsR0FBRyxFQUFFO1VBQ2hCQyxnQkFBZ0IsR0FBRyxDQUFDO1FBQ3RCO01BQ0Y7TUFFQUQsV0FBVyxDQUFDcEMsSUFBSSxDQUNkLENBQUMsSUFBSSxDQUNILEdBQUcsQ0FBQyxDQUFDLFFBQVF1QyxTQUFTLElBQUlPLE9BQU8sRUFBRSxDQUFDLENBQ3BDLGVBQWUsQ0FBQyxDQUFDTCxXQUFXLENBQUM7QUFFdkMsVUFBVSxDQUFDSSxRQUFRO0FBQ25CLFFBQVEsRUFBRSxJQUFJLENBQ1IsQ0FBQztNQUVEUixnQkFBZ0IsSUFBSXRGLFdBQVcsQ0FBQzhGLFFBQVEsQ0FBQztJQUMzQyxDQUFDLENBQUM7RUFDSixDQUFDLENBQUM7RUFFRixJQUFJVCxXQUFXLENBQUN6QyxNQUFNLEdBQUcsQ0FBQyxFQUFFO0lBQzFCc0MsWUFBWSxDQUFDakMsSUFBSSxDQUFDO01BQUVrQyxPQUFPLEVBQUVFLFdBQVc7TUFBRUQsWUFBWSxFQUFFRTtJQUFpQixDQUFDLENBQUM7RUFDN0U7O0VBRUE7RUFDQSxPQUFPSixZQUFZLENBQUNoRCxHQUFHLENBQUMsQ0FBQztJQUFFaUQsT0FBTztJQUFFQztFQUFhLENBQUMsRUFBRVksU0FBUyxLQUFLO0lBQ2hFLE1BQU1DLEdBQUcsR0FBRyxHQUFHekYsSUFBSSxJQUFJQyxDQUFDLElBQUl1RixTQUFTLEVBQUU7SUFDdkMsTUFBTUUsV0FBVyxHQUNmMUYsSUFBSSxLQUFLLEtBQUssR0FDVlksR0FBRyxHQUNELGlCQUFpQixHQUNqQixXQUFXLEdBQ2JBLEdBQUcsR0FDRCxtQkFBbUIsR0FDbkIsYUFBYTtJQUNyQixNQUFNK0UsT0FBTyxHQUFHSCxTQUFTLEtBQUssQ0FBQyxHQUFHdkYsQ0FBQyxHQUFHMkYsU0FBUztJQUMvQyxNQUFNQyxVQUFVLEdBQ2QsQ0FBQ0YsT0FBTyxLQUFLQyxTQUFTLEdBQ2xCRCxPQUFPLENBQUNHLFFBQVEsQ0FBQyxDQUFDLENBQUNDLFFBQVEsQ0FBQ3JDLFFBQVEsQ0FBQyxHQUNyQyxHQUFHLENBQUNzQyxNQUFNLENBQUN0QyxRQUFRLENBQUMsSUFBSSxHQUFHO0lBQ2pDO0lBQ0EsTUFBTXVDLFNBQVMsR0FBR0osVUFBVSxDQUFDekQsTUFBTSxHQUFHbUMsZUFBZSxHQUFHSyxZQUFZO0lBQ3BFLE1BQU1zQixPQUFPLEdBQUd0RCxJQUFJLENBQUM2QixHQUFHLENBQUMsQ0FBQyxFQUFFNUQsS0FBSyxHQUFHb0YsU0FBUyxDQUFDO0lBRTlDLE9BQ0UsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUNSLEdBQUcsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxLQUFLO0FBQ3hDLFFBQVEsQ0FBQyxRQUFRLENBQUMsWUFBWTtBQUM5QixVQUFVLENBQUMsSUFBSSxDQUNILEtBQUssQ0FBQyxDQUFDOUIsYUFBYSxHQUFHLE1BQU0sR0FBR2lDLFNBQVMsQ0FBQyxDQUMxQyxlQUFlLENBQUMsQ0FBQ0YsV0FBVyxDQUFDLENBQzdCLFFBQVEsQ0FBQyxDQUFDOUUsR0FBRyxDQUFDO0FBRTFCLFlBQVksQ0FBQ2lGLFVBQVU7QUFDdkIsWUFBWSxDQUFDdkIsVUFBVTtBQUN2QixVQUFVLEVBQUUsSUFBSTtBQUNoQixRQUFRLEVBQUUsUUFBUTtBQUNsQixRQUFRLENBQUMsSUFBSSxDQUNILEtBQUssQ0FBQyxDQUFDWCxhQUFhLEdBQUcsTUFBTSxHQUFHaUMsU0FBUyxDQUFDLENBQzFDLGVBQWUsQ0FBQyxDQUFDRixXQUFXLENBQUMsQ0FDN0IsUUFBUSxDQUFDLENBQUM5RSxHQUFHLENBQUM7QUFFeEIsVUFBVSxDQUFDK0QsT0FBTztBQUNsQixVQUFVLENBQUMsR0FBRyxDQUFDcUIsTUFBTSxDQUFDRSxPQUFPLENBQUM7QUFDOUIsUUFBUSxFQUFFLElBQUk7QUFDZCxNQUFNLEVBQUUsR0FBRyxDQUFDO0VBRVYsQ0FBQyxDQUFDO0FBQ0o7QUFFQSxTQUFTM0UsVUFBVUEsQ0FDakJGLEtBQUssRUFBRSxNQUFNLEVBQUUsRUFDZjhFLGtCQUFrQixFQUFFLE1BQU0sRUFDMUJ0RixLQUFLLEVBQUUsTUFBTSxFQUNiRCxHQUFHLEVBQUUsT0FBTyxFQUNaK0MsYUFBeUIsQ0FBWCxFQUFFcEUsU0FBUyxDQUMxQixFQUFFRixLQUFLLENBQUN1RSxTQUFTLEVBQUUsQ0FBQztFQUNuQjtFQUNBLE1BQU13QyxTQUFTLEdBQUd4RCxJQUFJLENBQUM2QixHQUFHLENBQUMsQ0FBQyxFQUFFN0IsSUFBSSxDQUFDeUQsS0FBSyxDQUFDeEYsS0FBSyxDQUFDLENBQUM7O0VBRWhEO0VBQ0EsTUFBTXFCLFdBQVcsR0FBR0osdUJBQXVCLENBQUNULEtBQUssQ0FBQzs7RUFFbEQ7RUFDQSxNQUFNYyxjQUFjLEdBQUdGLG9CQUFvQixDQUFDQyxXQUFXLENBQUM7O0VBRXhEO0VBQ0EsTUFBTW9FLEVBQUUsR0FBR0MsZUFBZSxDQUFDcEUsY0FBYyxFQUFFZ0Usa0JBQWtCLENBQUM7O0VBRTlEO0VBQ0EsTUFBTUssYUFBYSxHQUFHNUQsSUFBSSxDQUFDNkIsR0FBRyxDQUFDLEdBQUc2QixFQUFFLENBQUM1RSxHQUFHLENBQUMsQ0FBQztJQUFFekI7RUFBRSxDQUFDLEtBQUtBLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQztFQUMxRCxNQUFNeUQsUUFBUSxHQUFHZCxJQUFJLENBQUM2QixHQUFHLENBQUMrQixhQUFhLENBQUNWLFFBQVEsQ0FBQyxDQUFDLENBQUMxRCxNQUFNLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQzs7RUFFakU7RUFDQSxPQUFPa0UsRUFBRSxDQUFDRyxPQUFPLENBQUMsQ0FBQ2hELElBQUksQ0FBQyxFQUFFcEUsS0FBSyxDQUFDdUUsU0FBUyxFQUFFLElBQUk7SUFDN0MsTUFBTTtNQUFFNUQsSUFBSTtNQUFFRCxJQUFJO01BQUVFLENBQUM7TUFBRUUsUUFBUTtNQUFFQztJQUFZLENBQUMsR0FBR3FELElBQUk7O0lBRXJEO0lBQ0EsSUFBSXRELFFBQVEsSUFBSUMsV0FBVyxFQUFFO01BQzNCLE1BQU1zRyxnQkFBZ0IsR0FBR2xELHdCQUF3QixDQUMvQ0MsSUFBSSxFQUNKMkMsU0FBUyxFQUNUMUMsUUFBUSxFQUNSOUMsR0FBRyxFQUNIK0MsYUFDRixDQUFDOztNQUVEO01BQ0E7TUFDQSxJQUFJK0MsZ0JBQWdCLEtBQUssSUFBSSxFQUFFO1FBQzdCLE9BQU9BLGdCQUFnQjtNQUN6QjtJQUNGOztJQUVBO0lBQ0E7SUFDQSxNQUFNbkMsZUFBZSxHQUFHLENBQUMsRUFBQztJQUMxQixNQUFNQyxxQkFBcUIsR0FBRzVCLElBQUksQ0FBQzZCLEdBQUcsQ0FDcEMsQ0FBQyxFQUNEMkIsU0FBUyxHQUFHMUMsUUFBUSxHQUFHLENBQUMsR0FBR2EsZUFDN0IsQ0FBQyxFQUFDO0lBQ0YsTUFBTW9DLFdBQVcsR0FBRzlHLFFBQVEsQ0FBQ0UsSUFBSSxFQUFFeUUscUJBQXFCLEVBQUUsTUFBTSxDQUFDO0lBQ2pFLE1BQU1FLFlBQVksR0FBR2lDLFdBQVcsQ0FBQ3RCLEtBQUssQ0FBQyxJQUFJLENBQUM7SUFFNUMsT0FBT1gsWUFBWSxDQUFDaEQsR0FBRyxDQUFDLENBQUNjLElBQUksRUFBRWdELFNBQVMsS0FBSztNQUMzQyxNQUFNQyxHQUFHLEdBQUcsR0FBR3pGLElBQUksSUFBSUMsQ0FBQyxJQUFJdUYsU0FBUyxFQUFFO01BQ3ZDLE1BQU1HLE9BQU8sR0FBR0gsU0FBUyxLQUFLLENBQUMsR0FBR3ZGLENBQUMsR0FBRzJGLFNBQVM7TUFDL0MsTUFBTUMsVUFBVSxHQUNkLENBQUNGLE9BQU8sS0FBS0MsU0FBUyxHQUNsQkQsT0FBTyxDQUFDRyxRQUFRLENBQUMsQ0FBQyxDQUFDQyxRQUFRLENBQUNyQyxRQUFRLENBQUMsR0FDckMsR0FBRyxDQUFDc0MsTUFBTSxDQUFDdEMsUUFBUSxDQUFDLElBQUksR0FBRztNQUNqQyxNQUFNa0QsS0FBSyxHQUFHNUcsSUFBSSxLQUFLLEtBQUssR0FBRyxHQUFHLEdBQUdBLElBQUksS0FBSyxRQUFRLEdBQUcsR0FBRyxHQUFHLEdBQUc7TUFDbEU7TUFDQSxNQUFNNEUsWUFBWSxHQUFHaUIsVUFBVSxDQUFDekQsTUFBTSxHQUFHLENBQUMsR0FBRzVDLFdBQVcsQ0FBQ2dELElBQUksQ0FBQyxFQUFDO01BQy9ELE1BQU0wRCxPQUFPLEdBQUd0RCxJQUFJLENBQUM2QixHQUFHLENBQUMsQ0FBQyxFQUFFMkIsU0FBUyxHQUFHeEIsWUFBWSxDQUFDO01BRXJELE1BQU1pQyxPQUFPLEdBQ1g3RyxJQUFJLEtBQUssS0FBSyxHQUNWWSxHQUFHLEdBQ0QsaUJBQWlCLEdBQ2pCLFdBQVcsR0FDYlosSUFBSSxLQUFLLFFBQVEsR0FDZlksR0FBRyxHQUNELG1CQUFtQixHQUNuQixhQUFhLEdBQ2ZnRixTQUFTOztNQUVqQjtNQUNBO01BQ0E7TUFDQSxPQUNFLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDSCxHQUFHLENBQUMsQ0FBQyxhQUFhLENBQUMsS0FBSztBQUMxQyxVQUFVLENBQUMsUUFBUSxDQUFDLFlBQVk7QUFDaEMsWUFBWSxDQUFDLElBQUksQ0FDSCxLQUFLLENBQUMsQ0FBQzlCLGFBQWEsR0FBRyxNQUFNLEdBQUdpQyxTQUFTLENBQUMsQ0FDMUMsZUFBZSxDQUFDLENBQUNpQixPQUFPLENBQUMsQ0FDekIsUUFBUSxDQUFDLENBQUNqRyxHQUFHLElBQUlaLElBQUksS0FBSyxVQUFVLENBQUM7QUFFbkQsY0FBYyxDQUFDNkYsVUFBVTtBQUN6QixjQUFjLENBQUNlLEtBQUs7QUFDcEIsWUFBWSxFQUFFLElBQUk7QUFDbEIsVUFBVSxFQUFFLFFBQVE7QUFDcEIsVUFBVSxDQUFDLElBQUksQ0FDSCxLQUFLLENBQUMsQ0FBQ2pELGFBQWEsR0FBRyxNQUFNLEdBQUdpQyxTQUFTLENBQUMsQ0FDMUMsZUFBZSxDQUFDLENBQUNpQixPQUFPLENBQUMsQ0FDekIsUUFBUSxDQUFDLENBQUNqRyxHQUFHLENBQUM7QUFFMUIsWUFBWSxDQUFDNEIsSUFBSTtBQUNqQixZQUFZLENBQUMsR0FBRyxDQUFDd0QsTUFBTSxDQUFDRSxPQUFPLENBQUM7QUFDaEMsVUFBVSxFQUFFLElBQUk7QUFDaEIsUUFBUSxFQUFFLEdBQUcsQ0FBQztJQUVWLENBQUMsQ0FBQztFQUNKLENBQUMsQ0FBQztBQUNKO0FBRUEsT0FBTyxTQUFTSyxlQUFlQSxDQUM3Qi9FLElBQUksRUFBRW5CLFVBQVUsRUFBRSxFQUNsQnlHLFNBQVMsRUFBRSxNQUFNLENBQ2xCLEVBQUVoSCxRQUFRLEVBQUUsQ0FBQztFQUNaLElBQUlHLENBQUMsR0FBRzZHLFNBQVM7RUFDakIsTUFBTXhELE1BQU0sRUFBRXhELFFBQVEsRUFBRSxHQUFHLEVBQUU7RUFDN0IsTUFBTWlILEtBQUssR0FBRyxDQUFDLEdBQUd2RixJQUFJLENBQUM7RUFFdkIsT0FBT3VGLEtBQUssQ0FBQzNFLE1BQU0sR0FBRyxDQUFDLEVBQUU7SUFDdkIsTUFBTUMsT0FBTyxHQUFHMEUsS0FBSyxDQUFDQyxLQUFLLENBQUMsQ0FBQyxDQUFDO0lBQzlCLE1BQU07TUFBRWpILElBQUk7TUFBRUMsSUFBSTtNQUFFRSxZQUFZO01BQUVDLFFBQVE7TUFBRUM7SUFBWSxDQUFDLEdBQUdpQyxPQUFPO0lBQ25FLE1BQU1HLElBQUksR0FBRztNQUNYekMsSUFBSTtNQUNKQyxJQUFJO01BQ0pDLENBQUM7TUFDREMsWUFBWTtNQUNaQyxRQUFRO01BQ1JDO0lBQ0YsQ0FBQzs7SUFFRDtJQUNBLFFBQVFKLElBQUk7TUFDVixLQUFLLFVBQVU7UUFDYkMsQ0FBQyxFQUFFO1FBQ0hxRCxNQUFNLENBQUNiLElBQUksQ0FBQ0QsSUFBSSxDQUFDO1FBQ2pCO01BQ0YsS0FBSyxLQUFLO1FBQ1J2QyxDQUFDLEVBQUU7UUFDSHFELE1BQU0sQ0FBQ2IsSUFBSSxDQUFDRCxJQUFJLENBQUM7UUFDakI7TUFDRixLQUFLLFFBQVE7UUFBRTtVQUNiYyxNQUFNLENBQUNiLElBQUksQ0FBQ0QsSUFBSSxDQUFDO1VBQ2pCLElBQUl5RSxVQUFVLEdBQUcsQ0FBQztVQUNsQixPQUFPRixLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUvRyxJQUFJLEtBQUssUUFBUSxFQUFFO1lBQ2xDQyxDQUFDLEVBQUU7WUFDSCxNQUFNb0MsT0FBTyxHQUFHMEUsS0FBSyxDQUFDQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1lBQzlCLE1BQU07Y0FBRWpILElBQUk7Y0FBRUMsSUFBSTtjQUFFRSxZQUFZO2NBQUVDLFFBQVE7Y0FBRUM7WUFBWSxDQUFDLEdBQUdpQyxPQUFPO1lBQ25FLE1BQU1HLElBQUksR0FBRztjQUNYekMsSUFBSTtjQUNKQyxJQUFJO2NBQ0pDLENBQUM7Y0FDREMsWUFBWTtjQUNaQyxRQUFRO2NBQ1JDO1lBQ0YsQ0FBQztZQUNEa0QsTUFBTSxDQUFDYixJQUFJLENBQUNELElBQUksQ0FBQztZQUNqQnlFLFVBQVUsRUFBRTtVQUNkO1VBQ0FoSCxDQUFDLElBQUlnSCxVQUFVO1VBQ2Y7UUFDRjtJQUNGO0VBQ0Y7RUFFQSxPQUFPM0QsTUFBTTtBQUNmIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/src/components/StructuredDiff/colorDiff.ts b/src/components/StructuredDiff/colorDiff.ts new file mode 100644 index 0000000000000000000000000000000000000000..d3abaa280ad6d8882627299e0589826442b3db75 --- /dev/null +++ b/src/components/StructuredDiff/colorDiff.ts @@ -0,0 +1,37 @@ +import { + ColorDiff, + ColorFile, + getSyntaxTheme as nativeGetSyntaxTheme, + type SyntaxTheme, +} from 'color-diff-napi' +import { isEnvDefinedFalsy } from '../../utils/envUtils.js' + +export type ColorModuleUnavailableReason = 'env' + +/** + * Returns a static reason why the color-diff module is unavailable, or null if available. + * 'env' = disabled via CLAUDE_CODE_SYNTAX_HIGHLIGHT + * + * The TS port of color-diff works in all build modes, so the only way to + * disable it is via the env var. + */ +export function getColorModuleUnavailableReason(): ColorModuleUnavailableReason | null { + if (isEnvDefinedFalsy(process.env.CLAUDE_CODE_SYNTAX_HIGHLIGHT)) { + return 'env' + } + return null +} + +export function expectColorDiff(): typeof ColorDiff | null { + return getColorModuleUnavailableReason() === null ? ColorDiff : null +} + +export function expectColorFile(): typeof ColorFile | null { + return getColorModuleUnavailableReason() === null ? ColorFile : null +} + +export function getSyntaxTheme(themeName: string): SyntaxTheme | null { + return getColorModuleUnavailableReason() === null + ? nativeGetSyntaxTheme(themeName) + : null +} diff --git a/src/components/TrustDialog/TrustDialog.tsx b/src/components/TrustDialog/TrustDialog.tsx new file mode 100644 index 0000000000000000000000000000000000000000..3bfcc22c08c4dc356a1470e2b7e470c59623b88d --- /dev/null +++ b/src/components/TrustDialog/TrustDialog.tsx @@ -0,0 +1,290 @@ +import { c as _c } from "react/compiler-runtime"; +import { homedir } from 'os'; +import React from 'react'; +import { logEvent } from 'src/services/analytics/index.js'; +import { setSessionTrustAccepted } from '../../bootstrap/state.js'; +import type { Command } from '../../commands.js'; +import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js'; +import { Box, Link, Text } from '../../ink.js'; +import { useKeybinding } from '../../keybindings/useKeybinding.js'; +import { getMcpConfigsByScope } from '../../services/mcp/config.js'; +import { BASH_TOOL_NAME } from '../../tools/BashTool/toolName.js'; +import { checkHasTrustDialogAccepted, saveCurrentProjectConfig } from '../../utils/config.js'; +import { getCwd } from '../../utils/cwd.js'; +import { getFsImplementation } from '../../utils/fsOperations.js'; +import { gracefulShutdownSync } from '../../utils/gracefulShutdown.js'; +import { Select } from '../CustomSelect/index.js'; +import { PermissionDialog } from '../permissions/PermissionDialog.js'; +import { getApiKeyHelperSources, getAwsCommandsSources, getBashPermissionSources, getDangerousEnvVarsSources, getGcpCommandsSources, getHooksSources, getOtelHeadersHelperSources } from './utils.js'; +type Props = { + onDone(): void; + commands?: Command[]; +}; +export function TrustDialog(t0) { + const $ = _c(33); + const { + onDone, + commands + } = t0; + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = getMcpConfigsByScope("project"); + $[0] = t1; + } else { + t1 = $[0]; + } + const { + servers: projectServers + } = t1; + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t2 = Object.keys(projectServers); + $[1] = t2; + } else { + t2 = $[1]; + } + const hasMcpServers = t2.length > 0; + let t3; + if ($[2] === Symbol.for("react.memo_cache_sentinel")) { + t3 = getHooksSources(); + $[2] = t3; + } else { + t3 = $[2]; + } + const hooksSettingSources = t3; + const hasHooks = hooksSettingSources.length > 0; + let t4; + if ($[3] === Symbol.for("react.memo_cache_sentinel")) { + t4 = getBashPermissionSources(); + $[3] = t4; + } else { + t4 = $[3]; + } + const bashSettingSources = t4; + let t5; + if ($[4] === Symbol.for("react.memo_cache_sentinel")) { + t5 = getApiKeyHelperSources(); + $[4] = t5; + } else { + t5 = $[4]; + } + const apiKeyHelperSources = t5; + const hasApiKeyHelper = apiKeyHelperSources.length > 0; + let t6; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t6 = getAwsCommandsSources(); + $[5] = t6; + } else { + t6 = $[5]; + } + const awsCommandsSources = t6; + const hasAwsCommands = awsCommandsSources.length > 0; + let t7; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t7 = getGcpCommandsSources(); + $[6] = t7; + } else { + t7 = $[6]; + } + const gcpCommandsSources = t7; + const hasGcpCommands = gcpCommandsSources.length > 0; + let t8; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t8 = getOtelHeadersHelperSources(); + $[7] = t8; + } else { + t8 = $[7]; + } + const otelHeadersHelperSources = t8; + const hasOtelHeadersHelper = otelHeadersHelperSources.length > 0; + let t9; + if ($[8] === Symbol.for("react.memo_cache_sentinel")) { + t9 = getDangerousEnvVarsSources(); + $[8] = t9; + } else { + t9 = $[8]; + } + const dangerousEnvVarsSources = t9; + const hasDangerousEnvVars = dangerousEnvVarsSources.length > 0; + let t10; + if ($[9] !== commands) { + t10 = commands?.some(_temp2) ?? false; + $[9] = commands; + $[10] = t10; + } else { + t10 = $[10]; + } + const hasSlashCommandBash = t10; + let t11; + if ($[11] !== commands) { + t11 = commands?.some(_temp4) ?? false; + $[11] = commands; + $[12] = t11; + } else { + t11 = $[12]; + } + const hasSkillsBash = t11; + const hasAnyBashExecution = bashSettingSources.length > 0 || hasSlashCommandBash || hasSkillsBash; + const hasTrustDialogAccepted = checkHasTrustDialogAccepted(); + let t12; + let t13; + if ($[13] !== hasAnyBashExecution) { + t12 = () => { + const isHomeDir = homedir() === getCwd(); + logEvent("tengu_trust_dialog_shown", { + isHomeDir, + hasMcpServers, + hasHooks, + hasBashExecution: hasAnyBashExecution, + hasApiKeyHelper, + hasAwsCommands, + hasGcpCommands, + hasOtelHeadersHelper, + hasDangerousEnvVars + }); + }; + t13 = [hasMcpServers, hasHooks, hasAnyBashExecution, hasApiKeyHelper, hasAwsCommands, hasGcpCommands, hasOtelHeadersHelper, hasDangerousEnvVars]; + $[13] = hasAnyBashExecution; + $[14] = t12; + $[15] = t13; + } else { + t12 = $[14]; + t13 = $[15]; + } + React.useEffect(t12, t13); + let t14; + if ($[16] !== hasAnyBashExecution || $[17] !== onDone) { + t14 = function onChange(value) { + if (value === "exit") { + gracefulShutdownSync(1); + return; + } + const isHomeDir_0 = homedir() === getCwd(); + logEvent("tengu_trust_dialog_accept", { + isHomeDir: isHomeDir_0, + hasMcpServers, + hasHooks, + hasBashExecution: hasAnyBashExecution, + hasApiKeyHelper, + hasAwsCommands, + hasGcpCommands, + hasOtelHeadersHelper, + hasDangerousEnvVars + }); + if (isHomeDir_0) { + setSessionTrustAccepted(true); + } else { + saveCurrentProjectConfig(_temp5); + } + onDone(); + }; + $[16] = hasAnyBashExecution; + $[17] = onDone; + $[18] = t14; + } else { + t14 = $[18]; + } + const onChange = t14; + const exitState = useExitOnCtrlCDWithKeybindings(_temp6); + let t15; + if ($[19] === Symbol.for("react.memo_cache_sentinel")) { + t15 = { + context: "Confirmation" + }; + $[19] = t15; + } else { + t15 = $[19]; + } + useKeybinding("confirm:no", _temp7, t15); + if (hasTrustDialogAccepted) { + setTimeout(onDone); + return null; + } + let t16; + let t17; + let t18; + if ($[20] === Symbol.for("react.memo_cache_sentinel")) { + t16 = {getFsImplementation().cwd()}; + t17 = Quick safety check: Is this a project you created or one you trust? (Like your own code, a well-known open source project, or work from your team). If not, take a moment to review what{"'"}s in this folder first.; + t18 = Claude Code{"'"}ll be able to read, edit, and execute files here.; + $[20] = t16; + $[21] = t17; + $[22] = t18; + } else { + t16 = $[20]; + t17 = $[21]; + t18 = $[22]; + } + let t19; + if ($[23] === Symbol.for("react.memo_cache_sentinel")) { + t19 = Security guide; + $[23] = t19; + } else { + t19 = $[23]; + } + let t20; + if ($[24] === Symbol.for("react.memo_cache_sentinel")) { + t20 = [{ + label: "Yes, I trust this folder", + value: "enable_all" + }, { + label: "No, exit", + value: "exit" + }]; + $[24] = t20; + } else { + t20 = $[24]; + } + let t21; + if ($[25] !== onChange) { + t21 = ; + $[73] = handleMenuSelect; + $[74] = menuItems; + $[75] = t20; + $[76] = t21; + } else { + t21 = $[76]; + } + let t22; + if ($[77] !== changes) { + t22 = changes.length > 0 && {changes[changes.length - 1]}; + $[77] = changes; + $[78] = t22; + } else { + t22 = $[78]; + } + let t23; + if ($[79] !== t21 || $[80] !== t22) { + t23 = {t21}{t22}; + $[79] = t21; + $[80] = t22; + $[81] = t23; + } else { + t23 = $[81]; + } + let t24; + if ($[82] !== modeState.agent.agentType || $[83] !== t19 || $[84] !== t23) { + t24 = {t23}; + $[82] = modeState.agent.agentType; + $[83] = t19; + $[84] = t23; + $[85] = t24; + } else { + t24 = $[85]; + } + let t25; + if ($[86] === Symbol.for("react.memo_cache_sentinel")) { + t25 = ; + $[86] = t25; + } else { + t25 = $[86]; + } + let t26; + if ($[87] !== t24) { + t26 = <>{t24}{t25}; + $[87] = t24; + $[88] = t26; + } else { + t26 = $[88]; + } + return t26; + } + case "view-agent": + { + let t13; + if ($[89] !== allAgents || $[90] !== modeState.agent) { + let t14; + if ($[92] !== modeState.agent) { + t14 = a_8 => a_8.agentType === modeState.agent.agentType && a_8.source === modeState.agent.source; + $[92] = modeState.agent; + $[93] = t14; + } else { + t14 = $[93]; + } + t13 = allAgents.find(t14); + $[89] = allAgents; + $[90] = modeState.agent; + $[91] = t13; + } else { + t13 = $[91]; + } + const freshAgent_0 = t13; + const agentToDisplay = freshAgent_0 || modeState.agent; + let t14; + if ($[94] !== agentToDisplay || $[95] !== modeState.previousMode) { + t14 = () => setModeState({ + mode: "agent-menu", + agent: agentToDisplay, + previousMode: modeState.previousMode + }); + $[94] = agentToDisplay; + $[95] = modeState.previousMode; + $[96] = t14; + } else { + t14 = $[96]; + } + let t15; + if ($[97] !== agentToDisplay || $[98] !== modeState.previousMode) { + t15 = () => setModeState({ + mode: "agent-menu", + agent: agentToDisplay, + previousMode: modeState.previousMode + }); + $[97] = agentToDisplay; + $[98] = modeState.previousMode; + $[99] = t15; + } else { + t15 = $[99]; + } + let t16; + if ($[100] !== agentToDisplay || $[101] !== allAgents || $[102] !== mergedTools || $[103] !== t15) { + t16 = ; + $[100] = agentToDisplay; + $[101] = allAgents; + $[102] = mergedTools; + $[103] = t15; + $[104] = t16; + } else { + t16 = $[104]; + } + let t17; + if ($[105] !== agentToDisplay.agentType || $[106] !== t14 || $[107] !== t16) { + t17 = {t16}; + $[105] = agentToDisplay.agentType; + $[106] = t14; + $[107] = t16; + $[108] = t17; + } else { + t17 = $[108]; + } + let t18; + if ($[109] === Symbol.for("react.memo_cache_sentinel")) { + t18 = ; + $[109] = t18; + } else { + t18 = $[109]; + } + let t19; + if ($[110] !== t17) { + t19 = <>{t17}{t18}; + $[110] = t17; + $[111] = t19; + } else { + t19 = $[111]; + } + return t19; + } + case "delete-confirm": + { + let t13; + if ($[112] === Symbol.for("react.memo_cache_sentinel")) { + t13 = [{ + label: "Yes, delete", + value: "yes" + }, { + label: "No, cancel", + value: "no" + }]; + $[112] = t13; + } else { + t13 = $[112]; + } + const deleteOptions = t13; + let t14; + if ($[113] !== modeState) { + t14 = () => { + if ("previousMode" in modeState) { + setModeState(modeState.previousMode); + } + }; + $[113] = modeState; + $[114] = t14; + } else { + t14 = $[114]; + } + let t15; + if ($[115] !== modeState.agent.agentType) { + t15 = Are you sure you want to delete the agent{" "}{modeState.agent.agentType}?; + $[115] = modeState.agent.agentType; + $[116] = t15; + } else { + t15 = $[116]; + } + let t16; + if ($[117] !== modeState.agent.source) { + t16 = Source: {modeState.agent.source}; + $[117] = modeState.agent.source; + $[118] = t16; + } else { + t16 = $[118]; + } + let t17; + if ($[119] !== handleAgentDeleted || $[120] !== modeState) { + t17 = value => { + if (value === "yes") { + handleAgentDeleted(modeState.agent); + } else { + if ("previousMode" in modeState) { + setModeState(modeState.previousMode); + } + } + }; + $[119] = handleAgentDeleted; + $[120] = modeState; + $[121] = t17; + } else { + t17 = $[121]; + } + let t18; + if ($[122] !== modeState) { + t18 = () => { + if ("previousMode" in modeState) { + setModeState(modeState.previousMode); + } + }; + $[122] = modeState; + $[123] = t18; + } else { + t18 = $[123]; + } + let t19; + if ($[124] !== t17 || $[125] !== t18) { + t19 = ; + $[6] = defaultModel; + $[7] = modelOptions; + $[8] = onComplete; + $[9] = t3; + $[10] = t4; + } else { + t4 = $[10]; + } + return t4; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlRleHQiLCJnZXRBZ2VudE1vZGVsT3B0aW9ucyIsIlNlbGVjdCIsIk1vZGVsU2VsZWN0b3JQcm9wcyIsImluaXRpYWxNb2RlbCIsIm9uQ29tcGxldGUiLCJtb2RlbCIsIm9uQ2FuY2VsIiwiTW9kZWxTZWxlY3RvciIsInQwIiwiJCIsIl9jIiwidDEiLCJiYjAiLCJiYXNlIiwic29tZSIsIm8iLCJ2YWx1ZSIsImxhYmVsIiwiZGVzY3JpcHRpb24iLCJtb2RlbE9wdGlvbnMiLCJkZWZhdWx0TW9kZWwiLCJ0MiIsIlN5bWJvbCIsImZvciIsInQzIiwidW5kZWZpbmVkIiwidDQiXSwic291cmNlcyI6WyJNb2RlbFNlbGVjdG9yLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IEJveCwgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGdldEFnZW50TW9kZWxPcHRpb25zIH0gZnJvbSAnLi4vLi4vdXRpbHMvbW9kZWwvYWdlbnQuanMnXG5pbXBvcnQgeyBTZWxlY3QgfSBmcm9tICcuLi9DdXN0b21TZWxlY3Qvc2VsZWN0LmpzJ1xuXG5pbnRlcmZhY2UgTW9kZWxTZWxlY3RvclByb3BzIHtcbiAgaW5pdGlhbE1vZGVsPzogc3RyaW5nXG4gIG9uQ29tcGxldGU6IChtb2RlbD86IHN0cmluZykgPT4gdm9pZFxuICBvbkNhbmNlbD86ICgpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIE1vZGVsU2VsZWN0b3Ioe1xuICBpbml0aWFsTW9kZWwsXG4gIG9uQ29tcGxldGUsXG4gIG9uQ2FuY2VsLFxufTogTW9kZWxTZWxlY3RvclByb3BzKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgbW9kZWxPcHRpb25zID0gUmVhY3QudXNlTWVtbygoKSA9PiB7XG4gICAgY29uc3QgYmFzZSA9IGdldEFnZW50TW9kZWxPcHRpb25zKClcbiAgICAvLyBJZiB0aGUgYWdlbnQncyBjdXJyZW50IG1vZGVsIGlzIGEgZnVsbCBJRCAoZS5nLiAnY2xhdWRlLW9wdXMtNC01Jykgbm90XG4gICAgLy8gaW4gdGhlIGFsaWFzIGxpc3QsIGluamVjdCBpdCBhcyBhbiBvcHRpb24gc28gaXQgY2FuIHJvdW5kLXRyaXAgdGhyb3VnaFxuICAgIC8vIGNvbmZpcm0gd2l0aG91dCBiZWluZyBvdmVyd3JpdHRlbi5cbiAgICBpZiAoaW5pdGlhbE1vZGVsICYmICFiYXNlLnNvbWUobyA9PiBvLnZhbHVlID09PSBpbml0aWFsTW9kZWwpKSB7XG4gICAgICByZXR1cm4gW1xuICAgICAgICB7XG4gICAgICAgICAgdmFsdWU6IGluaXRpYWxNb2RlbCxcbiAgICAgICAgICBsYWJlbDogaW5pdGlhbE1vZGVsLFxuICAgICAgICAgIGRlc2NyaXB0aW9uOiAnQ3VycmVudCBtb2RlbCAoY3VzdG9tIElEKScsXG4gICAgICAgIH0sXG4gICAgICAgIC4uLmJhc2UsXG4gICAgICBdXG4gICAgfVxuICAgIHJldHVybiBiYXNlXG4gIH0sIFtpbml0aWFsTW9kZWxdKVxuXG4gIGNvbnN0IGRlZmF1bHRNb2RlbCA9IGluaXRpYWxNb2RlbCA/PyAnc29ubmV0J1xuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCI+XG4gICAgICA8Qm94IG1hcmdpbkJvdHRvbT17MX0+XG4gICAgICAgIDxUZXh0IGRpbUNvbG9yPlxuICAgICAgICAgIE1vZGVsIGRldGVybWluZXMgdGhlIGFnZW50JmFwb3M7cyByZWFzb25pbmcgY2FwYWJpbGl0aWVzIGFuZCBzcGVlZC5cbiAgICAgICAgPC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgICA8U2VsZWN0XG4gICAgICAgIG9wdGlvbnM9e21vZGVsT3B0aW9uc31cbiAgICAgICAgZGVmYXVsdFZhbHVlPXtkZWZhdWx0TW9kZWx9XG4gICAgICAgIG9uQ2hhbmdlPXtvbkNvbXBsZXRlfVxuICAgICAgICBvbkNhbmNlbD17KCkgPT4gKG9uQ2FuY2VsID8gb25DYW5jZWwoKSA6IG9uQ29tcGxldGUodW5kZWZpbmVkKSl9XG4gICAgICAvPlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLEdBQUcsRUFBRUMsSUFBSSxRQUFRLGNBQWM7QUFDeEMsU0FBU0Msb0JBQW9CLFFBQVEsNEJBQTRCO0FBQ2pFLFNBQVNDLE1BQU0sUUFBUSwyQkFBMkI7QUFFbEQsVUFBVUMsa0JBQWtCLENBQUM7RUFDM0JDLFlBQVksQ0FBQyxFQUFFLE1BQU07RUFDckJDLFVBQVUsRUFBRSxDQUFDQyxLQUFjLENBQVIsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQ3BDQyxRQUFRLENBQUMsRUFBRSxHQUFHLEdBQUcsSUFBSTtBQUN2QjtBQUVBLE9BQU8sU0FBQUMsY0FBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUF1QjtJQUFBUCxZQUFBO0lBQUFDLFVBQUE7SUFBQUU7RUFBQSxJQUFBRSxFQUlUO0VBQUEsSUFBQUcsRUFBQTtFQUFBLElBQUFGLENBQUEsUUFBQU4sWUFBQTtJQUFBUyxHQUFBO01BRWpCLE1BQUFDLElBQUEsR0FBYWIsb0JBQW9CLENBQUMsQ0FBQztNQUluQyxJQUFJRyxZQUF5RCxJQUF6RCxDQUFpQlUsSUFBSSxDQUFBQyxJQUFLLENBQUNDLENBQUEsSUFBS0EsQ0FBQyxDQUFBQyxLQUFNLEtBQUtiLFlBQVksQ0FBQztRQUMzRFEsRUFBQSxHQUFPLENBQ0w7VUFBQUssS0FBQSxFQUNTYixZQUFZO1VBQUFjLEtBQUEsRUFDWmQsWUFBWTtVQUFBZSxXQUFBLEVBQ047UUFDZixDQUFDLEtBQ0VMLElBQUksQ0FDUjtRQVBELE1BQUFELEdBQUE7TUFPQztNQUVIRCxFQUFBLEdBQU9FLElBQUk7SUFBQTtJQUFBSixDQUFBLE1BQUFOLFlBQUE7SUFBQU0sQ0FBQSxNQUFBRSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBRixDQUFBO0VBQUE7RUFmYixNQUFBVSxZQUFBLEdBQXFCUixFQWdCSDtFQUVsQixNQUFBUyxZQUFBLEdBQXFCakIsWUFBd0IsSUFBeEIsUUFBd0I7RUFBQSxJQUFBa0IsRUFBQTtFQUFBLElBQUFaLENBQUEsUUFBQWEsTUFBQSxDQUFBQyxHQUFBO0lBSXpDRixFQUFBLElBQUMsR0FBRyxDQUFlLFlBQUMsQ0FBRCxHQUFDLENBQ2xCLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBUixLQUFPLENBQUMsQ0FBQyw4REFFZixFQUZDLElBQUksQ0FHUCxFQUpDLEdBQUcsQ0FJRTtJQUFBWixDQUFBLE1BQUFZLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFaLENBQUE7RUFBQTtFQUFBLElBQUFlLEVBQUE7RUFBQSxJQUFBZixDQUFBLFFBQUFILFFBQUEsSUFBQUcsQ0FBQSxRQUFBTCxVQUFBO0lBS01vQixFQUFBLEdBQUFBLENBQUEsS0FBT2xCLFFBQVEsR0FBR0EsUUFBUSxDQUF5QixDQUFDLEdBQXJCRixVQUFVLENBQUNxQixTQUFTLENBQUU7SUFBQWhCLENBQUEsTUFBQUgsUUFBQTtJQUFBRyxDQUFBLE1BQUFMLFVBQUE7SUFBQUssQ0FBQSxNQUFBZSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBZixDQUFBO0VBQUE7RUFBQSxJQUFBaUIsRUFBQTtFQUFBLElBQUFqQixDQUFBLFFBQUFXLFlBQUEsSUFBQVgsQ0FBQSxRQUFBVSxZQUFBLElBQUFWLENBQUEsUUFBQUwsVUFBQSxJQUFBSyxDQUFBLFFBQUFlLEVBQUE7SUFWbkVFLEVBQUEsSUFBQyxHQUFHLENBQWUsYUFBUSxDQUFSLFFBQVEsQ0FDekIsQ0FBQUwsRUFJSyxDQUNMLENBQUMsTUFBTSxDQUNJRixPQUFZLENBQVpBLGFBQVcsQ0FBQyxDQUNQQyxZQUFZLENBQVpBLGFBQVcsQ0FBQyxDQUNoQmhCLFFBQVUsQ0FBVkEsV0FBUyxDQUFDLENBQ1YsUUFBcUQsQ0FBckQsQ0FBQW9CLEVBQW9ELENBQUMsR0FFbkUsRUFaQyxHQUFHLENBWUU7SUFBQWYsQ0FBQSxNQUFBVyxZQUFBO0lBQUFYLENBQUEsTUFBQVUsWUFBQTtJQUFBVixDQUFBLE1BQUFMLFVBQUE7SUFBQUssQ0FBQSxNQUFBZSxFQUFBO0lBQUFmLENBQUEsT0FBQWlCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFqQixDQUFBO0VBQUE7RUFBQSxPQVpOaUIsRUFZTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx b/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cfcb45010865d8bc0c682bb16279f86ee36afed4 --- /dev/null +++ b/src/components/agents/new-agent-creation/wizard-steps/MethodStep.tsx @@ -0,0 +1,80 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type ReactNode } from 'react'; +import { Box } from '../../../../ink.js'; +import { ConfigurableShortcutHint } from '../../../ConfigurableShortcutHint.js'; +import { Select } from '../../../CustomSelect/select.js'; +import { Byline } from '../../../design-system/Byline.js'; +import { KeyboardShortcutHint } from '../../../design-system/KeyboardShortcutHint.js'; +import { useWizard } from '../../../wizard/index.js'; +import { WizardDialogLayout } from '../../../wizard/WizardDialogLayout.js'; +import type { AgentWizardData } from '../types.js'; +export function MethodStep() { + const $ = _c(11); + const { + goNext, + goBack, + updateWizardData, + goToStep + } = useWizard(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = [{ + label: "Generate with Claude (recommended)", + value: "generate" + }, { + label: "Manual configuration", + value: "manual" + }]; + $[0] = t0; + } else { + t0 = $[0]; + } + const methodOptions = t0; + let t1; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = ; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== goNext || $[3] !== goToStep || $[4] !== updateWizardData) { + t2 = value => { + const method = value as 'generate' | 'manual'; + updateWizardData({ + method, + wasGenerated: method === "generate" + }); + if (method === "generate") { + goNext(); + } else { + goToStep(3); + } + }; + $[2] = goNext; + $[3] = goToStep; + $[4] = updateWizardData; + $[5] = t2; + } else { + t2 = $[5]; + } + let t3; + if ($[6] !== goBack) { + t3 = () => goBack(); + $[6] = goBack; + $[7] = t3; + } else { + t3 = $[7]; + } + let t4; + if ($[8] !== t2 || $[9] !== t3) { + t4 = { + onUpdateQuestionState(questionText, { + selectedValue: value_1 + }, false); + const textInput_0 = value_1 === "__other__" ? questionStates[questionText]?.textInputValue : undefined; + onAnswer(questionText, value_1, textInput_0); + }} onFocus={handleFocus} onCancel={onCancel} onDownFromLastItem={handleDownFromLastItem} isDisabled={isFooterFocused} layout="compact-vertical" onOpenEditor={handleOpenEditor} onImagePaste={onImagePaste} pastedContents={pastedContents} onRemoveImage={onRemoveImage} />}; + $[58] = currentQuestionIndex; + $[59] = handleFocus; + $[60] = handleOpenEditor; + $[61] = isFooterFocused; + $[62] = onAnswer; + $[63] = onCancel; + $[64] = onImagePaste; + $[65] = onRemoveImage; + $[66] = onSubmit; + $[67] = onUpdateQuestionState; + $[68] = options; + $[69] = pastedContents; + $[70] = question.multiSelect; + $[71] = question.question; + $[72] = questionStates; + $[73] = questionText; + $[74] = questions.length; + $[75] = t12; + } else { + t12 = $[75]; + } + let t13; + if ($[76] === Symbol.for("react.memo_cache_sentinel")) { + t13 = ; + $[76] = t13; + } else { + t13 = $[76]; + } + let t14; + if ($[77] !== footerIndex || $[78] !== isFooterFocused) { + t14 = isFooterFocused && footerIndex === 0 ? {figures.pointer} : ; + $[77] = footerIndex; + $[78] = isFooterFocused; + $[79] = t14; + } else { + t14 = $[79]; + } + const t15 = isFooterFocused && footerIndex === 0 ? "suggestion" : undefined; + const t16 = options.length + 1; + let t17; + if ($[80] !== t15 || $[81] !== t16) { + t17 = {t16}. Chat about this; + $[80] = t15; + $[81] = t16; + $[82] = t17; + } else { + t17 = $[82]; + } + let t18; + if ($[83] !== t14 || $[84] !== t17) { + t18 = {t14}{t17}; + $[83] = t14; + $[84] = t17; + $[85] = t18; + } else { + t18 = $[85]; + } + let t19; + if ($[86] !== footerIndex || $[87] !== isFooterFocused || $[88] !== isInPlanMode || $[89] !== options.length) { + t19 = isInPlanMode && {isFooterFocused && footerIndex === 1 ? {figures.pointer} : }{options.length + 2}. Skip interview and plan immediately; + $[86] = footerIndex; + $[87] = isFooterFocused; + $[88] = isInPlanMode; + $[89] = options.length; + $[90] = t19; + } else { + t19 = $[90]; + } + let t20; + if ($[91] !== t18 || $[92] !== t19) { + t20 = {t13}{t18}{t19}; + $[91] = t18; + $[92] = t19; + $[93] = t20; + } else { + t20 = $[93]; + } + let t21; + if ($[94] !== questions.length) { + t21 = questions.length === 1 ? <>{figures.arrowUp}/{figures.arrowDown} to navigate : "Tab/Arrow keys to navigate"; + $[94] = questions.length; + $[95] = t21; + } else { + t21 = $[95]; + } + let t22; + if ($[96] !== isOtherFocused) { + t22 = isOtherFocused && editorName && <> · ctrl+g to edit in {editorName}; + $[96] = isOtherFocused; + $[97] = t22; + } else { + t22 = $[97]; + } + let t23; + if ($[98] !== t21 || $[99] !== t22) { + t23 = Enter to select ·{" "}{t21}{t22}{" "}· Esc to cancel; + $[98] = t21; + $[99] = t22; + $[100] = t23; + } else { + t23 = $[100]; + } + let t24; + if ($[101] !== minContentHeight || $[102] !== t12 || $[103] !== t20 || $[104] !== t23) { + t24 = {t12}{t20}{t23}; + $[101] = minContentHeight; + $[102] = t12; + $[103] = t20; + $[104] = t23; + $[105] = t24; + } else { + t24 = $[105]; + } + let t25; + if ($[106] !== t10 || $[107] !== t11 || $[108] !== t24) { + t25 = {t10}{t11}{t24}; + $[106] = t10; + $[107] = t11; + $[108] = t24; + $[109] = t25; + } else { + t25 = $[109]; + } + let t26; + if ($[110] !== handleKeyDown || $[111] !== t25 || $[112] !== t8) { + t26 = {t8}{t9}{t25}; + $[110] = handleKeyDown; + $[111] = t25; + $[112] = t8; + $[113] = t26; + } else { + t26 = $[113]; + } + return t26; +} +function _temp4(v) { + return v !== "__other__"; +} +function _temp3(opt_0) { + return opt_0.preview; +} +function _temp2(opt) { + return { + type: "text" as const, + value: opt.label, + label: opt.label, + description: opt.description + }; +} +function _temp(s) { + return s.toolPermissionContext.mode; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmaWd1cmVzIiwiUmVhY3QiLCJ1c2VDYWxsYmFjayIsInVzZVN0YXRlIiwiS2V5Ym9hcmRFdmVudCIsIkJveCIsIlRleHQiLCJ1c2VBcHBTdGF0ZSIsIlF1ZXN0aW9uIiwiUXVlc3Rpb25PcHRpb24iLCJQYXN0ZWRDb250ZW50IiwiZ2V0RXh0ZXJuYWxFZGl0b3IiLCJ0b0lERURpc3BsYXlOYW1lIiwiSW1hZ2VEaW1lbnNpb25zIiwiZWRpdFByb21wdEluRWRpdG9yIiwiT3B0aW9uV2l0aERlc2NyaXB0aW9uIiwiU2VsZWN0IiwiU2VsZWN0TXVsdGkiLCJEaXZpZGVyIiwiRmlsZVBhdGhMaW5rIiwiUGVybWlzc2lvblJlcXVlc3RUaXRsZSIsIlByZXZpZXdRdWVzdGlvblZpZXciLCJRdWVzdGlvbk5hdmlnYXRpb25CYXIiLCJRdWVzdGlvblN0YXRlIiwiUHJvcHMiLCJxdWVzdGlvbiIsInF1ZXN0aW9ucyIsImN1cnJlbnRRdWVzdGlvbkluZGV4IiwiYW5zd2VycyIsIlJlY29yZCIsInF1ZXN0aW9uU3RhdGVzIiwiaGlkZVN1Ym1pdFRhYiIsInBsYW5GaWxlUGF0aCIsInBhc3RlZENvbnRlbnRzIiwibWluQ29udGVudEhlaWdodCIsIm1pbkNvbnRlbnRXaWR0aCIsIm9uVXBkYXRlUXVlc3Rpb25TdGF0ZSIsInF1ZXN0aW9uVGV4dCIsInVwZGF0ZXMiLCJQYXJ0aWFsIiwiaXNNdWx0aVNlbGVjdCIsIm9uQW5zd2VyIiwibGFiZWwiLCJ0ZXh0SW5wdXQiLCJzaG91bGRBZHZhbmNlIiwib25UZXh0SW5wdXRGb2N1cyIsImlzSW5JbnB1dCIsIm9uQ2FuY2VsIiwib25TdWJtaXQiLCJvblRhYlByZXYiLCJvblRhYk5leHQiLCJvblJlc3BvbmRUb0NsYXVkZSIsIm9uRmluaXNoUGxhbkludGVydmlldyIsIm9uSW1hZ2VQYXN0ZSIsImJhc2U2NEltYWdlIiwibWVkaWFUeXBlIiwiZmlsZW5hbWUiLCJkaW1lbnNpb25zIiwic291cmNlUGF0aCIsIm9uUmVtb3ZlSW1hZ2UiLCJpZCIsIlF1ZXN0aW9uVmlldyIsInQwIiwiJCIsIl9jIiwidDEiLCJ1bmRlZmluZWQiLCJpc0luUGxhbk1vZGUiLCJfdGVtcCIsImlzRm9vdGVyRm9jdXNlZCIsInNldElzRm9vdGVyRm9jdXNlZCIsImZvb3RlckluZGV4Iiwic2V0Rm9vdGVySW5kZXgiLCJpc090aGVyRm9jdXNlZCIsInNldElzT3RoZXJGb2N1c2VkIiwidDIiLCJTeW1ib2wiLCJmb3IiLCJlZGl0b3IiLCJlZGl0b3JOYW1lIiwidDMiLCJ2YWx1ZSIsImlzT3RoZXIiLCJoYW5kbGVGb2N1cyIsInQ0IiwiaGFuZGxlRG93bkZyb21MYXN0SXRlbSIsInQ1IiwiaGFuZGxlVXBGcm9tRm9vdGVyIiwidDYiLCJlIiwia2V5IiwiY3RybCIsInByZXZlbnREZWZhdWx0IiwiaGFuZGxlS2V5RG93biIsImhhbmRsZU9wZW5FZGl0b3IiLCJ0NyIsInRleHRPcHRpb25zIiwib3B0aW9ucyIsIm1hcCIsIl90ZW1wMiIsInF1ZXN0aW9uU3RhdGUiLCJ0OCIsIm11bHRpU2VsZWN0IiwiY3VycmVudFZhbHVlIiwic2V0VmFsdWUiLCJyZXN1bHQiLCJjb250ZW50IiwidGV4dElucHV0VmFsdWUiLCJ0OSIsInQxMCIsInQxMSIsInZhbHVlXzAiLCJ0MTIiLCJ0eXBlIiwiY29uc3QiLCJwbGFjZWhvbGRlciIsImluaXRpYWxWYWx1ZSIsIm9uQ2hhbmdlIiwib3RoZXJPcHRpb24iLCJoYXNBbnlQcmV2aWV3Iiwic29tZSIsIl90ZW1wMyIsImxlbmd0aCIsInNlbGVjdGVkVmFsdWUiLCJ2YWx1ZXMiLCJpbmNsdWRlcyIsImZpbmFsVmFsdWVzIiwiZmlsdGVyIiwiX3RlbXA0IiwiY29uY2F0IiwidmFsdWVfMSIsInRleHRJbnB1dF8wIiwidDEzIiwidDE0IiwicG9pbnRlciIsInQxNSIsInQxNiIsInQxNyIsInQxOCIsInQxOSIsInQyMCIsInQyMSIsImFycm93VXAiLCJhcnJvd0Rvd24iLCJ0MjIiLCJ0MjMiLCJ0MjQiLCJ0MjUiLCJ0MjYiLCJ2Iiwib3B0XzAiLCJvcHQiLCJwcmV2aWV3IiwiZGVzY3JpcHRpb24iLCJzIiwidG9vbFBlcm1pc3Npb25Db250ZXh0IiwibW9kZSJdLCJzb3VyY2VzIjpbIlF1ZXN0aW9uVmlldy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGZpZ3VyZXMgZnJvbSAnZmlndXJlcydcbmltcG9ydCBSZWFjdCwgeyB1c2VDYWxsYmFjaywgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uLy4uLy4uL2luay9ldmVudHMva2V5Ym9hcmQtZXZlbnQuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VBcHBTdGF0ZSB9IGZyb20gJy4uLy4uLy4uL3N0YXRlL0FwcFN0YXRlLmpzJ1xuaW1wb3J0IHR5cGUge1xuICBRdWVzdGlvbixcbiAgUXVlc3Rpb25PcHRpb24sXG59IGZyb20gJy4uLy4uLy4uL3Rvb2xzL0Fza1VzZXJRdWVzdGlvblRvb2wvQXNrVXNlclF1ZXN0aW9uVG9vbC5qcydcbmltcG9ydCB0eXBlIHsgUGFzdGVkQ29udGVudCB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL2NvbmZpZy5qcydcbmltcG9ydCB7IGdldEV4dGVybmFsRWRpdG9yIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvZWRpdG9yLmpzJ1xuaW1wb3J0IHsgdG9JREVEaXNwbGF5TmFtZSB9IGZyb20gJy4uLy4uLy4uL3V0aWxzL2lkZS5qcydcbmltcG9ydCB0eXBlIHsgSW1hZ2VEaW1lbnNpb25zIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvaW1hZ2VSZXNpemVyLmpzJ1xuaW1wb3J0IHsgZWRpdFByb21wdEluRWRpdG9yIH0gZnJvbSAnLi4vLi4vLi4vdXRpbHMvcHJvbXB0RWRpdG9yLmpzJ1xuaW1wb3J0IHtcbiAgdHlwZSBPcHRpb25XaXRoRGVzY3JpcHRpb24sXG4gIFNlbGVjdCxcbiAgU2VsZWN0TXVsdGksXG59IGZyb20gJy4uLy4uL0N1c3RvbVNlbGVjdC9pbmRleC5qcydcbmltcG9ydCB7IERpdmlkZXIgfSBmcm9tICcuLi8uLi9kZXNpZ24tc3lzdGVtL0RpdmlkZXIuanMnXG5pbXBvcnQgeyBGaWxlUGF0aExpbmsgfSBmcm9tICcuLi8uLi9GaWxlUGF0aExpbmsuanMnXG5pbXBvcnQgeyBQZXJtaXNzaW9uUmVxdWVzdFRpdGxlIH0gZnJvbSAnLi4vUGVybWlzc2lvblJlcXVlc3RUaXRsZS5qcydcbmltcG9ydCB7IFByZXZpZXdRdWVzdGlvblZpZXcgfSBmcm9tICcuL1ByZXZpZXdRdWVzdGlvblZpZXcuanMnXG5pbXBvcnQgeyBRdWVzdGlvbk5hdmlnYXRpb25CYXIgfSBmcm9tICcuL1F1ZXN0aW9uTmF2aWdhdGlvbkJhci5qcydcbmltcG9ydCB0eXBlIHsgUXVlc3Rpb25TdGF0ZSB9IGZyb20gJy4vdXNlLW11bHRpcGxlLWNob2ljZS1zdGF0ZS5qcydcblxudHlwZSBQcm9wcyA9IHtcbiAgcXVlc3Rpb246IFF1ZXN0aW9uXG4gIHF1ZXN0aW9uczogUXVlc3Rpb25bXVxuICBjdXJyZW50UXVlc3Rpb25JbmRleDogbnVtYmVyXG4gIGFuc3dlcnM6IFJlY29yZDxzdHJpbmcsIHN0cmluZz5cbiAgcXVlc3Rpb25TdGF0ZXM6IFJlY29yZDxzdHJpbmcsIFF1ZXN0aW9uU3RhdGU+XG4gIGhpZGVTdWJtaXRUYWI/OiBib29sZWFuXG4gIHBsYW5GaWxlUGF0aD86IHN0cmluZ1xuICBwYXN0ZWRDb250ZW50cz86IFJlY29yZDxudW1iZXIsIFBhc3RlZENvbnRlbnQ+XG4gIG1pbkNvbnRlbnRIZWlnaHQ/OiBudW1iZXJcbiAgbWluQ29udGVudFdpZHRoPzogbnVtYmVyXG4gIG9uVXBkYXRlUXVlc3Rpb25TdGF0ZTogKFxuICAgIHF1ZXN0aW9uVGV4dDogc3RyaW5nLFxuICAgIHVwZGF0ZXM6IFBhcnRpYWw8UXVlc3Rpb25TdGF0ZT4sXG4gICAgaXNNdWx0aVNlbGVjdDogYm9vbGVhbixcbiAgKSA9PiB2b2lkXG4gIG9uQW5zd2VyOiAoXG4gICAgcXVlc3Rpb25UZXh0OiBzdHJpbmcsXG4gICAgbGFiZWw6IHN0cmluZyB8IHN0cmluZ1tdLFxuICAgIHRleHRJbnB1dD86IHN0cmluZyxcbiAgICBzaG91bGRBZHZhbmNlPzogYm9vbGVhbixcbiAgKSA9PiB2b2lkXG4gIG9uVGV4dElucHV0Rm9jdXM6IChpc0luSW5wdXQ6IGJvb2xlYW4pID0+IHZvaWRcbiAgb25DYW5jZWw6ICgpID0+IHZvaWRcbiAgb25TdWJtaXQ6ICgpID0+IHZvaWRcbiAgb25UYWJQcmV2PzogKCkgPT4gdm9pZFxuICBvblRhYk5leHQ/OiAoKSA9PiB2b2lkXG4gIG9uUmVzcG9uZFRvQ2xhdWRlOiAoKSA9PiB2b2lkXG4gIG9uRmluaXNoUGxhbkludGVydmlldzogKCkgPT4gdm9pZFxuICBvbkltYWdlUGFzdGU/OiAoXG4gICAgYmFzZTY0SW1hZ2U6IHN0cmluZyxcbiAgICBtZWRpYVR5cGU/OiBzdHJpbmcsXG4gICAgZmlsZW5hbWU/OiBzdHJpbmcsXG4gICAgZGltZW5zaW9ucz86IEltYWdlRGltZW5zaW9ucyxcbiAgICBzb3VyY2VQYXRoPzogc3RyaW5nLFxuICApID0+IHZvaWRcbiAgb25SZW1vdmVJbWFnZT86IChpZDogbnVtYmVyKSA9PiB2b2lkXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBRdWVzdGlvblZpZXcoe1xuICBxdWVzdGlvbixcbiAgcXVlc3Rpb25zLFxuICBjdXJyZW50UXVlc3Rpb25JbmRleCxcbiAgYW5zd2VycyxcbiAgcXVlc3Rpb25TdGF0ZXMsXG4gIGhpZGVTdWJtaXRUYWIgPSBmYWxzZSxcbiAgcGxhbkZpbGVQYXRoLFxuICBtaW5Db250ZW50SGVpZ2h0LFxuICBtaW5Db250ZW50V2lkdGgsXG4gIG9uVXBkYXRlUXVlc3Rpb25TdGF0ZSxcbiAgb25BbnN3ZXIsXG4gIG9uVGV4dElucHV0Rm9jdXMsXG4gIG9uQ2FuY2VsLFxuICBvblN1Ym1pdCxcbiAgb25UYWJQcmV2LFxuICBvblRhYk5leHQsXG4gIG9uUmVzcG9uZFRvQ2xhdWRlLFxuICBvbkZpbmlzaFBsYW5JbnRlcnZpZXcsXG4gIG9uSW1hZ2VQYXN0ZSxcbiAgcGFzdGVkQ29udGVudHMsXG4gIG9uUmVtb3ZlSW1hZ2UsXG59OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IGlzSW5QbGFuTW9kZSA9IHVzZUFwcFN0YXRlKHMgPT4gcy50b29sUGVybWlzc2lvbkNvbnRleHQubW9kZSkgPT09ICdwbGFuJ1xuICBjb25zdCBbaXNGb290ZXJGb2N1c2VkLCBzZXRJc0Zvb3RlckZvY3VzZWRdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtmb290ZXJJbmRleCwgc2V0Rm9vdGVySW5kZXhdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgW2lzT3RoZXJGb2N1c2VkLCBzZXRJc090aGVyRm9jdXNlZF0gPSB1c2VTdGF0ZShmYWxzZSlcblxuICBjb25zdCBlZGl0b3IgPSBnZXRFeHRlcm5hbEVkaXRvcigpXG4gIGNvbnN0IGVkaXRvck5hbWUgPSBlZGl0b3IgPyB0b0lERURpc3BsYXlOYW1lKGVkaXRvcikgOiBudWxsXG5cbiAgY29uc3QgaGFuZGxlRm9jdXMgPSB1c2VDYWxsYmFjayhcbiAgICAodmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgY29uc3QgaXNPdGhlciA9IHZhbHVlID09PSAnX19vdGhlcl9fJ1xuICAgICAgc2V0SXNPdGhlckZvY3VzZWQoaXNPdGhlcilcbiAgICAgIG9uVGV4dElucHV0Rm9jdXMoaXNPdGhlcilcbiAgICB9LFxuICAgIFtvblRleHRJbnB1dEZvY3VzXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZURvd25Gcm9tTGFzdEl0ZW0gPSB1c2VDYWxsYmFjaygoKSA9PiB7XG4gICAgc2V0SXNGb290ZXJGb2N1c2VkKHRydWUpXG4gIH0sIFtdKVxuXG4gIGNvbnN0IGhhbmRsZVVwRnJvbUZvb3RlciA9IHVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBzZXRJc0Zvb3RlckZvY3VzZWQoZmFsc2UpXG4gIH0sIFtdKVxuXG4gIC8vIEhhbmRsZSBrZXlib2FyZCBpbnB1dCB3aGVuIGZvb3RlciBpcyBmb2N1c2VkXG4gIGNvbnN0IGhhbmRsZUtleURvd24gPSB1c2VDYWxsYmFjayhcbiAgICAoZTogS2V5Ym9hcmRFdmVudCkgPT4ge1xuICAgICAgaWYgKCFpc0Zvb3RlckZvY3VzZWQpIHJldHVyblxuXG4gICAgICBpZiAoZS5rZXkgPT09ICd1cCcgfHwgKGUuY3RybCAmJiBlLmtleSA9PT0gJ3AnKSkge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgaWYgKGZvb3RlckluZGV4ID09PSAwKSB7XG4gICAgICAgICAgaGFuZGxlVXBGcm9tRm9vdGVyKClcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBzZXRGb290ZXJJbmRleCgwKVxuICAgICAgICB9XG4gICAgICAgIHJldHVyblxuICAgICAgfVxuXG4gICAgICBpZiAoZS5rZXkgPT09ICdkb3duJyB8fCAoZS5jdHJsICYmIGUua2V5ID09PSAnbicpKSB7XG4gICAgICAgIGUucHJldmVudERlZmF1bHQoKVxuICAgICAgICBpZiAoaXNJblBsYW5Nb2RlICYmIGZvb3RlckluZGV4ID09PSAwKSB7XG4gICAgICAgICAgc2V0Rm9vdGVySW5kZXgoMSlcbiAgICAgICAgfVxuICAgICAgICByZXR1cm5cbiAgICAgIH1cblxuICAgICAgaWYgKGUua2V5ID09PSAncmV0dXJuJykge1xuICAgICAgICBlLnByZXZlbnREZWZhdWx0KClcbiAgICAgICAgaWYgKGZvb3RlckluZGV4ID09PSAwKSB7XG4gICAgICAgICAgb25SZXNwb25kVG9DbGF1ZGUoKVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIG9uRmluaXNoUGxhbkludGVydmlldygpXG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIGlmIChlLmtleSA9PT0gJ2VzY2FwZScpIHtcbiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIG9uQ2FuY2VsKClcbiAgICAgIH1cbiAgICB9LFxuICAgIFtcbiAgICAgIGlzRm9vdGVyRm9jdXNlZCxcbiAgICAgIGZvb3RlckluZGV4LFxuICAgICAgaXNJblBsYW5Nb2RlLFxuICAgICAgaGFuZGxlVXBGcm9tRm9vdGVyLFxuICAgICAgb25SZXNwb25kVG9DbGF1ZGUsXG4gICAgICBvbkZpbmlzaFBsYW5JbnRlcnZpZXcsXG4gICAgICBvbkNhbmNlbCxcbiAgICBdLFxuICApXG5cbiAgY29uc3QgdGV4dE9wdGlvbnM6IE9wdGlvbldpdGhEZXNjcmlwdGlvbjxzdHJpbmc+W10gPSBxdWVzdGlvbi5vcHRpb25zLm1hcChcbiAgICAob3B0OiBRdWVzdGlvbk9wdGlvbikgPT4gKHtcbiAgICAgIHR5cGU6ICd0ZXh0JyBhcyBjb25zdCxcbiAgICAgIHZhbHVlOiBvcHQubGFiZWwsXG4gICAgICBsYWJlbDogb3B0LmxhYmVsLFxuICAgICAgZGVzY3JpcHRpb246IG9wdC5kZXNjcmlwdGlvbixcbiAgICB9KSxcbiAgKVxuXG4gIGNvbnN0IHF1ZXN0aW9uVGV4dCA9IHF1ZXN0aW9uLnF1ZXN0aW9uXG4gIGNvbnN0IHF1ZXN0aW9uU3RhdGUgPSBxdWVzdGlvblN0YXRlc1txdWVzdGlvblRleHRdXG5cbiAgY29uc3QgaGFuZGxlT3BlbkVkaXRvciA9IHVzZUNhbGxiYWNrKFxuICAgIGFzeW5jIChjdXJyZW50VmFsdWU6IHN0cmluZywgc2V0VmFsdWU6ICh2YWx1ZTogc3RyaW5nKSA9PiB2b2lkKSA9PiB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBlZGl0UHJvbXB0SW5FZGl0b3IoY3VycmVudFZhbHVlKVxuXG4gICAgICBpZiAocmVzdWx0LmNvbnRlbnQgIT09IG51bGwgJiYgcmVzdWx0LmNvbnRlbnQgIT09IGN1cnJlbnRWYWx1ZSkge1xuICAgICAgICAvLyBVcGRhdGUgdGhlIFNlbGVjdCdzIGludGVybmFsIHN0YXRlIGZvciBpbW1lZGlhdGUgVUkgdXBkYXRlXG4gICAgICAgIHNldFZhbHVlKHJlc3VsdC5jb250ZW50KVxuICAgICAgICAvLyBBbHNvIHVwZGF0ZSB0aGUgcXVlc3Rpb24gc3RhdGUgZm9yIHBlcnNpc3RlbmNlXG4gICAgICAgIG9uVXBkYXRlUXVlc3Rpb25TdGF0ZShcbiAgICAgICAgICBxdWVzdGlvblRleHQsXG4gICAgICAgICAgeyB0ZXh0SW5wdXRWYWx1ZTogcmVzdWx0LmNvbnRlbnQgfSxcbiAgICAgICAgICBxdWVzdGlvbi5tdWx0aVNlbGVjdCA/PyBmYWxzZSxcbiAgICAgICAgKVxuICAgICAgfVxuICAgIH0sXG4gICAgW3F1ZXN0aW9uVGV4dCwgb25VcGRhdGVRdWVzdGlvblN0YXRlLCBxdWVzdGlvbi5tdWx0aVNlbGVjdF0sXG4gIClcblxuICBjb25zdCBvdGhlck9wdGlvbjogT3B0aW9uV2l0aERlc2NyaXB0aW9uPHN0cmluZz4gPSB7XG4gICAgdHlwZTogJ2lucHV0JyBhcyBjb25zdCxcbiAgICB2YWx1ZTogJ19fb3RoZXJfXycsXG4gICAgbGFiZWw6ICdPdGhlcicsXG4gICAgcGxhY2Vob2xkZXI6IHF1ZXN0aW9uLm11bHRpU2VsZWN0ID8gJ1R5cGUgc29tZXRoaW5nJyA6ICdUeXBlIHNvbWV0aGluZy4nLFxuICAgIGluaXRpYWxWYWx1ZTogcXVlc3Rpb25TdGF0ZT8udGV4dElucHV0VmFsdWUgPz8gJycsXG4gICAgb25DaGFuZ2U6ICh2YWx1ZTogc3RyaW5nKSA9PiB7XG4gICAgICBvblVwZGF0ZVF1ZXN0aW9uU3RhdGUoXG4gICAgICAgIHF1ZXN0aW9uVGV4dCxcbiAgICAgICAgeyB0ZXh0SW5wdXRWYWx1ZTogdmFsdWUgfSxcbiAgICAgICAgcXVlc3Rpb24ubXVsdGlTZWxlY3QgPz8gZmFsc2UsXG4gICAgICApXG4gICAgfSxcbiAgfVxuXG4gIGNvbnN0IG9wdGlvbnMgPSBbLi4udGV4dE9wdGlvbnMsIG90aGVyT3B0aW9uXVxuXG4gIC8vIENoZWNrIGlmIGFueSBvcHRpb24gaGFzIGEgcHJldmlldyBhbmQgaXQncyBub3QgbXVsdGktc2VsZWN0XG4gIC8vIFByZXZpZXdzIG9ubHkgc3VwcG9ydGVkIGZvciBzaW5nbGUtc2VsZWN0IHF1ZXN0aW9uc1xuICBjb25zdCBoYXNBbnlQcmV2aWV3ID1cbiAgICAhcXVlc3Rpb24ubXVsdGlTZWxlY3QgJiYgcXVlc3Rpb24ub3B0aW9ucy5zb21lKG9wdCA9PiBvcHQucHJldmlldylcblxuICAvLyBEZWxlZ2F0ZSB0byBQcmV2aWV3UXVlc3Rpb25WaWV3IGZvciBjYXJvdXNlbC1zdHlsZSBwcmV2aWV3IG1vZGVcbiAgaWYgKGhhc0FueVByZXZpZXcpIHtcbiAgICByZXR1cm4gKFxuICAgICAgPFByZXZpZXdRdWVzdGlvblZpZXdcbiAgICAgICAgcXVlc3Rpb249e3F1ZXN0aW9ufVxuICAgICAgICBxdWVzdGlvbnM9e3F1ZXN0aW9uc31cbiAgICAgICAgY3VycmVudFF1ZXN0aW9uSW5kZXg9e2N1cnJlbnRRdWVzdGlvbkluZGV4fVxuICAgICAgICBhbnN3ZXJzPXthbnN3ZXJzfVxuICAgICAgICBxdWVzdGlvblN0YXRlcz17cXVlc3Rpb25TdGF0ZXN9XG4gICAgICAgIGhpZGVTdWJtaXRUYWI9e2hpZGVTdWJtaXRUYWJ9XG4gICAgICAgIG1pbkNvbnRlbnRIZWlnaHQ9e21pbkNvbnRlbnRIZWlnaHR9XG4gICAgICAgIG1pbkNvbnRlbnRXaWR0aD17bWluQ29udGVudFdpZHRofVxuICAgICAgICBvblVwZGF0ZVF1ZXN0aW9uU3RhdGU9e29uVXBkYXRlUXVlc3Rpb25TdGF0ZX1cbiAgICAgICAgb25BbnN3ZXI9e29uQW5zd2VyfVxuICAgICAgICBvblRleHRJbnB1dEZvY3VzPXtvblRleHRJbnB1dEZvY3VzfVxuICAgICAgICBvbkNhbmNlbD17b25DYW5jZWx9XG4gICAgICAgIG9uVGFiUHJldj17b25UYWJQcmV2fVxuICAgICAgICBvblRhYk5leHQ9e29uVGFiTmV4dH1cbiAgICAgICAgb25SZXNwb25kVG9DbGF1ZGU9e29uUmVzcG9uZFRvQ2xhdWRlfVxuICAgICAgICBvbkZpbmlzaFBsYW5JbnRlcnZpZXc9e29uRmluaXNoUGxhbkludGVydmlld31cbiAgICAgIC8+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8Qm94XG4gICAgICBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCJcbiAgICAgIG1hcmdpblRvcD17MH1cbiAgICAgIHRhYkluZGV4PXswfVxuICAgICAgYXV0b0ZvY3VzXG4gICAgICBvbktleURvd249e2hhbmRsZUtleURvd259XG4gICAgPlxuICAgICAge2lzSW5QbGFuTW9kZSAmJiBwbGFuRmlsZVBhdGggJiYgKFxuICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBnYXA9ezB9PlxuICAgICAgICAgIDxEaXZpZGVyIGNvbG9yPVwiaW5hY3RpdmVcIiAvPlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwiaW5hY3RpdmVcIj5cbiAgICAgICAgICAgIFBsYW5uaW5nOiA8RmlsZVBhdGhMaW5rIGZpbGVQYXRoPXtwbGFuRmlsZVBhdGh9IC8+XG4gICAgICAgICAgPC9UZXh0PlxuICAgICAgICA8L0JveD5cbiAgICAgICl9XG4gICAgICA8Qm94IG1hcmdpblRvcD17LTF9PlxuICAgICAgICA8RGl2aWRlciBjb2xvcj1cImluYWN0aXZlXCIgLz5cbiAgICAgIDwvQm94PlxuICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgcGFkZGluZ1RvcD17MH0+XG4gICAgICAgIDxRdWVzdGlvbk5hdmlnYXRpb25CYXJcbiAgICAgICAgICBxdWVzdGlvbnM9e3F1ZXN0aW9uc31cbiAgICAgICAgICBjdXJyZW50UXVlc3Rpb25JbmRleD17Y3VycmVudFF1ZXN0aW9uSW5kZXh9XG4gICAgICAgICAgYW5zd2Vycz17YW5zd2Vyc31cbiAgICAgICAgICBoaWRlU3VibWl0VGFiPXtoaWRlU3VibWl0VGFifVxuICAgICAgICAvPlxuICAgICAgICA8UGVybWlzc2lvblJlcXVlc3RUaXRsZSB0aXRsZT17cXVlc3Rpb24ucXVlc3Rpb259IGNvbG9yPXsndGV4dCd9IC8+XG5cbiAgICAgICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWluSGVpZ2h0PXttaW5Db250ZW50SGVpZ2h0fT5cbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICB7cXVlc3Rpb24ubXVsdGlTZWxlY3QgPyAoXG4gICAgICAgICAgICAgIDxTZWxlY3RNdWx0aVxuICAgICAgICAgICAgICAgIGtleT17cXVlc3Rpb24ucXVlc3Rpb259XG4gICAgICAgICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU9e1xuICAgICAgICAgICAgICAgICAgcXVlc3Rpb25TdGF0ZXNbcXVlc3Rpb24ucXVlc3Rpb25dPy5zZWxlY3RlZFZhbHVlIGFzXG4gICAgICAgICAgICAgICAgICAgIHwgc3RyaW5nW11cbiAgICAgICAgICAgICAgICAgICAgfCB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgb25DaGFuZ2U9eyh2YWx1ZXM6IHN0cmluZ1tdKSA9PiB7XG4gICAgICAgICAgICAgICAgICBvblVwZGF0ZVF1ZXN0aW9uU3RhdGUoXG4gICAgICAgICAgICAgICAgICAgIHF1ZXN0aW9uVGV4dCxcbiAgICAgICAgICAgICAgICAgICAgeyBzZWxlY3RlZFZhbHVlOiB2YWx1ZXMgfSxcbiAgICAgICAgICAgICAgICAgICAgdHJ1ZSxcbiAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAgIGNvbnN0IHRleHRJbnB1dCA9IHZhbHVlcy5pbmNsdWRlcygnX19vdGhlcl9fJylcbiAgICAgICAgICAgICAgICAgICAgPyBxdWVzdGlvblN0YXRlc1txdWVzdGlvblRleHRdPy50ZXh0SW5wdXRWYWx1ZVxuICAgICAgICAgICAgICAgICAgICA6IHVuZGVmaW5lZFxuICAgICAgICAgICAgICAgICAgY29uc3QgZmluYWxWYWx1ZXMgPSB2YWx1ZXNcbiAgICAgICAgICAgICAgICAgICAgLmZpbHRlcih2ID0+IHYgIT09ICdfX290aGVyX18nKVxuICAgICAgICAgICAgICAgICAgICAuY29uY2F0KHRleHRJbnB1dCA/IFt0ZXh0SW5wdXRdIDogW10pXG4gICAgICAgICAgICAgICAgICBvbkFuc3dlcihxdWVzdGlvblRleHQsIGZpbmFsVmFsdWVzLCB1bmRlZmluZWQsIGZhbHNlKVxuICAgICAgICAgICAgICAgIH19XG4gICAgICAgICAgICAgICAgb25Gb2N1cz17aGFuZGxlRm9jdXN9XG4gICAgICAgICAgICAgICAgb25DYW5jZWw9e29uQ2FuY2VsfVxuICAgICAgICAgICAgICAgIHN1Ym1pdEJ1dHRvblRleHQ9e1xuICAgICAgICAgICAgICAgICAgY3VycmVudFF1ZXN0aW9uSW5kZXggPT09IHF1ZXN0aW9ucy5sZW5ndGggLSAxXG4gICAgICAgICAgICAgICAgICAgID8gJ1N1Ym1pdCdcbiAgICAgICAgICAgICAgICAgICAgOiAnTmV4dCdcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgb25TdWJtaXQ9e29uU3VibWl0fVxuICAgICAgICAgICAgICAgIG9uRG93bkZyb21MYXN0SXRlbT17aGFuZGxlRG93bkZyb21MYXN0SXRlbX1cbiAgICAgICAgICAgICAgICBpc0Rpc2FibGVkPXtpc0Zvb3RlckZvY3VzZWR9XG4gICAgICAgICAgICAgICAgb25PcGVuRWRpdG9yPXtoYW5kbGVPcGVuRWRpdG9yfVxuICAgICAgICAgICAgICAgIG9uSW1hZ2VQYXN0ZT17b25JbWFnZVBhc3RlfVxuICAgICAgICAgICAgICAgIHBhc3RlZENvbnRlbnRzPXtwYXN0ZWRDb250ZW50c31cbiAgICAgICAgICAgICAgICBvblJlbW92ZUltYWdlPXtvblJlbW92ZUltYWdlfVxuICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgPFNlbGVjdFxuICAgICAgICAgICAgICAgIGtleT17cXVlc3Rpb24ucXVlc3Rpb259XG4gICAgICAgICAgICAgICAgb3B0aW9ucz17b3B0aW9uc31cbiAgICAgICAgICAgICAgICBkZWZhdWx0VmFsdWU9e1xuICAgICAgICAgICAgICAgICAgcXVlc3Rpb25TdGF0ZXNbcXVlc3Rpb24ucXVlc3Rpb25dPy5zZWxlY3RlZFZhbHVlIGFzXG4gICAgICAgICAgICAgICAgICAgIHwgc3RyaW5nXG4gICAgICAgICAgICAgICAgICAgIHwgdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIG9uQ2hhbmdlPXsodmFsdWU6IHN0cmluZykgPT4ge1xuICAgICAgICAgICAgICAgICAgb25VcGRhdGVRdWVzdGlvblN0YXRlKFxuICAgICAgICAgICAgICAgICAgICBxdWVzdGlvblRleHQsXG4gICAgICAgICAgICAgICAgICAgIHsgc2VsZWN0ZWRWYWx1ZTogdmFsdWUgfSxcbiAgICAgICAgICAgICAgICAgICAgZmFsc2UsXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICBjb25zdCB0ZXh0SW5wdXQgPVxuICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9PT0gJ19fb3RoZXJfXydcbiAgICAgICAgICAgICAgICAgICAgICA/IHF1ZXN0aW9uU3RhdGVzW3F1ZXN0aW9uVGV4dF0/LnRleHRJbnB1dFZhbHVlXG4gICAgICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICAgIG9uQW5zd2VyKHF1ZXN0aW9uVGV4dCwgdmFsdWUsIHRleHRJbnB1dClcbiAgICAgICAgICAgICAgICB9fVxuICAgICAgICAgICAgICAgIG9uRm9jdXM9e2hhbmRsZUZvY3VzfVxuICAgICAgICAgICAgICAgIG9uQ2FuY2VsPXtvbkNhbmNlbH1cbiAgICAgICAgICAgICAgICBvbkRvd25Gcm9tTGFzdEl0ZW09e2hhbmRsZURvd25Gcm9tTGFzdEl0ZW19XG4gICAgICAgICAgICAgICAgaXNEaXNhYmxlZD17aXNGb290ZXJGb2N1c2VkfVxuICAgICAgICAgICAgICAgIGxheW91dD1cImNvbXBhY3QtdmVydGljYWxcIlxuICAgICAgICAgICAgICAgIG9uT3BlbkVkaXRvcj17aGFuZGxlT3BlbkVkaXRvcn1cbiAgICAgICAgICAgICAgICBvbkltYWdlUGFzdGU9e29uSW1hZ2VQYXN0ZX1cbiAgICAgICAgICAgICAgICBwYXN0ZWRDb250ZW50cz17cGFzdGVkQ29udGVudHN9XG4gICAgICAgICAgICAgICAgb25SZW1vdmVJbWFnZT17b25SZW1vdmVJbWFnZX1cbiAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICl9XG4gICAgICAgICAgPC9Cb3g+XG4gICAgICAgICAgey8qIEZvb3RlciBzZWN0aW9uIC0gYWx3YXlzIHZpc2libGUsIHNlcGFyYXRlIGZyb20gU2VsZWN0ICovfVxuICAgICAgICAgIDxCb3ggZmxleERpcmVjdGlvbj1cImNvbHVtblwiPlxuICAgICAgICAgICAgPERpdmlkZXIgY29sb3I9XCJpbmFjdGl2ZVwiIC8+XG4gICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9PlxuICAgICAgICAgICAgICB7aXNGb290ZXJGb2N1c2VkICYmIGZvb3RlckluZGV4ID09PSAwID8gKFxuICAgICAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VnZ2VzdGlvblwiPntmaWd1cmVzLnBvaW50ZXJ9PC9UZXh0PlxuICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgIDxUZXh0PiA8L1RleHQ+XG4gICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgY29sb3I9e1xuICAgICAgICAgICAgICAgICAgaXNGb290ZXJGb2N1c2VkICYmIGZvb3RlckluZGV4ID09PSAwXG4gICAgICAgICAgICAgICAgICAgID8gJ3N1Z2dlc3Rpb24nXG4gICAgICAgICAgICAgICAgICAgIDogdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAge29wdGlvbnMubGVuZ3RoICsgMX0uIENoYXQgYWJvdXQgdGhpc1xuICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8L0JveD5cbiAgICAgICAgICAgIHtpc0luUGxhbk1vZGUgJiYgKFxuICAgICAgICAgICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJyb3dcIiBnYXA9ezF9PlxuICAgICAgICAgICAgICAgIHtpc0Zvb3RlckZvY3VzZWQgJiYgZm9vdGVySW5kZXggPT09IDEgPyAoXG4gICAgICAgICAgICAgICAgICA8VGV4dCBjb2xvcj1cInN1Z2dlc3Rpb25cIj57ZmlndXJlcy5wb2ludGVyfTwvVGV4dD5cbiAgICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgICAgPFRleHQ+IDwvVGV4dD5cbiAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgIDxUZXh0XG4gICAgICAgICAgICAgICAgICBjb2xvcj17XG4gICAgICAgICAgICAgICAgICAgIGlzRm9vdGVyRm9jdXNlZCAmJiBmb290ZXJJbmRleCA9PT0gMVxuICAgICAgICAgICAgICAgICAgICAgID8gJ3N1Z2dlc3Rpb24nXG4gICAgICAgICAgICAgICAgICAgICAgOiB1bmRlZmluZWRcbiAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICB7b3B0aW9ucy5sZW5ndGggKyAyfS4gU2tpcCBpbnRlcnZpZXcgYW5kIHBsYW4gaW1tZWRpYXRlbHlcbiAgICAgICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICAgIDwvQm94PlxuICAgICAgICAgICAgKX1cbiAgICAgICAgICA8L0JveD5cbiAgICAgICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImluYWN0aXZlXCIgZGltQ29sb3I+XG4gICAgICAgICAgICAgIEVudGVyIHRvIHNlbGVjdCDCt3snICd9XG4gICAgICAgICAgICAgIHtxdWVzdGlvbnMubGVuZ3RoID09PSAxID8gKFxuICAgICAgICAgICAgICAgIDw+XG4gICAgICAgICAgICAgICAgICB7ZmlndXJlcy5hcnJvd1VwfS97ZmlndXJlcy5hcnJvd0Rvd259IHRvIG5hdmlnYXRlXG4gICAgICAgICAgICAgICAgPC8+XG4gICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgJ1RhYi9BcnJvdyBrZXlzIHRvIG5hdmlnYXRlJ1xuICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICB7aXNPdGhlckZvY3VzZWQgJiYgZWRpdG9yTmFtZSAmJiAoXG4gICAgICAgICAgICAgICAgPD4gwrcgY3RybCtnIHRvIGVkaXQgaW4ge2VkaXRvck5hbWV9PC8+XG4gICAgICAgICAgICAgICl9eycgJ31cbiAgICAgICAgICAgICAgwrcgRXNjIHRvIGNhbmNlbFxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgIDwvQm94PlxuICAgICAgICA8L0JveD5cbiAgICAgIDwvQm94PlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxPQUFPLE1BQU0sU0FBUztBQUM3QixPQUFPQyxLQUFLLElBQUlDLFdBQVcsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDcEQsY0FBY0MsYUFBYSxRQUFRLHVDQUF1QztBQUMxRSxTQUFTQyxHQUFHLEVBQUVDLElBQUksUUFBUSxpQkFBaUI7QUFDM0MsU0FBU0MsV0FBVyxRQUFRLDRCQUE0QjtBQUN4RCxjQUNFQyxRQUFRLEVBQ1JDLGNBQWMsUUFDVCwyREFBMkQ7QUFDbEUsY0FBY0MsYUFBYSxRQUFRLDBCQUEwQjtBQUM3RCxTQUFTQyxpQkFBaUIsUUFBUSwwQkFBMEI7QUFDNUQsU0FBU0MsZ0JBQWdCLFFBQVEsdUJBQXVCO0FBQ3hELGNBQWNDLGVBQWUsUUFBUSxnQ0FBZ0M7QUFDckUsU0FBU0Msa0JBQWtCLFFBQVEsZ0NBQWdDO0FBQ25FLFNBQ0UsS0FBS0MscUJBQXFCLEVBQzFCQyxNQUFNLEVBQ05DLFdBQVcsUUFDTiw2QkFBNkI7QUFDcEMsU0FBU0MsT0FBTyxRQUFRLGdDQUFnQztBQUN4RCxTQUFTQyxZQUFZLFFBQVEsdUJBQXVCO0FBQ3BELFNBQVNDLHNCQUFzQixRQUFRLDhCQUE4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 \ No newline at end of file diff --git a/src/entrypoints/sdk/controlSchemas.ts b/src/entrypoints/sdk/controlSchemas.ts new file mode 100644 index 0000000000000000000000000000000000000000..fccf13e1b1a7ef26a971f106fafca6d0a0b7f576 --- /dev/null +++ b/src/entrypoints/sdk/controlSchemas.ts @@ -0,0 +1,663 @@ +/** + * SDK Control Schemas - Zod schemas for the control protocol. + * + * These schemas define the control protocol between SDK implementations and the CLI. + * Used by SDK builders (e.g., Python SDK) to communicate with the CLI process. + * + * SDK consumers should use coreSchemas.ts instead. + */ + +import { z } from 'zod/v4' +import { lazySchema } from '../../utils/lazySchema.js' +import { + AccountInfoSchema, + AgentDefinitionSchema, + AgentInfoSchema, + FastModeStateSchema, + HookEventSchema, + HookInputSchema, + McpServerConfigForProcessTransportSchema, + McpServerStatusSchema, + ModelInfoSchema, + PermissionModeSchema, + PermissionUpdateSchema, + SDKMessageSchema, + SDKPostTurnSummaryMessageSchema, + SDKStreamlinedTextMessageSchema, + SDKStreamlinedToolUseSummaryMessageSchema, + SDKUserMessageSchema, + SlashCommandSchema, +} from './coreSchemas.js' + +// ============================================================================ +// External Type Placeholders +// ============================================================================ + +// JSONRPCMessage from @modelcontextprotocol/sdk - treat as unknown +export const JSONRPCMessagePlaceholder = lazySchema(() => z.unknown()) + +// ============================================================================ +// Hook Callback Types +// ============================================================================ + +export const SDKHookCallbackMatcherSchema = lazySchema(() => + z + .object({ + matcher: z.string().optional(), + hookCallbackIds: z.array(z.string()), + timeout: z.number().optional(), + }) + .describe('Configuration for matching and routing hook callbacks.'), +) + +// ============================================================================ +// Control Request Types +// ============================================================================ + +export const SDKControlInitializeRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('initialize'), + hooks: z + .record(HookEventSchema(), z.array(SDKHookCallbackMatcherSchema())) + .optional(), + sdkMcpServers: z.array(z.string()).optional(), + jsonSchema: z.record(z.string(), z.unknown()).optional(), + systemPrompt: z.string().optional(), + appendSystemPrompt: z.string().optional(), + agents: z.record(z.string(), AgentDefinitionSchema()).optional(), + promptSuggestions: z.boolean().optional(), + agentProgressSummaries: z.boolean().optional(), + }) + .describe( + 'Initializes the SDK session with hooks, MCP servers, and agent configuration.', + ), +) + +export const SDKControlInitializeResponseSchema = lazySchema(() => + z + .object({ + commands: z.array(SlashCommandSchema()), + agents: z.array(AgentInfoSchema()), + output_style: z.string(), + available_output_styles: z.array(z.string()), + models: z.array(ModelInfoSchema()), + account: AccountInfoSchema(), + pid: z + .number() + .optional() + .describe('@internal CLI process PID for tmux socket isolation'), + fast_mode_state: FastModeStateSchema().optional(), + }) + .describe( + 'Response from session initialization with available commands, models, and account info.', + ), +) + +export const SDKControlInterruptRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('interrupt'), + }) + .describe('Interrupts the currently running conversation turn.'), +) + + +export const SDKControlPermissionRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('can_use_tool'), + tool_name: z.string(), + input: z.record(z.string(), z.unknown()), + permission_suggestions: z.array(PermissionUpdateSchema()).optional(), + blocked_path: z.string().optional(), + decision_reason: z.string().optional(), + title: z.string().optional(), + display_name: z.string().optional(), + tool_use_id: z.string(), + agent_id: z.string().optional(), + description: z.string().optional(), + }) + .describe('Requests permission to use a tool with the given input.'), +) + +export const SDKControlSetPermissionModeRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('set_permission_mode'), + mode: PermissionModeSchema(), + ultraplan: z + .boolean() + .optional() + .describe('@internal CCR ultraplan session marker.'), + }) + .describe('Sets the permission mode for tool execution handling.'), +) + +export const SDKControlSetModelRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('set_model'), + model: z.string().optional(), + }) + .describe('Sets the model to use for subsequent conversation turns.'), +) + +export const SDKControlSetMaxThinkingTokensRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('set_max_thinking_tokens'), + max_thinking_tokens: z.number().nullable(), + }) + .describe( + 'Sets the maximum number of thinking tokens for extended thinking.', + ), +) + +export const SDKControlMcpStatusRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('mcp_status'), + }) + .describe('Requests the current status of all MCP server connections.'), +) + +export const SDKControlMcpStatusResponseSchema = lazySchema(() => + z + .object({ + mcpServers: z.array(McpServerStatusSchema()), + }) + .describe( + 'Response containing the current status of all MCP server connections.', + ), +) + +export const SDKControlGetContextUsageRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('get_context_usage'), + }) + .describe( + 'Requests a breakdown of current context window usage by category.', + ), +) + +const ContextCategorySchema = lazySchema(() => + z.object({ + name: z.string(), + tokens: z.number(), + color: z.string(), + isDeferred: z.boolean().optional(), + }), +) + +const ContextGridSquareSchema = lazySchema(() => + z.object({ + color: z.string(), + isFilled: z.boolean(), + categoryName: z.string(), + tokens: z.number(), + percentage: z.number(), + squareFullness: z.number(), + }), +) + +export const SDKControlGetContextUsageResponseSchema = lazySchema(() => + z + .object({ + categories: z.array(ContextCategorySchema()), + totalTokens: z.number(), + maxTokens: z.number(), + rawMaxTokens: z.number(), + percentage: z.number(), + gridRows: z.array(z.array(ContextGridSquareSchema())), + model: z.string(), + memoryFiles: z.array( + z.object({ + path: z.string(), + type: z.string(), + tokens: z.number(), + }), + ), + mcpTools: z.array( + z.object({ + name: z.string(), + serverName: z.string(), + tokens: z.number(), + isLoaded: z.boolean().optional(), + }), + ), + deferredBuiltinTools: z + .array( + z.object({ + name: z.string(), + tokens: z.number(), + isLoaded: z.boolean(), + }), + ) + .optional(), + systemTools: z + .array(z.object({ name: z.string(), tokens: z.number() })) + .optional(), + systemPromptSections: z + .array(z.object({ name: z.string(), tokens: z.number() })) + .optional(), + agents: z.array( + z.object({ + agentType: z.string(), + source: z.string(), + tokens: z.number(), + }), + ), + slashCommands: z + .object({ + totalCommands: z.number(), + includedCommands: z.number(), + tokens: z.number(), + }) + .optional(), + skills: z + .object({ + totalSkills: z.number(), + includedSkills: z.number(), + tokens: z.number(), + skillFrontmatter: z.array( + z.object({ + name: z.string(), + source: z.string(), + tokens: z.number(), + }), + ), + }) + .optional(), + autoCompactThreshold: z.number().optional(), + isAutoCompactEnabled: z.boolean(), + messageBreakdown: z + .object({ + toolCallTokens: z.number(), + toolResultTokens: z.number(), + attachmentTokens: z.number(), + assistantMessageTokens: z.number(), + userMessageTokens: z.number(), + toolCallsByType: z.array( + z.object({ + name: z.string(), + callTokens: z.number(), + resultTokens: z.number(), + }), + ), + attachmentsByType: z.array( + z.object({ name: z.string(), tokens: z.number() }), + ), + }) + .optional(), + apiUsage: z + .object({ + input_tokens: z.number(), + output_tokens: z.number(), + cache_creation_input_tokens: z.number(), + cache_read_input_tokens: z.number(), + }) + .nullable(), + }) + .describe( + 'Breakdown of current context window usage by category (system prompt, tools, messages, etc.).', + ), +) + +export const SDKControlRewindFilesRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('rewind_files'), + user_message_id: z.string(), + dry_run: z.boolean().optional(), + }) + .describe('Rewinds file changes made since a specific user message.'), +) + +export const SDKControlRewindFilesResponseSchema = lazySchema(() => + z + .object({ + canRewind: z.boolean(), + error: z.string().optional(), + filesChanged: z.array(z.string()).optional(), + insertions: z.number().optional(), + deletions: z.number().optional(), + }) + .describe('Result of a rewindFiles operation.'), +) + +export const SDKControlCancelAsyncMessageRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('cancel_async_message'), + message_uuid: z.string(), + }) + .describe( + 'Drops a pending async user message from the command queue by uuid. No-op if already dequeued for execution.', + ), +) + +export const SDKControlCancelAsyncMessageResponseSchema = lazySchema(() => + z + .object({ + cancelled: z.boolean(), + }) + .describe( + 'Result of a cancel_async_message operation. cancelled=false means the message was not in the queue (already dequeued or never enqueued).', + ), +) + +export const SDKControlSeedReadStateRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('seed_read_state'), + path: z.string(), + mtime: z.number(), + }) + .describe( + 'Seeds the readFileState cache with a path+mtime entry. Use when a prior Read was removed from context (e.g. by snip) so Edit validation would fail despite the client having observed the Read. The mtime lets the CLI detect if the file changed since the seeded Read — same staleness check as the normal path.', + ), +) + +export const SDKHookCallbackRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('hook_callback'), + callback_id: z.string(), + input: HookInputSchema(), + tool_use_id: z.string().optional(), + }) + .describe('Delivers a hook callback with its input data.'), +) + +export const SDKControlMcpMessageRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('mcp_message'), + server_name: z.string(), + message: JSONRPCMessagePlaceholder(), + }) + .describe('Sends a JSON-RPC message to a specific MCP server.'), +) + +export const SDKControlMcpSetServersRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('mcp_set_servers'), + servers: z.record(z.string(), McpServerConfigForProcessTransportSchema()), + }) + .describe('Replaces the set of dynamically managed MCP servers.'), +) + +export const SDKControlMcpSetServersResponseSchema = lazySchema(() => + z + .object({ + added: z.array(z.string()), + removed: z.array(z.string()), + errors: z.record(z.string(), z.string()), + }) + .describe( + 'Result of replacing the set of dynamically managed MCP servers.', + ), +) + +export const SDKControlReloadPluginsRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('reload_plugins'), + }) + .describe( + 'Reloads plugins from disk and returns the refreshed session components.', + ), +) + +export const SDKControlReloadPluginsResponseSchema = lazySchema(() => + z + .object({ + commands: z.array(SlashCommandSchema()), + agents: z.array(AgentInfoSchema()), + plugins: z.array( + z.object({ + name: z.string(), + path: z.string(), + source: z.string().optional(), + }), + ), + mcpServers: z.array(McpServerStatusSchema()), + error_count: z.number(), + }) + .describe( + 'Refreshed commands, agents, plugins, and MCP server status after reload.', + ), +) + +export const SDKControlMcpReconnectRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('mcp_reconnect'), + serverName: z.string(), + }) + .describe('Reconnects a disconnected or failed MCP server.'), +) + +export const SDKControlMcpToggleRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('mcp_toggle'), + serverName: z.string(), + enabled: z.boolean(), + }) + .describe('Enables or disables an MCP server.'), +) + + +export const SDKControlStopTaskRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('stop_task'), + task_id: z.string(), + }) + .describe('Stops a running task.'), +) + +export const SDKControlApplyFlagSettingsRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('apply_flag_settings'), + settings: z.record(z.string(), z.unknown()), + }) + .describe( + 'Merges the provided settings into the flag settings layer, updating the active configuration.', + ), +) + +export const SDKControlGetSettingsRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('get_settings'), + }) + .describe( + 'Returns the effective merged settings and the raw per-source settings.', + ), +) + +export const SDKControlGetSettingsResponseSchema = lazySchema(() => + z + .object({ + effective: z.record(z.string(), z.unknown()), + sources: z + .array( + z.object({ + source: z.enum([ + 'userSettings', + 'projectSettings', + 'localSettings', + 'flagSettings', + 'policySettings', + ]), + settings: z.record(z.string(), z.unknown()), + }), + ) + .describe( + 'Ordered low-to-high priority — later entries override earlier ones.', + ), + applied: z + .object({ + model: z.string(), + // String levels only — numeric effort is ant-only and the + // Zod→proto generator can't emit enum∪number unions. + effort: z.enum(['low', 'medium', 'high', 'max']).nullable(), + }) + .optional() + .describe( + 'Runtime-resolved values after env overrides, session state, and model-specific defaults are applied. Unlike `effective` (disk merge), these reflect what will actually be sent to the API.', + ), + }) + .describe( + 'Effective merged settings plus raw per-source settings in merge order.', + ), +) + +export const SDKControlElicitationRequestSchema = lazySchema(() => + z + .object({ + subtype: z.literal('elicitation'), + mcp_server_name: z.string(), + message: z.string(), + mode: z.enum(['form', 'url']).optional(), + url: z.string().optional(), + elicitation_id: z.string().optional(), + requested_schema: z.record(z.string(), z.unknown()).optional(), + }) + .describe( + 'Requests the SDK consumer to handle an MCP elicitation (user input request).', + ), +) + +export const SDKControlElicitationResponseSchema = lazySchema(() => + z + .object({ + action: z.enum(['accept', 'decline', 'cancel']), + content: z.record(z.string(), z.unknown()).optional(), + }) + .describe('Response from the SDK consumer for an elicitation request.'), +) + + +// ============================================================================ +// Control Request/Response Wrappers +// ============================================================================ + +export const SDKControlRequestInnerSchema = lazySchema(() => + z.union([ + SDKControlInterruptRequestSchema(), + SDKControlPermissionRequestSchema(), + SDKControlInitializeRequestSchema(), + SDKControlSetPermissionModeRequestSchema(), + SDKControlSetModelRequestSchema(), + SDKControlSetMaxThinkingTokensRequestSchema(), + SDKControlMcpStatusRequestSchema(), + SDKControlGetContextUsageRequestSchema(), + SDKHookCallbackRequestSchema(), + SDKControlMcpMessageRequestSchema(), + SDKControlRewindFilesRequestSchema(), + SDKControlCancelAsyncMessageRequestSchema(), + SDKControlSeedReadStateRequestSchema(), + SDKControlMcpSetServersRequestSchema(), + SDKControlReloadPluginsRequestSchema(), + SDKControlMcpReconnectRequestSchema(), + SDKControlMcpToggleRequestSchema(), + SDKControlStopTaskRequestSchema(), + SDKControlApplyFlagSettingsRequestSchema(), + SDKControlGetSettingsRequestSchema(), + SDKControlElicitationRequestSchema(), + ]), +) + +export const SDKControlRequestSchema = lazySchema(() => + z.object({ + type: z.literal('control_request'), + request_id: z.string(), + request: SDKControlRequestInnerSchema(), + }), +) + +export const ControlResponseSchema = lazySchema(() => + z.object({ + subtype: z.literal('success'), + request_id: z.string(), + response: z.record(z.string(), z.unknown()).optional(), + }), +) + +export const ControlErrorResponseSchema = lazySchema(() => + z.object({ + subtype: z.literal('error'), + request_id: z.string(), + error: z.string(), + pending_permission_requests: z + .array(z.lazy(() => SDKControlRequestSchema())) + .optional(), + }), +) + +export const SDKControlResponseSchema = lazySchema(() => + z.object({ + type: z.literal('control_response'), + response: z.union([ControlResponseSchema(), ControlErrorResponseSchema()]), + }), +) + +export const SDKControlCancelRequestSchema = lazySchema(() => + z + .object({ + type: z.literal('control_cancel_request'), + request_id: z.string(), + }) + .describe('Cancels a currently open control request.'), +) + +export const SDKKeepAliveMessageSchema = lazySchema(() => + z + .object({ + type: z.literal('keep_alive'), + }) + .describe('Keep-alive message to maintain WebSocket connection.'), +) + +export const SDKUpdateEnvironmentVariablesMessageSchema = lazySchema(() => + z + .object({ + type: z.literal('update_environment_variables'), + variables: z.record(z.string(), z.string()), + }) + .describe('Updates environment variables at runtime.'), +) + +// ============================================================================ +// Aggregate Message Types +// ============================================================================ + +export const StdoutMessageSchema = lazySchema(() => + z.union([ + SDKMessageSchema(), + SDKStreamlinedTextMessageSchema(), + SDKStreamlinedToolUseSummaryMessageSchema(), + SDKPostTurnSummaryMessageSchema(), + SDKControlResponseSchema(), + SDKControlRequestSchema(), + SDKControlCancelRequestSchema(), + SDKKeepAliveMessageSchema(), + ]), +) + +export const StdinMessageSchema = lazySchema(() => + z.union([ + SDKUserMessageSchema(), + SDKControlRequestSchema(), + SDKControlResponseSchema(), + SDKKeepAliveMessageSchema(), + SDKUpdateEnvironmentVariablesMessageSchema(), + ]), +) diff --git a/src/harness/evaluation/README.md b/src/harness/evaluation/README.md new file mode 100644 index 0000000000000000000000000000000000000000..736498f75900fa308c5d605208f362ad38bfc6d8 --- /dev/null +++ b/src/harness/evaluation/README.md @@ -0,0 +1,108 @@ +# Evaluation Harness + +This module instantiates a public task bundle into an isolated run directory, +runs one source-native Claude Code `QueryEngine` session, executes hidden judge +attempts, and writes compact JSONL trajectories for later skill extraction. + +## Source-Native Architecture + +The harness entrypoint calls `runSourceTaskLoop`, which creates exactly one `SourceClaudeSessionAgent` for the run. That session +constructs `QueryEngine` directly from the local Claude source and reuses it +across judge feedback rounds. + +Per run: + +1. `createTaskRun` copies only public task files into `runs//public`. +2. `resolveTaskRuntime` verifies the task Python before agent startup. +3. `SourceClaudeSessionAgent` builds a minimal tool pool from Claude source + tools plus the in-process `finalize_submission` tool. +4. `createHarnessCanUseTool` enforces run-local read/write policy for tool use. +5. `DefaultJudgeRunner` copies private judge assets into `.judge_private` only + when a judge attempt is triggered. + +Batch mode is source-only: repeated `--task` values are dispatched as one worker +process per run. The removed legacy runtime is rejected explicitly. + +## Loop Semantics + +`max_rounds` means maximum judge attempts, not maximum agent tool calls. A judge +attempt is triggered only when the agent calls `finalize_submission` and output +validation passes against the public output contract. Validation failures are +returned as recoverable tool feedback and do not consume a judge round. + +Natural-language messages such as "done" are ignored. If an agent turn ends +without a passing `finalize_submission`, the harness gives exactly one +same-round no-finalize recovery prompt. Recovery does not auto-validate, +auto-submit, or auto-judge; if it still ends without a passing +`finalize_submission`, the run fails or times out without a judge attempt. + +Missing `workspace/plan.md` or `workspace/plans/round_NN.md` files are recorded +as warnings, not hard blockers, when outputs are otherwise valid. + +## Task Visibility + +Agent-visible paths are limited to the current run: + +- `public/`: read-only public task bundle. +- `workspace/`: writable solver code, notes, and scratch files. +- `outputs/`: writable final submission files. +- `logs/agent/`: writable agent logs. + +Private `evaluation/`, `std_code/`, reference output, and judge data paths are +not copied into the agent-visible workspace. They are available only to the +external judge under `runs/.judge_private//`. + +The initial prompt now includes a compact public file manifest, output contract, +visible case summary, and the full public README. No `public/HARNESS_HINTS.md` +file is generated. + +## Runtime Resolution + +The harness resolves Python once before creating the Claude session. It reads +`public/envs/env_manifest.json`, chooses the platform-specific Python path, and +falls back to conventional runtime locations: + +- Windows: `public/envs/runtime/.venv/Scripts/python.exe` +- POSIX/WSL: `public/envs/runtime/.venv/bin/python` +- POSIX/WSL alternate: `public/envs/runtime/.venv-posix/bin/python` + +If no runtime exists, the run exits as `infra_error`; the agent is not asked to +repair virtual environments or install packages. + +Virtual-environment directories are linked into the run instead of copied, so a +run does not spend minutes duplicating `site-packages`. + +## Trajectories + +`SourceTrajectoryWriter` writes two append-only files: + +- `logs/trajectory.clean.jsonl`: compact training-friendly records. +- `logs/trajectory.raw.jsonl`: raw source events for debugging. + +Static context is written once as `run_context`. Judge feedback is compacted +before being sent as a continuation prompt in the same session, while the raw +judge JSON stays in `trajectory.raw.jsonl` for debugging. + +Clean trajectories also record validation pass/fail events, run warnings, and +no-finalize recovery markers. Raw trajectories preserve source events exactly; +clean trajectories filter standalone punctuation-only assistant text such as +`.` or `...`. + +## CLI + +```bash +bun src/harness/evaluation/cli.ts --task --runs-dir output/runs --max-rounds 3 --timeout-seconds 1800 --temperature 1 --thinking disabled +``` + +`--system-prompt ` is a debug-only explicit override. Normal source-native +runs use the built-in lean system contract plus the generated initial prompt; +there is no default `config/AGENT.md`. + +The default LLM options are `--thinking disabled --temperature 1`. With +thinking disabled, temperature is sent to the model. With `--thinking adaptive`, +temperature is intentionally omitted and logged as ignored in run metadata. + +`--max-turns-per-round ` caps agentic turns within one judge round. It is +optional; full implementation regressions usually leave it unset and rely on +`--timeout-seconds` as the whole-loop hard timeout. Set a cap when diagnosing +no-finalize loops or running short smoke tests. diff --git a/src/harness/evaluation/batchRunner.test.ts b/src/harness/evaluation/batchRunner.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..c81995f35c677eac33baf68e0608a5cc261e13fc --- /dev/null +++ b/src/harness/evaluation/batchRunner.test.ts @@ -0,0 +1,140 @@ +import { describe, expect, test } from 'bun:test' +import { runEvaluationBatch, type SpawnEvaluationWorker } from './batchRunner.js' + +describe('runEvaluationBatch', () => { + test('spawns one worker process per task in source batch mode', async () => { + const spawned: Array<{ command: string; args: string[]; timeoutMs?: number }> = [] + const spawnWorker: SpawnEvaluationWorker = async request => { + spawned.push({ + command: request.command, + args: request.args, + timeoutMs: request.timeoutMs, + }) + return { taskId: request.taskId, exitCode: 0 } + } + + const result = await runEvaluationBatch({ + taskIds: ['task_a', 'task_b'], + tasksDir: 'tasks', + runsDir: 'output/runs', + maxRounds: 2, + maxTurnsPerRound: 9, + timeoutSeconds: 120, + concurrency: 3, + workerTimeoutGraceSeconds: 10, + temperature: 0.2, + thinking: 'adaptive', + timestamp: '20260513_010203', + systemPromptPath: 'config/debug-prompt.md', + verbose: false, + spawnWorker, + }) + + expect(result.ok).toBe(true) + expect(spawned).toHaveLength(2) + expect(spawned[0].args).toContain('--worker-run') + expect(spawned[0].args).toContain('--task') + expect(spawned[0].args).toContain('task_a') + expect(spawned[0].args).toContain('--max-turns-per-round') + expect(spawned[0].args).toContain('9') + expect(spawned[0].args).toContain('--temperature') + expect(spawned[0].args).toContain('0.2') + expect(spawned[0].args).toContain('--thinking') + expect(spawned[0].args).toContain('adaptive') + expect(spawned[0].timeoutMs).toBe(130000) + expect(spawned[1].args).toContain('task_b') + }) + + test('runs workers as a fixed-size pipeline', async () => { + const started: string[] = [] + const resolvers = new Map void>() + const spawnWorker: SpawnEvaluationWorker = request => { + started.push(request.taskId) + return new Promise(resolve => { + resolvers.set(request.taskId, exitCode => + resolve({ taskId: request.taskId, exitCode }), + ) + }) + } + + const running = runEvaluationBatch({ + taskIds: ['a', 'b', 'c', 'd', 'e'], + tasksDir: 'tasks', + runsDir: 'output/runs', + maxRounds: 1, + timeoutSeconds: 120, + concurrency: 3, + temperature: 1, + thinking: 'disabled', + verbose: false, + spawnWorker, + }) + + await Promise.resolve() + expect(started).toEqual(['a', 'b', 'c']) + + resolvers.get('b')?.(0) + await Promise.resolve() + expect(started).toEqual(['a', 'b', 'c', 'd']) + + resolvers.get('a')?.(0) + await Promise.resolve() + expect(started).toEqual(['a', 'b', 'c', 'd', 'e']) + + for (const taskId of ['c', 'd', 'e']) { + resolvers.get(taskId)?.(0) + } + const result = await running + expect(result.ok).toBe(true) + expect(result.workers.map(worker => worker.taskId)).toEqual(['a', 'b', 'c', 'd', 'e']) + }) + + test('continues launching queued workers after a worker fails', async () => { + const started: string[] = [] + const spawnWorker: SpawnEvaluationWorker = async request => { + started.push(request.taskId) + return { taskId: request.taskId, exitCode: request.taskId === 'a' ? 1 : 0 } + } + + const result = await runEvaluationBatch({ + taskIds: ['a', 'b', 'c'], + tasksDir: 'tasks', + runsDir: 'output/runs', + maxRounds: 1, + timeoutSeconds: 120, + concurrency: 1, + temperature: 1, + thinking: 'disabled', + verbose: false, + spawnWorker, + }) + + expect(started).toEqual(['a', 'b', 'c']) + expect(result.ok).toBe(false) + expect(result.workers.map(worker => worker.exitCode)).toEqual([1, 0, 0]) + }) + + test('passes worker watchdog timeout to spawned workers', async () => { + const timeouts: Array = [] + const spawnWorker: SpawnEvaluationWorker = async request => { + timeouts.push(request.timeoutMs) + return { taskId: request.taskId, exitCode: 0 } + } + + await runEvaluationBatch({ + taskIds: ['task_a'], + tasksDir: 'tasks', + runsDir: 'output/runs', + maxRounds: 1, + timeoutSeconds: 10, + workerTimeoutGraceSeconds: 7, + concurrency: 3, + temperature: 1, + thinking: 'disabled', + verbose: false, + spawnWorker, + }) + + expect(timeouts).toEqual([17000]) + }) +}) diff --git a/src/harness/evaluation/batchRunner.ts b/src/harness/evaluation/batchRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..28e1ea1c12be18e4b3901708cfadfb3ce70712e2 --- /dev/null +++ b/src/harness/evaluation/batchRunner.ts @@ -0,0 +1,148 @@ +export type EvaluationWorkerRequest = { + taskId: string + command: string + args: string[] + timeoutMs?: number +} + +export type EvaluationWorkerResult = { + taskId: string + exitCode: number +} + +export type SpawnEvaluationWorker = ( + request: EvaluationWorkerRequest, +) => Promise + +export type RunEvaluationBatchInput = { + taskIds: string[] + tasksDir: string + runsDir: string + maxRounds: number + maxTurnsPerRound?: number + timeoutSeconds: number + temperature: number + thinking: 'disabled' | 'adaptive' + systemPromptPath?: string + timestamp?: string + concurrency?: number + workerTimeoutGraceSeconds?: number + verbose: boolean + spawnWorker?: SpawnEvaluationWorker +} + +export type RunEvaluationBatchResult = { + ok: boolean + workers: EvaluationWorkerResult[] +} + +function pushOption(args: string[], name: string, value: string | number | undefined): void { + if (value === undefined) return + args.push(name, String(value)) +} + +function workerArgs(input: RunEvaluationBatchInput, taskId: string): string[] { + const args = [ + 'src/harness/evaluation/cli.ts', + '--worker-run', + '--agent-runtime', + 'source', + '--task', + taskId, + ] + pushOption(args, '--tasks-dir', input.tasksDir) + pushOption(args, '--runs-dir', input.runsDir) + pushOption(args, '--max-rounds', input.maxRounds) + pushOption(args, '--max-turns-per-round', input.maxTurnsPerRound) + pushOption(args, '--timeout-seconds', input.timeoutSeconds) + pushOption(args, '--temperature', input.temperature) + pushOption(args, '--thinking', input.thinking) + pushOption(args, '--system-prompt', input.systemPromptPath) + pushOption(args, '--timestamp', input.timestamp) + if (!input.verbose) args.push('--quiet') + return args +} + +const defaultSpawnWorker: SpawnEvaluationWorker = request => { + const child = Bun.spawn([request.command, ...request.args], { + stdout: 'inherit', + stderr: 'inherit', + env: process.env, + }) + const exited = child.exited.then(exitCode => ({ + taskId: request.taskId, + exitCode, + })) + if (!request.timeoutMs) return exited + + let timedOut = false + let timeoutTimer: ReturnType | undefined + let killTimer: ReturnType | undefined + const timeout = new Promise(resolve => { + timeoutTimer = setTimeout(() => { + timedOut = true + child.kill('SIGTERM') + killTimer = setTimeout(() => child.kill('SIGKILL'), 5000) + killTimer.unref?.() + resolve({ taskId: request.taskId, exitCode: 124 }) + }, request.timeoutMs) + timeoutTimer.unref?.() + }) + + void child.exited.finally(() => { + if (killTimer) clearTimeout(killTimer) + }) + + return Promise.race([exited, timeout]).finally(() => { + if (timeoutTimer) clearTimeout(timeoutTimer) + if (!timedOut && killTimer) clearTimeout(killTimer) + }) +} + +export async function runEvaluationBatch( + input: RunEvaluationBatchInput, +): Promise { + const spawnWorker = input.spawnWorker ?? defaultSpawnWorker + const command = process.execPath + const concurrency = Math.max(1, input.concurrency ?? 3) + const timeoutMs = + (input.timeoutSeconds + (input.workerTimeoutGraceSeconds ?? 60)) * 1000 + const workers: Array = new Array( + input.taskIds.length, + ) + let nextIndex = 0 + + async function runLane(): Promise { + for (;;) { + const index = nextIndex + nextIndex++ + if (index >= input.taskIds.length) return + + const taskId = input.taskIds[index] + try { + workers[index] = await spawnWorker({ + taskId, + command, + args: workerArgs(input, taskId), + timeoutMs, + }) + } catch { + workers[index] = { taskId, exitCode: 1 } + } + } + } + + const laneCount = Math.min(concurrency, input.taskIds.length) + await Promise.all(Array.from({ length: laneCount }, () => runLane())) + + const completedWorkers = workers.map((worker, index) => + worker ?? { + taskId: input.taskIds[index], + exitCode: 1, + }, + ) + return { + ok: completedWorkers.every(worker => worker.exitCode === 0), + workers: completedWorkers, + } +} diff --git a/src/harness/evaluation/biomnibenchAdapter.ts b/src/harness/evaluation/biomnibenchAdapter.ts new file mode 100644 index 0000000000000000000000000000000000000000..cfa7af77addf5b268498331a4f7c7b5cb343451a --- /dev/null +++ b/src/harness/evaluation/biomnibenchAdapter.ts @@ -0,0 +1,120 @@ +/** + * biomnibenchAdapter.ts + * + * Adapter for BioMniBench-organized tasks (Docker-based data analysis tasks + * with `da-X-Y/` structure). These tasks differ from the imaging task format + * that `my_claude` was originally designed for: + * + * Imaging tasks (original): BioMniBench tasks (this adapter): + * - envs/env_manifest.json - envs/Dockerfile (no manifest) + * - envs/runtime/.venv/bin/python - shared venv at /shared_venv + * - output_schema.json - README.md (free-form output) + * - visible_data/cases.json - visible_data/ (raw data files) + * - evaluation/judge.py - evaluation/llm_judge.py + rubric.txt + * - submission as outputs/*.npz - submission as trace.md + answer.txt + * - full task_manifest.json - minimal task_manifest.json (only task_id) + * + * Detection is conservative: a task is treated as BioMniBench when the task + * directory contains BOTH `envs/Dockerfile` and `evaluation/rubric.txt`. Tasks + * that look like imaging tasks (env_manifest.json present) always take the + * legacy code path. + */ + +import { existsSync } from 'fs' +import { readFile } from 'fs/promises' +import { join, resolve, dirname } from 'path' +import { fileURLToPath } from 'url' +import type { TaskManifest } from './types.js' + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +/** Repository root for my_claude_biomnibench (3 levels above this file). */ +const REPO_ROOT = resolve(__dirname, '..', '..', '..') + +/** Default shared Python venv (conda env via symlink) used by all biomnibench tasks. */ +export const SHARED_VENV_DIR = resolve(REPO_ROOT, 'shared_venv') + +/** Path to the qwen-backed LLM judge script bundled with the harness. */ +export const QWEN_JUDGE_SCRIPT = resolve(REPO_ROOT, 'llm_judge_qwen.py') + +/** Heuristic detection of a BioMniBench-organized task directory. */ +export function isBioMniBenchTask(taskDir: string): boolean { + const dockerfile = join(taskDir, 'envs', 'Dockerfile') + const rubric = join(taskDir, 'evaluation', 'rubric.txt') + const envManifest = join(taskDir, 'envs', 'env_manifest.json') + // If env_manifest.json is present, treat as imaging task (legacy path). + if (existsSync(envManifest)) return false + return existsSync(dockerfile) && existsSync(rubric) +} + +/** Resolve the Python interpreter to use for a BioMniBench task. */ +export function resolveSharedPython(): string | null { + const candidates = [ + join(SHARED_VENV_DIR, 'bin', 'python'), + join(SHARED_VENV_DIR, 'bin', 'python3'), + ] + for (const candidate of candidates) { + if (existsSync(candidate)) return candidate + } + return null +} + +/** + * Construct an in-memory `TaskManifest` for a BioMniBench task. The on-disk + * `task_manifest.json` only contains the task id; this synthesises the fields + * the rest of the harness depends on. + */ +export function buildBioMniBenchManifest(taskId: string): TaskManifest { + return { + version: 1, + task_id: taskId, + public_bundle: [ + 'README.md', + 'data', + 'visible_data', + ], + private_judge_bundle: ['evaluation/'], + entrypoints: { + judge: 'evaluation/llm_judge.py', + }, + submission: { + output_dir: 'outputs', + }, + } +} + +/** Read and merge an on-disk task_manifest.json with the synthesized one. */ +export async function loadBioMniBenchManifest(taskDir: string, taskId: string): Promise { + const synthesised = buildBioMniBenchManifest(taskId) + const manifestPath = join(taskDir, 'task_manifest.json') + if (!existsSync(manifestPath)) return synthesised + try { + const raw = await readFile(manifestPath, 'utf8') + const onDisk = JSON.parse(raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw) as Partial + return { + ...synthesised, + ...onDisk, + task_id: taskId, + // Always keep the synthesised entrypoints/submission unless caller already set them. + entrypoints: { ...synthesised.entrypoints, ...(onDisk.entrypoints ?? {}) }, + submission: { ...synthesised.submission, ...(onDisk.submission ?? {}) }, + public_bundle: onDisk.public_bundle ?? synthesised.public_bundle, + private_judge_bundle: onDisk.private_judge_bundle ?? synthesised.private_judge_bundle, + } + } catch { + return synthesised + } +} + +/** Build the initial agent prompt for a BioMniBench task using the README. */ +export async function buildBioMniBenchSystemContext(taskDir: string): Promise { + const readmePath = join(taskDir, 'README.md') + let readme = '' + try { + readme = await readFile(readmePath, 'utf8') + } catch { + readme = '(no README available)' + } + return readme +} diff --git a/src/harness/evaluation/cli.test.ts b/src/harness/evaluation/cli.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ea876cb6b31fad5cd098444216ee6256218e75e --- /dev/null +++ b/src/harness/evaluation/cli.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, test } from 'bun:test' +import { exitCodeForLoopStatus, parseEvaluationCliArgs } from './cli.js' + +describe('parseEvaluationCliArgs', () => { + test('requires task id and parses loop controls', () => { + const parsed = parseEvaluationCliArgs([ + '--task', + 'demo_task', + '--max-rounds', + '4', + '--timeout-seconds', + '120', + '--concurrency', + '3', + '--worker-timeout-grace-seconds', + '15', + '--max-turns-per-round', + '9', + '--agent-runtime', + 'source', + '--runs-dir', + 'output/runs', + ]) + + expect(parsed.taskId).toBe('demo_task') + expect(parsed.maxRounds).toBe(4) + expect(parsed.maxTurnsPerRound).toBe(9) + expect(parsed.timeoutSeconds).toBe(120) + expect(parsed.concurrency).toBe(3) + expect(parsed.workerTimeoutGraceSeconds).toBe(15) + expect(parsed.runsDir).toBe('output/runs') + expect(parsed.agentRuntime).toBe('source') + expect(parsed.temperature).toBe(1) + expect(parsed.thinking).toBe('disabled') + expect(parsed.taskIds).toEqual(['demo_task']) + expect(parsed.systemPromptPath).toBeUndefined() + }) + + test('parses repeated tasks for source batch mode', () => { + const parsed = parseEvaluationCliArgs([ + '--task', + 'task_a,task_b', + '--task', + 'task_c', + '--quiet', + ]) + + expect(parsed.taskId).toBe('task_a') + expect(parsed.taskIds).toEqual(['task_a', 'task_b', 'task_c']) + expect(parsed.maxTurnsPerRound).toBeUndefined() + expect(parsed.verbose).toBe(false) + }) + + test('rejects removed legacy subprocess runtime', () => { + expect(() => + parseEvaluationCliArgs(['--task', 'demo_task', '--agent-runtime', 'legacy-subprocess']), + ).toThrow('legacy-subprocess has been removed') + }) + + test('parses temperature and thinking controls', () => { + const parsed = parseEvaluationCliArgs([ + '--task', + 'demo_task', + '--temperature', + '0.2', + '--thinking', + 'adaptive', + ]) + + expect(parsed.temperature).toBe(0.2) + expect(parsed.thinking).toBe('adaptive') + }) + + test('rejects invalid temperature and thinking values', () => { + expect(() => + parseEvaluationCliArgs(['--task', 'demo_task', '--temperature', 'hot']), + ).toThrow('--temperature') + expect(() => + parseEvaluationCliArgs(['--task', 'demo_task', '--temperature', '2']), + ).toThrow('--temperature') + expect(() => + parseEvaluationCliArgs(['--task', 'demo_task', '--thinking', 'enabled']), + ).toThrow('--thinking') + }) + + test('maps loop status to process exit code', () => { + expect(exitCodeForLoopStatus('success')).toBe(0) + expect(exitCodeForLoopStatus('failed')).toBe(1) + expect(exitCodeForLoopStatus('timeout')).toBe(1) + expect(exitCodeForLoopStatus('infra_error')).toBe(1) + }) +}) diff --git a/src/harness/evaluation/runEventLogger.ts b/src/harness/evaluation/runEventLogger.ts new file mode 100644 index 0000000000000000000000000000000000000000..62bfdf97e85074083e28ea89f700f1c373a898b9 --- /dev/null +++ b/src/harness/evaluation/runEventLogger.ts @@ -0,0 +1,139 @@ +import { appendFile, mkdir } from 'fs/promises' +import { join } from 'path' +import type { SourceAgentEvent, TaskRun } from './types.js' + +export type RunEventType = + | 'run_started' + | 'agent_step_started' + | 'agent_step_finished' + | 'agent_step_error' + | 'agent_recovery_started' + | 'agent_recovery_finished' + | 'agent_event' + | 'submission_validation_failed' + | 'submission_validation_passed' + | 'run_warning' + | 'judge_started' + | 'judge_finished' + | 'run_finished' + +export type RunEvent = { + timestamp: string + run_id: string + task_id: string + type: RunEventType + judge_round?: number + agent_step?: number + message?: string + details?: unknown +} + +export type RunEventLoggerInput = { + taskRun: TaskRun + verbose?: boolean +} + +function shorten(value: string, maxLength = 240): string { + return value.length <= maxLength ? value : `${value.slice(0, maxLength)}...` +} + +export function summarizeAgentEvent(event: SourceAgentEvent): Record { + if (event.type === 'assistant_text') { + return { type: event.type, text_length: event.text.length } + } + if (event.type === 'tool_call') { + return { type: event.type, tool: event.tool, toolUseId: event.toolUseId } + } + if (event.type === 'tool_result') { + return { + type: event.type, + ok: event.ok, + toolUseId: event.toolUseId, + text_tail: event.text?.slice(-1000), + } + } + if (event.type === 'policy_deny') { + return { type: event.type, tool: event.tool, reason: event.reason } + } + if (event.type === 'trajectory_warning') { + return { + type: event.type, + code: event.code, + message: event.message, + details: event.details, + } + } + if (event.type === 'run_warning') { + return { + type: event.type, + code: event.code, + message: event.message, + details: event.details, + } + } + if (event.type === 'submission_validation_failed') { + return { + type: event.type, + ok: event.result.ok, + issue_count: event.result.issues.length, + normalized_files: event.result.normalizedFiles, + issues: event.result.issues, + } + } + if (event.type === 'submission_validation_passed') { + return { + type: event.type, + ok: event.result.ok, + normalized_files: event.result.normalizedFiles, + } + } + if (event.type === 'agent_result') { + return { + type: event.type, + subtype: event.subtype, + stopReason: event.stopReason, + isError: event.isError, + durationMs: event.durationMs, + errors: event.errors, + } + } + return { type: event.type, summary: event.summary, files: event.files } +} + +function formatConsoleEvent(event: RunEvent): string { + const prefix = `[${event.timestamp}] ${event.type}` + const round = event.judge_round ? ` round=${event.judge_round}` : '' + const step = event.agent_step ? ` step=${event.agent_step}` : '' + const message = event.message ? ` ${shorten(event.message)}` : '' + return `${prefix}${round}${step}${message}` +} + +export class RunEventLogger { + readonly path: string + private readonly taskRun: TaskRun + private readonly verbose: boolean + + constructor(input: RunEventLoggerInput) { + this.taskRun = input.taskRun + this.verbose = Boolean(input.verbose) + this.path = join(input.taskRun.logsDir, 'run_events.jsonl') + } + + async log( + type: RunEventType, + event: Omit, 'timestamp' | 'run_id' | 'task_id' | 'type'> = {}, + ): Promise { + const record: RunEvent = { + timestamp: new Date().toISOString(), + run_id: this.taskRun.runId, + task_id: this.taskRun.taskId, + type, + ...event, + } + await mkdir(this.taskRun.logsDir, { recursive: true }) + await appendFile(this.path, `${JSON.stringify(record)}\n`, 'utf8') + if (this.verbose) { + process.stderr.write(`${formatConsoleEvent(record)}\n`) + } + } +} diff --git a/src/harness/evaluation/sourceContextBuilder.ts b/src/harness/evaluation/sourceContextBuilder.ts new file mode 100644 index 0000000000000000000000000000000000000000..30905c7f84f823bfa0a76b801b8b2a9030eb56b5 --- /dev/null +++ b/src/harness/evaluation/sourceContextBuilder.ts @@ -0,0 +1,466 @@ +import { readdir, readFile } from 'fs/promises' +import { join, relative } from 'path' +import type { + JudgeResult, + PriorSubtaskContext, + RuntimeInfo, + TaskRun, +} from './types.js' +import { isBioMniBenchTask } from './biomnibenchAdapter.js' + +type OutputSchema = { + submission?: { + path_template?: string + } + path_template?: string + arrays?: Array<{ + key?: string + shape?: unknown + dtype?: unknown + }> + validation?: { + finite_only?: boolean + } +} + +type CasesConfig = { + cases?: Array<{ + id?: string + input_dir?: string + params?: string + expected_output?: string + description?: string + }> +} + +type CompactJudgeFeedback = { + format: string + reasons: string[] + failedMetrics: string[] + passedMetrics: string[] + message?: string +} + +function stripUtf8Bom(value: string): string { + return value.charCodeAt(0) === 0xfeff ? value.slice(1) : value +} + +async function readJsonIfExists(path: string): Promise { + try { + return JSON.parse(stripUtf8Bom(await readFile(path, 'utf8'))) as T + } catch { + return undefined + } +} + +function roundPlanPath(round: number): string { + return `workspace/plans/round_${String(round).padStart(2, '0')}.md` +} + +function shouldSkipPublicPath(relativePath: string): boolean { + const parts = relativePath.replace(/\\/g, '/').split('/') + return parts.includes('.venv') || parts.includes('.venv-posix') +} + +async function collectPublicFiles( + publicDir: string, + currentDir = publicDir, +): Promise { + const entries = await readdir(currentDir, { withFileTypes: true }) + const files: string[] = [] + for (const entry of entries) { + const absolutePath = join(currentDir, entry.name) + const relativePath = relative(publicDir, absolutePath).replace(/\\/g, '/') + if (shouldSkipPublicPath(relativePath)) continue + if (entry.isDirectory()) { + files.push(...(await collectPublicFiles(publicDir, absolutePath))) + continue + } + if (entry.isFile()) files.push(relativePath) + } + return files.sort((a, b) => a.localeCompare(b)) +} + +function formatPublicFiles(files: string[]): string { + if (files.length === 0) return '(No public files found.)' + return files.map(file => `- ${file}`).join('\n') +} + +function formatShape(shape: unknown): string { + return Array.isArray(shape) ? `[${shape.join(',')}]` : 'unspecified' +} + +function formatDtype(dtype: unknown): string { + if (Array.isArray(dtype)) return dtype.map(String).join('|') + if (typeof dtype === 'string') return dtype + return 'unspecified' +} + +function buildOutputContract(schema: OutputSchema | undefined): string { + if (!schema) { + return 'Output schema summary unavailable. Read public/output_schema.json if needed.' + } + const pattern = + schema.submission?.path_template ?? schema.path_template ?? 'outputs/{case_id}.npz' + const lines = [`Required file pattern: ${pattern}`, 'Required arrays:'] + const arrays = schema.arrays ?? [] + if (arrays.length === 0) { + lines.push('- (No array requirements found in output_schema.json.)') + } else { + const finite = schema.validation?.finite_only === false ? '' : ', finite' + for (const item of arrays) { + lines.push( + `- ${item.key ?? '(unnamed)'}: shape ${formatShape(item.shape)}, dtype ${formatDtype(item.dtype)}${finite}`, + ) + } + } + return lines.join('\n') +} + +function buildVisibleCases(cases: CasesConfig | undefined): string { + const items = cases?.cases ?? [] + if (items.length === 0) return '(No visible cases listed.)' + return items + .map(item => + [ + `- ${item.id ?? '(unknown_case)'}:`, + item.input_dir ? ` input_dir: ${item.input_dir}` : undefined, + item.params ? ` params: ${item.params}` : undefined, + item.expected_output ? ` expected_output: ${item.expected_output}` : undefined, + item.description ? ` description: ${item.description}` : undefined, + ] + .filter(Boolean) + .join('\n'), + ) + .join('\n') +} + +/** Maximum characters of generated code we inline per prior sub-task. */ +const PRIOR_CODE_CHAR_LIMIT = 6000 + +/** Maximum characters of description we inline per prior sub-task. */ +const PRIOR_DESCRIPTION_CHAR_LIMIT = 1200 + +function truncateForPrompt(value: string, limit: number, label: string): string { + if (!value) return '' + if (value.length <= limit) return value + const head = value.slice(0, limit) + return `${head}\n... [${label} truncated: ${value.length - limit} more chars elided]` +} + +/** + * Render the prior sub-task block that gets prepended to each new sub-task's + * initial prompt in true-serial mode. Returns an empty string when no prior + * sub-tasks are supplied so the prompt stays identical to single-task mode. + */ +function formatPriorSubtasks(priorSubtasks: PriorSubtaskContext[] | undefined): string { + if (!priorSubtasks || priorSubtasks.length === 0) return '' + const blocks: string[] = [ + '', + 'You are continuing a serial multi-sub-task study. Earlier sub-tasks from the', + 'same parent paper have already been solved by you in a separate evaluation', + 'session. Their final solver code and outcomes are provided below so you can:', + ' - reuse data-loading patterns, column names, and preprocessing logic you', + ' already established, instead of re-deriving them;', + ' - avoid repeating mistakes that previously failed judge;', + ' - keep results consistent across sub-tasks (e.g. same filtering thresholds,', + ' same train/test splits, same statistical conventions).', + 'Each sub-task is independently judged - do NOT assume their outputs/ files', + 'are available to your current run. Always re-read the data files yourself.', + '', + ] + for (const prior of priorSubtasks) { + blocks.push('') + blocks.push(`task_id: ${prior.taskId}`) + if (typeof prior.taskIdx === 'number') { + blocks.push(`task_idx: ${prior.taskIdx}`) + } + if (prior.status) { + blocks.push(`status: ${prior.status}`) + } + if (typeof prior.passed === 'boolean') { + blocks.push(`judge_passed: ${prior.passed}`) + } + if (prior.description) { + blocks.push('') + blocks.push( + truncateForPrompt( + prior.description.trim(), + PRIOR_DESCRIPTION_CHAR_LIMIT, + 'description', + ), + ) + blocks.push('') + } + if (prior.generatedCode) { + blocks.push(``) + blocks.push( + truncateForPrompt( + prior.generatedCode, + PRIOR_CODE_CHAR_LIMIT, + 'generated_code', + ), + ) + blocks.push('') + } + if (prior.judgeFeedback) { + blocks.push('') + blocks.push(prior.judgeFeedback.trim()) + blocks.push('') + } + if (prior.notes) { + blocks.push('') + blocks.push(prior.notes.trim()) + blocks.push('') + } + blocks.push('') + blocks.push('') + } + blocks.push('') + return blocks.join('\n') +} + +export function buildSourceSystemPrompt(extra?: string): string { + return [ + 'You are an autonomous solver inside a source-native evaluation harness.', + '', + 'Goal: produce valid final submission files for the current public task.', + '', + 'Use only run-local paths:', + '- public/: read-only public task data.', + '- workspace/: writable scratch, plans, and solver code.', + '- outputs/: writable final submission files.', + '- logs/agent/: optional notes.', + '', + 'Do not access private judge data, original task directories, other runs, parent directories, home directories, or system secrets.', + '', + 'Use the provided Python runtime. Do not install packages or create/repair environments.', + '', + 'Planning discipline:', + '- You may inspect necessary public files before planning.', + '- For each judge round, once you understand the task and any judge feedback, write a detailed, task-specific plan before implementation, long computation, or submission.', + '- Write the round plan to workspace/plans/round_NN.md and copy the current round plan to workspace/plan.md.', + '- The plan should be as detailed as needed: include task understanding, public data observations, assumptions, algorithm strategy, validation strategy, risks, and concrete execution steps.', + '- Avoid generic filler. Every plan section should contain information specific to this task or this judge feedback.', + '- Do not use TodoWrite; plans must be file artifacts.', + '', + 'Round closure discipline:', + '- Judge feedback is more valuable than private speculation. Prefer submitting a valid best attempt over running open-ended experiments.', + '- In each judge round, run only a small set of focused experiments, then select the best solver, regenerate outputs/, validate the file format, and call finalize_submission.', + '- If you believe outputs/ already contains files matching the output contract, submit the best available valid output instead of continuing unbounded exploration.', + '', + 'Experiment discipline:', + '- Put non-trivial Python experiments in workspace/experiments/*.py or workspace/*.py.', + '- Do not put long Python programs in Bash python -c; Bash should run scripts or short validation commands.', + '', + 'When final output files exist under outputs/, call finalize_submission. A text answer is not a submission.', + extra?.trim(), + ] + .filter(Boolean) + .join('\n') +} + +export async function buildInitialSourcePrompt(input: { + taskRun: TaskRun + runtime: RuntimeInfo + userTask: string + maxRounds: number + priorSubtasks?: PriorSubtaskContext[] +}): Promise { + const { taskRun, runtime, userTask, maxRounds, priorSubtasks } = input + const priorBlock = formatPriorSubtasks(priorSubtasks) + + // BioMniBench tasks have free-form output (trace.md + answer.txt) instead + // of array-based submissions; use a tailored prompt that omits schema/cases. + if (isBioMniBenchTask(taskRun.taskDir)) { + const publicFiles = await collectPublicFiles(taskRun.publicDir) + return [ + '', + `task_id: ${taskRun.taskId}`, + 'cwd: run root', + `python: ${runtime.displayPath}`, + `max_judge_rounds: ${maxRounds}`, + 'submission_dir: outputs/', + 'current_plan_file: workspace/plan.md', + `round_plan_file: ${roundPlanPath(1)}`, + '', + '', + '', + formatPublicFiles(publicFiles), + '', + ...(priorBlock ? ['', priorBlock] : []), + '', + '', + userTask.trim() || '(No README.md content was found; inspect public/.)', + '', + '', + '', + 'This is a BioMniBench data analysis task. The judge is an LLM that scores', + 'your work against a rubric. You MUST produce exactly two files:', + ' - outputs/trace.md Detailed analysis trace: code you ran, intermediate', + ' results, plots described in text, reasoning.', + ' - outputs/answer.txt Concise final answer to the task question.', + 'Both files are evaluated by an LLM judge against a hidden rubric. Be specific,', + 'show numerical results, and reference the data you actually computed.', + '', + '', + '', + 'No discrete cases. Data files are in public/data/ and public/visible_data/.', + 'Read public/README.md for the full task description and data dictionary.', + '', + '', + '', + '1. Read public/README.md carefully for the task question, data files, and any constraints.', + '2. Inspect public/data/ and public/visible_data/ to understand the actual file formats.', + '3. Write a detailed task-specific plan to ' + roundPlanPath(1) + ' and refresh workspace/plan.md.', + '4. Write Python solver code under workspace/ or workspace/experiments/.', + '5. IMPORTANT: cwd is run root. Use public/data/ etc., NOT ../public/data/.', + '6. The shared Python env already has pandas, numpy, scipy, scanpy, anndata, sklearn, statsmodels, matplotlib, seaborn, openpyxl. Do NOT install packages.', + '7. After computing your answer, write outputs/trace.md (analysis trace) and outputs/answer.txt (final answer).', + '8. Call finalize_submission. The judge will score against a rubric.', + '', + ].join('\n') + } + + const [publicFiles, schema, cases] = await Promise.all([ + collectPublicFiles(taskRun.publicDir), + readJsonIfExists(join(taskRun.publicDir, 'output_schema.json')), + readJsonIfExists(join(taskRun.publicDir, 'visible_data', 'cases.json')), + ]) + + return [ + '', + `task_id: ${taskRun.taskId}`, + 'cwd: run root', + `python: ${runtime.displayPath}`, + `max_judge_rounds: ${maxRounds}`, + 'submission_dir: outputs/', + 'current_plan_file: workspace/plan.md', + `round_plan_file: ${roundPlanPath(1)}`, + '', + '', + '', + formatPublicFiles(publicFiles), + '', + ...(priorBlock ? ['', priorBlock] : []), + '', + '', + userTask.trim() || '(No README.md content was found; inspect public/.)', + '', + '', + '', + buildOutputContract(schema), + '', + '', + '', + buildVisibleCases(cases), + '', + '', + '', + '1. Use and as the submission contract. Inspect relevant public case input files for raw keys, shapes, dtypes, finite status, and value ranges before choosing a solver; do not assume array structure.', + `2. Once you understand the task, write ${roundPlanPath(1)} and refresh workspace/plan.md.`, + '3. Write solver code under workspace/ and longer experiments under workspace/experiments/.', + '4. IMPORTANT: Your cwd is run root. Always use paths relative to run root. Data files are at public/workdir/, NOT ../public/workdir/. When writing code in workspace/, use public/workdir/ for data paths.', + '5. Use Bash for short commands or to run scripts; do not use long inline python -c programs.', + '6. Run a bounded set of focused experiments, then choose the best current solver.', + '7. Write final submission files under outputs/ and run a quick local format check against .', + '8. Call finalize_submission. The judge feedback is more valuable than private speculation.', + '', + ].join('\n') +} + +function unique(values: string[]): string[] { + return [...new Set(values.filter(Boolean))] +} + +export function compactJudgeFeedback(judgeResult: JudgeResult): CompactJudgeFeedback { + const raw = judgeResult.raw as + | { + cases?: Array<{ + reason?: unknown + format?: { status?: unknown } + metrics?: Array<{ name?: unknown; status?: unknown }> + }> + } + | undefined + const cases = raw && typeof raw === 'object' && Array.isArray(raw.cases) ? raw.cases : [] + if (cases.length === 0) { + return { + format: 'unknown', + reasons: [], + failedMetrics: [], + passedMetrics: [], + message: judgeResult.feedback || '(no feedback)', + } + } + + const formatStatuses = unique( + cases.map(item => String(item.format?.status ?? 'unknown')), + ) + const reasons = unique(cases.map(item => String(item.reason ?? '')).filter(Boolean)) + const metrics = cases.flatMap(item => item.metrics ?? []) + return { + format: formatStatuses.length === 1 ? formatStatuses[0] : formatStatuses.join('|'), + reasons, + failedMetrics: unique( + metrics + .filter(item => item.status === 'fail') + .map(item => String(item.name ?? '(unnamed_metric)')), + ), + passedMetrics: unique( + metrics + .filter(item => item.status === 'pass') + .map(item => String(item.name ?? '(unnamed_metric)')), + ), + } +} + +function formatList(label: string, values: string[]): string[] { + if (values.length === 0) return [`${label}: []`] + return [`${label}:`, ...values.map(value => `- ${value}`)] +} + +export function buildJudgeFeedbackPrompt(input: { + round: number + maxRounds: number + judgeResult: JudgeResult +}): string { + const { round, maxRounds, judgeResult } = input + const nextRound = round + 1 + const compact = compactJudgeFeedback(judgeResult) + return [ + '', + `round: ${round}/${maxRounds}`, + `status: ${judgeResult.status}`, + `reward: ${judgeResult.reward}`, + `format: ${compact.format}`, + `reason: ${compact.reasons.join('|') || 'none'}`, + ...formatList('failed_metrics', compact.failedMetrics), + ...formatList('passed_metrics', compact.passedMetrics), + ...(compact.message ? [`message: ${compact.message}`] : []), + '', + '', + '', + `Before modifying code for this feedback, understand the feedback, then write ${roundPlanPath(nextRound)} and refresh workspace/plan.md.`, + 'Make one focused fix or a small bounded experiment set, validate again, regenerate outputs, then submit the best current output to the judge with finalize_submission.', + 'Before finalize_submission, revalidate outputs against the same contract.', + 'Use workspace/experiments/*.py for longer probes; do not put long Python programs in Bash python -c.', + '', + ].join('\n') +} + +export function buildNoFinalizeRecoveryPrompt(input: { + round: number + maxRounds: number +}): string { + return [ + '', + `round: ${input.round}/${input.maxRounds}`, + 'Your previous turn ended without finalize_submission.', + 'Do not start new open-ended research or long experiments.', + 'If you believe outputs/ contains valid final files, call finalize_submission now with a concise summary.', + 'If outputs/ is missing or invalid, make only the shortest necessary format fix, then call finalize_submission.', + 'If you cannot create a valid output, briefly explain the blocker and stop.', + '', + ].join('\n') +} diff --git a/src/harness/evaluation/sourceLlmOptions.ts b/src/harness/evaluation/sourceLlmOptions.ts new file mode 100644 index 0000000000000000000000000000000000000000..a8e060c2fa3304d926d3c6724a24433af01ca4bc --- /dev/null +++ b/src/harness/evaluation/sourceLlmOptions.ts @@ -0,0 +1,23 @@ +import type { ThinkingConfig } from '../../utils/thinking.js' +import type { EvaluationLlmOptions } from './types.js' + +export type SourceLlmQueryOptions = { + thinkingConfig: ThinkingConfig + temperatureOverride?: number +} + +export function buildSourceLlmQueryOptions( + options: EvaluationLlmOptions | undefined, +): SourceLlmQueryOptions { + const llmOptions = options ?? { temperature: 1, thinking: 'disabled' as const } + if (llmOptions.thinking === 'adaptive') { + return { + thinkingConfig: { type: 'adaptive' }, + temperatureOverride: undefined, + } + } + return { + thinkingConfig: { type: 'disabled' }, + temperatureOverride: llmOptions.temperature, + } +} diff --git a/src/harness/evaluation/sourcePublicIntegrity.ts b/src/harness/evaluation/sourcePublicIntegrity.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f5c2483152d60c4b334b0e08d13924d7e5bb10b --- /dev/null +++ b/src/harness/evaluation/sourcePublicIntegrity.ts @@ -0,0 +1,203 @@ +import { lstat, mkdir, readFile, readdir, rm, stat, writeFile } from 'fs/promises' +import { dirname, join, relative, resolve } from 'path' + +export type PublicSnapshotEntry = { + entry_type: 'file' | 'dir' + size: number + mtimeMs: number + content?: Buffer +} + +export type PublicSnapshot = Map + +export type PublicSnapshotOptions = { + includeFileContents?: boolean + contentPathFilter?: (relativePath: string) => boolean +} + +export type PublicMutation = { + type: 'created' | 'modified' | 'deleted' + path: string + entry_type: 'file' | 'dir' +} + +export type PublicRestoreResult = { + removedCreatedPaths: string[] + restoredPaths: string[] + unrestoredPaths: string[] +} + +function toPublicRelative(root: string, path: string): string { + return relative(root, path).replace(/\\/g, '/') +} + +async function walkPublic( + root: string, + current: string, + snapshot: PublicSnapshot, + options: PublicSnapshotOptions, +): Promise { + const entries = await readdir(current, { withFileTypes: true }) + for (const entry of entries) { + const fullPath = join(current, entry.name) + const linkMetadata = await lstat(fullPath) + const isSymlink = linkMetadata.isSymbolicLink() + let metadata = linkMetadata + if (isSymlink) { + try { + metadata = await stat(fullPath) + } catch { + metadata = linkMetadata + } + } + const isDirectory = metadata.isDirectory() + const relativePath = toPublicRelative(root, fullPath) + const snapshotEntry: PublicSnapshotEntry = { + entry_type: isDirectory ? 'dir' : 'file', + size: linkMetadata.size, + mtimeMs: linkMetadata.mtimeMs, + } + if ( + !isDirectory && + !isSymlink && + options.includeFileContents && + (!options.contentPathFilter || options.contentPathFilter(relativePath)) + ) { + snapshotEntry.content = await readFile(fullPath) + } + snapshot.set(relativePath, snapshotEntry) + if (isDirectory && !isSymlink) { + await walkPublic(root, fullPath, snapshot, options) + } + } +} + +export async function takePublicSnapshot( + publicDir: string, + options: PublicSnapshotOptions = {}, +): Promise { + const snapshot: PublicSnapshot = new Map() + await walkPublic(publicDir, publicDir, snapshot, options) + return snapshot +} + +export function diffPublicSnapshots( + before: PublicSnapshot, + after: PublicSnapshot, + limit = 50, +): PublicMutation[] { + const mutations: PublicMutation[] = [] + + for (const [path, afterEntry] of after.entries()) { + const beforeEntry = before.get(path) + if (!beforeEntry) { + mutations.push({ + type: 'created', + path, + entry_type: afterEntry.entry_type, + }) + } else if ( + beforeEntry.entry_type !== afterEntry.entry_type || + beforeEntry.size !== afterEntry.size || + beforeEntry.mtimeMs !== afterEntry.mtimeMs || + (beforeEntry.content !== undefined && + afterEntry.content !== undefined && + !beforeEntry.content.equals(afterEntry.content)) + ) { + mutations.push({ + type: 'modified', + path, + entry_type: afterEntry.entry_type, + }) + } + if (mutations.length >= limit) return mutations + } + + for (const [path, beforeEntry] of before.entries()) { + if (!after.has(path)) { + mutations.push({ + type: 'deleted', + path, + entry_type: beforeEntry.entry_type, + }) + } + if (mutations.length >= limit) return mutations + } + + return mutations +} + +function isInside(path: string, parent: string): boolean { + const resolvedChild = resolve(path) + const resolvedBase = resolve(parent) + const child = process.platform === 'win32' ? resolvedChild.toLowerCase() : resolvedChild + const base = process.platform === 'win32' ? resolvedBase.toLowerCase() : resolvedBase + return child === base || child.startsWith(`${base}\\`) || child.startsWith(`${base}/`) +} + +export async function cleanupCreatedPublicEntries( + publicDir: string, + mutations: PublicMutation[], +): Promise { + const created = mutations + .filter(mutation => mutation.type === 'created') + .map(mutation => mutation.path) + .sort((a, b) => b.length - a.length) + const removed: string[] = [] + + for (const relativePath of created) { + const target = resolve(publicDir, relativePath) + if (!isInside(target, publicDir) || target === resolve(publicDir)) continue + await rm(target, { recursive: true, force: true }) + removed.push(relativePath) + } + + return removed +} + +export async function restorePublicSnapshotMutations( + publicDir: string, + before: PublicSnapshot, + mutations: PublicMutation[], +): Promise { + const result: PublicRestoreResult = { + removedCreatedPaths: [], + restoredPaths: [], + unrestoredPaths: [], + } + + result.removedCreatedPaths = await cleanupCreatedPublicEntries(publicDir, mutations) + + const deletedDirs = mutations + .filter(mutation => mutation.type === 'deleted') + .filter(mutation => before.get(mutation.path)?.entry_type === 'dir') + .map(mutation => mutation.path) + .sort((a, b) => a.length - b.length) + + for (const relativePath of deletedDirs) { + const target = resolve(publicDir, relativePath) + if (!isInside(target, publicDir) || target === resolve(publicDir)) continue + await mkdir(target, { recursive: true }) + result.restoredPaths.push(relativePath) + } + + const filesToRestore = mutations + .filter(mutation => mutation.type === 'modified' || mutation.type === 'deleted') + .filter(mutation => before.get(mutation.path)?.entry_type === 'file') + .map(mutation => mutation.path) + .sort((a, b) => a.length - b.length) + + for (const relativePath of filesToRestore) { + const beforeEntry = before.get(relativePath) + const target = resolve(publicDir, relativePath) + if (!beforeEntry?.content || !isInside(target, publicDir)) { + result.unrestoredPaths.push(relativePath) + continue + } + await mkdir(dirname(target), { recursive: true }) + await writeFile(target, beforeEntry.content) + result.restoredPaths.push(relativePath) + } + + return result +} diff --git a/src/harness/evaluation/taskEnvironment.ts b/src/harness/evaluation/taskEnvironment.ts new file mode 100644 index 0000000000000000000000000000000000000000..4ebcad706dd0cd1c96aabaca54c58d0ff3048cca --- /dev/null +++ b/src/harness/evaluation/taskEnvironment.ts @@ -0,0 +1,202 @@ +import { existsSync } from 'fs' +import { cp, mkdir, readFile, symlink, writeFile } from 'fs/promises' +import { basename, dirname, isAbsolute, join, relative, resolve } from 'path' +import type { TaskManifest, TaskRun } from './types.js' +import { isBioMniBenchTask, loadBioMniBenchManifest } from './biomnibenchAdapter.js' + +export type CreateTaskRunInput = { + taskId: string + tasksDir?: string + runsDir?: string + timestamp?: string +} + +function makeTimestamp(date = new Date()): string { + const pad = (value: number) => String(value).padStart(2, '0') + return [ + date.getFullYear(), + pad(date.getMonth() + 1), + pad(date.getDate()), + '_', + pad(date.getHours()), + pad(date.getMinutes()), + pad(date.getSeconds()), + ].join('') +} + +function isPathInside(child: string, parent: string): boolean { + const normalizedChild = resolve(child) + const normalizedParent = resolve(parent) + const comparisonChild = + process.platform === 'win32' ? normalizedChild.toLowerCase() : normalizedChild + const comparisonParent = + process.platform === 'win32' ? normalizedParent.toLowerCase() : normalizedParent + return ( + comparisonChild === comparisonParent || + comparisonChild.startsWith(`${comparisonParent}\\`) || + comparisonChild.startsWith(`${comparisonParent}/`) + ) +} + +function assertRelativeBundlePath(path: string): string { + const normalized = path.replace(/\\/g, '/').replace(/\/+$/, '') + if (!normalized || isAbsolute(normalized) || normalized.split('/').includes('..')) { + throw new Error(`Unsafe task bundle path: ${path}`) + } + return normalized +} + +function resolveInside(base: string, relativePath: string): string { + const target = resolve(base, relativePath) + if (!isPathInside(target, base)) { + throw new Error(`Unsafe task bundle path escapes root: ${relativePath}`) + } + return target +} + +function isPrivatePath(path: string): boolean { + const first = path.replace(/\\/g, '/').split('/')[0] + return first === 'evaluation' || first === 'std_code' +} + +async function readTaskManifest(taskDir: string): Promise { + const raw = await readFile(join(taskDir, 'task_manifest.json'), 'utf8') + return JSON.parse(stripUtf8Bom(raw)) as TaskManifest +} + +async function loadTaskManifestForTask(taskDir: string, taskId: string): Promise { + // BioMniBench tasks have a minimal on-disk manifest; synthesise/merge fields. + if (isBioMniBenchTask(taskDir)) { + return loadBioMniBenchManifest(taskDir, taskId) + } + return readTaskManifest(taskDir) +} + +function stripUtf8Bom(value: string): string { + return value.charCodeAt(0) === 0xfeff ? value.slice(1) : value +} + +function uniquePublicBundleEntries(manifest: TaskManifest): string[] { + const entries = new Set() + for (const entry of manifest.public_bundle ?? []) { + entries.add(assertRelativeBundlePath(entry)) + } + + // Some task manifests expose envs/runtime/ but put env_manifest.json in the + // environment entrypoint. The manifest is public and is needed to resolve + // cross-platform Python paths, so include it explicitly. + if (manifest.entrypoints?.environment) { + entries.add(assertRelativeBundlePath(manifest.entrypoints.environment)) + } + + return [...entries].filter(entry => !isPrivatePath(entry)) +} + +async function copyPublicEntry(taskDir: string, publicDir: string, entry: string): Promise { + const source = resolveInside(taskDir, entry) + const destination = resolveInside(publicDir, entry) + if (!existsSync(source)) return + + await mkdir(dirname(destination), { recursive: true }) + await cp(source, destination, { + recursive: true, + force: true, + errorOnExist: false, + filter: sourcePath => shouldCopyForCurrentHost(sourcePath), + }) +} + +function isVirtualEnvPath(sourcePath: string): boolean { + const segments = sourcePath.replace(/\\/g, '/').split('/') + return segments.includes('.venv') || segments.includes('.venv-posix') +} + +function shouldCopyForCurrentHost(sourcePath: string): boolean { + return !isVirtualEnvPath(sourcePath) +} + +async function linkDirectory(source: string, destination: string): Promise { + if (existsSync(destination)) return + await mkdir(dirname(destination), { recursive: true }) + await symlink(source, destination, process.platform === 'win32' ? 'junction' : 'dir') +} + +async function linkHostRuntime(taskDir: string, publicDir: string): Promise { + const runtimeRoot = join('envs', 'runtime') + const candidates = + process.platform === 'win32' ? ['.venv'] : ['.venv-posix', '.venv'] + + for (const candidate of candidates) { + const source = resolveInside(taskDir, join(runtimeRoot, candidate)) + if (!existsSync(source)) continue + const destination = resolveInside(publicDir, join(runtimeRoot, candidate)) + await linkDirectory(source, destination) + return + } +} + +export async function createTaskRun(input: CreateTaskRunInput): Promise { + const tasksDir = resolve(input.tasksDir ?? 'tasks') + const runsDir = resolve(input.runsDir ?? 'runs') + const taskDir = resolveInside(tasksDir, input.taskId) + const manifest = await loadTaskManifestForTask(taskDir, input.taskId) + if (manifest.task_id !== input.taskId) { + throw new Error( + `Task manifest id mismatch: expected ${input.taskId}, got ${manifest.task_id}`, + ) + } + + const timestamp = input.timestamp ?? makeTimestamp() + const runId = `${input.taskId}_${timestamp}` + const runDir = join(runsDir, runId) + const judgeDir = join(runsDir, '.judge_private', runId) + const publicDir = join(runDir, 'public') + const workspaceDir = join(runDir, 'workspace') + const outputsDir = join(runDir, manifest.submission?.output_dir ?? 'outputs') + const logsDir = join(runDir, 'logs') + + await mkdir(publicDir, { recursive: true }) + await mkdir(workspaceDir, { recursive: true }) + await mkdir(join(workspaceDir, 'plans'), { recursive: true }) + await mkdir(join(workspaceDir, 'experiments'), { recursive: true }) + await mkdir(outputsDir, { recursive: true }) + await mkdir(logsDir, { recursive: true }) + await mkdir(join(logsDir, 'agent'), { recursive: true }) + await mkdir(judgeDir, { recursive: true }) + + for (const entry of uniquePublicBundleEntries(manifest)) { + await copyPublicEntry(taskDir, publicDir, entry) + } + await linkHostRuntime(taskDir, publicDir) + + const runManifest = { + version: 1, + task_id: input.taskId, + run_id: runId, + source_task_dir: taskDir, + public_dir: relative(runDir, publicDir), + workspace_dir: basename(workspaceDir), + outputs_dir: relative(runDir, outputsDir), + logs_dir: basename(logsDir), + created_at: new Date().toISOString(), + } + + await writeFile( + join(runDir, 'run_manifest.json'), + `${JSON.stringify(runManifest, null, 2)}\n`, + 'utf8', + ) + + return { + taskId: input.taskId, + runId, + runDir, + judgeDir, + publicDir, + workspaceDir, + outputsDir, + logsDir, + taskDir, + manifest, + } +} diff --git a/src/hooks/notifs/useAutoModeUnavailableNotification.ts b/src/hooks/notifs/useAutoModeUnavailableNotification.ts new file mode 100644 index 0000000000000000000000000000000000000000..028a80cadae51173e80b74cd08dd0b2904c577a3 --- /dev/null +++ b/src/hooks/notifs/useAutoModeUnavailableNotification.ts @@ -0,0 +1,56 @@ +import { feature } from 'bun:bundle' +import { useEffect, useRef } from 'react' +import { useNotifications } from 'src/context/notifications.js' +import { getIsRemoteMode } from '../../bootstrap/state.js' +import { useAppState } from '../../state/AppState.js' +import type { PermissionMode } from '../../utils/permissions/PermissionMode.js' +import { + getAutoModeUnavailableNotification, + getAutoModeUnavailableReason, +} from '../../utils/permissions/permissionSetup.js' +import { hasAutoModeOptIn } from '../../utils/settings/settings.js' + +/** + * Shows a one-shot notification when the shift-tab carousel wraps past where + * auto mode would have been. Covers all reasons (settings, circuit-breaker, + * org-allowlist). The startup case (defaultMode: auto silently downgraded) is + * handled by verifyAutoModeGateAccess → checkAndDisableAutoModeIfNeeded. + */ +export function useAutoModeUnavailableNotification(): void { + const { addNotification } = useNotifications() + const mode = useAppState(s => s.toolPermissionContext.mode) + const isAutoModeAvailable = useAppState( + s => s.toolPermissionContext.isAutoModeAvailable, + ) + const shownRef = useRef(false) + const prevModeRef = useRef(mode) + + useEffect(() => { + const prevMode = prevModeRef.current + prevModeRef.current = mode + + if (!feature('TRANSCRIPT_CLASSIFIER')) return + if (getIsRemoteMode()) return + if (shownRef.current) return + + const wrappedPastAutoSlot = + mode === 'default' && + prevMode !== 'default' && + prevMode !== 'auto' && + !isAutoModeAvailable && + hasAutoModeOptIn() + + if (!wrappedPastAutoSlot) return + + const reason = getAutoModeUnavailableReason() + if (!reason) return + + shownRef.current = true + addNotification({ + key: 'auto-mode-unavailable', + text: getAutoModeUnavailableNotification(reason), + color: 'warning', + priority: 'medium', + }) + }, [mode, isAutoModeAvailable, addNotification]) +} diff --git a/src/hooks/notifs/useFastModeNotification.tsx b/src/hooks/notifs/useFastModeNotification.tsx new file mode 100644 index 0000000000000000000000000000000000000000..786eb378e71b89ebee240082451026efca978eba --- /dev/null +++ b/src/hooks/notifs/useFastModeNotification.tsx @@ -0,0 +1,162 @@ +import { c as _c } from "react/compiler-runtime"; +import { useEffect } from 'react'; +import { useNotifications } from 'src/context/notifications.js'; +import { useAppState, useSetAppState } from 'src/state/AppState.js'; +import { type CooldownReason, isFastModeEnabled, onCooldownExpired, onCooldownTriggered, onFastModeOverageRejection, onOrgFastModeChanged } from 'src/utils/fastMode.js'; +import { formatDuration } from 'src/utils/format.js'; +import { getIsRemoteMode } from '../../bootstrap/state.js'; +const COOLDOWN_STARTED_KEY = 'fast-mode-cooldown-started'; +const COOLDOWN_EXPIRED_KEY = 'fast-mode-cooldown-expired'; +const ORG_CHANGED_KEY = 'fast-mode-org-changed'; +const OVERAGE_REJECTED_KEY = 'fast-mode-overage-rejected'; +export function useFastModeNotification() { + const $ = _c(13); + const { + addNotification + } = useNotifications(); + const isFastMode = useAppState(_temp); + const setAppState = useSetAppState(); + let t0; + let t1; + if ($[0] !== addNotification || $[1] !== isFastMode || $[2] !== setAppState) { + t0 = () => { + if (getIsRemoteMode()) { + return; + } + if (!isFastModeEnabled()) { + return; + } + return onOrgFastModeChanged(orgEnabled => { + if (orgEnabled) { + addNotification({ + key: ORG_CHANGED_KEY, + color: "fastMode", + priority: "immediate", + text: "Fast mode is now available \xB7 /fast to turn on" + }); + } else { + if (isFastMode) { + setAppState(_temp2); + addNotification({ + key: ORG_CHANGED_KEY, + color: "warning", + priority: "immediate", + text: "Fast mode has been disabled by your organization" + }); + } + } + }); + }; + t1 = [addNotification, isFastMode, setAppState]; + $[0] = addNotification; + $[1] = isFastMode; + $[2] = setAppState; + $[3] = t0; + $[4] = t1; + } else { + t0 = $[3]; + t1 = $[4]; + } + useEffect(t0, t1); + let t2; + let t3; + if ($[5] !== addNotification || $[6] !== setAppState) { + t2 = () => { + if (getIsRemoteMode()) { + return; + } + if (!isFastModeEnabled()) { + return; + } + return onFastModeOverageRejection(message => { + setAppState(_temp3); + addNotification({ + key: OVERAGE_REJECTED_KEY, + color: "warning", + priority: "immediate", + text: message + }); + }); + }; + t3 = [addNotification, setAppState]; + $[5] = addNotification; + $[6] = setAppState; + $[7] = t2; + $[8] = t3; + } else { + t2 = $[7]; + t3 = $[8]; + } + useEffect(t2, t3); + let t4; + let t5; + if ($[9] !== addNotification || $[10] !== isFastMode) { + t4 = () => { + if (getIsRemoteMode()) { + return; + } + if (!isFastMode) { + return; + } + const unsubTriggered = onCooldownTriggered((resetAt, reason) => { + const resetIn = formatDuration(resetAt - Date.now(), { + hideTrailingZeros: true + }); + const message_0 = getCooldownMessage(reason, resetIn); + addNotification({ + key: COOLDOWN_STARTED_KEY, + invalidates: [COOLDOWN_EXPIRED_KEY], + text: message_0, + color: "warning", + priority: "immediate" + }); + }); + const unsubExpired = onCooldownExpired(() => { + addNotification({ + key: COOLDOWN_EXPIRED_KEY, + invalidates: [COOLDOWN_STARTED_KEY], + color: "fastMode", + text: "Fast limit reset \xB7 now using fast mode", + priority: "immediate" + }); + }); + return () => { + unsubTriggered(); + unsubExpired(); + }; + }; + t5 = [addNotification, isFastMode]; + $[9] = addNotification; + $[10] = isFastMode; + $[11] = t4; + $[12] = t5; + } else { + t4 = $[11]; + t5 = $[12]; + } + useEffect(t4, t5); +} +function _temp3(prev_0) { + return { + ...prev_0, + fastMode: false + }; +} +function _temp2(prev) { + return { + ...prev, + fastMode: false + }; +} +function _temp(s) { + return s.fastMode; +} +function getCooldownMessage(reason: CooldownReason, resetIn: string): string { + switch (reason) { + case 'overloaded': + return `Fast mode overloaded and is temporarily unavailable · resets in ${resetIn}`; + case 'rate_limit': + return `Fast limit reached and temporarily disabled · resets in ${resetIn}`; + } +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJ1c2VFZmZlY3QiLCJ1c2VOb3RpZmljYXRpb25zIiwidXNlQXBwU3RhdGUiLCJ1c2VTZXRBcHBTdGF0ZSIsIkNvb2xkb3duUmVhc29uIiwiaXNGYXN0TW9kZUVuYWJsZWQiLCJvbkNvb2xkb3duRXhwaXJlZCIsIm9uQ29vbGRvd25UcmlnZ2VyZWQiLCJvbkZhc3RNb2RlT3ZlcmFnZVJlamVjdGlvbiIsIm9uT3JnRmFzdE1vZGVDaGFuZ2VkIiwiZm9ybWF0RHVyYXRpb24iLCJnZXRJc1JlbW90ZU1vZGUiLCJDT09MRE9XTl9TVEFSVEVEX0tFWSIsIkNPT0xET1dOX0VYUElSRURfS0VZIiwiT1JHX0NIQU5HRURfS0VZIiwiT1ZFUkFHRV9SRUpFQ1RFRF9LRVkiLCJ1c2VGYXN0TW9kZU5vdGlmaWNhdGlvbiIsIiQiLCJfYyIsImFkZE5vdGlmaWNhdGlvbiIsImlzRmFzdE1vZGUiLCJfdGVtcCIsInNldEFwcFN0YXRlIiwidDAiLCJ0MSIsIm9yZ0VuYWJsZWQiLCJrZXkiLCJjb2xvciIsInByaW9yaXR5IiwidGV4dCIsIl90ZW1wMiIsInQyIiwidDMiLCJtZXNzYWdlIiwiX3RlbXAzIiwidDQiLCJ0NSIsInVuc3ViVHJpZ2dlcmVkIiwicmVzZXRBdCIsInJlYXNvbiIsInJlc2V0SW4iLCJEYXRlIiwibm93IiwiaGlkZVRyYWlsaW5nWmVyb3MiLCJtZXNzYWdlXzAiLCJnZXRDb29sZG93bk1lc3NhZ2UiLCJpbnZhbGlkYXRlcyIsInVuc3ViRXhwaXJlZCIsInByZXZfMCIsInByZXYiLCJmYXN0TW9kZSIsInMiXSwic291cmNlcyI6WyJ1c2VGYXN0TW9kZU5vdGlmaWNhdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnc3JjL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlLCB1c2VTZXRBcHBTdGF0ZSB9IGZyb20gJ3NyYy9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7XG4gIHR5cGUgQ29vbGRvd25SZWFzb24sXG4gIGlzRmFzdE1vZGVFbmFibGVkLFxuICBvbkNvb2xkb3duRXhwaXJlZCxcbiAgb25Db29sZG93blRyaWdnZXJlZCxcbiAgb25GYXN0TW9kZU92ZXJhZ2VSZWplY3Rpb24sXG4gIG9uT3JnRmFzdE1vZGVDaGFuZ2VkLFxufSBmcm9tICdzcmMvdXRpbHMvZmFzdE1vZGUuanMnXG5pbXBvcnQgeyBmb3JtYXREdXJhdGlvbiB9IGZyb20gJ3NyYy91dGlscy9mb3JtYXQuanMnXG5pbXBvcnQgeyBnZXRJc1JlbW90ZU1vZGUgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5cbmNvbnN0IENPT0xET1dOX1NUQVJURURfS0VZID0gJ2Zhc3QtbW9kZS1jb29sZG93bi1zdGFydGVkJ1xuY29uc3QgQ09PTERPV05fRVhQSVJFRF9LRVkgPSAnZmFzdC1tb2RlLWNvb2xkb3duLWV4cGlyZWQnXG5jb25zdCBPUkdfQ0hBTkdFRF9LRVkgPSAnZmFzdC1tb2RlLW9yZy1jaGFuZ2VkJ1xuY29uc3QgT1ZFUkFHRV9SRUpFQ1RFRF9LRVkgPSAnZmFzdC1tb2RlLW92ZXJhZ2UtcmVqZWN0ZWQnXG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VGYXN0TW9kZU5vdGlmaWNhdGlvbigpOiB2b2lkIHtcbiAgY29uc3QgeyBhZGROb3RpZmljYXRpb24gfSA9IHVzZU5vdGlmaWNhdGlvbnMoKVxuICBjb25zdCBpc0Zhc3RNb2RlID0gdXNlQXBwU3RhdGUocyA9PiBzLmZhc3RNb2RlKVxuICBjb25zdCBzZXRBcHBTdGF0ZSA9IHVzZVNldEFwcFN0YXRlKClcblxuICAvLyBOb3RpZnkgd2hlbiBvcmcgZmFzdCBtb2RlIHN0YXR1cyBjaGFuZ2VzXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICBpZiAoIWlzRmFzdE1vZGVFbmFibGVkKCkpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHJldHVybiBvbk9yZ0Zhc3RNb2RlQ2hhbmdlZChvcmdFbmFibGVkID0+IHtcbiAgICAgIGlmIChvcmdFbmFibGVkKSB7XG4gICAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgICAga2V5OiBPUkdfQ0hBTkdFRF9LRVksXG4gICAgICAgICAgY29sb3I6ICdmYXN0TW9kZScsXG4gICAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgICAgIHRleHQ6ICdGYXN0IG1vZGUgaXMgbm93IGF2YWlsYWJsZSDCtyAvZmFzdCB0byB0dXJuIG9uJyxcbiAgICAgICAgfSlcbiAgICAgIH0gZWxzZSBpZiAoaXNGYXN0TW9kZSkge1xuICAgICAgICAvLyBPcmcgZGlzYWJsZWQgZmFzdCBtb2RlIOKAlCBwZXJtYW5lbnRseSB0dXJuIG9mZiBmYXN0IG1vZGVcbiAgICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiAoeyAuLi5wcmV2LCBmYXN0TW9kZTogZmFsc2UgfSkpXG4gICAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgICAga2V5OiBPUkdfQ0hBTkdFRF9LRVksXG4gICAgICAgICAgY29sb3I6ICd3YXJuaW5nJyxcbiAgICAgICAgICBwcmlvcml0eTogJ2ltbWVkaWF0ZScsXG4gICAgICAgICAgdGV4dDogJ0Zhc3QgbW9kZSBoYXMgYmVlbiBkaXNhYmxlZCBieSB5b3VyIG9yZ2FuaXphdGlvbicsXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfSlcbiAgfSwgW2FkZE5vdGlmaWNhdGlvbiwgaXNGYXN0TW9kZSwgc2V0QXBwU3RhdGVdKVxuXG4gIC8vIE5vdGlmeSB3aGVuIGZhc3QgbW9kZSBpcyByZWplY3RlZCBkdWUgdG8gb3ZlcmFnZS9leHRyYSB1c2FnZSBpc3N1ZXNcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZ2V0SXNSZW1vdGVNb2RlKCkpIHJldHVyblxuICAgIGlmICghaXNGYXN0TW9kZUVuYWJsZWQoKSkgcmV0dXJuXG5cbiAgICByZXR1cm4gb25GYXN0TW9kZU92ZXJhZ2VSZWplY3Rpb24obWVzc2FnZSA9PiB7XG4gICAgICBzZXRBcHBTdGF0ZShwcmV2ID0+ICh7IC4uLnByZXYsIGZhc3RNb2RlOiBmYWxzZSB9KSlcbiAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgIGtleTogT1ZFUkFHRV9SRUpFQ1RFRF9LRVksXG4gICAgICAgIGNvbG9yOiAnd2FybmluZycsXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgICAgdGV4dDogbWVzc2FnZSxcbiAgICAgIH0pXG4gICAgfSlcbiAgfSwgW2FkZE5vdGlmaWNhdGlvbiwgc2V0QXBwU3RhdGVdKVxuXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICBpZiAoIWlzRmFzdE1vZGUpIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGNvbnN0IHVuc3ViVHJpZ2dlcmVkID0gb25Db29sZG93blRyaWdnZXJlZCgocmVzZXRBdCwgcmVhc29uKSA9PiB7XG4gICAgICBjb25zdCByZXNldEluID0gZm9ybWF0RHVyYXRpb24ocmVzZXRBdCAtIERhdGUubm93KCksIHtcbiAgICAgICAgaGlkZVRyYWlsaW5nWmVyb3M6IHRydWUsXG4gICAgICB9KVxuICAgICAgY29uc3QgbWVzc2FnZSA9IGdldENvb2xkb3duTWVzc2FnZShyZWFzb24sIHJlc2V0SW4pXG4gICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICBrZXk6IENPT0xET1dOX1NUQVJURURfS0VZLFxuICAgICAgICBpbnZhbGlkYXRlczogW0NPT0xET1dOX0VYUElSRURfS0VZXSxcbiAgICAgICAgdGV4dDogbWVzc2FnZSxcbiAgICAgICAgY29sb3I6ICd3YXJuaW5nJyxcbiAgICAgICAgcHJpb3JpdHk6ICdpbW1lZGlhdGUnLFxuICAgICAgfSlcbiAgICB9KVxuICAgIGNvbnN0IHVuc3ViRXhwaXJlZCA9IG9uQ29vbGRvd25FeHBpcmVkKCgpID0+IHtcbiAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgIGtleTogQ09PTERPV05fRVhQSVJFRF9LRVksXG4gICAgICAgIGludmFsaWRhdGVzOiBbQ09PTERPV05fU1RBUlRFRF9LRVldLFxuICAgICAgICBjb2xvcjogJ2Zhc3RNb2RlJyxcbiAgICAgICAgdGV4dDogYEZhc3QgbGltaXQgcmVzZXQgwrcgbm93IHVzaW5nIGZhc3QgbW9kZWAsXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgIH0pXG4gICAgfSlcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgdW5zdWJUcmlnZ2VyZWQoKVxuICAgICAgdW5zdWJFeHBpcmVkKClcbiAgICB9XG4gIH0sIFthZGROb3RpZmljYXRpb24sIGlzRmFzdE1vZGVdKVxufVxuXG5mdW5jdGlvbiBnZXRDb29sZG93bk1lc3NhZ2UocmVhc29uOiBDb29sZG93blJlYXNvbiwgcmVzZXRJbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgc3dpdGNoIChyZWFzb24pIHtcbiAgICBjYXNlICdvdmVybG9hZGVkJzpcbiAgICAgIHJldHVybiBgRmFzdCBtb2RlIG92ZXJsb2FkZWQgYW5kIGlzIHRlbXBvcmFyaWx5IHVuYXZhaWxhYmxlIMK3IHJlc2V0cyBpbiAke3Jlc2V0SW59YFxuICAgIGNhc2UgJ3JhdGVfbGltaXQnOlxuICAgICAgcmV0dXJuIGBGYXN0IGxpbWl0IHJlYWNoZWQgYW5kIHRlbXBvcmFyaWx5IGRpc2FibGVkIMK3IHJlc2V0cyBpbiAke3Jlc2V0SW59YFxuICB9XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxTQUFTQSxTQUFTLFFBQVEsT0FBTztBQUNqQyxTQUFTQyxnQkFBZ0IsUUFBUSw4QkFBOEI7QUFDL0QsU0FBU0MsV0FBVyxFQUFFQyxjQUFjLFFBQVEsdUJBQXVCO0FBQ25FLFNBQ0UsS0FBS0MsY0FBYyxFQUNuQkMsaUJBQWlCLEVBQ2pCQyxpQkFBaUIsRUFDakJDLG1CQUFtQixFQUNuQkMsMEJBQTBCLEVBQzFC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 \ No newline at end of file diff --git a/src/hooks/notifs/useIDEStatusIndicator.tsx b/src/hooks/notifs/useIDEStatusIndicator.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1b46333e6427b4454d413c562ea17971d64f633b --- /dev/null +++ b/src/hooks/notifs/useIDEStatusIndicator.tsx @@ -0,0 +1,186 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { useEffect, useRef } from 'react'; +import { useNotifications } from 'src/context/notifications.js'; +import { Text } from 'src/ink.js'; +import type { MCPServerConnection } from 'src/services/mcp/types.js'; +import { getGlobalConfig, saveGlobalConfig } from 'src/utils/config.js'; +import { detectIDEs, type IDEExtensionInstallationStatus, isJetBrainsIde, isSupportedTerminal } from 'src/utils/ide.js'; +import { getIsRemoteMode } from '../../bootstrap/state.js'; +import { useIdeConnectionStatus } from '../useIdeConnectionStatus.js'; +import type { IDESelection } from '../useIdeSelection.js'; +const MAX_IDE_HINT_SHOW_COUNT = 5; +type Props = { + ideInstallationStatus: IDEExtensionInstallationStatus | null; + ideSelection: IDESelection | undefined; + mcpClients: MCPServerConnection[]; +}; +export function useIDEStatusIndicator(t0) { + const $ = _c(26); + const { + ideSelection, + mcpClients, + ideInstallationStatus + } = t0; + const { + addNotification, + removeNotification + } = useNotifications(); + const { + status: ideStatus, + ideName + } = useIdeConnectionStatus(mcpClients); + const hasShownHintRef = useRef(false); + let t1; + if ($[0] !== ideInstallationStatus) { + t1 = ideInstallationStatus ? isJetBrainsIde(ideInstallationStatus?.ideType) : false; + $[0] = ideInstallationStatus; + $[1] = t1; + } else { + t1 = $[1]; + } + const isJetBrains = t1; + const showIDEInstallErrorOrJetBrainsInfo = ideInstallationStatus?.error || isJetBrains; + const shouldShowIdeSelection = ideStatus === "connected" && (ideSelection?.filePath || ideSelection?.text && ideSelection.lineCount > 0); + const shouldShowConnected = ideStatus === "connected" && !shouldShowIdeSelection; + const showIDEInstallError = showIDEInstallErrorOrJetBrainsInfo && !isJetBrains && !shouldShowConnected && !shouldShowIdeSelection; + const showJetBrainsInfo = showIDEInstallErrorOrJetBrainsInfo && isJetBrains && !shouldShowConnected && !shouldShowIdeSelection; + let t2; + let t3; + if ($[2] !== addNotification || $[3] !== ideStatus || $[4] !== removeNotification || $[5] !== showJetBrainsInfo) { + t2 = () => { + if (getIsRemoteMode()) { + return; + } + if (isSupportedTerminal() || ideStatus !== null || showJetBrainsInfo) { + removeNotification("ide-status-hint"); + return; + } + if (hasShownHintRef.current || (getGlobalConfig().ideHintShownCount ?? 0) >= MAX_IDE_HINT_SHOW_COUNT) { + return; + } + const timeoutId = setTimeout(_temp2, 3000, hasShownHintRef, addNotification); + return () => clearTimeout(timeoutId); + }; + t3 = [addNotification, removeNotification, ideStatus, showJetBrainsInfo]; + $[2] = addNotification; + $[3] = ideStatus; + $[4] = removeNotification; + $[5] = showJetBrainsInfo; + $[6] = t2; + $[7] = t3; + } else { + t2 = $[6]; + t3 = $[7]; + } + useEffect(t2, t3); + let t4; + let t5; + if ($[8] !== addNotification || $[9] !== ideName || $[10] !== ideStatus || $[11] !== removeNotification || $[12] !== showIDEInstallError || $[13] !== showJetBrainsInfo) { + t4 = () => { + if (getIsRemoteMode()) { + return; + } + if (showIDEInstallError || showJetBrainsInfo || ideStatus !== "disconnected" || !ideName) { + removeNotification("ide-status-disconnected"); + return; + } + addNotification({ + key: "ide-status-disconnected", + text: `${ideName} disconnected`, + color: "error", + priority: "medium" + }); + }; + t5 = [addNotification, removeNotification, ideStatus, ideName, showIDEInstallError, showJetBrainsInfo]; + $[8] = addNotification; + $[9] = ideName; + $[10] = ideStatus; + $[11] = removeNotification; + $[12] = showIDEInstallError; + $[13] = showJetBrainsInfo; + $[14] = t4; + $[15] = t5; + } else { + t4 = $[14]; + t5 = $[15]; + } + useEffect(t4, t5); + let t6; + let t7; + if ($[16] !== addNotification || $[17] !== removeNotification || $[18] !== showJetBrainsInfo) { + t6 = () => { + if (getIsRemoteMode()) { + return; + } + if (!showJetBrainsInfo) { + removeNotification("ide-status-jetbrains-disconnected"); + return; + } + addNotification({ + key: "ide-status-jetbrains-disconnected", + text: "IDE plugin not connected \xB7 /status for info", + priority: "medium" + }); + }; + t7 = [addNotification, removeNotification, showJetBrainsInfo]; + $[16] = addNotification; + $[17] = removeNotification; + $[18] = showJetBrainsInfo; + $[19] = t6; + $[20] = t7; + } else { + t6 = $[19]; + t7 = $[20]; + } + useEffect(t6, t7); + let t8; + let t9; + if ($[21] !== addNotification || $[22] !== removeNotification || $[23] !== showIDEInstallError) { + t8 = () => { + if (getIsRemoteMode()) { + return; + } + if (!showIDEInstallError) { + removeNotification("ide-status-install-error"); + return; + } + addNotification({ + key: "ide-status-install-error", + text: "IDE extension install failed (see /status for info)", + color: "error", + priority: "medium" + }); + }; + t9 = [addNotification, removeNotification, showIDEInstallError]; + $[21] = addNotification; + $[22] = removeNotification; + $[23] = showIDEInstallError; + $[24] = t8; + $[25] = t9; + } else { + t8 = $[24]; + t9 = $[25]; + } + useEffect(t8, t9); +} +function _temp2(hasShownHintRef_0, addNotification_0) { + detectIDEs(true).then(infos => { + const ideName_0 = infos[0]?.name; + if (ideName_0 && !hasShownHintRef_0.current) { + hasShownHintRef_0.current = true; + saveGlobalConfig(_temp); + addNotification_0({ + key: "ide-status-hint", + jsx: /ide for {ideName_0}, + priority: "low" + }); + } + }); +} +function _temp(current) { + return { + ...current, + ideHintShownCount: (current.ideHintShownCount ?? 0) + 1 + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVJlZiIsInVzZU5vdGlmaWNhdGlvbnMiLCJUZXh0IiwiTUNQU2VydmVyQ29ubmVjdGlvbiIsImdldEdsb2JhbENvbmZpZyIsInNhdmVHbG9iYWxDb25maWciLCJkZXRlY3RJREVzIiwiSURFRXh0ZW5zaW9uSW5zdGFsbGF0aW9uU3RhdHVzIiwiaXNKZXRCcmFpbnNJZGUiLCJpc1N1cHBvcnRlZFRlcm1pbmFsIiwiZ2V0SXNSZW1vdGVNb2RlIiwidXNlSWRlQ29ubmVjdGlvblN0YXR1cyIsIklERVNlbGVjdGlvbiIsIk1BWF9JREVfSElOVF9TSE9XX0NPVU5UIiwiUHJvcHMiLCJpZGVJbnN0YWxsYXRpb25TdGF0dXMiLCJpZGVTZWxlY3Rpb24iLCJtY3BDbGllbnRzIiwidXNlSURFU3RhdHVzSW5kaWNhdG9yIiwidDAiLCIkIiwiX2MiLCJhZGROb3RpZmljYXRpb24iLCJyZW1vdmVOb3RpZmljYXRpb24iLCJzdGF0dXMiLCJpZGVTdGF0dXMiLCJpZGVOYW1lIiwiaGFzU2hvd25IaW50UmVmIiwidDEiLCJpZGVUeXBlIiwiaXNKZXRCcmFpbnMiLCJzaG93SURFSW5zdGFsbEVycm9yT3JKZXRCcmFpbnNJbmZvIiwiZXJyb3IiLCJzaG91bGRTaG93SWRlU2VsZWN0aW9uIiwiZmlsZVBhdGgiLCJ0ZXh0IiwibGluZUNvdW50Iiwic2hvdWxkU2hvd0Nvbm5lY3RlZCIsInNob3dJREVJbnN0YWxsRXJyb3IiLCJzaG93SmV0QnJhaW5zSW5mbyIsInQyIiwidDMiLCJjdXJyZW50IiwiaWRlSGludFNob3duQ291bnQiLCJ0aW1lb3V0SWQiLCJzZXRUaW1lb3V0IiwiX3RlbXAyIiwiY2xlYXJUaW1lb3V0IiwidDQiLCJ0NSIsImtleSIsImNvbG9yIiwicHJpb3JpdHkiLCJ0NiIsInQ3IiwidDgiLCJ0OSIsImhhc1Nob3duSGludFJlZl8wIiwiYWRkTm90aWZpY2F0aW9uXzAiLCJ0aGVuIiwiaW5mb3MiLCJpZGVOYW1lXzAiLCJuYW1lIiwiX3RlbXAiLCJqc3giXSwic291cmNlcyI6WyJ1c2VJREVTdGF0dXNJbmRpY2F0b3IudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyB1c2VFZmZlY3QsIHVzZVJlZiB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlTm90aWZpY2F0aW9ucyB9IGZyb20gJ3NyYy9jb250ZXh0L25vdGlmaWNhdGlvbnMuanMnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnc3JjL2luay5qcydcbmltcG9ydCB0eXBlIHsgTUNQU2VydmVyQ29ubmVjdGlvbiB9IGZyb20gJ3NyYy9zZXJ2aWNlcy9tY3AvdHlwZXMuanMnXG5pbXBvcnQgeyBnZXRHbG9iYWxDb25maWcsIHNhdmVHbG9iYWxDb25maWcgfSBmcm9tICdzcmMvdXRpbHMvY29uZmlnLmpzJ1xuaW1wb3J0IHtcbiAgZGV0ZWN0SURFcyxcbiAgdHlwZSBJREVFeHRlbnNpb25JbnN0YWxsYXRpb25TdGF0dXMsXG4gIGlzSmV0QnJhaW5zSWRlLFxuICBpc1N1cHBvcnRlZFRlcm1pbmFsLFxufSBmcm9tICdzcmMvdXRpbHMvaWRlLmpzJ1xuaW1wb3J0IHsgZ2V0SXNSZW1vdGVNb2RlIH0gZnJvbSAnLi4vLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHsgdXNlSWRlQ29ubmVjdGlvblN0YXR1cyB9IGZyb20gJy4uL3VzZUlkZUNvbm5lY3Rpb25TdGF0dXMuanMnXG5pbXBvcnQgdHlwZSB7IElERVNlbGVjdGlvbiB9IGZyb20gJy4uL3VzZUlkZVNlbGVjdGlvbi5qcydcblxuY29uc3QgTUFYX0lERV9ISU5UX1NIT1dfQ09VTlQgPSA1XG5cbnR5cGUgUHJvcHMgPSB7XG4gIGlkZUluc3RhbGxhdGlvblN0YXR1czogSURFRXh0ZW5zaW9uSW5zdGFsbGF0aW9uU3RhdHVzIHwgbnVsbFxuICBpZGVTZWxlY3Rpb246IElERVNlbGVjdGlvbiB8IHVuZGVmaW5lZFxuICBtY3BDbGllbnRzOiBNQ1BTZXJ2ZXJDb25uZWN0aW9uW11cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIHVzZUlERVN0YXR1c0luZGljYXRvcih7XG4gIGlkZVNlbGVjdGlvbixcbiAgbWNwQ2xpZW50cyxcbiAgaWRlSW5zdGFsbGF0aW9uU3RhdHVzLFxufTogUHJvcHMpOiB2b2lkIHtcbiAgY29uc3QgeyBhZGROb3RpZmljYXRpb24sIHJlbW92ZU5vdGlmaWNhdGlvbiB9ID0gdXNlTm90aWZpY2F0aW9ucygpXG4gIGNvbnN0IHsgc3RhdHVzOiBpZGVTdGF0dXMsIGlkZU5hbWUgfSA9IHVzZUlkZUNvbm5lY3Rpb25TdGF0dXMobWNwQ2xpZW50cylcbiAgY29uc3QgaGFzU2hvd25IaW50UmVmID0gdXNlUmVmKGZhbHNlKVxuXG4gIGNvbnN0IGlzSmV0QnJhaW5zID0gaWRlSW5zdGFsbGF0aW9uU3RhdHVzXG4gICAgPyBpc0pldEJyYWluc0lkZShpZGVJbnN0YWxsYXRpb25TdGF0dXM/LmlkZVR5cGUpXG4gICAgOiBmYWxzZVxuICBjb25zdCBzaG93SURFSW5zdGFsbEVycm9yT3JKZXRCcmFpbnNJbmZvID1cbiAgICBpZGVJbnN0YWxsYXRpb25TdGF0dXM/LmVycm9yIHx8IGlzSmV0QnJhaW5zXG5cbiAgY29uc3Qgc2hvdWxkU2hvd0lkZVNlbGVjdGlvbiA9XG4gICAgaWRlU3RhdHVzID09PSAnY29ubmVjdGVkJyAmJlxuICAgIChpZGVTZWxlY3Rpb24/LmZpbGVQYXRoIHx8XG4gICAgICAoaWRlU2VsZWN0aW9uPy50ZXh0ICYmIGlkZVNlbGVjdGlvbi5saW5lQ291bnQgPiAwKSlcblxuICAvLyBPbmx5IHNob3cgdGhlIGNvbm5lY3RlZCBpZiBub3Qgc2hvd2luZyBjb250ZXh0XG4gIGNvbnN0IHNob3VsZFNob3dDb25uZWN0ZWQgPVxuICAgIGlkZVN0YXR1cyA9PT0gJ2Nvbm5lY3RlZCcgJiYgIXNob3VsZFNob3dJZGVTZWxlY3Rpb25cblxuICBjb25zdCBzaG93SURFSW5zdGFsbEVycm9yID1cbiAgICBzaG93SURFSW5zdGFsbEVycm9yT3JKZXRCcmFpbnNJbmZvICYmXG4gICAgIWlzSmV0QnJhaW5zICYmXG4gICAgIXNob3VsZFNob3dDb25uZWN0ZWQgJiZcbiAgICAhc2hvdWxkU2hvd0lkZVNlbGVjdGlvblxuXG4gIGNvbnN0IHNob3dKZXRCcmFpbnNJbmZvID1cbiAgICBzaG93SURFSW5zdGFsbEVycm9yT3JKZXRCcmFpbnNJbmZvICYmXG4gICAgaXNKZXRCcmFpbnMgJiZcbiAgICAhc2hvdWxkU2hvd0Nvbm5lY3RlZCAmJlxuICAgICFzaG91bGRTaG93SWRlU2VsZWN0aW9uXG5cbiAgLy8gU2hvdyB0aGUgL2lkZSBjb21tYW5kIGhpbnQgaWYgcnVubmluZyBmcm9tIGFuIGV4dGVybmFsIHRlcm1pbmFsIGFuZCBmb3VuZCBydW5uaW5nIElERShzKVxuICAvLyBEZWxheSBzaG93aW5nIGhpbnQgdG8gYXZvaWQgYnJpZWYgZmxhc2ggZHVyaW5nIGF1dG8tY29ubmVjdCBzdGFydHVwXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICBpZiAoaXNTdXBwb3J0ZWRUZXJtaW5hbCgpIHx8IGlkZVN0YXR1cyAhPT0gbnVsbCB8fCBzaG93SmV0QnJhaW5zSW5mbykge1xuICAgICAgcmVtb3ZlTm90aWZpY2F0aW9uKCdpZGUtc3RhdHVzLWhpbnQnKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIC8vIFdhaXQgYSBiaXQgdG8gbGV0IGF1dG8tY29ubmVjdCBoYXBwZW4gZmlyc3QsIGF2b2lkaW5nIGJyaWVmIGhpbnQgZmxhc2hcbiAgICBpZiAoXG4gICAgICBoYXNTaG93bkhpbnRSZWYuY3VycmVudCB8fFxuICAgICAgKGdldEdsb2JhbENvbmZpZygpLmlkZUhpbnRTaG93bkNvdW50ID8/IDApID49IE1BWF9JREVfSElOVF9TSE9XX0NPVU5UXG4gICAgKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgY29uc3QgdGltZW91dElkID0gc2V0VGltZW91dChcbiAgICAgIChoYXNTaG93bkhpbnRSZWYsIGFkZE5vdGlmaWNhdGlvbikgPT4ge1xuICAgICAgICB2b2lkIGRldGVjdElERXModHJ1ZSkudGhlbihpbmZvcyA9PiB7XG4gICAgICAgICAgY29uc3QgaWRlTmFtZSA9IGluZm9zWzBdPy5uYW1lXG4gICAgICAgICAgaWYgKGlkZU5hbWUgJiYgIWhhc1Nob3duSGludFJlZi5jdXJyZW50KSB7XG4gICAgICAgICAgICBoYXNTaG93bkhpbnRSZWYuY3VycmVudCA9IHRydWVcbiAgICAgICAgICAgIHNhdmVHbG9iYWxDb25maWcoY3VycmVudCA9PiAoe1xuICAgICAgICAgICAgICAuLi5jdXJyZW50LFxuICAgICAgICAgICAgICBpZGVIaW50U2hvd25Db3VudDogKGN1cnJlbnQuaWRlSGludFNob3duQ291bnQgPz8gMCkgKyAxLFxuICAgICAgICAgICAgfSkpXG4gICAgICAgICAgICBhZGROb3RpZmljYXRpb24oe1xuICAgICAgICAgICAgICBrZXk6ICdpZGUtc3RhdHVzLWhpbnQnLFxuICAgICAgICAgICAgICBqc3g6IChcbiAgICAgICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj5cbiAgICAgICAgICAgICAgICAgIC9pZGUgZm9yIDxUZXh0IGNvbG9yPVwiaWRlXCI+e2lkZU5hbWV9PC9UZXh0PlxuICAgICAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgICAgKSxcbiAgICAgICAgICAgICAgcHJpb3JpdHk6ICdsb3cnLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICB9XG4gICAgICAgIH0pXG4gICAgICB9LFxuICAgICAgMzAwMCxcbiAgICAgIGhhc1Nob3duSGludFJlZixcbiAgICAgIGFkZE5vdGlmaWNhdGlvbixcbiAgICApXG4gICAgcmV0dXJuICgpID0+IGNsZWFyVGltZW91dCh0aW1lb3V0SWQpXG4gIH0sIFthZGROb3RpZmljYXRpb24sIHJlbW92ZU5vdGlmaWNhdGlvbiwgaWRlU3RhdHVzLCBzaG93SmV0QnJhaW5zSW5mb10pXG5cbiAgLy8gU2hvdyBJREUgZGlzY29ubmVjdGVkL2ZhaWxlZCBub3RpZmljYXRpb24gd2hlbiBzdGF0dXMgaXMgZGlzY29ubmVjdGVkXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICBpZiAoXG4gICAgICBzaG93SURFSW5zdGFsbEVycm9yIHx8XG4gICAgICBzaG93SmV0QnJhaW5zSW5mbyB8fFxuICAgICAgaWRlU3RhdHVzICE9PSAnZGlzY29ubmVjdGVkJyB8fFxuICAgICAgIWlkZU5hbWVcbiAgICApIHtcbiAgICAgIHJlbW92ZU5vdGlmaWNhdGlvbignaWRlLXN0YXR1cy1kaXNjb25uZWN0ZWQnKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICBrZXk6ICdpZGUtc3RhdHVzLWRpc2Nvbm5lY3RlZCcsXG4gICAgICB0ZXh0OiBgJHtpZGVOYW1lfSBkaXNjb25uZWN0ZWRgLFxuICAgICAgY29sb3I6ICdlcnJvcicsXG4gICAgICBwcmlvcml0eTogJ21lZGl1bScsXG4gICAgfSlcbiAgfSwgW1xuICAgIGFkZE5vdGlmaWNhdGlvbixcbiAgICByZW1vdmVOb3RpZmljYXRpb24sXG4gICAgaWRlU3RhdHVzLFxuICAgIGlkZU5hbWUsXG4gICAgc2hvd0lERUluc3RhbGxFcnJvcixcbiAgICBzaG93SmV0QnJhaW5zSW5mbyxcbiAgXSlcblxuICAvLyBTaG93IEpldEJyYWlucyBwbHVnaW4gbm90IGNvbm5lY3RlZCBoaW50XG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICBpZiAoIXNob3dKZXRCcmFpbnNJbmZvKSB7XG4gICAgICByZW1vdmVOb3RpZmljYXRpb24oJ2lkZS1zdGF0dXMtamV0YnJhaW5zLWRpc2Nvbm5lY3RlZCcpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgIGtleTogJ2lkZS1zdGF0dXMtamV0YnJhaW5zLWRpc2Nvbm5lY3RlZCcsXG4gICAgICB0ZXh0OiAnSURFIHBsdWdpbiBub3QgY29ubmVjdGVkIMK3IC9zdGF0dXMgZm9yIGluZm8nLFxuICAgICAgcHJpb3JpdHk6ICdtZWRpdW0nLFxuICAgIH0pXG4gIH0sIFthZGROb3RpZmljYXRpb24sIHJlbW92ZU5vdGlmaWNhdGlvbiwgc2hvd0pldEJyYWluc0luZm9dKVxuXG4gIC8vIFNob3cgSURFIGluc3RhbGwgZXJyb3JcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZ2V0SXNSZW1vdGVNb2RlKCkpIHJldHVyblxuICAgIGlmICghc2hvd0lERUluc3RhbGxFcnJvcikge1xuICAgICAgcmVtb3ZlTm90aWZpY2F0aW9uKCdpZGUtc3RhdHVzLWluc3RhbGwtZXJyb3InKVxuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICBrZXk6ICdpZGUtc3RhdHVzLWluc3RhbGwtZXJyb3InLFxuICAgICAgdGV4dDogJ0lERSBleHRlbnNpb24gaW5zdGFsbCBmYWlsZWQgKHNlZSAvc3RhdHVzIGZvciBpbmZvKScsXG4gICAgICBjb2xvcjogJ2Vycm9yJyxcbiAgICAgIHByaW9yaXR5OiAnbWVkaXVtJyxcbiAgICB9KVxuICB9LCBbYWRkTm90aWZpY2F0aW9uLCByZW1vdmVOb3RpZmljYXRpb24sIHNob3dJREVJbnN0YWxsRXJyb3JdKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJQyxTQUFTLEVBQUVDLE1BQU0sUUFBUSxPQUFPO0FBQ2hELFNBQVNDLGdCQUFnQixRQUFRLDhCQUE4QjtBQUMvRCxTQUFTQyxJQUFJLFFBQVEsWUFBWTtBQUNqQyxjQUFjQyxtQkFBbUIsUUFBUSwyQkFBMkI7QUFDcEUsU0FBU0MsZUFBZSxFQUFFQyxnQkFBZ0IsUUFBUSxxQkFBcUI7QUFDdkUsU0FDRUMsVUFBVSxFQUNWLEtBQUtDLDhCQUE4QixFQUNuQ0MsY0FBYyxFQUNkQyxtQkFBbUIsUUFDZCxrQkFBa0I7QUFDekIsU0FBU0MsZUFBZSxRQUFRLDBCQUEwQjtBQUMx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O1FBQUE7TUFBQTtNQUNyQixJQUFJLENBQUM0QixtQkFBbUI7UUFDdEJmLGtCQUFrQixDQUFDLDBCQUEwQixDQUFDO1FBQUE7TUFBQTtNQUdoREQsZUFBZSxDQUFDO1FBQUE0QixHQUFBLEVBQ1QsMEJBQTBCO1FBQUFmLElBQUEsRUFDekIscURBQXFEO1FBQUFnQixLQUFBLEVBQ3BELE9BQU87UUFBQUMsUUFBQSxFQUNKO01BQ1osQ0FBQyxDQUFDO0lBQUEsQ0FDSDtJQUFFSSxFQUFBLElBQUNsQyxlQUFlLEVBQUVDLGtCQUFrQixFQUFFZSxtQkFBbUIsQ0FBQztJQUFBbEIsQ0FBQSxPQUFBRSxlQUFBO0lBQUFGLENBQUEsT0FBQUcsa0JBQUE7SUFBQUgsQ0FBQSxPQUFBa0IsbUJBQUE7SUFBQWxCLENBQUEsT0FBQW1DLEVBQUE7SUFBQW5DLENBQUEsT0FBQW9DLEVBQUE7RUFBQTtJQUFBRCxFQUFBLEdBQUFuQyxDQUFBO0lBQUFvQyxFQUFBLEdBQUFwQyxDQUFBO0VBQUE7RUFaN0RyQixTQUFTLENBQUN3RCxFQVlULEVBQUVDLEVBQTBELENBQUM7QUFBQTtBQXRJekQsU0FBQVYsT0FBQVcsaUJBQUEsRUFBQUMsaUJBQUE7RUFxRE1wRCxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUFxRCxJQUFLLENBQUNDLEtBQUE7SUFDekIsTUFBQUMsU0FBQSxHQUFnQkQsS0FBSyxHQUFTLEVBQUFFLElBQUE7SUFDOUIsSUFBSUQsU0FBbUMsSUFBbkMsQ0FBWWxDLGlCQUFlLENBQUFlLE9BQVE7TUFDckNmLGlCQUFlLENBQUFlLE9BQUEsR0FBVyxJQUFIO01BQ3ZCckMsZ0JBQWdCLENBQUMwRCxLQUdmLENBQUM7TUFDSHpDLGlCQUFlLENBQUM7UUFBQTRCLEdBQUEsRUFDVCxpQkFBaUI7UUFBQWMsR0FBQSxFQUVwQixDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsU0FDSixDQUFDLElBQUksQ0FBTyxLQUFLLENBQUwsS0FBSyxDQUFFdEMsVUFBTSxDQUFFLEVBQTFCLElBQUksQ0FDaEIsRUFGQyxJQUFJLENBRUU7UUFBQTBCLFFBQUEsRUFFQztNQUNaLENBQUMsQ0FBQztJQUFBO0VBQ0gsQ0FDRixDQUFDO0FBQUE7QUF2RUgsU0FBQVcsTUFBQXJCLE9BQUE7RUFBQSxPQXlEa0M7SUFBQSxHQUN4QkEsT0FBTztJQUFBQyxpQkFBQSxFQUNTLENBQUNELE9BQU8sQ0FBQUMsaUJBQXVCLElBQTlCLENBQThCLElBQUk7RUFDeEQsQ0FBQztBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/src/hooks/notifs/useLspInitializationNotification.tsx b/src/hooks/notifs/useLspInitializationNotification.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e253e9a203763bbcd9c3d31f575ef01bf4cb47d1 --- /dev/null +++ b/src/hooks/notifs/useLspInitializationNotification.tsx @@ -0,0 +1,143 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useInterval } from 'usehooks-ts'; +import { getIsRemoteMode, getIsScrollDraining } from '../../bootstrap/state.js'; +import { useNotifications } from '../../context/notifications.js'; +import { Text } from '../../ink.js'; +import { getInitializationStatus, getLspServerManager } from '../../services/lsp/manager.js'; +import { useSetAppState } from '../../state/AppState.js'; +import { logForDebugging } from '../../utils/debug.js'; +import { isEnvTruthy } from '../../utils/envUtils.js'; +const LSP_POLL_INTERVAL_MS = 5000; + +/** + * Hook that polls LSP status and shows a notification when: + * 1. Manager initialization fails + * 2. Any LSP server enters an error state + * + * Also adds errors to appState.plugins.errors for /doctor display. + * + * Only active when ENABLE_LSP_TOOL is set. + */ +export function useLspInitializationNotification() { + const $ = _c(10); + const { + addNotification + } = useNotifications(); + const setAppState = useSetAppState(); + const [shouldPoll, setShouldPoll] = React.useState(_temp); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = new Set(); + $[0] = t0; + } else { + t0 = $[0]; + } + const notifiedErrorsRef = React.useRef(t0); + let t1; + if ($[1] !== addNotification || $[2] !== setAppState) { + t1 = (source, errorMessage) => { + const errorKey = `${source}:${errorMessage}`; + if (notifiedErrorsRef.current.has(errorKey)) { + return; + } + notifiedErrorsRef.current.add(errorKey); + logForDebugging(`LSP error: ${source} - ${errorMessage}`); + setAppState(prev => { + const existingKeys = new Set(prev.plugins.errors.map(_temp2)); + const stateErrorKey = `generic-error:${source}:${errorMessage}`; + if (existingKeys.has(stateErrorKey)) { + return prev; + } + return { + ...prev, + plugins: { + ...prev.plugins, + errors: [...prev.plugins.errors, { + type: "generic-error" as const, + source, + error: errorMessage + }] + } + }; + }); + const displayName = source.startsWith("plugin:") ? source.split(":")[1] ?? source : source; + addNotification({ + key: `lsp-error-${source}`, + jsx: <>LSP for {displayName} failed · /plugin for details, + priority: "medium", + timeoutMs: 8000 + }); + }; + $[1] = addNotification; + $[2] = setAppState; + $[3] = t1; + } else { + t1 = $[3]; + } + const addError = t1; + let t2; + if ($[4] !== addError) { + t2 = () => { + if (getIsRemoteMode()) { + return; + } + if (getIsScrollDraining()) { + return; + } + const status = getInitializationStatus(); + if (status.status === "failed") { + addError("lsp-manager", status.error.message); + setShouldPoll(false); + return; + } + if (status.status === "pending" || status.status === "not-started") { + return; + } + const manager = getLspServerManager(); + if (manager) { + const servers = manager.getAllServers(); + for (const [serverName, server] of servers) { + if (server.state === "error" && server.lastError) { + addError(serverName, server.lastError.message); + } + } + } + }; + $[4] = addError; + $[5] = t2; + } else { + t2 = $[5]; + } + const poll = t2; + useInterval(poll, shouldPoll ? LSP_POLL_INTERVAL_MS : null); + let t3; + let t4; + if ($[6] !== poll || $[7] !== shouldPoll) { + t3 = () => { + if (getIsRemoteMode() || !shouldPoll) { + return; + } + poll(); + }; + t4 = [poll, shouldPoll]; + $[6] = poll; + $[7] = shouldPoll; + $[8] = t3; + $[9] = t4; + } else { + t3 = $[8]; + t4 = $[9]; + } + React.useEffect(t3, t4); +} +function _temp2(e) { + if (e.type === "generic-error") { + return `generic-error:${e.source}:${e.error}`; + } + return `${e.type}:${e.source}`; +} +function _temp() { + return isEnvTruthy("true"); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUludGVydmFsIiwiZ2V0SXNSZW1vdGVNb2RlIiwiZ2V0SXNTY3JvbGxEcmFpbmluZyIsInVzZU5vdGlmaWNhdGlvbnMiLCJUZXh0IiwiZ2V0SW5pdGlhbGl6YXRpb25TdGF0dXMiLCJnZXRMc3BTZXJ2ZXJNYW5hZ2VyIiwidXNlU2V0QXBwU3RhdGUiLCJsb2dGb3JEZWJ1Z2dpbmciLCJpc0VudlRydXRoeSIsIkxTUF9QT0xMX0lOVEVSVkFMX01TIiwidXNlTHNwSW5pdGlhbGl6YXRpb25Ob3RpZmljYXRpb24iLCIkIiwiX2MiLCJhZGROb3RpZmljYXRpb24iLCJzZXRBcHBTdGF0ZSIsInNob3VsZFBvbGwiLCJzZXRTaG91bGRQb2xsIiwidXNlU3RhdGUiLCJfdGVtcCIsInQwIiwiU3ltYm9sIiwiZm9yIiwiU2V0Iiwibm90aWZpZWRFcnJvcnNSZWYiLCJ1c2VSZWYiLCJ0MSIsInNvdXJjZSIsImVycm9yTWVzc2FnZSIsImVycm9yS2V5IiwiY3VycmVudCIsImhhcyIsImFkZCIsInByZXYiLCJleGlzdGluZ0tleXMiLCJwbHVnaW5zIiwiZXJyb3JzIiwibWFwIiwiX3RlbXAyIiwic3RhdGVFcnJvcktleSIsInR5cGUiLCJjb25zdCIsImVycm9yIiwiZGlzcGxheU5hbWUiLCJzdGFydHNXaXRoIiwic3BsaXQiLCJrZXkiLCJqc3giLCJwcmlvcml0eSIsInRpbWVvdXRNcyIsImFkZEVycm9yIiwidDIiLCJzdGF0dXMiLCJtZXNzYWdlIiwibWFuYWdlciIsInNlcnZlcnMiLCJnZXRBbGxTZXJ2ZXJzIiwic2VydmVyTmFtZSIsInNlcnZlciIsInN0YXRlIiwibGFzdEVycm9yIiwicG9sbCIsInQzIiwidDQiLCJ1c2VFZmZlY3QiLCJlIl0sInNvdXJjZXMiOlsidXNlTHNwSW5pdGlhbGl6YXRpb25Ob3RpZmljYXRpb24udHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlSW50ZXJ2YWwgfSBmcm9tICd1c2Vob29rcy10cydcbmltcG9ydCB7IGdldElzUmVtb3RlTW9kZSwgZ2V0SXNTY3JvbGxEcmFpbmluZyB9IGZyb20gJy4uLy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7IHVzZU5vdGlmaWNhdGlvbnMgfSBmcm9tICcuLi8uLi9jb250ZXh0L25vdGlmaWNhdGlvbnMuanMnXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHtcbiAgZ2V0SW5pdGlhbGl6YXRpb25TdGF0dXMsXG4gIGdldExzcFNlcnZlck1hbmFnZXIsXG59IGZyb20gJy4uLy4uL3NlcnZpY2VzL2xzcC9tYW5hZ2VyLmpzJ1xuaW1wb3J0IHsgdXNlU2V0QXBwU3RhdGUgfSBmcm9tICcuLi8uLi9zdGF0ZS9BcHBTdGF0ZS5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uLy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHsgaXNFbnZUcnV0aHkgfSBmcm9tICcuLi8uLi91dGlscy9lbnZVdGlscy5qcydcblxuY29uc3QgTFNQX1BPTExfSU5URVJWQUxfTVMgPSA1MDAwXG5cbi8qKlxuICogSG9vayB0aGF0IHBvbGxzIExTUCBzdGF0dXMgYW5kIHNob3dzIGEgbm90aWZpY2F0aW9uIHdoZW46XG4gKiAxLiBNYW5hZ2VyIGluaXRpYWxpemF0aW9uIGZhaWxzXG4gKiAyLiBBbnkgTFNQIHNlcnZlciBlbnRlcnMgYW4gZXJyb3Igc3RhdGVcbiAqXG4gKiBBbHNvIGFkZHMgZXJyb3JzIHRvIGFwcFN0YXRlLnBsdWdpbnMuZXJyb3JzIGZvciAvZG9jdG9yIGRpc3BsYXkuXG4gKlxuICogT25seSBhY3RpdmUgd2hlbiBFTkFCTEVfTFNQX1RPT0wgaXMgc2V0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlTHNwSW5pdGlhbGl6YXRpb25Ob3RpZmljYXRpb24oKTogdm9pZCB7XG4gIGNvbnN0IHsgYWRkTm90aWZpY2F0aW9uIH0gPSB1c2VOb3RpZmljYXRpb25zKClcbiAgY29uc3Qgc2V0QXBwU3RhdGUgPSB1c2VTZXRBcHBTdGF0ZSgpXG4gIC8vIExhenkgaW5pdGlhbGl6ZXIg4oCUIGVhZ2VyIGZvcm0gcmUtZXZhbHVhdGVzIGlzRW52VHJ1dGh5IG9uIGV2ZXJ5IFJFUExcbiAgLy8gcmVuZGVyICh0aGUgYXJnIGV4cHJlc3Npb24gcnVucyBldmVuIHRob3VnaCB1c2VTdGF0ZSBpZ25vcmVzIGl0IGFmdGVyXG4gIC8vIG1vdW50KS4gU2hvd2VkIHVwIGFzIDcuMnMgaXNFbnZUcnV0aHkgc2VsZi10aW1lIGR1cmluZyBQYWdlVXAgc3BhbVxuICAvLyBhZnRlciAjMjQ0OTggc3dhcHBlZCBjaGVhcCAhIXByb2Nlc3MuZW52LlggZm9yIGlzRW52VHJ1dGh5KCkuXG4gIGNvbnN0IFtzaG91bGRQb2xsLCBzZXRTaG91bGRQb2xsXSA9IFJlYWN0LnVzZVN0YXRlKCgpID0+XG4gICAgaXNFbnZUcnV0aHkoXCJ0cnVlXCIpLFxuICApXG4gIC8vIFRyYWNrIHdoaWNoIGVycm9ycyB3ZSd2ZSBhbHJlYWR5IG5vdGlmaWVkIGFib3V0IHRvIGF2b2lkIGR1cGxpY2F0ZXNcbiAgY29uc3Qgbm90aWZpZWRFcnJvcnNSZWYgPSBSZWFjdC51c2VSZWY8U2V0PHN0cmluZz4+KG5ldyBTZXQoKSlcblxuICBjb25zdCBhZGRFcnJvciA9IFJlYWN0LnVzZUNhbGxiYWNrKFxuICAgIChzb3VyY2U6IHN0cmluZywgZXJyb3JNZXNzYWdlOiBzdHJpbmcpID0+IHtcbiAgICAgIGNvbnN0IGVycm9yS2V5ID0gYCR7c291cmNlfToke2Vycm9yTWVzc2FnZX1gXG4gICAgICBpZiAobm90aWZpZWRFcnJvcnNSZWYuY3VycmVudC5oYXMoZXJyb3JLZXkpKSB7XG4gICAgICAgIHJldHVybiAvLyBBbHJlYWR5IG5vdGlmaWVkXG4gICAgICB9XG4gICAgICBub3RpZmllZEVycm9yc1JlZi5jdXJyZW50LmFkZChlcnJvcktleSlcblxuICAgICAgbG9nRm9yRGVidWdnaW5nKGBMU1AgZXJyb3I6ICR7c291cmNlfSAtICR7ZXJyb3JNZXNzYWdlfWApXG5cbiAgICAgIC8vIEFkZCBlcnJvciB0byBhcHBTdGF0ZS5wbHVnaW5zLmVycm9yc1xuICAgICAgc2V0QXBwU3RhdGUocHJldiA9PiB7XG4gICAgICAgIC8vIENoZWNrIGlmIHRoaXMgZXJyb3IgYWxyZWFkeSBleGlzdHMgdG8gYXZvaWQgZHVwbGljYXRlc1xuICAgICAgICBjb25zdCBleGlzdGluZ0tleXMgPSBuZXcgU2V0KFxuICAgICAgICAgIHByZXYucGx1Z2lucy5lcnJvcnMubWFwKGUgPT4ge1xuICAgICAgICAgICAgaWYgKGUudHlwZSA9PT0gJ2dlbmVyaWMtZXJyb3InKSB7XG4gICAgICAgICAgICAgIHJldHVybiBgZ2VuZXJpYy1lcnJvcjoke2Uuc291cmNlfToke2UuZXJyb3J9YFxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIGAke2UudHlwZX06JHtlLnNvdXJjZX1gXG4gICAgICAgICAgfSksXG4gICAgICAgIClcblxuICAgICAgICBjb25zdCBzdGF0ZUVycm9yS2V5ID0gYGdlbmVyaWMtZXJyb3I6JHtzb3VyY2V9OiR7ZXJyb3JNZXNzYWdlfWBcbiAgICAgICAgaWYgKGV4aXN0aW5nS2V5cy5oYXMoc3RhdGVFcnJvcktleSkpIHtcbiAgICAgICAgICByZXR1cm4gcHJldlxuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAuLi5wcmV2LFxuICAgICAgICAgIHBsdWdpbnM6IHtcbiAgICAgICAgICAgIC4uLnByZXYucGx1Z2lucyxcbiAgICAgICAgICAgIGVycm9yczogW1xuICAgICAgICAgICAgICAuLi5wcmV2LnBsdWdpbnMuZXJyb3JzLFxuICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgdHlwZTogJ2dlbmVyaWMtZXJyb3InIGFzIGNvbnN0LFxuICAgICAgICAgICAgICAgIHNvdXJjZSxcbiAgICAgICAgICAgICAgICBlcnJvcjogZXJyb3JNZXNzYWdlLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgXSxcbiAgICAgICAgICB9LFxuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICAvLyBTaG93IG5vdGlmaWNhdGlvbiAtIGV4dHJhY3QgcGx1Z2luIG5hbWUgZnJvbSBzb3VyY2UgbGlrZSBcInBsdWdpbjp0eXBlc2NyaXB0LWxzcDp0eXBlc2NyaXB0XCJcbiAgICAgIGNvbnN0IGRpc3BsYXlOYW1lID0gc291cmNlLnN0YXJ0c1dpdGgoJ3BsdWdpbjonKVxuICAgICAgICA/IChzb3VyY2Uuc3BsaXQoJzonKVsxXSA/PyBzb3VyY2UpXG4gICAgICAgIDogc291cmNlXG5cbiAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgIGtleTogYGxzcC1lcnJvci0ke3NvdXJjZX1gLFxuICAgICAgICBqc3g6IChcbiAgICAgICAgICA8PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPkxTUCBmb3Ige2Rpc3BsYXlOYW1lfSBmYWlsZWQ8L1RleHQ+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj4gwrcgL3BsdWdpbiBmb3IgZGV0YWlsczwvVGV4dD5cbiAgICAgICAgICA8Lz5cbiAgICAgICAgKSxcbiAgICAgICAgcHJpb3JpdHk6ICdtZWRpdW0nLFxuICAgICAgICB0aW1lb3V0TXM6IDgwMDAsXG4gICAgICB9KVxuICAgIH0sXG4gICAgW2FkZE5vdGlmaWNhdGlvbiwgc2V0QXBwU3RhdGVdLFxuICApXG5cbiAgY29uc3QgcG9sbCA9IFJlYWN0LnVzZUNhbGxiYWNrKCgpID0+IHtcbiAgICBpZiAoZ2V0SXNSZW1vdGVNb2RlKCkpIHJldHVyblxuICAgIC8vIFNraXAgZHVyaW5nIHNjcm9sbCBkcmFpbiDigJQgaXRlcmF0aW5nIGFsbCBMU1Agc2VydmVycyArIHNldEFwcFN0YXRlXG4gICAgLy8gY29tcGV0ZXMgZm9yIHRoZSBldmVudCBsb29wIHdpdGggc2Nyb2xsIGZyYW1lcy4gTmV4dCBpbnRlcnZhbCBwaWNrcyB1cC5cbiAgICBpZiAoZ2V0SXNTY3JvbGxEcmFpbmluZygpKSByZXR1cm5cblxuICAgIGNvbnN0IHN0YXR1cyA9IGdldEluaXRpYWxpemF0aW9uU3RhdHVzKClcblxuICAgIC8vIENoZWNrIG1hbmFnZXIgaW5pdGlhbGl6YXRpb24gc3RhdHVzXG4gICAgaWYgKHN0YXR1cy5zdGF0dXMgPT09ICdmYWlsZWQnKSB7XG4gICAgICBhZGRFcnJvcignbHNwLW1hbmFnZXInLCBzdGF0dXMuZXJyb3IubWVzc2FnZSlcbiAgICAgIHNldFNob3VsZFBvbGwoZmFsc2UpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAoc3RhdHVzLnN0YXR1cyA9PT0gJ3BlbmRpbmcnIHx8IHN0YXR1cy5zdGF0dXMgPT09ICdub3Qtc3RhcnRlZCcpIHtcbiAgICAgIC8vIFN0aWxsIGluaXRpYWxpemluZywgY29udGludWUgcG9sbGluZ1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gTWFuYWdlciBpbml0aWFsaXplZCBzdWNjZXNzZnVsbHkgLSBjaGVjayBmb3Igc2VydmVyIGVycm9yc1xuICAgIGNvbnN0IG1hbmFnZXIgPSBnZXRMc3BTZXJ2ZXJNYW5hZ2VyKClcbiAgICBpZiAobWFuYWdlcikge1xuICAgICAgY29uc3Qgc2VydmVycyA9IG1hbmFnZXIuZ2V0QWxsU2VydmVycygpXG4gICAgICBmb3IgKGNvbnN0IFtzZXJ2ZXJOYW1lLCBzZXJ2ZXJdIG9mIHNlcnZlcnMpIHtcbiAgICAgICAgaWYgKHNlcnZlci5zdGF0ZSA9PT0gJ2Vycm9yJyAmJiBzZXJ2ZXIubGFzdEVycm9yKSB7XG4gICAgICAgICAgYWRkRXJyb3Ioc2VydmVyTmFtZSwgc2VydmVyLmxhc3RFcnJvci5tZXNzYWdlKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIC8vIENvbnRpbnVlIHBvbGxpbmcgdG8gZGV0ZWN0IGZ1dHVyZSBzZXJ2ZXIgZXJyb3JzXG4gIH0sIFthZGRFcnJvcl0pXG5cbiAgdXNlSW50ZXJ2YWwocG9sbCwgc2hvdWxkUG9sbCA/IExTUF9QT0xMX0lOVEVSVkFMX01TIDogbnVsbClcblxuICAvLyBJbml0aWFsIHBvbGwgb24gbW91bnRcbiAgUmVhY3QudXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZ2V0SXNSZW1vdGVNb2RlKCkgfHwgIXNob3VsZFBvbGwpIHJldHVyblxuICAgIHBvbGwoKVxuICB9LCBbcG9sbCwgc2hvdWxkUG9sbF0pXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtBQUtBLEtBQUssTUFBTSxPQUFPO0FBQzlCLFNBQVNDLFdBQVcsUUFBUSxhQUFhO0FBQ3pDLFNBQVNDLGVBQWUsRUFBRUMsbUJBQW1CLFFBQVEsMEJBQTBCO0FBQy9FLFNBQVNDLGdCQUFnQixRQUFRLGdDQUFnQztBQUNqRSxTQUFTQyxJQUFJLFFBQVEsY0FBYztBQUNuQyxTQUNFQyx1QkFBdUIsRUFDdkJDLG1CQUFtQixRQUNkLCtCQUErQjtBQUN0QyxTQUFTQyxjQUFjLFFBQVEseUJBQXlCO0FBQ3hELFNBQVNDLGVBQWUsUUFBUSxzQkFBc0I7QUFDdEQsU0FBU0MsV0FBVyxRQUFRLHlCQUF5QjtBQUVyRCxNQUFNQyxvQkFBb0IsR0FBRyxJQUFJOztBQUVqQztBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFDLGlDQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQ0w7SUFBQUM7RUFBQSxJQUE0QlgsZ0JBQWdCLENBQUMsQ0FBQztFQUM5QyxNQUFBWSxXQUFBLEdBQW9CUixjQUFjLENBQUMsQ0FBQztFQUtwQyxPQUFBUyxVQUFBLEVBQUFDLGFBQUEsSUFBb0NsQixLQUFLLENBQUFtQixRQUFTLENBQUNDLEtBRW5ELENBQUM7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQVIsQ0FBQSxRQUFBUyxNQUFBLENBQUFDLEdBQUE7SUFFbURGLEVBQUEsT0FBSUcsR0FBRyxDQUFDLENBQUM7SUFBQVgsQ0FBQSxNQUFBUSxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBUixDQUFBO0VBQUE7RUFBN0QsTUFBQVks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 \ No newline at end of file diff --git a/src/hooks/notifs/useMcpConnectivityStatus.tsx b/src/hooks/notifs/useMcpConnectivityStatus.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6fe571716463c4d8ea61b05f00174aa3b27d31c3 --- /dev/null +++ b/src/hooks/notifs/useMcpConnectivityStatus.tsx @@ -0,0 +1,88 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useEffect } from 'react'; +import { useNotifications } from 'src/context/notifications.js'; +import { getIsRemoteMode } from '../../bootstrap/state.js'; +import { Text } from '../../ink.js'; +import { hasClaudeAiMcpEverConnected } from '../../services/mcp/claudeai.js'; +import type { MCPServerConnection } from '../../services/mcp/types.js'; +type Props = { + mcpClients?: MCPServerConnection[]; +}; +const EMPTY_MCP_CLIENTS: MCPServerConnection[] = []; +export function useMcpConnectivityStatus(t0) { + const $ = _c(4); + const { + mcpClients: t1 + } = t0; + const mcpClients = t1 === undefined ? EMPTY_MCP_CLIENTS : t1; + const { + addNotification + } = useNotifications(); + let t2; + let t3; + if ($[0] !== addNotification || $[1] !== mcpClients) { + t2 = () => { + if (getIsRemoteMode()) { + return; + } + const failedLocalClients = mcpClients.filter(_temp); + const failedClaudeAiClients = mcpClients.filter(_temp2); + const needsAuthLocalServers = mcpClients.filter(_temp3); + const needsAuthClaudeAiServers = mcpClients.filter(_temp4); + if (failedLocalClients.length === 0 && failedClaudeAiClients.length === 0 && needsAuthLocalServers.length === 0 && needsAuthClaudeAiServers.length === 0) { + return; + } + if (failedLocalClients.length > 0) { + addNotification({ + key: "mcp-failed", + jsx: <>{failedLocalClients.length} MCP{" "}{failedLocalClients.length === 1 ? "server" : "servers"} failed · /mcp, + priority: "medium" + }); + } + if (failedClaudeAiClients.length > 0) { + addNotification({ + key: "mcp-claudeai-failed", + jsx: <>{failedClaudeAiClients.length} claude.ai{" "}{failedClaudeAiClients.length === 1 ? "connector" : "connectors"}{" "}unavailable · /mcp, + priority: "medium" + }); + } + if (needsAuthLocalServers.length > 0) { + addNotification({ + key: "mcp-needs-auth", + jsx: <>{needsAuthLocalServers.length} MCP{" "}{needsAuthLocalServers.length === 1 ? "server needs" : "servers need"}{" "}auth · /mcp, + priority: "medium" + }); + } + if (needsAuthClaudeAiServers.length > 0) { + addNotification({ + key: "mcp-claudeai-needs-auth", + jsx: <>{needsAuthClaudeAiServers.length} claude.ai{" "}{needsAuthClaudeAiServers.length === 1 ? "connector needs" : "connectors need"}{" "}auth · /mcp, + priority: "medium" + }); + } + }; + t3 = [addNotification, mcpClients]; + $[0] = addNotification; + $[1] = mcpClients; + $[2] = t2; + $[3] = t3; + } else { + t2 = $[2]; + t3 = $[3]; + } + useEffect(t2, t3); +} +function _temp4(client_2) { + return client_2.type === "needs-auth" && client_2.config.type === "claudeai-proxy" && hasClaudeAiMcpEverConnected(client_2.name); +} +function _temp3(client_1) { + return client_1.type === "needs-auth" && client_1.config.type !== "claudeai-proxy"; +} +function _temp2(client_0) { + return client_0.type === "failed" && client_0.config.type === "claudeai-proxy" && hasClaudeAiMcpEverConnected(client_0.name); +} +function _temp(client) { + return client.type === "failed" && client.config.type !== "sse-ide" && client.config.type !== "ws-ide" && client.config.type !== "claudeai-proxy"; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZU5vdGlmaWNhdGlvbnMiLCJnZXRJc1JlbW90ZU1vZGUiLCJUZXh0IiwiaGFzQ2xhdWRlQWlNY3BFdmVyQ29ubmVjdGVkIiwiTUNQU2VydmVyQ29ubmVjdGlvbiIsIlByb3BzIiwibWNwQ2xpZW50cyIsIkVNUFRZX01DUF9DTElFTlRTIiwidXNlTWNwQ29ubmVjdGl2aXR5U3RhdHVzIiwidDAiLCIkIiwiX2MiLCJ0MSIsInVuZGVmaW5lZCIsImFkZE5vdGlmaWNhdGlvbiIsInQyIiwidDMiLCJmYWlsZWRMb2NhbENsaWVudHMiLCJmaWx0ZXIiLCJfdGVtcCIsImZhaWxlZENsYXVkZUFpQ2xpZW50cyIsIl90ZW1wMiIsIm5lZWRzQXV0aExvY2FsU2VydmVycyIsIl90ZW1wMyIsIm5lZWRzQXV0aENsYXVkZUFpU2VydmVycyIsIl90ZW1wNCIsImxlbmd0aCIsImtleSIsImpzeCIsInByaW9yaXR5IiwiY2xpZW50XzIiLCJjbGllbnQiLCJ0eXBlIiwiY29uZmlnIiwibmFtZSIsImNsaWVudF8xIiwiY2xpZW50XzAiXSwic291cmNlcyI6WyJ1c2VNY3BDb25uZWN0aXZpdHlTdGF0dXMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlRWZmZWN0IH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnc3JjL2NvbnRleHQvbm90aWZpY2F0aW9ucy5qcydcbmltcG9ydCB7IGdldElzUmVtb3RlTW9kZSB9IGZyb20gJy4uLy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcbmltcG9ydCB7IFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyBoYXNDbGF1ZGVBaU1jcEV2ZXJDb25uZWN0ZWQgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvY2xhdWRlYWkuanMnXG5pbXBvcnQgdHlwZSB7IE1DUFNlcnZlckNvbm5lY3Rpb24gfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvdHlwZXMuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG1jcENsaWVudHM/OiBNQ1BTZXJ2ZXJDb25uZWN0aW9uW11cbn1cblxuY29uc3QgRU1QVFlfTUNQX0NMSUVOVFM6IE1DUFNlcnZlckNvbm5lY3Rpb25bXSA9IFtdXG5cbmV4cG9ydCBmdW5jdGlvbiB1c2VNY3BDb25uZWN0aXZpdHlTdGF0dXMoe1xuICBtY3BDbGllbnRzID0gRU1QVFlfTUNQX0NMSUVOVFMsXG59OiBQcm9wcyk6IHZvaWQge1xuICBjb25zdCB7IGFkZE5vdGlmaWNhdGlvbiB9ID0gdXNlTm90aWZpY2F0aW9ucygpXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICBjb25zdCBmYWlsZWRMb2NhbENsaWVudHMgPSBtY3BDbGllbnRzLmZpbHRlcihcbiAgICAgIGNsaWVudCA9PlxuICAgICAgICBjbGllbnQudHlwZSA9PT0gJ2ZhaWxlZCcgJiZcbiAgICAgICAgY2xpZW50LmNvbmZpZy50eXBlICE9PSAnc3NlLWlkZScgJiZcbiAgICAgICAgY2xpZW50LmNvbmZpZy50eXBlICE9PSAnd3MtaWRlJyAmJlxuICAgICAgICBjbGllbnQuY29uZmlnLnR5cGUgIT09ICdjbGF1ZGVhaS1wcm94eScsXG4gICAgKVxuICAgIC8vIGNsYXVkZS5haSBmYWlsdXJlcyBnZXQgYSBzZXBhcmF0ZSBub3RpZmljYXRpb246IHRoZXkgYWxtb3N0IGFsd2F5cyBpbmRpY2F0ZVxuICAgIC8vIGEgdG9vbGJveC1zZXJ2aWNlIG91dGFnZSAoc2hhcmVkIGF1dGggYmFja2VuZCksIG5vdCBhIGxvY2FsIGNvbmZpZyBpc3N1ZS5cbiAgICAvLyBPbmx5IGZsYWcgY29ubmVjdG9ycyB0aGF0IGhhdmUgcHJldmlvdXNseSBjb25uZWN0ZWQgc3VjY2Vzc2Z1bGx5IOKAlCBhblxuICAgIC8vIG9yZy1jb25maWd1cmVkIGNvbm5lY3RvciB0aGF0J3MgYmVlbiBuZWVkcy1hdXRoIHNpbmNlIGl0IGFwcGVhcmVkIGlzIG9uZVxuICAgIC8vIHRoZSB1c2VyIGhhcyBpZ25vcmVkIGFuZCBzaG91bGRuJ3QgbmFnIGFib3V0OyBvbmUgdGhhdCB3YXMgd29ya2luZ1xuICAgIC8vIHllc3RlcmRheSBhbmQgaXMgbm93IGZhaWxlZCBpcyBhIHN0YXRlIGNoYW5nZSB3b3J0aCBzdXJmYWNpbmcuXG4gICAgY29uc3QgZmFpbGVkQ2xhdWRlQWlDbGllbnRzID0gbWNwQ2xpZW50cy5maWx0ZXIoXG4gICAgICBjbGllbnQgPT5cbiAgICAgICAgY2xpZW50LnR5cGUgPT09ICdmYWlsZWQnICYmXG4gICAgICAgIGNsaWVudC5jb25maWcudHlwZSA9PT0gJ2NsYXVkZWFpLXByb3h5JyAmJlxuICAgICAgICBoYXNDbGF1ZGVBaU1jcEV2ZXJDb25uZWN0ZWQoY2xpZW50Lm5hbWUpLFxuICAgIClcbiAgICBjb25zdCBuZWVkc0F1dGhMb2NhbFNlcnZlcnMgPSBtY3BDbGllbnRzLmZpbHRlcihcbiAgICAgIGNsaWVudCA9PlxuICAgICAgICBjbGllbnQudHlwZSA9PT0gJ25lZWRzLWF1dGgnICYmIGNsaWVudC5jb25maWcudHlwZSAhPT0gJ2NsYXVkZWFpLXByb3h5JyxcbiAgICApXG4gICAgY29uc3QgbmVlZHNBdXRoQ2xhdWRlQWlTZXJ2ZXJzID0gbWNwQ2xpZW50cy5maWx0ZXIoXG4gICAgICBjbGllbnQgPT5cbiAgICAgICAgY2xpZW50LnR5cGUgPT09ICduZWVkcy1hdXRoJyAmJlxuICAgICAgICBjbGllbnQuY29uZmlnLnR5cGUgPT09ICdjbGF1ZGVhaS1wcm94eScgJiZcbiAgICAgICAgaGFzQ2xhdWRlQWlNY3BFdmVyQ29ubmVjdGVkKGNsaWVudC5uYW1lKSxcbiAgICApXG4gICAgaWYgKFxuICAgICAgZmFpbGVkTG9jYWxDbGllbnRzLmxlbmd0aCA9PT0gMCAmJlxuICAgICAgZmFpbGVkQ2xhdWRlQWlDbGllbnRzLmxlbmd0aCA9PT0gMCAmJlxuICAgICAgbmVlZHNBdXRoTG9jYWxTZXJ2ZXJzLmxlbmd0aCA9PT0gMCAmJlxuICAgICAgbmVlZHNBdXRoQ2xhdWRlQWlTZXJ2ZXJzLmxlbmd0aCA9PT0gMFxuICAgICkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuICAgIGlmIChmYWlsZWRMb2NhbENsaWVudHMubGVuZ3RoID4gMCkge1xuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAnbWNwLWZhaWxlZCcsXG4gICAgICAgIGpzeDogKFxuICAgICAgICAgIDw+XG4gICAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+XG4gICAgICAgICAgICAgIHtmYWlsZWRMb2NhbENsaWVudHMubGVuZ3RofSBNQ1B7JyAnfVxuICAgICAgICAgICAgICB7ZmFpbGVkTG9jYWxDbGllbnRzLmxlbmd0aCA9PT0gMSA/ICdzZXJ2ZXInIDogJ3NlcnZlcnMnfSBmYWlsZWRcbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiDCtyAvbWNwPC9UZXh0PlxuICAgICAgICAgIDwvPlxuICAgICAgICApLFxuICAgICAgICBwcmlvcml0eTogJ21lZGl1bScsXG4gICAgICB9KVxuICAgIH1cbiAgICBpZiAoZmFpbGVkQ2xhdWRlQWlDbGllbnRzLmxlbmd0aCA+IDApIHtcbiAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgIGtleTogJ21jcC1jbGF1ZGVhaS1mYWlsZWQnLFxuICAgICAgICBqc3g6IChcbiAgICAgICAgICA8PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJlcnJvclwiPlxuICAgICAgICAgICAgICB7ZmFpbGVkQ2xhdWRlQWlDbGllbnRzLmxlbmd0aH0gY2xhdWRlLmFpeycgJ31cbiAgICAgICAgICAgICAge2ZhaWxlZENsYXVkZUFpQ2xpZW50cy5sZW5ndGggPT09IDEgPyAnY29ubmVjdG9yJyA6ICdjb25uZWN0b3JzJ317JyAnfVxuICAgICAgICAgICAgICB1bmF2YWlsYWJsZVxuICAgICAgICAgICAgPC9UZXh0PlxuICAgICAgICAgICAgPFRleHQgZGltQ29sb3I+IMK3IC9tY3A8L1RleHQ+XG4gICAgICAgICAgPC8+XG4gICAgICAgICksXG4gICAgICAgIHByaW9yaXR5OiAnbWVkaXVtJyxcbiAgICAgIH0pXG4gICAgfVxuICAgIGlmIChuZWVkc0F1dGhMb2NhbFNlcnZlcnMubGVuZ3RoID4gMCkge1xuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAnbWNwLW5lZWRzLWF1dGgnLFxuICAgICAgICBqc3g6IChcbiAgICAgICAgICA8PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJ3YXJuaW5nXCI+XG4gICAgICAgICAgICAgIHtuZWVkc0F1dGhMb2NhbFNlcnZlcnMubGVuZ3RofSBNQ1B7JyAnfVxuICAgICAgICAgICAgICB7bmVlZHNBdXRoTG9jYWxTZXJ2ZXJzLmxlbmd0aCA9PT0gMVxuICAgICAgICAgICAgICAgID8gJ3NlcnZlciBuZWVkcydcbiAgICAgICAgICAgICAgICA6ICdzZXJ2ZXJzIG5lZWQnfXsnICd9XG4gICAgICAgICAgICAgIGF1dGhcbiAgICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICAgIDxUZXh0IGRpbUNvbG9yPiDCtyAvbWNwPC9UZXh0PlxuICAgICAgICAgIDwvPlxuICAgICAgICApLFxuICAgICAgICBwcmlvcml0eTogJ21lZGl1bScsXG4gICAgICB9KVxuICAgIH1cbiAgICBpZiAobmVlZHNBdXRoQ2xhdWRlQWlTZXJ2ZXJzLmxlbmd0aCA+IDApIHtcbiAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgIGtleTogJ21jcC1jbGF1ZGVhaS1uZWVkcy1hdXRoJyxcbiAgICAgICAganN4OiAoXG4gICAgICAgICAgPD5cbiAgICAgICAgICAgIDxUZXh0IGNvbG9yPVwid2FybmluZ1wiPlxuICAgICAgICAgICAgICB7bmVlZHNBdXRoQ2xhdWRlQWlTZXJ2ZXJzLmxlbmd0aH0gY2xhdWRlLmFpeycgJ31cbiAgICAgICAgICAgICAge25lZWRzQXV0aENsYXVkZUFpU2VydmVycy5sZW5ndGggPT09IDFcbiAgICAgICAgICAgICAgICA/ICdjb25uZWN0b3IgbmVlZHMnXG4gICAgICAgICAgICAgICAgOiAnY29ubmVjdG9ycyBuZWVkJ317JyAnfVxuICAgICAgICAgICAgICBhdXRoXG4gICAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgICA8VGV4dCBkaW1Db2xvcj4gwrcgL21jcDwvVGV4dD5cbiAgICAgICAgICA8Lz5cbiAgICAgICAgKSxcbiAgICAgICAgcHJpb3JpdHk6ICdtZWRpdW0nLFxuICAgICAgfSlcbiAgICB9XG4gIH0sIFthZGROb3RpZmljYXRpb24sIG1jcENsaWVudHNdKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBTyxLQUFLQSxLQUFLLE1BQU0sT0FBTztBQUM5QixTQUFTQyxTQUFTLFFBQVEsT0FBTztBQUNqQyxTQUFTQyxnQkFBZ0IsUUFBUSw4QkFBOEI7QUFDL0QsU0FBU0MsZUFBZSxRQUFRLDBCQUEwQjtBQUMxRCxTQUFTQyxJQUFJLFFBQVEsY0FBYztBQUNuQyxTQUFTQywyQkFBMkIsUUFBUSxnQ0FBZ0M7QUFDNUUsY0FBY0MsbUJBQW1CLFFBQVEsNkJBQTZCO0FBRXRFLEtBQUtDLEtBQUssR0FBRztFQUNYQyxVQUFVLENBQUMsRUFBRUYsbUJBQW1CLEVBQUU7QUFDcEMsQ0FBQztBQUVELE1BQU1HLGlCQUFpQixFQUFFSCxtQkFBbUIsRUFBRSxHQUFHLEVBQUU7QUFFbkQsT0FBTyxTQUFBSSx5QkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFrQztJQUFBTCxVQUFBLEVBQUFNO0VBQUEsSUFBQUgsRUFFakM7RUFETixNQUFBSCxVQUFBLEdBQUFNLEVBQThCLEtBQTlCQyxTQUE4QixHQUE5Qk4saUJBQThCLEdBQTlCSyxFQUE4QjtFQUU5QjtJQUFBRTtFQUFBLElBQTRCZCxnQkFBZ0IsQ0FBQyxDQUFDO0VBQUEsSUFBQWUsRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBTixDQUFBLFFB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QixDQUFBRSxNQUFPLEtBQUssQ0FFaEIsR0FGcEIsaUJBRW9CLEdBRnBCLGlCQUVtQixDQUFHLElBQUUsQ0FBRSxJQUU3QixFQU5DLElBQUksQ0FPTCxDQUFDLElBQUksQ0FBQyxRQUFRLENBQVIsS0FBTyxDQUFDLENBQUMsT0FBTyxFQUFyQixJQUFJLENBQXdCLEdBQzVCO1VBQUFHLFFBQUEsRUFFSztRQUNaLENBQUMsQ0FBQztNQUFBO0lBQ0gsQ0FDRjtJQUFFYixFQUFBLElBQUNGLGVBQWUsRUFBRVIsVUFBVSxDQUFDO0lBQUFJLENBQUEsTUFBQUksZUFBQTtJQUFBSixDQUFBLE1BQUFKLFVBQUE7SUFBQUksQ0FBQSxNQUFBSyxFQUFBO0lBQUFMLENBQUEsTUFBQU0sRUFBQTtFQUFBO0lBQUFELEVBQUEsR0FBQUwsQ0FBQTtJQUFBTSxFQUFBLEdBQUFOLENBQUE7RUFBQTtFQTFHaENYLFNBQVMsQ0FBQ2dCLEVBMEdULEVBQUVDLEVBQTZCLENBQUM7QUFBQTtBQTlHNUIsU0FBQVMsT0FBQUssUUFBQTtFQUFBLE9BK0JDQyxRQUFNLENBQUFDLElBQUssS0FBSyxZQUN1QixJQUF2Q0QsUUFBTSxDQUFBRSxNQUFPLENBQUFELElBQUssS0FBSyxnQkFDaUIsSUFBeEM3QiwyQkFBMkIsQ0FBQzRCLFFBQU0sQ0FBQUcsSUFBSyxDQUFDO0FBQUE7QUFqQ3pDLFNBQUFYLE9BQUFZLFFBQUE7RUFBQSxPQTJCQ0osUUFBTSxDQUFBQyxJQUFLLEtBQUssWUFBdUQsSUFBdkNELFFBQU0sQ0FBQUUsTUFBTyxDQUFBRCxJQUFLLEtBQUssZ0JBQWdCO0FBQUE7QUEzQnhFLFNBQUFYLE9BQUFlLFFBQUE7RUFBQSxPQXFCQ0wsUUFBTSxDQUFBQyxJQUFLLEtBQUssUUFDdUIsSUFBdkNELFFBQU0sQ0FBQUUsTUFBTyxDQUFBRCxJQUFLLEtBQUssZ0JBQ2lCLElBQXhDN0IsMkJBQTJCLENBQUM0QixRQUFNLENBQUFHLElBQUssQ0FBQztBQUFBO0FBdkJ6QyxTQUFBZixNQUFBWSxNQUFBO0VBQUEsT0FRQ0EsTUFBTSxDQUFBQyxJQUFLLEtBQUssUUFDZ0IsSUFBaENELE1BQU0sQ0FBQUUsTUFBTyxDQUFBRCxJQUFLLEtBQUssU0FDUSxJQUEvQkQsTUFBTSxDQUFBRSxNQUFPLENBQUFELElBQUssS0FBSyxRQUNnQixJQUF2Q0QsTUFBTSxDQUFBRSxNQUFPLENBQUFELElBQUssS0FBSyxnQkFBZ0I7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/src/hooks/notifs/useModelMigrationNotifications.tsx b/src/hooks/notifs/useModelMigrationNotifications.tsx new file mode 100644 index 0000000000000000000000000000000000000000..728b13acb8b6d1a1d15d67e0cc450eccf768d001 --- /dev/null +++ b/src/hooks/notifs/useModelMigrationNotifications.tsx @@ -0,0 +1,52 @@ +import type { Notification } from 'src/context/notifications.js'; +import { type GlobalConfig, getGlobalConfig } from 'src/utils/config.js'; +import { useStartupNotification } from './useStartupNotification.js'; + +// Shows a one-time notification right after a model migration writes its +// timestamp to config. Each entry reads its own timestamp field(s) and emits +// a notification if the write happened within the last 3s (i.e. this launch). +// Future model migrations: add an entry to MIGRATIONS below. +const MIGRATIONS: ((c: GlobalConfig) => Notification | undefined)[] = [ +// Sonnet 4.5 → 4.6 (pro/max/team premium) +c => { + if (!recent(c.sonnet45To46MigrationTimestamp)) return; + return { + key: 'sonnet-46-update', + text: 'Model updated to Sonnet 4.6', + color: 'suggestion', + priority: 'high', + timeoutMs: 3000 + }; +}, +// Opus Pro → default, or pinned 4.0/4.1 → opus alias. Both land on the +// current Opus default (4.6 for 1P). +c => { + const isLegacyRemap = Boolean(c.legacyOpusMigrationTimestamp); + const ts = c.legacyOpusMigrationTimestamp ?? c.opusProMigrationTimestamp; + if (!recent(ts)) return; + return { + key: 'opus-pro-update', + text: isLegacyRemap ? 'Model updated to Opus 4.6 · Set CLAUDE_CODE_DISABLE_LEGACY_MODEL_REMAP=1 to opt out' : 'Model updated to Opus 4.6', + color: 'suggestion', + priority: 'high', + timeoutMs: isLegacyRemap ? 8000 : 3000 + }; +}]; +export function useModelMigrationNotifications() { + useStartupNotification(_temp); +} +function _temp() { + const config = getGlobalConfig(); + const notifs = []; + for (const migration of MIGRATIONS) { + const notif = migration(config); + if (notif) { + notifs.push(notif); + } + } + return notifs.length > 0 ? notifs : null; +} +function recent(ts: number | undefined): boolean { + return ts !== undefined && Date.now() - ts < 3000; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJOb3RpZmljYXRpb24iLCJHbG9iYWxDb25maWciLCJnZXRHbG9iYWxDb25maWciLCJ1c2VTdGFydHVwTm90aWZpY2F0aW9uIiwiTUlHUkFUSU9OUyIsImMiLCJyZWNlbnQiLCJzb25uZXQ0NVRvNDZNaWdyYXRpb25UaW1lc3RhbXAiLCJrZXkiLCJ0ZXh0IiwiY29sb3IiLCJwcmlvcml0eSIsInRpbWVvdXRNcyIsImlzTGVnYWN5UmVtYXAiLCJCb29sZWFuIiwibGVnYWN5T3B1c01pZ3JhdGlvblRpbWVzdGFtcCIsInRzIiwib3B1c1Byb01pZ3JhdGlvblRpbWVzdGFtcCIsInVzZU1vZGVsTWlncmF0aW9uTm90aWZpY2F0aW9ucyIsIl90ZW1wIiwiY29uZmlnIiwibm90aWZzIiwibWlncmF0aW9uIiwibm90aWYiLCJwdXNoIiwibGVuZ3RoIiwidW5kZWZpbmVkIiwiRGF0ZSIsIm5vdyJdLCJzb3VyY2VzIjpbInVzZU1vZGVsTWlncmF0aW9uTm90aWZpY2F0aW9ucy50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBOb3RpZmljYXRpb24gfSBmcm9tICdzcmMvY29udGV4dC9ub3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IHsgdHlwZSBHbG9iYWxDb25maWcsIGdldEdsb2JhbENvbmZpZyB9IGZyb20gJ3NyYy91dGlscy9jb25maWcuanMnXG5pbXBvcnQgeyB1c2VTdGFydHVwTm90aWZpY2F0aW9uIH0gZnJvbSAnLi91c2VTdGFydHVwTm90aWZpY2F0aW9uLmpzJ1xuXG4vLyBTaG93cyBhIG9uZS10aW1lIG5vdGlmaWNhdGlvbiByaWdodCBhZnRlciBhIG1vZGVsIG1pZ3JhdGlvbiB3cml0ZXMgaXRzXG4vLyB0aW1lc3RhbXAgdG8gY29uZmlnLiBFYWNoIGVudHJ5IHJlYWRzIGl0cyBvd24gdGltZXN0YW1wIGZpZWxkKHMpIGFuZCBlbWl0c1xuLy8gYSBub3RpZmljYXRpb24gaWYgdGhlIHdyaXRlIGhhcHBlbmVkIHdpdGhpbiB0aGUgbGFzdCAzcyAoaS5lLiB0aGlzIGxhdW5jaCkuXG4vLyBGdXR1cmUgbW9kZWwgbWlncmF0aW9uczogYWRkIGFuIGVudHJ5IHRvIE1JR1JBVElPTlMgYmVsb3cuXG5jb25zdCBNSUdSQVRJT05TOiAoKGM6IEdsb2JhbENvbmZpZykgPT4gTm90aWZpY2F0aW9uIHwgdW5kZWZpbmVkKVtdID0gW1xuICAvLyBTb25uZXQgNC41IOKGkiA0LjYgKHByby9tYXgvdGVhbSBwcmVtaXVtKVxuICBjID0+IHtcbiAgICBpZiAoIXJlY2VudChjLnNvbm5ldDQ1VG80Nk1pZ3JhdGlvblRpbWVzdGFtcCkpIHJldHVyblxuICAgIHJldHVybiB7XG4gICAgICBrZXk6ICdzb25uZXQtNDYtdXBkYXRlJyxcbiAgICAgIHRleHQ6ICdNb2RlbCB1cGRhdGVkIHRvIFNvbm5ldCA0LjYnLFxuICAgICAgY29sb3I6ICdzdWdnZXN0aW9uJyxcbiAgICAgIHByaW9yaXR5OiAnaGlnaCcsXG4gICAgICB0aW1lb3V0TXM6IDMwMDAsXG4gICAgfVxuICB9LFxuICAvLyBPcHVzIFBybyDihpIgZGVmYXVsdCwgb3IgcGlubmVkIDQuMC80LjEg4oaSIG9wdXMgYWxpYXMuIEJvdGggbGFuZCBvbiB0aGVcbiAgLy8gY3VycmVudCBPcHVzIGRlZmF1bHQgKDQuNiBmb3IgMVApLlxuICBjID0+IHtcbiAgICBjb25zdCBpc0xlZ2FjeVJlbWFwID0gQm9vbGVhbihjLmxlZ2FjeU9wdXNNaWdyYXRpb25UaW1lc3RhbXApXG4gICAgY29uc3QgdHMgPSBjLmxlZ2FjeU9wdXNNaWdyYXRpb25UaW1lc3RhbXAgPz8gYy5vcHVzUHJvTWlncmF0aW9uVGltZXN0YW1wXG4gICAgaWYgKCFyZWNlbnQodHMpKSByZXR1cm5cbiAgICByZXR1cm4ge1xuICAgICAga2V5OiAnb3B1cy1wcm8tdXBkYXRlJyxcbiAgICAgIHRleHQ6IGlzTGVnYWN5UmVtYXBcbiAgICAgICAgPyAnTW9kZWwgdXBkYXRlZCB0byBPcHVzIDQuNiDCtyBTZXQgQ0xBVURFX0NPREVfRElTQUJMRV9MRUdBQ1lfTU9ERUxfUkVNQVA9MSB0byBvcHQgb3V0J1xuICAgICAgICA6ICdNb2RlbCB1cGRhdGVkIHRvIE9wdXMgNC42JyxcbiAgICAgIGNvbG9yOiAnc3VnZ2VzdGlvbicsXG4gICAgICBwcmlvcml0eTogJ2hpZ2gnLFxuICAgICAgdGltZW91dE1zOiBpc0xlZ2FjeVJlbWFwID8gODAwMCA6IDMwMDAsXG4gICAgfVxuICB9LFxuXVxuXG5leHBvcnQgZnVuY3Rpb24gdXNlTW9kZWxNaWdyYXRpb25Ob3RpZmljYXRpb25zKCk6IHZvaWQge1xuICB1c2VTdGFydHVwTm90aWZpY2F0aW9uKCgpID0+IHtcbiAgICBjb25zdCBjb25maWcgPSBnZXRHbG9iYWxDb25maWcoKVxuICAgIGNvbnN0IG5vdGlmczogTm90aWZpY2F0aW9uW10gPSBbXVxuICAgIGZvciAoY29uc3QgbWlncmF0aW9uIG9mIE1JR1JBVElPTlMpIHtcbiAgICAgIGNvbnN0IG5vdGlmID0gbWlncmF0aW9uKGNvbmZpZylcbiAgICAgIGlmIChub3RpZikgbm90aWZzLnB1c2gobm90aWYpXG4gICAgfVxuICAgIHJldHVybiBub3RpZnMubGVuZ3RoID4gMCA/IG5vdGlmcyA6IG51bGxcbiAgfSlcbn1cblxuZnVuY3Rpb24gcmVjZW50KHRzOiBudW1iZXIgfCB1bmRlZmluZWQpOiBib29sZWFuIHtcbiAgcmV0dXJuIHRzICE9PSB1bmRlZmluZWQgJiYgRGF0ZS5ub3coKSAtIHRzIDwgMzAwMFxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxjQUFjQSxZQUFZLFFBQVEsOEJBQThCO0FBQ2hFLFNBQVMsS0FBS0MsWUFBWSxFQUFFQyxlQUFlLFFBQVEscUJBQXFCO0FBQ3hFLFNBQVNDLHNCQUFzQixRQUFRLDZCQUE2Qjs7QUFFcEU7QUFDQTtBQUNBO0FBQ0E7QUFDQSxNQUFNQyxVQUFVLEVBQUUsQ0FBQyxDQUFDQyxDQUFDLEVBQUVKLFlBQVksRUFBRSxHQUFHRCxZQUFZLEdBQUcsU0FBUyxDQUFDLEVBQUUsR0FBRztBQUNwRTtBQUNBSyxDQUFDLElBQUk7RUFDSCxJQUFJLENBQUNDLE1BQU0sQ0FBQ0QsQ0FBQyxDQUFDRSw4QkFBOEIsQ0FBQyxFQUFFO0VBQy9DLE9BQU87SUFDTEMsR0FBRyxFQUFFLGtCQUFrQjtJQUN2QkMsSUFBSSxFQUFFLDZCQUE2QjtJQUNuQ0MsS0FBSyxFQUFFLFlBQVk7SUFDbkJDLFFBQVEsRUFBRSxNQUFNO0lBQ2hCQyxTQUFTLEVBQUU7RUFDYixDQUFDO0FBQ0gsQ0FBQztBQUNEO0FBQ0E7QUFDQVAsQ0FBQyxJQUFJO0VBQ0gsTUFBTVEsYUFBYSxHQUFHQyxPQUFPLENBQUNULENBQUMsQ0FBQ1UsNEJBQTRCLENBQUM7RUFDN0QsTUFBTUMsRUFBRSxHQUFHWCxDQUFDLENBQUNVLDRCQUE0QixJQUFJVixDQUFDLENBQUNZLHlCQUF5QjtFQUN4RSxJQUFJLENBQUNYLE1BQU0sQ0FBQ1UsRUFBRSxDQUFDLEVBQUU7RUFDakIsT0FBTztJQUNMUixHQUFHLEVBQUUsaUJBQWlCO0lBQ3RCQyxJQUFJLEVBQUVJLGFBQWEsR0FDZixxRkFBcUYsR0FDckYsMkJBQTJCO0lBQy9CSCxLQUFLLEVBQUUsWUFBWTtJQUNuQkMsUUFBUSxFQUFFLE1BQU07SUFDaEJDLFNBQVMsRUFBRUMsYUFBYSxHQUFHLElBQUksR0FBRztFQUNwQyxDQUFDO0FBQ0gsQ0FBQyxDQUNGO0FBRUQsT0FBTyxTQUFBSywrQkFBQTtFQUNMZixzQkFBc0IsQ0FBQ2dCLEtBUXRCLENBQUM7QUFBQTtBQVRHLFNBQUFBLE1BQUE7RUFFSCxNQUFBQyxNQUFBLEdBQWVsQixlQUFlLENBQUMsQ0FBQztFQUNoQyxNQUFBbUIsTUFBQSxHQUErQixFQUFFO0VBQ2pDLEtBQUssTUFBQUMsU0FBZSxJQUFJbEIsVUFBVTtJQUNoQyxNQUFBbUIsS0FBQSxHQUFjRCxTQUFTLENBQUNGLE1BQU0sQ0FBQztJQUMvQixJQUFJRyxLQUFLO01BQUVGLE1BQU0sQ0FBQUcsSUFBSyxDQUFDRCxLQUFLLENBQUM7SUFBQTtFQUFBO0VBQzlCLE9BQ01GLE1BQU0sQ0FBQUksTUFBTyxHQUFHLENBQWlCLEdBQWpDSixNQUFpQyxHQUFqQyxJQUFpQztBQUFBO0FBSTVDLFNBQVNmLE1BQU1BLENBQUNVLEVBQUUsRUFBRSxNQUFNLEdBQUcsU0FBUyxDQUFDLEVBQUUsT0FBTyxDQUFDO0VBQy9DLE9BQU9BLEVBQUUsS0FBS1UsU0FBUyxJQUFJQyxJQUFJLENBQUNDLEdBQUcsQ0FBQyxDQUFDLEdBQUdaLEVBQUUsR0FBRyxJQUFJO0FBQ25EIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/src/hooks/notifs/useNpmDeprecationNotification.tsx b/src/hooks/notifs/useNpmDeprecationNotification.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f0e9d2927f24cda4292ac12963a0bdb3f3cf980f --- /dev/null +++ b/src/hooks/notifs/useNpmDeprecationNotification.tsx @@ -0,0 +1,25 @@ +import { isInBundledMode } from 'src/utils/bundledMode.js'; +import { getCurrentInstallationType } from 'src/utils/doctorDiagnostic.js'; +import { isEnvTruthy } from 'src/utils/envUtils.js'; +import { useStartupNotification } from './useStartupNotification.js'; +const NPM_DEPRECATION_MESSAGE = 'Claude Code has switched from npm to native installer. Run `claude install` or see https://docs.anthropic.com/en/docs/claude-code/getting-started for more options.'; +export function useNpmDeprecationNotification() { + useStartupNotification(_temp); +} +async function _temp() { + if (isInBundledMode() || isEnvTruthy(process.env.DISABLE_INSTALLATION_CHECKS)) { + return null; + } + const installationType = await getCurrentInstallationType(); + if (installationType === "development") { + return null; + } + return { + timeoutMs: 15000, + key: "npm-deprecation-warning", + text: NPM_DEPRECATION_MESSAGE, + color: "warning", + priority: "high" + }; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJpc0luQnVuZGxlZE1vZGUiLCJnZXRDdXJyZW50SW5zdGFsbGF0aW9uVHlwZSIsImlzRW52VHJ1dGh5IiwidXNlU3RhcnR1cE5vdGlmaWNhdGlvbiIsIk5QTV9ERVBSRUNBVElPTl9NRVNTQUdFIiwidXNlTnBtRGVwcmVjYXRpb25Ob3RpZmljYXRpb24iLCJfdGVtcCIsInByb2Nlc3MiLCJlbnYiLCJESVNBQkxFX0lOU1RBTExBVElPTl9DSEVDS1MiLCJpbnN0YWxsYXRpb25UeXBlIiwidGltZW91dE1zIiwia2V5IiwidGV4dCIsImNvbG9yIiwicHJpb3JpdHkiXSwic291cmNlcyI6WyJ1c2VOcG1EZXByZWNhdGlvbk5vdGlmaWNhdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgaXNJbkJ1bmRsZWRNb2RlIH0gZnJvbSAnc3JjL3V0aWxzL2J1bmRsZWRNb2RlLmpzJ1xuaW1wb3J0IHsgZ2V0Q3VycmVudEluc3RhbGxhdGlvblR5cGUgfSBmcm9tICdzcmMvdXRpbHMvZG9jdG9yRGlhZ25vc3RpYy5qcydcbmltcG9ydCB7IGlzRW52VHJ1dGh5IH0gZnJvbSAnc3JjL3V0aWxzL2VudlV0aWxzLmpzJ1xuaW1wb3J0IHsgdXNlU3RhcnR1cE5vdGlmaWNhdGlvbiB9IGZyb20gJy4vdXNlU3RhcnR1cE5vdGlmaWNhdGlvbi5qcydcblxuY29uc3QgTlBNX0RFUFJFQ0FUSU9OX01FU1NBR0UgPVxuICAnQ2xhdWRlIENvZGUgaGFzIHN3aXRjaGVkIGZyb20gbnBtIHRvIG5hdGl2ZSBpbnN0YWxsZXIuIFJ1biBgY2xhdWRlIGluc3RhbGxgIG9yIHNlZSBodHRwczovL2RvY3MuYW50aHJvcGljLmNvbS9lbi9kb2NzL2NsYXVkZS1jb2RlL2dldHRpbmctc3RhcnRlZCBmb3IgbW9yZSBvcHRpb25zLidcblxuZXhwb3J0IGZ1bmN0aW9uIHVzZU5wbURlcHJlY2F0aW9uTm90aWZpY2F0aW9uKCk6IHZvaWQge1xuICB1c2VTdGFydHVwTm90aWZpY2F0aW9uKGFzeW5jICgpID0+IHtcbiAgICBpZiAoXG4gICAgICBpc0luQnVuZGxlZE1vZGUoKSB8fFxuICAgICAgaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuRElTQUJMRV9JTlNUQUxMQVRJT05fQ0hFQ0tTKVxuICAgICkge1xuICAgICAgcmV0dXJuIG51bGxcbiAgICB9XG4gICAgY29uc3QgaW5zdGFsbGF0aW9uVHlwZSA9IGF3YWl0IGdldEN1cnJlbnRJbnN0YWxsYXRpb25UeXBlKClcbiAgICBpZiAoaW5zdGFsbGF0aW9uVHlwZSA9PT0gJ2RldmVsb3BtZW50JykgcmV0dXJuIG51bGxcbiAgICByZXR1cm4ge1xuICAgICAgdGltZW91dE1zOiAxNTAwMCxcbiAgICAgIGtleTogJ25wbS1kZXByZWNhdGlvbi13YXJuaW5nJyxcbiAgICAgIHRleHQ6IE5QTV9ERVBSRUNBVElPTl9NRVNTQUdFLFxuICAgICAgY29sb3I6ICd3YXJuaW5nJyxcbiAgICAgIHByaW9yaXR5OiAnaGlnaCcsXG4gICAgfVxuICB9KVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQSxTQUFTQSxlQUFlLFFBQVEsMEJBQTBCO0FBQzFELFNBQVNDLDBCQUEwQixRQUFRLCtCQUErQjtBQUMxRSxTQUFTQyxXQUFXLFFBQVEsdUJBQXVCO0FBQ25ELFNBQVNDLHNCQUFzQixRQUFRLDZCQUE2QjtBQUVwRSxNQUFNQyx1QkFBdUIsR0FDM0IscUtBQXFLO0FBRXZLLE9BQU8sU0FBQUMsOEJBQUE7RUFDTEYsc0JBQXNCLENBQUNHLEtBZ0J0QixDQUFDO0FBQUE7QUFqQkcsZUFBQUEsTUFBQTtFQUVILElBQ0VOLGVBQWUsQ0FDb0MsQ0FBQyxJQUFwREUsV0FBVyxDQUFDSyxPQUFPLENBQUFDLEdBQUksQ0FBQUMsMkJBQTRCLENBQUM7SUFBQSxPQUU3QyxJQUFJO0VBQUE7RUFFYixNQUFBQyxnQkFBQSxHQUF5QixNQUFNVCwwQkFBMEIsQ0FBQyxDQUFDO0VBQzNELElBQUlTLGdCQUFnQixLQUFLLGFBQWE7SUFBQSxPQUFTLElBQUk7RUFBQTtFQUFBLE9BQzVDO0lBQUFDLFNBQUEsRUFDTSxLQUFLO0lBQUFDLEdBQUEsRUFDWCx5QkFBeUI7SUFBQUMsSUFBQSxFQUN4QlQsdUJBQXVCO0lBQUFVLEtBQUEsRUFDdEIsU0FBUztJQUFBQyxRQUFBLEVBQ047RUFDWixDQUFDO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/src/hooks/notifs/usePluginAutoupdateNotification.tsx b/src/hooks/notifs/usePluginAutoupdateNotification.tsx new file mode 100644 index 0000000000000000000000000000000000000000..32a19ce44540ebfc5f3f6ecf3f95fb23eabca3ba --- /dev/null +++ b/src/hooks/notifs/usePluginAutoupdateNotification.tsx @@ -0,0 +1,83 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import { getIsRemoteMode } from '../../bootstrap/state.js'; +import { useNotifications } from '../../context/notifications.js'; +import { Text } from '../../ink.js'; +import { logForDebugging } from '../../utils/debug.js'; +import { onPluginsAutoUpdated } from '../../utils/plugins/pluginAutoupdate.js'; + +/** + * Hook that displays a notification when plugins have been auto-updated. + * The notification tells the user to run /reload-plugins to apply the updates. + */ +export function usePluginAutoupdateNotification() { + const $ = _c(7); + const { + addNotification + } = useNotifications(); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = []; + $[0] = t0; + } else { + t0 = $[0]; + } + const [updatedPlugins, setUpdatedPlugins] = useState(t0); + let t1; + let t2; + if ($[1] === Symbol.for("react.memo_cache_sentinel")) { + t1 = () => { + if (getIsRemoteMode()) { + return; + } + const unsubscribe = onPluginsAutoUpdated(plugins => { + logForDebugging(`Plugin autoupdate notification: ${plugins.length} plugin(s) updated`); + setUpdatedPlugins(plugins); + }); + return unsubscribe; + }; + t2 = []; + $[1] = t1; + $[2] = t2; + } else { + t1 = $[1]; + t2 = $[2]; + } + useEffect(t1, t2); + let t3; + let t4; + if ($[3] !== addNotification || $[4] !== updatedPlugins) { + t3 = () => { + if (getIsRemoteMode()) { + return; + } + if (updatedPlugins.length === 0) { + return; + } + const pluginNames = updatedPlugins.map(_temp); + const displayNames = pluginNames.length <= 2 ? pluginNames.join(" and ") : `${pluginNames.length} plugins`; + addNotification({ + key: "plugin-autoupdate-restart", + jsx: <>{pluginNames.length === 1 ? "Plugin" : "Plugins"} updated:{" "}{displayNames} · Run /reload-plugins to apply, + priority: "low", + timeoutMs: 10000 + }); + logForDebugging(`Showing plugin autoupdate notification for: ${pluginNames.join(", ")}`); + }; + t4 = [updatedPlugins, addNotification]; + $[3] = addNotification; + $[4] = updatedPlugins; + $[5] = t3; + $[6] = t4; + } else { + t3 = $[5]; + t4 = $[6]; + } + useEffect(t3, t4); +} +function _temp(id) { + const atIndex = id.indexOf("@"); + return atIndex > 0 ? id.substring(0, atIndex) : id; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZVN0YXRlIiwiZ2V0SXNSZW1vdGVNb2RlIiwidXNlTm90aWZpY2F0aW9ucyIsIlRleHQiLCJsb2dGb3JEZWJ1Z2dpbmciLCJvblBsdWdpbnNBdXRvVXBkYXRlZCIsInVzZVBsdWdpbkF1dG91cGRhdGVOb3RpZmljYXRpb24iLCIkIiwiX2MiLCJhZGROb3RpZmljYXRpb24iLCJ0MCIsIlN5bWJvbCIsImZvciIsInVwZGF0ZWRQbHVnaW5zIiwic2V0VXBkYXRlZFBsdWdpbnMiLCJ0MSIsInQyIiwidW5zdWJzY3JpYmUiLCJwbHVnaW5zIiwibGVuZ3RoIiwidDMiLCJ0NCIsInBsdWdpbk5hbWVzIiwibWFwIiwiX3RlbXAiLCJkaXNwbGF5TmFtZXMiLCJqb2luIiwia2V5IiwianN4IiwicHJpb3JpdHkiLCJ0aW1lb3V0TXMiLCJpZCIsImF0SW5kZXgiLCJpbmRleE9mIiwic3Vic3RyaW5nIl0sInNvdXJjZXMiOlsidXNlUGx1Z2luQXV0b3VwZGF0ZU5vdGlmaWNhdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBnZXRJc1JlbW90ZU1vZGUgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnLi4vLi4vY29udGV4dC9ub3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uLy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHsgb25QbHVnaW5zQXV0b1VwZGF0ZWQgfSBmcm9tICcuLi8uLi91dGlscy9wbHVnaW5zL3BsdWdpbkF1dG91cGRhdGUuanMnXG5cbi8qKlxuICogSG9vayB0aGF0IGRpc3BsYXlzIGEgbm90aWZpY2F0aW9uIHdoZW4gcGx1Z2lucyBoYXZlIGJlZW4gYXV0by11cGRhdGVkLlxuICogVGhlIG5vdGlmaWNhdGlvbiB0ZWxscyB0aGUgdXNlciB0byBydW4gL3JlbG9hZC1wbHVnaW5zIHRvIGFwcGx5IHRoZSB1cGRhdGVzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlUGx1Z2luQXV0b3VwZGF0ZU5vdGlmaWNhdGlvbigpOiB2b2lkIHtcbiAgY29uc3QgeyBhZGROb3RpZmljYXRpb24gfSA9IHVzZU5vdGlmaWNhdGlvbnMoKVxuICBjb25zdCBbdXBkYXRlZFBsdWdpbnMsIHNldFVwZGF0ZWRQbHVnaW5zXSA9IHVzZVN0YXRlPHN0cmluZ1tdPihbXSlcblxuICAvLyBSZWdpc3RlciBmb3IgYXV0b3VwZGF0ZSBub3RpZmljYXRpb25zXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICBjb25zdCB1bnN1YnNjcmliZSA9IG9uUGx1Z2luc0F1dG9VcGRhdGVkKHBsdWdpbnMgPT4ge1xuICAgICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgICBgUGx1Z2luIGF1dG91cGRhdGUgbm90aWZpY2F0aW9uOiAke3BsdWdpbnMubGVuZ3RofSBwbHVnaW4ocykgdXBkYXRlZGAsXG4gICAgICApXG4gICAgICBzZXRVcGRhdGVkUGx1Z2lucyhwbHVnaW5zKVxuICAgIH0pXG5cbiAgICByZXR1cm4gdW5zdWJzY3JpYmVcbiAgfSwgW10pXG5cbiAgLy8gU2hvdyBub3RpZmljYXRpb24gd2hlbiBwbHVnaW5zIGFyZSB1cGRhdGVkXG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgaWYgKGdldElzUmVtb3RlTW9kZSgpKSByZXR1cm5cbiAgICBpZiAodXBkYXRlZFBsdWdpbnMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBFeHRyYWN0IHBsdWdpbiBuYW1lcyBmcm9tIHBsdWdpbiBJRHMgKGZvcm1hdDogXCJuYW1lQG1hcmtldHBsYWNlXCIpXG4gICAgY29uc3QgcGx1Z2luTmFtZXMgPSB1cGRhdGVkUGx1Z2lucy5tYXAoaWQgPT4ge1xuICAgICAgY29uc3QgYXRJbmRleCA9IGlkLmluZGV4T2YoJ0AnKVxuICAgICAgcmV0dXJuIGF0SW5kZXggPiAwID8gaWQuc3Vic3RyaW5nKDAsIGF0SW5kZXgpIDogaWRcbiAgICB9KVxuXG4gICAgY29uc3QgZGlzcGxheU5hbWVzID1cbiAgICAgIHBsdWdpbk5hbWVzLmxlbmd0aCA8PSAyXG4gICAgICAgID8gcGx1Z2luTmFtZXMuam9pbignIGFuZCAnKVxuICAgICAgICA6IGAke3BsdWdpbk5hbWVzLmxlbmd0aH0gcGx1Z2luc2BcblxuICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICBrZXk6ICdwbHVnaW4tYXV0b3VwZGF0ZS1yZXN0YXJ0JyxcbiAgICAgIGpzeDogKFxuICAgICAgICA8PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPVwic3VjY2Vzc1wiPlxuICAgICAgICAgICAge3BsdWdpbk5hbWVzLmxlbmd0aCA9PT0gMSA/ICdQbHVnaW4nIDogJ1BsdWdpbnMnfSB1cGRhdGVkOnsnICd9XG4gICAgICAgICAgICB7ZGlzcGxheU5hbWVzfVxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dCBkaW1Db2xvcj4gwrcgUnVuIC9yZWxvYWQtcGx1Z2lucyB0byBhcHBseTwvVGV4dD5cbiAgICAgICAgPC8+XG4gICAgICApLFxuICAgICAgcHJpb3JpdHk6ICdsb3cnLFxuICAgICAgdGltZW91dE1zOiAxMDAwMCxcbiAgICB9KVxuXG4gICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgYFNob3dpbmcgcGx1Z2luIGF1dG91cGRhdGUgbm90aWZpY2F0aW9uIGZvcjogJHtwbHVnaW5OYW1lcy5qb2luKCcsICcpfWAsXG4gICAgKVxuICB9LCBbdXBkYXRlZFBsdWdpbnMsIGFkZE5vdGlmaWNhdGlvbl0pXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLEtB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 \ No newline at end of file diff --git a/src/hooks/notifs/usePluginInstallationStatus.tsx b/src/hooks/notifs/usePluginInstallationStatus.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9be20c3e80bdf5fad015d740f623a3a60abd2f44 --- /dev/null +++ b/src/hooks/notifs/usePluginInstallationStatus.tsx @@ -0,0 +1,128 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useEffect, useMemo } from 'react'; +import { getIsRemoteMode } from '../../bootstrap/state.js'; +import { useNotifications } from '../../context/notifications.js'; +import { Text } from '../../ink.js'; +import { useAppState } from '../../state/AppState.js'; +import { logForDebugging } from '../../utils/debug.js'; +import { plural } from '../../utils/stringUtils.js'; +export function usePluginInstallationStatus() { + const $ = _c(20); + const { + addNotification + } = useNotifications(); + const installationStatus = useAppState(_temp); + let t0; + bb0: { + if (!installationStatus) { + let t1; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t1 = { + totalFailed: 0, + failedMarketplacesCount: 0, + failedPluginsCount: 0 + }; + $[0] = t1; + } else { + t1 = $[0]; + } + t0 = t1; + break bb0; + } + let t1; + if ($[1] !== installationStatus.marketplaces) { + t1 = installationStatus.marketplaces.filter(_temp2); + $[1] = installationStatus.marketplaces; + $[2] = t1; + } else { + t1 = $[2]; + } + const failedMarketplaces = t1; + let t2; + if ($[3] !== installationStatus.plugins) { + t2 = installationStatus.plugins.filter(_temp3); + $[3] = installationStatus.plugins; + $[4] = t2; + } else { + t2 = $[4]; + } + const failedPlugins = t2; + const t3 = failedMarketplaces.length + failedPlugins.length; + let t4; + if ($[5] !== failedMarketplaces.length || $[6] !== failedPlugins.length || $[7] !== t3) { + t4 = { + totalFailed: t3, + failedMarketplacesCount: failedMarketplaces.length, + failedPluginsCount: failedPlugins.length + }; + $[5] = failedMarketplaces.length; + $[6] = failedPlugins.length; + $[7] = t3; + $[8] = t4; + } else { + t4 = $[8]; + } + t0 = t4; + } + const { + totalFailed, + failedMarketplacesCount, + failedPluginsCount + } = t0; + let t1; + if ($[9] !== addNotification || $[10] !== failedMarketplacesCount || $[11] !== failedPluginsCount || $[12] !== installationStatus || $[13] !== totalFailed) { + t1 = () => { + if (getIsRemoteMode()) { + return; + } + if (!installationStatus) { + logForDebugging("No installation status to monitor"); + return; + } + if (totalFailed === 0) { + return; + } + logForDebugging(`Plugin installation status: ${failedMarketplacesCount} failed marketplaces, ${failedPluginsCount} failed plugins`); + if (totalFailed === 0) { + return; + } + logForDebugging(`Adding notification for ${totalFailed} failed installations`); + addNotification({ + key: "plugin-install-failed", + jsx: <>{totalFailed} {plural(totalFailed, "plugin")} failed to install · /plugin for details, + priority: "medium" + }); + }; + $[9] = addNotification; + $[10] = failedMarketplacesCount; + $[11] = failedPluginsCount; + $[12] = installationStatus; + $[13] = totalFailed; + $[14] = t1; + } else { + t1 = $[14]; + } + let t2; + if ($[15] !== addNotification || $[16] !== failedMarketplacesCount || $[17] !== failedPluginsCount || $[18] !== totalFailed) { + t2 = [addNotification, totalFailed, failedMarketplacesCount, failedPluginsCount]; + $[15] = addNotification; + $[16] = failedMarketplacesCount; + $[17] = failedPluginsCount; + $[18] = totalFailed; + $[19] = t2; + } else { + t2 = $[19]; + } + useEffect(t1, t2); +} +function _temp3(p) { + return p.status === "failed"; +} +function _temp2(m) { + return m.status === "failed"; +} +function _temp(s) { + return s.plugins.installationStatus; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJnZXRJc1JlbW90ZU1vZGUiLCJ1c2VOb3RpZmljYXRpb25zIiwiVGV4dCIsInVzZUFwcFN0YXRlIiwibG9nRm9yRGVidWdnaW5nIiwicGx1cmFsIiwidXNlUGx1Z2luSW5zdGFsbGF0aW9uU3RhdHVzIiwiJCIsIl9jIiwiYWRkTm90aWZpY2F0aW9uIiwiaW5zdGFsbGF0aW9uU3RhdHVzIiwiX3RlbXAiLCJ0MCIsImJiMCIsInQxIiwiU3ltYm9sIiwiZm9yIiwidG90YWxGYWlsZWQiLCJmYWlsZWRNYXJrZXRwbGFjZXNDb3VudCIsImZhaWxlZFBsdWdpbnNDb3VudCIsIm1hcmtldHBsYWNlcyIsImZpbHRlciIsIl90ZW1wMiIsImZhaWxlZE1hcmtldHBsYWNlcyIsInQyIiwicGx1Z2lucyIsIl90ZW1wMyIsImZhaWxlZFBsdWdpbnMiLCJ0MyIsImxlbmd0aCIsInQ0Iiwia2V5IiwianN4IiwicHJpb3JpdHkiLCJwIiwic3RhdHVzIiwibSIsInMiXSwic291cmNlcyI6WyJ1c2VQbHVnaW5JbnN0YWxsYXRpb25TdGF0dXMudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VNZW1vIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBnZXRJc1JlbW90ZU1vZGUgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgeyB1c2VOb3RpZmljYXRpb25zIH0gZnJvbSAnLi4vLi4vY29udGV4dC9ub3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJy4uLy4uL2luay5qcydcbmltcG9ydCB7IHVzZUFwcFN0YXRlIH0gZnJvbSAnLi4vLi4vc3RhdGUvQXBwU3RhdGUuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi8uLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IHBsdXJhbCB9IGZyb20gJy4uLy4uL3V0aWxzL3N0cmluZ1V0aWxzLmpzJ1xuXG5leHBvcnQgZnVuY3Rpb24gdXNlUGx1Z2luSW5zdGFsbGF0aW9uU3RhdHVzKCk6IHZvaWQge1xuICBjb25zdCB7IGFkZE5vdGlmaWNhdGlvbiB9ID0gdXNlTm90aWZpY2F0aW9ucygpXG4gIGNvbnN0IGluc3RhbGxhdGlvblN0YXR1cyA9IHVzZUFwcFN0YXRlKHMgPT4gcy5wbHVnaW5zLmluc3RhbGxhdGlvblN0YXR1cylcblxuICAvLyBNZW1vaXplIHRoZSBmYWlsZWQgY291bnRzIHRvIHByZXZlbnQgdW5uZWNlc3NhcnkgZWZmZWN0IHRyaWdnZXJzXG4gIGNvbnN0IHsgdG90YWxGYWlsZWQsIGZhaWxlZE1hcmtldHBsYWNlc0NvdW50LCBmYWlsZWRQbHVnaW5zQ291bnQgfSA9XG4gICAgdXNlTWVtbygoKSA9PiB7XG4gICAgICBpZiAoIWluc3RhbGxhdGlvblN0YXR1cykge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIHRvdGFsRmFpbGVkOiAwLFxuICAgICAgICAgIGZhaWxlZE1hcmtldHBsYWNlc0NvdW50OiAwLFxuICAgICAgICAgIGZhaWxlZFBsdWdpbnNDb3VudDogMCxcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBjb25zdCBmYWlsZWRNYXJrZXRwbGFjZXMgPSBpbnN0YWxsYXRpb25TdGF0dXMubWFya2V0cGxhY2VzLmZpbHRlcihcbiAgICAgICAgbSA9PiBtLnN0YXR1cyA9PT0gJ2ZhaWxlZCcsXG4gICAgICApXG4gICAgICBjb25zdCBmYWlsZWRQbHVnaW5zID0gaW5zdGFsbGF0aW9uU3RhdHVzLnBsdWdpbnMuZmlsdGVyKFxuICAgICAgICBwID0+IHAuc3RhdHVzID09PSAnZmFpbGVkJyxcbiAgICAgIClcblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgdG90YWxGYWlsZWQ6IGZhaWxlZE1hcmtldHBsYWNlcy5sZW5ndGggKyBmYWlsZWRQbHVnaW5zLmxlbmd0aCxcbiAgICAgICAgZmFpbGVkTWFya2V0cGxhY2VzQ291bnQ6IGZhaWxlZE1hcmtldHBsYWNlcy5sZW5ndGgsXG4gICAgICAgIGZhaWxlZFBsdWdpbnNDb3VudDogZmFpbGVkUGx1Z2lucy5sZW5ndGgsXG4gICAgICB9XG4gICAgfSwgW2luc3RhbGxhdGlvblN0YXR1c10pXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZ2V0SXNSZW1vdGVNb2RlKCkpIHJldHVyblxuICAgIGlmICghaW5zdGFsbGF0aW9uU3RhdHVzKSB7XG4gICAgICBsb2dGb3JEZWJ1Z2dpbmcoJ05vIGluc3RhbGxhdGlvbiBzdGF0dXMgdG8gbW9uaXRvcicpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBpZiAodG90YWxGYWlsZWQgPT09IDApIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgIGBQbHVnaW4gaW5zdGFsbGF0aW9uIHN0YXR1czogJHtmYWlsZWRNYXJrZXRwbGFjZXNDb3VudH0gZmFpbGVkIG1hcmtldHBsYWNlcywgJHtmYWlsZWRQbHVnaW5zQ291bnR9IGZhaWxlZCBwbHVnaW5zYCxcbiAgICApXG5cbiAgICBpZiAodG90YWxGYWlsZWQgPT09IDApIHtcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIEFkZCBub3RpZmljYXRpb24gZm9yIGZhaWx1cmVzXG4gICAgbG9nRm9yRGVidWdnaW5nKFxuICAgICAgYEFkZGluZyBub3RpZmljYXRpb24gZm9yICR7dG90YWxGYWlsZWR9IGZhaWxlZCBpbnN0YWxsYXRpb25zYCxcbiAgICApXG4gICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgIGtleTogJ3BsdWdpbi1pbnN0YWxsLWZhaWxlZCcsXG4gICAgICBqc3g6IChcbiAgICAgICAgPD5cbiAgICAgICAgICA8VGV4dCBjb2xvcj1cImVycm9yXCI+XG4gICAgICAgICAgICB7dG90YWxGYWlsZWR9IHtwbHVyYWwodG90YWxGYWlsZWQsICdwbHVnaW4nKX0gZmFpbGVkIHRvIGluc3RhbGxcbiAgICAgICAgICA8L1RleHQ+XG4gICAgICAgICAgPFRleHQgZGltQ29sb3I+IMK3IC9wbHVnaW4gZm9yIGRldGFpbHM8L1RleHQ+XG4gICAgICAgIDwvPlxuICAgICAgKSxcbiAgICAgIHByaW9yaXR5OiAnbWVkaXVtJyxcbiAgICB9KVxuICB9LCBbXG4gICAgYWRkTm90aWZpY2F0aW9uLFxuICAgIHRvdGFsRmFpbGVkLFxuICAgIGZhaWxlZE1hcmtldHBsYWNlc0NvdW50LFxuICAgIGZhaWxlZFBsdWdpbnNDb3VudCxcbiAgXSlcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsU0FBUyxFQUFFQyxPQUFPLFFBQVEsT0FBTztBQUMxQyxTQUFTQyxlQUFlLFFBQVEsMEJBQTBCO0FBQzFELFNBQVNDLGdCQUFnQixRQUFRLGdDQUFnQztBQUNqRSxTQUFTQyxJQUFJLFFBQVEsY0FBYztBQUNuQyxTQUFTQyxXQUFXLFFBQVEseUJBQXlCO0FBQ3JELFNBQVNDLGVBQWUsUUFBUSxzQkFBc0I7QUFDdEQsU0FBU0MsTUFBTSxRQUFRLDRCQUE0QjtBQUVuRCxPQUFPLFNBQUFDLDRCQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQ0w7SUFBQUM7RUFBQSxJQUE0QlIsZ0JBQWdCLENBQUMsQ0FBQztFQUM5QyxNQUFBUyxrQkFBQSxHQUEyQlAsV0FBVyxDQUFDUSxLQUFpQyxDQUFDO0VBQUEsSUFBQUMsRUFBQTtFQUFBQyxHQUFBO0lBS3JFLElBQUksQ0FBQ0gsa0JBQWtCO01BQUEsSUFBQUksRUFBQTtNQUFBLElBQUFQLENBQUEsUUFBQVEsTUFBQSxDQUFBQyxHQUFBO1FBQ2RGLEVBQUE7VUFBQUcsV0FBQSxFQUNRLENBQUM7VUFBQUMsdUJBQUEsRUFDVyxDQUFDO1VBQUFDLGtCQUFBLEVBQ047UUFDdEIsQ0FBQztRQUFBWixDQUFBLE1BQUFPLEVBQUE7TUFBQTtRQUFBQSxFQUFBLEdBQUFQLENBQUE7TUFBQTtNQUpESyxFQUFBLEdB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 \ No newline at end of file diff --git a/src/hooks/notifs/useRateLimitWarningNotification.tsx b/src/hooks/notifs/useRateLimitWarningNotification.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f54535524fa74ba547bfe80a7253f53af5d41ef4 --- /dev/null +++ b/src/hooks/notifs/useRateLimitWarningNotification.tsx @@ -0,0 +1,114 @@ +import { c as _c } from "react/compiler-runtime"; +import * as React from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { useNotifications } from 'src/context/notifications.js'; +import { Text } from 'src/ink.js'; +import { getRateLimitWarning, getUsingOverageText } from 'src/services/claudeAiLimits.js'; +import { useClaudeAiLimits } from 'src/services/claudeAiLimitsHook.js'; +import { getSubscriptionType } from 'src/utils/auth.js'; +import { hasClaudeAiBillingAccess } from 'src/utils/billing.js'; +import { getIsRemoteMode } from '../../bootstrap/state.js'; +export function useRateLimitWarningNotification(model) { + const $ = _c(17); + const { + addNotification + } = useNotifications(); + const claudeAiLimits = useClaudeAiLimits(); + let t0; + if ($[0] !== claudeAiLimits || $[1] !== model) { + t0 = getRateLimitWarning(claudeAiLimits, model); + $[0] = claudeAiLimits; + $[1] = model; + $[2] = t0; + } else { + t0 = $[2]; + } + const rateLimitWarning = t0; + let t1; + if ($[3] !== claudeAiLimits) { + t1 = getUsingOverageText(claudeAiLimits); + $[3] = claudeAiLimits; + $[4] = t1; + } else { + t1 = $[4]; + } + const usingOverageText = t1; + const shownWarningRef = useRef(null); + let t2; + if ($[5] === Symbol.for("react.memo_cache_sentinel")) { + t2 = getSubscriptionType(); + $[5] = t2; + } else { + t2 = $[5]; + } + const subscriptionType = t2; + let t3; + if ($[6] === Symbol.for("react.memo_cache_sentinel")) { + t3 = hasClaudeAiBillingAccess(); + $[6] = t3; + } else { + t3 = $[6]; + } + const hasBillingAccess = t3; + const isTeamOrEnterprise = subscriptionType === "team" || subscriptionType === "enterprise"; + const [hasShownOverageNotification, setHasShownOverageNotification] = useState(false); + let t4; + let t5; + if ($[7] !== addNotification || $[8] !== claudeAiLimits.isUsingOverage || $[9] !== hasShownOverageNotification || $[10] !== usingOverageText) { + t4 = () => { + if (getIsRemoteMode()) { + return; + } + if (claudeAiLimits.isUsingOverage && !hasShownOverageNotification && (!isTeamOrEnterprise || hasBillingAccess)) { + addNotification({ + key: "limit-reached", + text: usingOverageText, + priority: "immediate" + }); + setHasShownOverageNotification(true); + } else { + if (!claudeAiLimits.isUsingOverage && hasShownOverageNotification) { + setHasShownOverageNotification(false); + } + } + }; + t5 = [claudeAiLimits.isUsingOverage, usingOverageText, hasShownOverageNotification, addNotification, hasBillingAccess, isTeamOrEnterprise]; + $[7] = addNotification; + $[8] = claudeAiLimits.isUsingOverage; + $[9] = hasShownOverageNotification; + $[10] = usingOverageText; + $[11] = t4; + $[12] = t5; + } else { + t4 = $[11]; + t5 = $[12]; + } + useEffect(t4, t5); + let t6; + let t7; + if ($[13] !== addNotification || $[14] !== rateLimitWarning) { + t6 = () => { + if (getIsRemoteMode()) { + return; + } + if (rateLimitWarning && rateLimitWarning !== shownWarningRef.current) { + shownWarningRef.current = rateLimitWarning; + addNotification({ + key: "rate-limit-warning", + jsx: {rateLimitWarning}, + priority: "high" + }); + } + }; + t7 = [rateLimitWarning, addNotification]; + $[13] = addNotification; + $[14] = rateLimitWarning; + $[15] = t6; + $[16] = t7; + } else { + t6 = $[15]; + t7 = $[16]; + } + useEffect(t6, t7); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZUVmZmVjdCIsInVzZU1lbW8iLCJ1c2VSZWYiLCJ1c2VTdGF0ZSIsInVzZU5vdGlmaWNhdGlvbnMiLCJUZXh0IiwiZ2V0UmF0ZUxpbWl0V2FybmluZyIsImdldFVzaW5nT3ZlcmFnZVRleHQiLCJ1c2VDbGF1ZGVBaUxpbWl0cyIsImdldFN1YnNjcmlwdGlvblR5cGUiLCJoYXNDbGF1ZGVBaUJpbGxpbmdBY2Nlc3MiLCJnZXRJc1JlbW90ZU1vZGUiLCJ1c2VSYXRlTGltaXRXYXJuaW5nTm90aWZpY2F0aW9uIiwibW9kZWwiLCIkIiwiX2MiLCJhZGROb3RpZmljYXRpb24iLCJjbGF1ZGVBaUxpbWl0cyIsInQwIiwicmF0ZUxpbWl0V2FybmluZyIsInQxIiwidXNpbmdPdmVyYWdlVGV4dCIsInNob3duV2FybmluZ1JlZiIsInQyIiwiU3ltYm9sIiwiZm9yIiwic3Vic2NyaXB0aW9uVHlwZSIsInQzIiwiaGFzQmlsbGluZ0FjY2VzcyIsImlzVGVhbU9yRW50ZXJwcmlzZSIsImhhc1Nob3duT3ZlcmFnZU5vdGlmaWNhdGlvbiIsInNldEhhc1Nob3duT3ZlcmFnZU5vdGlmaWNhdGlvbiIsInQ0IiwidDUiLCJpc1VzaW5nT3ZlcmFnZSIsImtleSIsInRleHQiLCJwcmlvcml0eSIsInQ2IiwidDciLCJjdXJyZW50IiwianN4Il0sInNvdXJjZXMiOlsidXNlUmF0ZUxpbWl0V2FybmluZ05vdGlmaWNhdGlvbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyB1c2VFZmZlY3QsIHVzZU1lbW8sIHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZU5vdGlmaWNhdGlvbnMgfSBmcm9tICdzcmMvY29udGV4dC9ub3RpZmljYXRpb25zLmpzJ1xuaW1wb3J0IHsgVGV4dCB9IGZyb20gJ3NyYy9pbmsuanMnXG5pbXBvcnQge1xuICBnZXRSYXRlTGltaXRXYXJuaW5nLFxuICBnZXRVc2luZ092ZXJhZ2VUZXh0LFxufSBmcm9tICdzcmMvc2VydmljZXMvY2xhdWRlQWlMaW1pdHMuanMnXG5pbXBvcnQgeyB1c2VDbGF1ZGVBaUxpbWl0cyB9IGZyb20gJ3NyYy9zZXJ2aWNlcy9jbGF1ZGVBaUxpbWl0c0hvb2suanMnXG5pbXBvcnQgeyBnZXRTdWJzY3JpcHRpb25UeXBlIH0gZnJvbSAnc3JjL3V0aWxzL2F1dGguanMnXG5pbXBvcnQgeyBoYXNDbGF1ZGVBaUJpbGxpbmdBY2Nlc3MgfSBmcm9tICdzcmMvdXRpbHMvYmlsbGluZy5qcydcbmltcG9ydCB7IGdldElzUmVtb3RlTW9kZSB9IGZyb20gJy4uLy4uL2Jvb3RzdHJhcC9zdGF0ZS5qcydcblxuZXhwb3J0IGZ1bmN0aW9uIHVzZVJhdGVMaW1pdFdhcm5pbmdOb3RpZmljYXRpb24obW9kZWw6IHN0cmluZyk6IHZvaWQge1xuICBjb25zdCB7IGFkZE5vdGlmaWNhdGlvbiB9ID0gdXNlTm90aWZpY2F0aW9ucygpXG4gIGNvbnN0IGNsYXVkZUFpTGltaXRzID0gdXNlQ2xhdWRlQWlMaW1pdHMoKVxuICAvLyBjbGF1ZGVBaUxpbWl0cyByZWZlcmVuY2UgaXMgc3RhYmxlIHVudGlsIHN0YXR1c0xpc3RlbmVycyBmaXJlIChBUElcbiAgLy8gcmVzcG9uc2UpLCBzbyB0aGVzZSBza2lwIHRoZSBJbnRsIGZvcm1hdHRpbmcgd29yayBvbiBtb3N0IFJFUEwgcmVuZGVycy5cbiAgY29uc3QgcmF0ZUxpbWl0V2FybmluZyA9IHVzZU1lbW8oXG4gICAgKCkgPT4gZ2V0UmF0ZUxpbWl0V2FybmluZyhjbGF1ZGVBaUxpbWl0cywgbW9kZWwpLFxuICAgIFtjbGF1ZGVBaUxpbWl0cywgbW9kZWxdLFxuICApXG4gIGNvbnN0IHVzaW5nT3ZlcmFnZVRleHQgPSB1c2VNZW1vKFxuICAgICgpID0+IGdldFVzaW5nT3ZlcmFnZVRleHQoY2xhdWRlQWlMaW1pdHMpLFxuICAgIFtjbGF1ZGVBaUxpbWl0c10sXG4gIClcbiAgY29uc3Qgc2hvd25XYXJuaW5nUmVmID0gdXNlUmVmPHN0cmluZyB8IG51bGw+KG51bGwpXG4gIGNvbnN0IHN1YnNjcmlwdGlvblR5cGUgPSBnZXRTdWJzY3JpcHRpb25UeXBlKClcbiAgY29uc3QgaGFzQmlsbGluZ0FjY2VzcyA9IGhhc0NsYXVkZUFpQmlsbGluZ0FjY2VzcygpXG4gIGNvbnN0IGlzVGVhbU9yRW50ZXJwcmlzZSA9XG4gICAgc3Vic2NyaXB0aW9uVHlwZSA9PT0gJ3RlYW0nIHx8IHN1YnNjcmlwdGlvblR5cGUgPT09ICdlbnRlcnByaXNlJ1xuXG4gIC8vIFRyYWNrIG92ZXJhZ2UgbW9kZSB0cmFuc2l0aW9uc1xuICBjb25zdCBbaGFzU2hvd25PdmVyYWdlTm90aWZpY2F0aW9uLCBzZXRIYXNTaG93bk92ZXJhZ2VOb3RpZmljYXRpb25dID1cbiAgICB1c2VTdGF0ZShmYWxzZSlcblxuICAvLyBTaG93IGltbWVkaWF0ZSBub3RpZmljYXRpb24gd2hlbiBlbnRlcmluZyBvdmVyYWdlIG1vZGVcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZ2V0SXNSZW1vdGVNb2RlKCkpIHJldHVyblxuICAgIGlmIChcbiAgICAgIGNsYXVkZUFpTGltaXRzLmlzVXNpbmdPdmVyYWdlICYmXG4gICAgICAhaGFzU2hvd25PdmVyYWdlTm90aWZpY2F0aW9uICYmXG4gICAgICAoIWlzVGVhbU9yRW50ZXJwcmlzZSB8fCBoYXNCaWxsaW5nQWNjZXNzKVxuICAgICkge1xuICAgICAgYWRkTm90aWZpY2F0aW9uKHtcbiAgICAgICAga2V5OiAnbGltaXQtcmVhY2hlZCcsXG4gICAgICAgIHRleHQ6IHVzaW5nT3ZlcmFnZVRleHQsXG4gICAgICAgIHByaW9yaXR5OiAnaW1tZWRpYXRlJyxcbiAgICAgIH0pXG4gICAgICBzZXRIYXNTaG93bk92ZXJhZ2VOb3RpZmljYXRpb24odHJ1ZSlcbiAgICB9IGVsc2UgaWYgKCFjbGF1ZGVBaUxpbWl0cy5pc1VzaW5nT3ZlcmFnZSAmJiBoYXNTaG93bk92ZXJhZ2VOb3RpZmljYXRpb24pIHtcbiAgICAgIC8vIFJlc2V0IHdoZW4gbm8gbG9uZ2VyIGluIG92ZXJhZ2UgbW9kZVxuICAgICAgc2V0SGFzU2hvd25PdmVyYWdlTm90aWZpY2F0aW9uKGZhbHNlKVxuICAgIH1cbiAgfSwgW1xuICAgIGNsYXVkZUFpTGltaXRzLmlzVXNpbmdPdmVyYWdlLFxuICAgIHVzaW5nT3ZlcmFnZVRleHQsXG4gICAgaGFzU2hvd25PdmVyYWdlTm90aWZpY2F0aW9uLFxuICAgIGFkZE5vdGlmaWNhdGlvbixcbiAgICBoYXNCaWxsaW5nQWNjZXNzLFxuICAgIGlzVGVhbU9yRW50ZXJwcmlzZSxcbiAgXSlcblxuICAvLyBTaG93IHdhcm5pbmcgbm90aWZpY2F0aW9uIGZvciBhcHByb2FjaGluZyBsaW1pdHNcbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZ2V0SXNSZW1vdGVNb2RlKCkpIHJldHVyblxuICAgIGlmIChyYXRlTGltaXRXYXJuaW5nICYmIHJhdGVMaW1pdFdhcm5pbmcgIT09IHNob3duV2FybmluZ1JlZi5jdXJyZW50KSB7XG4gICAgICBzaG93bldhcm5pbmdSZWYuY3VycmVudCA9IHJhdGVMaW1pdFdhcm5pbmdcbiAgICAgIGFkZE5vdGlmaWNhdGlvbih7XG4gICAgICAgIGtleTogJ3JhdGUtbGltaXQtd2FybmluZycsXG4gICAgICAgIGpzeDogKFxuICAgICAgICAgIDxUZXh0PlxuICAgICAgICAgICAgPFRleHQgY29sb3I9XCJ3YXJuaW5nXCI+e3JhdGVMaW1pdFdhcm5pbmd9PC9UZXh0PlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgKSxcbiAgICAgICAgcHJpb3JpdHk6ICdoaWdoJyxcbiAgICAgIH0pXG4gICAgfVxuICB9LCBbcmF0ZUxpbWl0V2FybmluZywgYWRkTm90aWZpY2F0aW9uXSlcbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sS0FBS0EsS0FBSyxNQUFNLE9BQU87QUFDOUIsU0FBU0MsU0FBUyxFQUFFQyxPQUFPLEVBQUVDLE1BQU0sRUFBRUMsUUFBUSxRQUFRLE9BQU87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LENBQUM7UUFBQTtNQUN0QztJQUFBLENBQ0Y7SUFBRUUsRUFBQSxJQUNEaEIsY0FBYyxDQUFBaUIsY0FBZSxFQUM3QmIsZ0JBQWdCLEVBQ2hCUywyQkFBMkIsRUFDM0JkLGVBQWUsRUFDZlksZ0JBQWdCLEVBQ2hCQyxrQkFBa0IsQ0FDbkI7SUFBQWYsQ0FBQSxNQUFBRSxlQUFBO0lBQUFGLENBQUEsTUFBQUcsY0FBQSxDQUFBaUIsY0FBQTtJQUFBcEIsQ0FBQSxNQUFBZ0IsMkJBQUE7SUFBQWhCLENBQUEsT0FBQU8sZ0JBQUE7SUFBQVAsQ0FBQSxPQUFBa0IsRUFBQTtJQUFBbEIsQ0FBQSxPQUFBbUIsRUFBQTtFQUFBO0lBQUFELEVBQUEsR0FBQWxCLENBQUE7SUFBQW1CLEVBQUEsR0FBQW5CLENBQUE7RUFBQTtFQXhCRGQsU0FBUyxDQUFDZ0MsRUFpQlQsRUFBRUMsRUFPRixDQUFDO0VBQUEsSUFBQUssRUFBQTtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBekIsQ0FBQSxTQUFBRSxlQUFBLElBQUFGLENBQUEsU0FBQUssZ0JBQUE7SUFHUW1CLEVBQUEsR0FBQUEsQ0FBQTtNQUNSLElBQUkzQixlQUFlLENBQUMsQ0FBQztRQUFBO01BQUE7TUFDckIsSUFBSVEsZ0JBQWdFLElBQTVDQSxnQkFBZ0IsS0FBS0csZUFBZSxDQUFBa0IsT0FBUTtRQUNsRWxCLGVBQWUsQ0FBQWtCLE9BQUEsR0FBV3JCLGdCQUFIO1FBQ3ZCSCxlQUFlLENBQUM7VUFBQW1CLEdBQUEsRUFDVCxvQkFBb0I7VUFBQU0sR0FBQSxFQUV2QixDQUFDLElBQUksQ0FDSCxDQUFDLElBQUksQ0FBTyxLQUFTLENBQVQsU0FBUyxDQUFFdEIsaUJBQWUsQ0FBRSxFQUF2QyxJQUFJLENBQ1AsRUFGQyxJQUFJLENBRUU7VUFBQWtCLFFBQUEsRUFFQztRQUNaLENBQUMsQ0FBQztNQUFBO0lBQ0gsQ0FDRjtJQUFFRSxFQUFBLElBQUNwQixnQkFBZ0IsRUFBRUgsZUFBZSxDQUFDO0lBQUFGLENBQUEsT0FBQUUsZUFBQTtJQUFBRixDQUFBLE9BQUFLLGdCQUFBO0lBQUFMLENBQUEsT0FBQXdCLEVBQUE7SUFBQXhCLENBQUEsT0FBQXlCLEVBQUE7RUFBQTtJQUFBRCxFQUFBLEdBQUF4QixDQUFBO0lBQUF5QixFQUFBLEdBQUF6QixDQUFBO0VBQUE7RUFkdENkLFNBQVMsQ0FBQ3NDLEVBY1QsRUFBRUMsRUFBbUMsQ0FBQztBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/src/hooks/notifs/useStartupNotification.ts b/src/hooks/notifs/useStartupNotification.ts new file mode 100644 index 0000000000000000000000000000000000000000..4e2fa8f96174ca75631b8efa220f85012970dc16 --- /dev/null +++ b/src/hooks/notifs/useStartupNotification.ts @@ -0,0 +1,41 @@ +import { useEffect, useRef } from 'react' +import { getIsRemoteMode } from '../../bootstrap/state.js' +import { + type Notification, + useNotifications, +} from '../../context/notifications.js' +import { logError } from '../../utils/log.js' + +type Result = Notification | Notification[] | null + +/** + * Fires notification(s) once on mount. Encapsulates the remote-mode gate and + * once-per-session ref guard that was hand-rolled across 10+ notifs/ hooks. + * + * The compute fn runs exactly once on first effect. Return null to skip, + * a Notification to fire one, or an array to fire several. Sync or async. + * Rejections are routed to logError. + */ +export function useStartupNotification( + compute: () => Result | Promise, +): void { + const { addNotification } = useNotifications() + const hasRunRef = useRef(false) + const computeRef = useRef(compute) + computeRef.current = compute + + useEffect(() => { + if (getIsRemoteMode() || hasRunRef.current) return + hasRunRef.current = true + + void Promise.resolve() + .then(() => computeRef.current()) + .then(result => { + if (!result) return + for (const n of Array.isArray(result) ? result : [result]) { + addNotification(n) + } + }) + .catch(logError) + }, [addNotification]) +} diff --git a/src/hooks/notifs/useTeammateShutdownNotification.ts b/src/hooks/notifs/useTeammateShutdownNotification.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b17a2637d067cf6cd24945ba9887192dc53eb6a --- /dev/null +++ b/src/hooks/notifs/useTeammateShutdownNotification.ts @@ -0,0 +1,78 @@ +import { useEffect, useRef } from 'react' +import { getIsRemoteMode } from '../../bootstrap/state.js' +import { + type Notification, + useNotifications, +} from '../../context/notifications.js' +import { useAppState } from '../../state/AppState.js' +import { isInProcessTeammateTask } from '../../tasks/InProcessTeammateTask/types.js' + +function parseCount(notif: Notification): number { + if (!('text' in notif)) { + return 1 + } + const match = notif.text.match(/^(\d+)/) + return match?.[1] ? parseInt(match[1], 10) : 1 +} + +function foldSpawn(acc: Notification, _incoming: Notification): Notification { + return makeSpawnNotif(parseCount(acc) + 1) +} + +function makeSpawnNotif(count: number): Notification { + return { + key: 'teammate-spawn', + text: count === 1 ? '1 agent spawned' : `${count} agents spawned`, + priority: 'low', + timeoutMs: 5000, + fold: foldSpawn, + } +} + +function foldShutdown( + acc: Notification, + _incoming: Notification, +): Notification { + return makeShutdownNotif(parseCount(acc) + 1) +} + +function makeShutdownNotif(count: number): Notification { + return { + key: 'teammate-shutdown', + text: count === 1 ? '1 agent shut down' : `${count} agents shut down`, + priority: 'low', + timeoutMs: 5000, + fold: foldShutdown, + } +} + +/** + * Fires batched notifications when in-process teammates spawn or shut down. + * Uses fold() to combine repeated events into a single notification + * like "3 agents spawned" or "2 agents shut down". + */ +export function useTeammateLifecycleNotification(): void { + const tasks = useAppState(s => s.tasks) + const { addNotification } = useNotifications() + const seenRunningRef = useRef>(new Set()) + const seenCompletedRef = useRef>(new Set()) + + useEffect(() => { + if (getIsRemoteMode()) return + for (const [id, task] of Object.entries(tasks)) { + if (!isInProcessTeammateTask(task)) { + continue + } + + if (task.status === 'running' && !seenRunningRef.current.has(id)) { + seenRunningRef.current.add(id) + addNotification(makeSpawnNotif(1)) + } + + if (task.status === 'completed' && !seenCompletedRef.current.has(id)) { + seenCompletedRef.current.add(id) + addNotification(makeShutdownNotif(1)) + } + } + }, [tasks, addNotification]) +} diff --git a/src/hooks/toolPermission/handlers/coordinatorHandler.ts b/src/hooks/toolPermission/handlers/coordinatorHandler.ts new file mode 100644 index 0000000000000000000000000000000000000000..8bfe298072cc135af473defaaaa0f120ec8cc3f9 --- /dev/null +++ b/src/hooks/toolPermission/handlers/coordinatorHandler.ts @@ -0,0 +1,65 @@ +import { feature } from 'bun:bundle' +import type { PendingClassifierCheck } from '../../../types/permissions.js' +import { logError } from '../../../utils/log.js' +import type { PermissionDecision } from '../../../utils/permissions/PermissionResult.js' +import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js' +import type { PermissionContext } from '../PermissionContext.js' + +type CoordinatorPermissionParams = { + ctx: PermissionContext + pendingClassifierCheck?: PendingClassifierCheck | undefined + updatedInput: Record | undefined + suggestions: PermissionUpdate[] | undefined + permissionMode: string | undefined +} + +/** + * Handles the coordinator worker permission flow. + * + * For coordinator workers, automated checks (hooks and classifier) are + * awaited sequentially before falling through to the interactive dialog. + * + * Returns a PermissionDecision if the automated checks resolved the + * permission, or null if the caller should fall through to the + * interactive dialog. + */ +async function handleCoordinatorPermission( + params: CoordinatorPermissionParams, +): Promise { + const { ctx, updatedInput, suggestions, permissionMode } = params + + try { + // 1. Try permission hooks first (fast, local) + const hookResult = await ctx.runHooks( + permissionMode, + suggestions, + updatedInput, + ) + if (hookResult) return hookResult + + // 2. Try classifier (slow, inference -- bash only) + const classifierResult = feature('BASH_CLASSIFIER') + ? await ctx.tryClassifier?.(params.pendingClassifierCheck, updatedInput) + : null + if (classifierResult) { + return classifierResult + } + } catch (error) { + // If automated checks fail unexpectedly, fall through to show the dialog + // so the user can decide manually. Non-Error throws get a context prefix + // so the log is traceable — intentionally NOT toError(), which would drop + // the prefix. + if (error instanceof Error) { + logError(error) + } else { + logError(new Error(`Automated permission check failed: ${String(error)}`)) + } + } + + // 3. Neither resolved (or checks failed) -- fall through to dialog below. + // Hooks already ran, classifier already consumed. + return null +} + +export { handleCoordinatorPermission } +export type { CoordinatorPermissionParams } diff --git a/src/hooks/toolPermission/handlers/interactiveHandler.ts b/src/hooks/toolPermission/handlers/interactiveHandler.ts new file mode 100644 index 0000000000000000000000000000000000000000..6b3e4e80deff0881d468e7f56c5acaa1e08dcff9 --- /dev/null +++ b/src/hooks/toolPermission/handlers/interactiveHandler.ts @@ -0,0 +1,536 @@ +import { feature } from 'bun:bundle' +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs' +import { randomUUID } from 'crypto' +import { logForDebugging } from 'src/utils/debug.js' +import { getAllowedChannels } from '../../../bootstrap/state.js' +import type { BridgePermissionCallbacks } from '../../../bridge/bridgePermissionCallbacks.js' +import { getTerminalFocused } from '../../../ink/terminal-focus-state.js' +import { + CHANNEL_PERMISSION_REQUEST_METHOD, + type ChannelPermissionRequestParams, + findChannelEntry, +} from '../../../services/mcp/channelNotification.js' +import type { ChannelPermissionCallbacks } from '../../../services/mcp/channelPermissions.js' +import { + filterPermissionRelayClients, + shortRequestId, + truncateForPreview, +} from '../../../services/mcp/channelPermissions.js' +import { executeAsyncClassifierCheck } from '../../../tools/BashTool/bashPermissions.js' +import { BASH_TOOL_NAME } from '../../../tools/BashTool/toolName.js' +import { + clearClassifierChecking, + setClassifierApproval, + setClassifierChecking, + setYoloClassifierApproval, +} from '../../../utils/classifierApprovals.js' +import { errorMessage } from '../../../utils/errors.js' +import type { PermissionDecision } from '../../../utils/permissions/PermissionResult.js' +import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js' +import { hasPermissionsToUseTool } from '../../../utils/permissions/permissions.js' +import type { PermissionContext } from '../PermissionContext.js' +import { createResolveOnce } from '../PermissionContext.js' + +type InteractivePermissionParams = { + ctx: PermissionContext + description: string + result: PermissionDecision & { behavior: 'ask' } + awaitAutomatedChecksBeforeDialog: boolean | undefined + bridgeCallbacks?: BridgePermissionCallbacks + channelCallbacks?: ChannelPermissionCallbacks +} + +/** + * Handles the interactive (main-agent) permission flow. + * + * Pushes a ToolUseConfirm entry to the confirm queue with callbacks: + * onAbort, onAllow, onReject, recheckPermission, onUserInteraction. + * + * Runs permission hooks and bash classifier checks asynchronously in the + * background, racing them against user interaction. Uses a resolve-once + * guard and `userInteracted` flag to prevent multiple resolutions. + * + * This function does NOT return a Promise -- it sets up callbacks that + * eventually call `resolve()` to resolve the outer promise owned by + * the caller. + */ +function handleInteractivePermission( + params: InteractivePermissionParams, + resolve: (decision: PermissionDecision) => void, +): void { + const { + ctx, + description, + result, + awaitAutomatedChecksBeforeDialog, + bridgeCallbacks, + channelCallbacks, + } = params + + const { resolve: resolveOnce, isResolved, claim } = createResolveOnce(resolve) + let userInteracted = false + let checkmarkTransitionTimer: ReturnType | undefined + // Hoisted so onDismissCheckmark (Esc during checkmark window) can also + // remove the abort listener — not just the timer callback. + let checkmarkAbortHandler: (() => void) | undefined + const bridgeRequestId = bridgeCallbacks ? randomUUID() : undefined + // Hoisted so local/hook/classifier wins can remove the pending channel + // entry. No "tell remote to dismiss" equivalent — the text sits in your + // phone, and a stale "yes abc123" after local-resolve falls through + // tryConsumeReply (entry gone) and gets enqueued as normal chat. + let channelUnsubscribe: (() => void) | undefined + + const permissionPromptStartTimeMs = Date.now() + const displayInput = result.updatedInput ?? ctx.input + + function clearClassifierIndicator(): void { + if (feature('BASH_CLASSIFIER')) { + ctx.updateQueueItem({ classifierCheckInProgress: false }) + } + } + + ctx.pushToQueue({ + assistantMessage: ctx.assistantMessage, + tool: ctx.tool, + description, + input: displayInput, + toolUseContext: ctx.toolUseContext, + toolUseID: ctx.toolUseID, + permissionResult: result, + permissionPromptStartTimeMs, + ...(feature('BASH_CLASSIFIER') + ? { + classifierCheckInProgress: + !!result.pendingClassifierCheck && + !awaitAutomatedChecksBeforeDialog, + } + : {}), + onUserInteraction() { + // Called when user starts interacting with the permission dialog + // (e.g., arrow keys, tab, typing feedback) + // Hide the classifier indicator since auto-approve is no longer possible + // + // Grace period: ignore interactions in the first 200ms to prevent + // accidental keypresses from canceling the classifier prematurely + const GRACE_PERIOD_MS = 200 + if (Date.now() - permissionPromptStartTimeMs < GRACE_PERIOD_MS) { + return + } + userInteracted = true + clearClassifierChecking(ctx.toolUseID) + clearClassifierIndicator() + }, + onDismissCheckmark() { + if (checkmarkTransitionTimer) { + clearTimeout(checkmarkTransitionTimer) + checkmarkTransitionTimer = undefined + if (checkmarkAbortHandler) { + ctx.toolUseContext.abortController.signal.removeEventListener( + 'abort', + checkmarkAbortHandler, + ) + checkmarkAbortHandler = undefined + } + ctx.removeFromQueue() + } + }, + onAbort() { + if (!claim()) return + if (bridgeCallbacks && bridgeRequestId) { + bridgeCallbacks.sendResponse(bridgeRequestId, { + behavior: 'deny', + message: 'User aborted', + }) + bridgeCallbacks.cancelRequest(bridgeRequestId) + } + channelUnsubscribe?.() + ctx.logCancelled() + ctx.logDecision( + { decision: 'reject', source: { type: 'user_abort' } }, + { permissionPromptStartTimeMs }, + ) + resolveOnce(ctx.cancelAndAbort(undefined, true)) + }, + async onAllow( + updatedInput, + permissionUpdates: PermissionUpdate[], + feedback?: string, + contentBlocks?: ContentBlockParam[], + ) { + if (!claim()) return // atomic check-and-mark before await + + if (bridgeCallbacks && bridgeRequestId) { + bridgeCallbacks.sendResponse(bridgeRequestId, { + behavior: 'allow', + updatedInput, + updatedPermissions: permissionUpdates, + }) + bridgeCallbacks.cancelRequest(bridgeRequestId) + } + channelUnsubscribe?.() + + resolveOnce( + await ctx.handleUserAllow( + updatedInput, + permissionUpdates, + feedback, + permissionPromptStartTimeMs, + contentBlocks, + result.decisionReason, + ), + ) + }, + onReject(feedback?: string, contentBlocks?: ContentBlockParam[]) { + if (!claim()) return + + if (bridgeCallbacks && bridgeRequestId) { + bridgeCallbacks.sendResponse(bridgeRequestId, { + behavior: 'deny', + message: feedback ?? 'User denied permission', + }) + bridgeCallbacks.cancelRequest(bridgeRequestId) + } + channelUnsubscribe?.() + + ctx.logDecision( + { + decision: 'reject', + source: { type: 'user_reject', hasFeedback: !!feedback }, + }, + { permissionPromptStartTimeMs }, + ) + resolveOnce(ctx.cancelAndAbort(feedback, undefined, contentBlocks)) + }, + async recheckPermission() { + if (isResolved()) return + const freshResult = await hasPermissionsToUseTool( + ctx.tool, + ctx.input, + ctx.toolUseContext, + ctx.assistantMessage, + ctx.toolUseID, + ) + if (freshResult.behavior === 'allow') { + // claim() (atomic check-and-mark), not isResolved() — the async + // hasPermissionsToUseTool call above opens a window where CCR + // could have responded in flight. Matches onAllow/onReject/hook + // paths. cancelRequest tells CCR to dismiss its prompt — without + // it, the web UI shows a stale prompt for a tool that's already + // executing (particularly visible when recheck is triggered by + // a CCR-initiated mode switch, the very case this callback exists + // for after useReplBridge started calling it). + if (!claim()) return + if (bridgeCallbacks && bridgeRequestId) { + bridgeCallbacks.cancelRequest(bridgeRequestId) + } + channelUnsubscribe?.() + ctx.removeFromQueue() + ctx.logDecision({ decision: 'accept', source: 'config' }) + resolveOnce(ctx.buildAllow(freshResult.updatedInput ?? ctx.input)) + } + }, + }) + + // Race 4: Bridge permission response from CCR (claude.ai) + // When the bridge is connected, send the permission request to CCR and + // subscribe for a response. Whichever side (CLI or CCR) responds first + // wins via claim(). + // + // All tools are forwarded — CCR's generic allow/deny modal handles any + // tool, and can return `updatedInput` when it has a dedicated renderer + // (e.g. plan edit). Tools whose local dialog injects fields (ReviewArtifact + // `selected`, AskUserQuestion `answers`) tolerate the field being missing + // so generic remote approval degrades gracefully instead of throwing. + if (bridgeCallbacks && bridgeRequestId) { + bridgeCallbacks.sendRequest( + bridgeRequestId, + ctx.tool.name, + displayInput, + ctx.toolUseID, + description, + result.suggestions, + result.blockedPath, + ) + + const signal = ctx.toolUseContext.abortController.signal + const unsubscribe = bridgeCallbacks.onResponse( + bridgeRequestId, + response => { + if (!claim()) return // Local user/hook/classifier already responded + signal.removeEventListener('abort', unsubscribe) + clearClassifierChecking(ctx.toolUseID) + clearClassifierIndicator() + ctx.removeFromQueue() + channelUnsubscribe?.() + + if (response.behavior === 'allow') { + if (response.updatedPermissions?.length) { + void ctx.persistPermissions(response.updatedPermissions) + } + ctx.logDecision( + { + decision: 'accept', + source: { + type: 'user', + permanent: !!response.updatedPermissions?.length, + }, + }, + { permissionPromptStartTimeMs }, + ) + resolveOnce(ctx.buildAllow(response.updatedInput ?? displayInput)) + } else { + ctx.logDecision( + { + decision: 'reject', + source: { + type: 'user_reject', + hasFeedback: !!response.message, + }, + }, + { permissionPromptStartTimeMs }, + ) + resolveOnce(ctx.cancelAndAbort(response.message)) + } + }, + ) + + signal.addEventListener('abort', unsubscribe, { once: true }) + } + + // Channel permission relay — races alongside the bridge block above. Send a + // permission prompt to every active channel (Telegram, iMessage, etc.) via + // its MCP send_message tool, then race the reply against local/bridge/hook/ + // classifier. The inbound "yes abc123" is intercepted in the notification + // handler (useManageMCPConnections.ts) BEFORE enqueue, so it never reaches + // Claude as a conversation turn. + // + // Unlike the bridge block, this still guards on `requiresUserInteraction` — + // channel replies are pure yes/no with no `updatedInput` path. In practice + // the guard is dead code today: all three `requiresUserInteraction` tools + // (ExitPlanMode, AskUserQuestion, ReviewArtifact) return `isEnabled()===false` + // when channels are configured, so they never reach this handler. + // + // Fire-and-forget send: if callTool fails (channel down, tool missing), + // the subscription never fires and another racer wins. Graceful degradation + // — the local dialog is always there as the floor. + if ( + (feature('KAIROS') || feature('KAIROS_CHANNELS')) && + channelCallbacks && + !ctx.tool.requiresUserInteraction?.() + ) { + const channelRequestId = shortRequestId(ctx.toolUseID) + const allowedChannels = getAllowedChannels() + const channelClients = filterPermissionRelayClients( + ctx.toolUseContext.getAppState().mcp.clients, + name => findChannelEntry(name, allowedChannels) !== undefined, + ) + + if (channelClients.length > 0) { + // Outbound is structured too (Kenneth's symmetry ask) — server owns + // message formatting for its platform (Telegram markdown, iMessage + // rich text, Discord embed). CC sends the RAW parts; server composes. + // The old callTool('send_message', {text,content,message}) triple-key + // hack is gone — no more guessing which arg name each plugin takes. + const params: ChannelPermissionRequestParams = { + request_id: channelRequestId, + tool_name: ctx.tool.name, + description, + input_preview: truncateForPreview(displayInput), + } + + for (const client of channelClients) { + if (client.type !== 'connected') continue // refine for TS + void client.client + .notification({ + method: CHANNEL_PERMISSION_REQUEST_METHOD, + params, + }) + .catch(e => { + logForDebugging( + `Channel permission_request failed for ${client.name}: ${errorMessage(e)}`, + { level: 'error' }, + ) + }) + } + + const channelSignal = ctx.toolUseContext.abortController.signal + // Wrap so BOTH the map delete AND the abort-listener teardown happen + // at every call site. The 6 channelUnsubscribe?.() sites after local/ + // hook/classifier wins previously only deleted the map entry — the + // dead closure stayed registered on the session-scoped abort signal + // until the session ended. Not a functional bug (Map.delete is + // idempotent), but it held the closure alive. + const mapUnsub = channelCallbacks.onResponse( + channelRequestId, + response => { + if (!claim()) return // Another racer won + channelUnsubscribe?.() // both: map delete + listener remove + clearClassifierChecking(ctx.toolUseID) + clearClassifierIndicator() + ctx.removeFromQueue() + // Bridge is the other remote — tell it we're done. + if (bridgeCallbacks && bridgeRequestId) { + bridgeCallbacks.cancelRequest(bridgeRequestId) + } + + if (response.behavior === 'allow') { + ctx.logDecision( + { + decision: 'accept', + source: { type: 'user', permanent: false }, + }, + { permissionPromptStartTimeMs }, + ) + resolveOnce(ctx.buildAllow(displayInput)) + } else { + ctx.logDecision( + { + decision: 'reject', + source: { type: 'user_reject', hasFeedback: false }, + }, + { permissionPromptStartTimeMs }, + ) + resolveOnce( + ctx.cancelAndAbort(`Denied via channel ${response.fromServer}`), + ) + } + }, + ) + channelUnsubscribe = () => { + mapUnsub() + channelSignal.removeEventListener('abort', channelUnsubscribe!) + } + + channelSignal.addEventListener('abort', channelUnsubscribe, { + once: true, + }) + } + } + + // Skip hooks if they were already awaited in the coordinator branch above + if (!awaitAutomatedChecksBeforeDialog) { + // Execute PermissionRequest hooks asynchronously + // If hook returns a decision before user responds, apply it + void (async () => { + if (isResolved()) return + const currentAppState = ctx.toolUseContext.getAppState() + const hookDecision = await ctx.runHooks( + currentAppState.toolPermissionContext.mode, + result.suggestions, + result.updatedInput, + permissionPromptStartTimeMs, + ) + if (!hookDecision || !claim()) return + if (bridgeCallbacks && bridgeRequestId) { + bridgeCallbacks.cancelRequest(bridgeRequestId) + } + channelUnsubscribe?.() + ctx.removeFromQueue() + resolveOnce(hookDecision) + })() + } + + // Execute bash classifier check asynchronously (if applicable) + if ( + feature('BASH_CLASSIFIER') && + result.pendingClassifierCheck && + ctx.tool.name === BASH_TOOL_NAME && + !awaitAutomatedChecksBeforeDialog + ) { + // UI indicator for "classifier running" — set here (not in + // toolExecution.ts) so commands that auto-allow via prefix rules + // don't flash the indicator for a split second before allow returns. + setClassifierChecking(ctx.toolUseID) + void executeAsyncClassifierCheck( + result.pendingClassifierCheck, + ctx.toolUseContext.abortController.signal, + ctx.toolUseContext.options.isNonInteractiveSession, + { + shouldContinue: () => !isResolved() && !userInteracted, + onComplete: () => { + clearClassifierChecking(ctx.toolUseID) + clearClassifierIndicator() + }, + onAllow: decisionReason => { + if (!claim()) return + if (bridgeCallbacks && bridgeRequestId) { + bridgeCallbacks.cancelRequest(bridgeRequestId) + } + channelUnsubscribe?.() + clearClassifierChecking(ctx.toolUseID) + + const matchedRule = + decisionReason.type === 'classifier' + ? (decisionReason.reason.match( + /^Allowed by prompt rule: "(.+)"$/, + )?.[1] ?? decisionReason.reason) + : undefined + + // Show auto-approved transition with dimmed options + if (feature('TRANSCRIPT_CLASSIFIER')) { + ctx.updateQueueItem({ + classifierCheckInProgress: false, + classifierAutoApproved: true, + classifierMatchedRule: matchedRule, + }) + } + + if ( + feature('TRANSCRIPT_CLASSIFIER') && + decisionReason.type === 'classifier' + ) { + if (decisionReason.classifier === 'auto-mode') { + setYoloClassifierApproval(ctx.toolUseID, decisionReason.reason) + } else if (matchedRule) { + setClassifierApproval(ctx.toolUseID, matchedRule) + } + } + + ctx.logDecision( + { decision: 'accept', source: { type: 'classifier' } }, + { permissionPromptStartTimeMs }, + ) + resolveOnce(ctx.buildAllow(ctx.input, { decisionReason })) + + // Keep checkmark visible, then remove dialog. + // 3s if terminal is focused (user can see it), 1s if not. + // User can dismiss early with Esc via onDismissCheckmark. + const signal = ctx.toolUseContext.abortController.signal + checkmarkAbortHandler = () => { + if (checkmarkTransitionTimer) { + clearTimeout(checkmarkTransitionTimer) + checkmarkTransitionTimer = undefined + // Sibling Bash error can fire this (StreamingToolExecutor + // cascades via siblingAbortController) — must drop the + // cosmetic ✓ dialog or it blocks the next queued item. + ctx.removeFromQueue() + } + } + const checkmarkMs = getTerminalFocused() ? 3000 : 1000 + checkmarkTransitionTimer = setTimeout(() => { + checkmarkTransitionTimer = undefined + if (checkmarkAbortHandler) { + signal.removeEventListener('abort', checkmarkAbortHandler) + checkmarkAbortHandler = undefined + } + ctx.removeFromQueue() + }, checkmarkMs) + signal.addEventListener('abort', checkmarkAbortHandler, { + once: true, + }) + }, + }, + ).catch(error => { + // Log classifier API errors for debugging but don't propagate them as interruptions + // These errors can be network failures, rate limits, or model issues - not user cancellations + logForDebugging(`Async classifier check failed: ${errorMessage(error)}`, { + level: 'error', + }) + }) + } +} + +// -- + +export { handleInteractivePermission } +export type { InteractivePermissionParams } diff --git a/src/hooks/toolPermission/handlers/swarmWorkerHandler.ts b/src/hooks/toolPermission/handlers/swarmWorkerHandler.ts new file mode 100644 index 0000000000000000000000000000000000000000..57930ca8f30abe6d2e7d61b230b0a7594577e010 --- /dev/null +++ b/src/hooks/toolPermission/handlers/swarmWorkerHandler.ts @@ -0,0 +1,159 @@ +import { feature } from 'bun:bundle' +import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs' +import type { PendingClassifierCheck } from '../../../types/permissions.js' +import { isAgentSwarmsEnabled } from '../../../utils/agentSwarmsEnabled.js' +import { toError } from '../../../utils/errors.js' +import { logError } from '../../../utils/log.js' +import type { PermissionDecision } from '../../../utils/permissions/PermissionResult.js' +import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js' +import { + createPermissionRequest, + isSwarmWorker, + sendPermissionRequestViaMailbox, +} from '../../../utils/swarm/permissionSync.js' +import { registerPermissionCallback } from '../../useSwarmPermissionPoller.js' +import type { PermissionContext } from '../PermissionContext.js' +import { createResolveOnce } from '../PermissionContext.js' + +type SwarmWorkerPermissionParams = { + ctx: PermissionContext + description: string + pendingClassifierCheck?: PendingClassifierCheck | undefined + updatedInput: Record | undefined + suggestions: PermissionUpdate[] | undefined +} + +/** + * Handles the swarm worker permission flow. + * + * When running as a swarm worker: + * 1. Tries classifier auto-approval for bash commands + * 2. Forwards the permission request to the leader via mailbox + * 3. Registers callbacks for when the leader responds + * 4. Sets the pending indicator while waiting + * + * Returns a PermissionDecision if the classifier auto-approves, + * or a Promise that resolves when the leader responds. + * Returns null if swarms are not enabled or this is not a swarm worker, + * so the caller can fall through to interactive handling. + */ +async function handleSwarmWorkerPermission( + params: SwarmWorkerPermissionParams, +): Promise { + if (!isAgentSwarmsEnabled() || !isSwarmWorker()) { + return null + } + + const { ctx, description, updatedInput, suggestions } = params + + // For bash commands, try classifier auto-approval before forwarding to + // the leader. Agents await the classifier result (rather than racing it + // against user interaction like the main agent). + const classifierResult = feature('BASH_CLASSIFIER') + ? await ctx.tryClassifier?.(params.pendingClassifierCheck, updatedInput) + : null + if (classifierResult) { + return classifierResult + } + + // Forward permission request to the leader via mailbox + try { + const clearPendingRequest = (): void => + ctx.toolUseContext.setAppState(prev => ({ + ...prev, + pendingWorkerRequest: null, + })) + + const decision = await new Promise(resolve => { + const { resolve: resolveOnce, claim } = createResolveOnce(resolve) + + // Create the permission request + const request = createPermissionRequest({ + toolName: ctx.tool.name, + toolUseId: ctx.toolUseID, + input: ctx.input, + description, + permissionSuggestions: suggestions, + }) + + // Register callback BEFORE sending the request to avoid race condition + // where leader responds before callback is registered + registerPermissionCallback({ + requestId: request.id, + toolUseId: ctx.toolUseID, + async onAllow( + allowedInput: Record | undefined, + permissionUpdates: PermissionUpdate[], + feedback?: string, + contentBlocks?: ContentBlockParam[], + ) { + if (!claim()) return // atomic check-and-mark before await + clearPendingRequest() + + // Merge the updated input with the original input + const finalInput = + allowedInput && Object.keys(allowedInput).length > 0 + ? allowedInput + : ctx.input + + resolveOnce( + await ctx.handleUserAllow( + finalInput, + permissionUpdates, + feedback, + undefined, + contentBlocks, + ), + ) + }, + onReject(feedback?: string, contentBlocks?: ContentBlockParam[]) { + if (!claim()) return + clearPendingRequest() + + ctx.logDecision({ + decision: 'reject', + source: { type: 'user_reject', hasFeedback: !!feedback }, + }) + + resolveOnce(ctx.cancelAndAbort(feedback, undefined, contentBlocks)) + }, + }) + + // Now that callback is registered, send the request to the leader + void sendPermissionRequestViaMailbox(request) + + // Show visual indicator that we're waiting for leader approval + ctx.toolUseContext.setAppState(prev => ({ + ...prev, + pendingWorkerRequest: { + toolName: ctx.tool.name, + toolUseId: ctx.toolUseID, + description, + }, + })) + + // If the abort signal fires while waiting for the leader response, + // resolve the promise with a cancel decision so it does not hang. + ctx.toolUseContext.abortController.signal.addEventListener( + 'abort', + () => { + if (!claim()) return + clearPendingRequest() + ctx.logCancelled() + resolveOnce(ctx.cancelAndAbort(undefined, true)) + }, + { once: true }, + ) + }) + + return decision + } catch (error) { + // If swarm permission submission fails, fall back to local handling + logError(toError(error)) + // Continue to local UI handling below + return null + } +} + +export { handleSwarmWorkerPermission } +export type { SwarmWorkerPermissionParams } diff --git a/src/hooks/toolPermission/permissionLogging.ts b/src/hooks/toolPermission/permissionLogging.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa0dc5aad783c2cfe00ff2d916079be4450887ef --- /dev/null +++ b/src/hooks/toolPermission/permissionLogging.ts @@ -0,0 +1,238 @@ +// Centralized analytics/telemetry logging for tool permission decisions. +// All permission approve/reject events flow through logPermissionDecision(), +// which fans out to Statsig analytics, OTel telemetry, and code-edit metrics. +import { feature } from 'bun:bundle' +import { + type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + logEvent, +} from 'src/services/analytics/index.js' +import { sanitizeToolNameForAnalytics } from 'src/services/analytics/metadata.js' +import { getCodeEditToolDecisionCounter } from '../../bootstrap/state.js' +import type { Tool as ToolType, ToolUseContext } from '../../Tool.js' +import { getLanguageName } from '../../utils/cliHighlight.js' +import { SandboxManager } from '../../utils/sandbox/sandbox-adapter.js' +import { logOTelEvent } from '../../utils/telemetry/events.js' +import type { + PermissionApprovalSource, + PermissionRejectionSource, +} from './PermissionContext.js' + +type PermissionLogContext = { + tool: ToolType + input: unknown + toolUseContext: ToolUseContext + messageId: string + toolUseID: string +} + +// Discriminated union: 'accept' pairs with approval sources, 'reject' with rejection sources +type PermissionDecisionArgs = + | { decision: 'accept'; source: PermissionApprovalSource | 'config' } + | { decision: 'reject'; source: PermissionRejectionSource | 'config' } + +const CODE_EDITING_TOOLS = ['Edit', 'Write', 'NotebookEdit'] + +function isCodeEditingTool(toolName: string): boolean { + return CODE_EDITING_TOOLS.includes(toolName) +} + +// Builds OTel counter attributes for code editing tools, enriching with +// language when the tool's target file path can be extracted from input +async function buildCodeEditToolAttributes( + tool: ToolType, + input: unknown, + decision: 'accept' | 'reject', + source: string, +): Promise> { + // Derive language from file path if the tool exposes one (e.g., Edit, Write) + let language: string | undefined + if (tool.getPath && input) { + const parseResult = tool.inputSchema.safeParse(input) + if (parseResult.success) { + const filePath = tool.getPath(parseResult.data) + if (filePath) { + language = await getLanguageName(filePath) + } + } + } + + return { + decision, + source, + tool_name: tool.name, + ...(language && { language }), + } +} + +// Flattens structured source into a string label for analytics/OTel events +function sourceToString( + source: PermissionApprovalSource | PermissionRejectionSource, +): string { + if ( + (feature('BASH_CLASSIFIER') || feature('TRANSCRIPT_CLASSIFIER')) && + source.type === 'classifier' + ) { + return 'classifier' + } + switch (source.type) { + case 'hook': + return 'hook' + case 'user': + return source.permanent ? 'user_permanent' : 'user_temporary' + case 'user_abort': + return 'user_abort' + case 'user_reject': + return 'user_reject' + default: + return 'unknown' + } +} + +function baseMetadata( + messageId: string, + toolName: string, + waitMs: number | undefined, +): { [key: string]: boolean | number | undefined } { + return { + messageID: + messageId as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, + toolName: sanitizeToolNameForAnalytics(toolName), + sandboxEnabled: SandboxManager.isSandboxingEnabled(), + // Only include wait time when the user was actually prompted (not auto-approved) + ...(waitMs !== undefined && { waiting_for_user_permission_ms: waitMs }), + } +} + +// Emits a distinct analytics event name per approval source for funnel analysis +function logApprovalEvent( + tool: ToolType, + messageId: string, + source: PermissionApprovalSource | 'config', + waitMs: number | undefined, +): void { + if (source === 'config') { + // Auto-approved by allowlist in settings -- no user wait time + logEvent( + 'tengu_tool_use_granted_in_config', + baseMetadata(messageId, tool.name, undefined), + ) + return + } + if ( + (feature('BASH_CLASSIFIER') || feature('TRANSCRIPT_CLASSIFIER')) && + source.type === 'classifier' + ) { + logEvent( + 'tengu_tool_use_granted_by_classifier', + baseMetadata(messageId, tool.name, waitMs), + ) + return + } + switch (source.type) { + case 'user': + logEvent( + source.permanent + ? 'tengu_tool_use_granted_in_prompt_permanent' + : 'tengu_tool_use_granted_in_prompt_temporary', + baseMetadata(messageId, tool.name, waitMs), + ) + break + case 'hook': + logEvent('tengu_tool_use_granted_by_permission_hook', { + ...baseMetadata(messageId, tool.name, waitMs), + permanent: source.permanent ?? false, + }) + break + default: + break + } +} + +// Rejections share a single event name, differentiated by metadata fields +function logRejectionEvent( + tool: ToolType, + messageId: string, + source: PermissionRejectionSource | 'config', + waitMs: number | undefined, +): void { + if (source === 'config') { + // Denied by denylist in settings + logEvent( + 'tengu_tool_use_denied_in_config', + baseMetadata(messageId, tool.name, undefined), + ) + return + } + logEvent('tengu_tool_use_rejected_in_prompt', { + ...baseMetadata(messageId, tool.name, waitMs), + // Distinguish hook rejections from user rejections via separate fields + ...(source.type === 'hook' + ? { isHook: true } + : { + hasFeedback: + source.type === 'user_reject' ? source.hasFeedback : false, + }), + }) +} + +// Single entry point for all permission decision logging. Called by permission +// handlers after every approve/reject. Fans out to: analytics events, OTel +// telemetry, code-edit OTel counters, and toolUseContext decision storage. +function logPermissionDecision( + ctx: PermissionLogContext, + args: PermissionDecisionArgs, + permissionPromptStartTimeMs?: number, +): void { + const { tool, input, toolUseContext, messageId, toolUseID } = ctx + const { decision, source } = args + + const waiting_for_user_permission_ms = + permissionPromptStartTimeMs !== undefined + ? Date.now() - permissionPromptStartTimeMs + : undefined + + // Log the analytics event + if (args.decision === 'accept') { + logApprovalEvent( + tool, + messageId, + args.source, + waiting_for_user_permission_ms, + ) + } else { + logRejectionEvent( + tool, + messageId, + args.source, + waiting_for_user_permission_ms, + ) + } + + const sourceString = source === 'config' ? 'config' : sourceToString(source) + + // Track code editing tool metrics + if (isCodeEditingTool(tool.name)) { + void buildCodeEditToolAttributes(tool, input, decision, sourceString).then( + attributes => getCodeEditToolDecisionCounter()?.add(1, attributes), + ) + } + + // Persist decision on the context so downstream code can inspect what happened + if (!toolUseContext.toolDecisions) { + toolUseContext.toolDecisions = new Map() + } + toolUseContext.toolDecisions.set(toolUseID, { + source: sourceString, + decision, + timestamp: Date.now(), + }) + + void logOTelEvent('tool_decision', { + decision, + source: sourceString, + tool_name: sanitizeToolNameForAnalytics(tool.name), + }) +} + +export { isCodeEditingTool, buildCodeEditToolAttributes, logPermissionDecision } +export type { PermissionLogContext, PermissionDecisionArgs } diff --git a/src/ink/components/AlternateScreen.tsx b/src/ink/components/AlternateScreen.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b736f9299dde3bdf65e2521509e84eca370cc013 --- /dev/null +++ b/src/ink/components/AlternateScreen.tsx @@ -0,0 +1,80 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type PropsWithChildren, useContext, useInsertionEffect } from 'react'; +import instances from '../instances.js'; +import { DISABLE_MOUSE_TRACKING, ENABLE_MOUSE_TRACKING, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN } from '../termio/dec.js'; +import { TerminalWriteContext } from '../useTerminalNotification.js'; +import Box from './Box.js'; +import { TerminalSizeContext } from './TerminalSizeContext.js'; +type Props = PropsWithChildren<{ + /** Enable SGR mouse tracking (wheel + click/drag). Default true. */ + mouseTracking?: boolean; +}>; + +/** + * Run children in the terminal's alternate screen buffer, constrained to + * the viewport height. While mounted: + * + * - Enters the alt screen (DEC 1049), clears it, homes the cursor + * - Constrains its own height to the terminal row count, so overflow must + * be handled via `overflow: scroll` / flexbox (no native scrollback) + * - Optionally enables SGR mouse tracking (wheel + click/drag) — events + * surface as `ParsedKey` (wheel) and update the Ink instance's + * selection state (click/drag) + * + * On unmount, disables mouse tracking and exits the alt screen, restoring + * the main screen's content. Safe for use in ctrl-o transcript overlays + * and similar temporary fullscreen views — the main screen is preserved. + * + * Notifies the Ink instance via `setAltScreenActive()` so the renderer + * keeps the cursor inside the viewport (preventing the cursor-restore LF + * from scrolling content) and so signal-exit cleanup can exit the alt + * screen if the component's own unmount doesn't run. + */ +export function AlternateScreen(t0) { + const $ = _c(7); + const { + children, + mouseTracking: t1 + } = t0; + const mouseTracking = t1 === undefined ? true : t1; + const size = useContext(TerminalSizeContext); + const writeRaw = useContext(TerminalWriteContext); + let t2; + let t3; + if ($[0] !== mouseTracking || $[1] !== writeRaw) { + t2 = () => { + const ink = instances.get(process.stdout); + if (!writeRaw) { + return; + } + writeRaw(ENTER_ALT_SCREEN + "\x1B[2J\x1B[H" + (mouseTracking ? ENABLE_MOUSE_TRACKING : "")); + ink?.setAltScreenActive(true, mouseTracking); + return () => { + ink?.setAltScreenActive(false); + ink?.clearTextSelection(); + writeRaw((mouseTracking ? DISABLE_MOUSE_TRACKING : "") + EXIT_ALT_SCREEN); + }; + }; + t3 = [writeRaw, mouseTracking]; + $[0] = mouseTracking; + $[1] = writeRaw; + $[2] = t2; + $[3] = t3; + } else { + t2 = $[2]; + t3 = $[3]; + } + useInsertionEffect(t2, t3); + const t4 = size?.rows ?? 24; + let t5; + if ($[4] !== children || $[5] !== t4) { + t5 = {children}; + $[4] = children; + $[5] = t4; + $[6] = t5; + } else { + t5 = $[6]; + } + return t5; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzV2l0aENoaWxkcmVuIiwidXNlQ29udGV4dCIsInVzZUluc2VydGlvbkVmZmVjdCIsImluc3RhbmNlcyIsIkRJU0FCTEVfTU9VU0VfVFJBQ0tJTkciLCJFTkFCTEVfTU9VU0VfVFJBQ0tJTkciLCJFTlRFUl9BTFRfU0NSRUVOIiwiRVhJVF9BTFRfU0NSRUVOIiwiVGVybWluYWxXcml0ZUNvbnRleHQiLCJCb3giLCJUZXJtaW5hbFNpemVDb250ZXh0IiwiUHJvcHMiLCJtb3VzZVRyYWNraW5nIiwiQWx0ZXJuYXRlU2NyZWVuIiwidDAiLCIkIiwiX2MiLCJjaGlsZHJlbiIsInQxIiwidW5kZWZpbmVkIiwic2l6ZSIsIndyaXRlUmF3IiwidDIiLCJ0MyIsImluayIsImdldCIsInByb2Nlc3MiLCJzdGRvdXQiLCJzZXRBbHRTY3JlZW5BY3RpdmUiLCJjbGVhclRleHRTZWxlY3Rpb24iLCJ0NCIsInJvd3MiLCJ0NSJdLCJzb3VyY2VzIjpbIkFsdGVybmF0ZVNjcmVlbi50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7XG4gIHR5cGUgUHJvcHNXaXRoQ2hpbGRyZW4sXG4gIHVzZUNvbnRleHQsXG4gIHVzZUluc2VydGlvbkVmZmVjdCxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgaW5zdGFuY2VzIGZyb20gJy4uL2luc3RhbmNlcy5qcydcbmltcG9ydCB7XG4gIERJU0FCTEVfTU9VU0VfVFJBQ0tJTkcsXG4gIEVOQUJMRV9NT1VTRV9UUkFDS0lORyxcbiAgRU5URVJfQUxUX1NDUkVFTixcbiAgRVhJVF9BTFRfU0NSRUVOLFxufSBmcm9tICcuLi90ZXJtaW8vZGVjLmpzJ1xuaW1wb3J0IHsgVGVybWluYWxXcml0ZUNvbnRleHQgfSBmcm9tICcuLi91c2VUZXJtaW5hbE5vdGlmaWNhdGlvbi5qcydcbmltcG9ydCBCb3ggZnJvbSAnLi9Cb3guanMnXG5pbXBvcnQgeyBUZXJtaW5hbFNpemVDb250ZXh0IH0gZnJvbSAnLi9UZXJtaW5hbFNpemVDb250ZXh0LmpzJ1xuXG50eXBlIFByb3BzID0gUHJvcHNXaXRoQ2hpbGRyZW48e1xuICAvKiogRW5hYmxlIFNHUiBtb3VzZSB0cmFja2luZyAod2hlZWwgKyBjbGljay9kcmFnKS4gRGVmYXVsdCB0cnVlLiAqL1xuICBtb3VzZVRyYWNraW5nPzogYm9vbGVhblxufT5cblxuLyoqXG4gKiBSdW4gY2hpbGRyZW4gaW4gdGhlIHRlcm1pbmFsJ3MgYWx0ZXJuYXRlIHNjcmVlbiBidWZmZXIsIGNvbnN0cmFpbmVkIHRvXG4gKiB0aGUgdmlld3BvcnQgaGVpZ2h0LiBXaGlsZSBtb3VudGVkOlxuICpcbiAqIC0gRW50ZXJzIHRoZSBhbHQgc2NyZWVuIChERUMgMTA0OSksIGNsZWFycyBpdCwgaG9tZXMgdGhlIGN1cnNvclxuICogLSBDb25zdHJhaW5zIGl0cyBvd24gaGVpZ2h0IHRvIHRoZSB0ZXJtaW5hbCByb3cgY291bnQsIHNvIG92ZXJmbG93IG11c3RcbiAqICAgYmUgaGFuZGxlZCB2aWEgYG92ZXJmbG93OiBzY3JvbGxgIC8gZmxleGJveCAobm8gbmF0aXZlIHNjcm9sbGJhY2spXG4gKiAtIE9wdGlvbmFsbHkgZW5hYmxlcyBTR1IgbW91c2UgdHJhY2tpbmcgKHdoZWVsICsgY2xpY2svZHJhZykg4oCUIGV2ZW50c1xuICogICBzdXJmYWNlIGFzIGBQYXJzZWRLZXlgICh3aGVlbCkgYW5kIHVwZGF0ZSB0aGUgSW5rIGluc3RhbmNlJ3NcbiAqICAgc2VsZWN0aW9uIHN0YXRlIChjbGljay9kcmFnKVxuICpcbiAqIE9uIHVubW91bnQsIGRpc2FibGVzIG1vdXNlIHRyYWNraW5nIGFuZCBleGl0cyB0aGUgYWx0IHNjcmVlbiwgcmVzdG9yaW5nXG4gKiB0aGUgbWFpbiBzY3JlZW4ncyBjb250ZW50LiBTYWZlIGZvciB1c2UgaW4gY3RybC1vIHRyYW5zY3JpcHQgb3ZlcmxheXNcbiAqIGFuZCBzaW1pbGFyIHRlbXBvcmFyeSBmdWxsc2NyZWVuIHZpZXdzIOKAlCB0aGUgbWFpbiBzY3JlZW4gaXMgcHJlc2VydmVkLlxuICpcbiAqIE5vdGlmaWVzIHRoZSBJbmsgaW5zdGFuY2UgdmlhIGBzZXRBbHRTY3JlZW5BY3RpdmUoKWAgc28gdGhlIHJlbmRlcmVyXG4gKiBrZWVwcyB0aGUgY3Vyc29yIGluc2lkZSB0aGUgdmlld3BvcnQgKHByZXZlbnRpbmcgdGhlIGN1cnNvci1yZXN0b3JlIExGXG4gKiBmcm9tIHNjcm9sbGluZyBjb250ZW50KSBhbmQgc28gc2lnbmFsLWV4aXQgY2xlYW51cCBjYW4gZXhpdCB0aGUgYWx0XG4gKiBzY3JlZW4gaWYgdGhlIGNvbXBvbmVudCdzIG93biB1bm1vdW50IGRvZXNuJ3QgcnVuLlxuICovXG5leHBvcnQgZnVuY3Rpb24gQWx0ZXJuYXRlU2NyZWVuKHtcbiAgY2hpbGRyZW4sXG4gIG1vdXNlVHJhY2tpbmcgPSB0cnVlLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBzaXplID0gdXNlQ29udGV4dChUZXJtaW5hbFNpemVDb250ZXh0KVxuICBjb25zdCB3cml0ZVJhdyA9IHVzZUNvbnRleHQoVGVybWluYWxXcml0ZUNvbnRleHQpXG5cbiAgLy8gdXNlSW5zZXJ0aW9uRWZmZWN0IChub3QgdXNlTGF5b3V0RWZmZWN0KTogcmVhY3QtcmVjb25jaWxlciBjYWxsc1xuICAvLyByZXNldEFmdGVyQ29tbWl0IGJldHdlZW4gdGhlIG11dGF0aW9uIGFuZCBsYXlvdXQgY29tbWl0IHBoYXNlcywgYW5kXG4gIC8vIEluaydzIHJlc2V0QWZ0ZXJDb21taXQgdHJpZ2dlcnMgb25SZW5kZXIuIFdpdGggdXNlTGF5b3V0RWZmZWN0LCB0aGF0XG4gIC8vIGZpcnN0IG9uUmVuZGVyIGZpcmVzIEJFRk9SRSB0aGlzIGVmZmVjdCDigJQgd3JpdGluZyBhIGZ1bGwgZnJhbWUgdG8gdGhlXG4gIC8vIG1haW4gc2NyZWVuIHdpdGggYWx0U2NyZWVuPWZhbHNlLiBUaGF0IGZyYW1lIGlzIHByZXNlcnZlZCB3aGVuIHdlXG4gIC8vIGVudGVyIGFsdCBzY3JlZW4gYW5kIHJldmVhbGVkIG9uIGV4aXQgYXMgYSBicm9rZW4gdmlldy4gSW5zZXJ0aW9uXG4gIC8vIGVmZmVjdHMgZmlyZSBkdXJpbmcgdGhlIG11dGF0aW9uIHBoYXNlLCBiZWZvcmUgcmVzZXRBZnRlckNvbW1pdCwgc29cbiAgLy8gRU5URVJfQUxUX1NDUkVFTiByZWFjaGVzIHRoZSB0ZXJtaW5hbCBiZWZvcmUgdGhlIGZpcnN0IGZyYW1lIGRvZXMuXG4gIC8vIENsZWFudXAgdGltaW5nIGlzIHVuY2hhbmdlZDogYm90aCBpbnNlcnRpb24gYW5kIGxheW91dCBlZmZlY3QgY2xlYW51cFxuICAvLyBydW4gaW4gdGhlIG11dGF0aW9uIHBoYXNlIG9uIHVubW91bnQsIGJlZm9yZSByZXNldEFmdGVyQ29tbWl0LlxuICB1c2VJbnNlcnRpb25FZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IGluayA9IGluc3RhbmNlcy5nZXQocHJvY2Vzcy5zdGRvdXQpXG4gICAgaWYgKCF3cml0ZVJhdykgcmV0dXJuXG5cbiAgICB3cml0ZVJhdyhcbiAgICAgIEVOVEVSX0FMVF9TQ1JFRU4gK1xuICAgICAgICAnXFx4MWJbMkpcXHgxYltIJyArXG4gICAgICAgIChtb3VzZVRyYWNraW5nID8gRU5BQkxFX01PVVNFX1RSQUNLSU5HIDogJycpLFxuICAgIClcbiAgICBpbms/LnNldEFsdFNjcmVlbkFjdGl2ZSh0cnVlLCBtb3VzZVRyYWNraW5nKVxuXG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGluaz8uc2V0QWx0U2NyZWVuQWN0aXZlKGZhbHNlKVxuICAgICAgaW5rPy5jbGVhclRleHRTZWxlY3Rpb24oKVxuICAgICAgd3JpdGVSYXcoKG1vdXNlVHJhY2tpbmcgPyBESVNBQkxFX01PVVNFX1RSQUNLSU5HIDogJycpICsgRVhJVF9BTFRfU0NSRUVOKVxuICAgIH1cbiAgfSwgW3dyaXRlUmF3LCBtb3VzZVRyYWNraW5nXSlcblxuICByZXR1cm4gKFxuICAgIDxCb3hcbiAgICAgIGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIlxuICAgICAgaGVpZ2h0PXtzaXplPy5yb3dzID8/IDI0fVxuICAgICAgd2lkdGg9XCIxMDAlXCJcbiAgICAgIGZsZXhTaHJpbms9ezB9XG4gICAgPlxuICAgICAge2NoaWxkcmVufVxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQ1YsS0FBS0MsaUJBQWlCLEVBQ3RCQyxVQUFVLEVBQ1ZDLGtCQUFrQixRQUNiLE9BQU87QUFDZCxPQUFPQyxTQUFTLE1BQU0saUJBQWlCO0FBQ3ZDLFNBQ0VDLHNCQUFzQixFQUN0QkMscUJBQXFCLEVBQ3JCQyxnQkFBZ0IsRUFDaEJDLGVBQWUsUUFDVixrQkFBa0I7QUFDekIsU0FBU0Msb0JBQW9CLFFBQVEsK0JBQStCO0FBQ3BFLE9BQU9DLEdBQUcsTUFBTSxVQUFVO0FBQzFCLFNBQVNDLG1CQUFtQixRQUFRLDBCQUEwQjtBQUU5RCxLQUFLQyxLQUFLLEdBQUdYLGlCQUFpQixDQUFDO0VBQzdCO0VBQ0FZLGFBQWEsQ0FBQyxFQUFFLE9BQU87QUFDekIsQ0FBQyxDQUFDOztBQUVGO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFDLGdCQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXlCO0lBQUFDLFFBQUE7SUFBQUwsYUFBQSxFQUFBTTtFQUFBLElBQUFKLEVBR3hCO0VBRE4sTUFBQUYsYUFBQSxHQUFBTSxFQUFvQixLQUFwQkMsU0FBb0IsR0FBcEIsSUFBb0IsR0FBcEJELEVBQW9CO0VBRXBCLE1BQUFFLElBQUEsR0FBYW5CLFVBQVUsQ0FBQ1MsbUJBQW1CLENBQUM7RUFDNUMsTUFBQVcsUUFBQSxHQUFpQnBCLFVBQVUsQ0FBQ08sb0JBQW9CLENBQUM7RUFBQSxJQUFBYyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFSLENBQUEsUUFBQUgsYUFBQSxJQUFBRyxDQUFBLFFBQUFNLFFBQUE7SUFZOUJDLEVBQUEsR0FBQUEsQ0FBQTtNQUNqQixNQUFBRSxHQUFBLEdBQVlyQixTQUFTLENBQUFzQixHQUFJLENBQUNDLE9BQU8sQ0FBQUMsTUFBTyxDQUFDO01BQ3pDLElBQUksQ0FBQ04sUUFBUTtRQUFBO01BQUE7TUFFYkEsUUFBUSxDQUNOZixnQkFBZ0IsR0FDZCxlQUFlLElBQ2RNLGFBQWEsR0FBYlAscUJBQTBDLEdBQTFDLEVBQTBDLENBQy9DLENBQUM7TUFDRG1CLEdBQUcsRUFBQUksa0JBQXlDLENBQXBCLElBQUksRUFBRWhCLGFBQWEsQ0FBQztNQUFBLE9BRXJDO1FBQ0xZLEdBQUcsRUFBQUksa0JBQTJCLENBQU4sS0FBSyxDQUFDO1FBQzlCSixHQUFHLEVBQUFLLGtCQUFzQixDQUFELENBQUM7UUFDekJSLFFBQVEsQ0FBQyxDQUFDVCxhQUFhLEdBQWJSLHNCQUEyQyxHQUEzQyxFQUEyQyxJQUFJRyxlQUFlLENBQUM7TUFBQSxDQUMxRTtJQUFBLENBQ0Y7SUFBRWdCLEVBQUEsSUFBQ0YsUUFBUSxFQUFFVCxhQUFhLENBQUM7SUFBQUcsQ0FBQSxNQUFBSCxhQUFBO0lBQUFHLENBQUEsTUFBQU0sUUFBQTtJQUFBTixDQUFBLE1BQUFPLEVBQUE7SUFBQVAsQ0FBQSxNQUFBUSxFQUFBO0VBQUE7SUFBQUQsRUFBQSxHQUFBUCxDQUFBO0lBQUFRLEVBQUEsR0FBQVIsQ0FBQTtFQUFBO0VBaEI1QmIsa0JBQWtCLENBQUNvQixFQWdCbEIsRUFBRUMsRUFBeUIsQ0FBQztFQUtqQixNQUFBTyxFQUFBLEdBQUFWLElBQUksRUFBQVcsSUFBWSxJQUFoQixFQUFnQjtFQUFBLElBQUFDLEVBQUE7RUFBQSxJQUFBakIsQ0FBQSxRQUFBRSxRQUFBLElBQUFGLENBQUEsUUFBQWUsRUFBQTtJQUYxQkUsRUFBQSxJQUFDLEdBQUcsQ0FDWSxhQUFRLENBQVIsUUFBUSxDQUNkLE1BQWdCLENBQWhCLENBQUFGLEVBQWUsQ0FBQyxDQUNsQixLQUFNLENBQU4sTUFBTSxDQUNBLFVBQUMsQ0FBRCxHQUFDLENBRVpiLFNBQU8sQ0FDVixFQVBDLEdBQUcsQ0FPRTtJQUFBRixDQUFBLE1BQUFFLFFBQUE7SUFBQUYsQ0FBQSxNQUFBZSxFQUFBO0lBQUFmLENBQUEsTUFBQWlCLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFqQixDQUFBO0VBQUE7RUFBQSxPQVBOaUIsRUFPTTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/src/ink/components/App.tsx b/src/ink/components/App.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f9d248d389dc5164adc553bdbee41bce44bd928c --- /dev/null +++ b/src/ink/components/App.tsx @@ -0,0 +1,658 @@ +import React, { PureComponent, type ReactNode } from 'react'; +import { updateLastInteractionTime } from '../../bootstrap/state.js'; +import { logForDebugging } from '../../utils/debug.js'; +import { stopCapturingEarlyInput } from '../../utils/earlyInput.js'; +import { isEnvTruthy } from '../../utils/envUtils.js'; +import { isMouseClicksDisabled } from '../../utils/fullscreen.js'; +import { logError } from '../../utils/log.js'; +import { EventEmitter } from '../events/emitter.js'; +import { InputEvent } from '../events/input-event.js'; +import { TerminalFocusEvent } from '../events/terminal-focus-event.js'; +import { INITIAL_STATE, type ParsedInput, type ParsedKey, type ParsedMouse, parseMultipleKeypresses } from '../parse-keypress.js'; +import reconciler from '../reconciler.js'; +import { finishSelection, hasSelection, type SelectionState, startSelection } from '../selection.js'; +import { isXtermJs, setXtversionName, supportsExtendedKeys } from '../terminal.js'; +import { getTerminalFocused, setTerminalFocused } from '../terminal-focus-state.js'; +import { TerminalQuerier, xtversion } from '../terminal-querier.js'; +import { DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, ENABLE_KITTY_KEYBOARD, ENABLE_MODIFY_OTHER_KEYS, FOCUS_IN, FOCUS_OUT } from '../termio/csi.js'; +import { DBP, DFE, DISABLE_MOUSE_TRACKING, EBP, EFE, HIDE_CURSOR, SHOW_CURSOR } from '../termio/dec.js'; +import AppContext from './AppContext.js'; +import { ClockProvider } from './ClockContext.js'; +import CursorDeclarationContext, { type CursorDeclarationSetter } from './CursorDeclarationContext.js'; +import ErrorOverview from './ErrorOverview.js'; +import StdinContext from './StdinContext.js'; +import { TerminalFocusProvider } from './TerminalFocusContext.js'; +import { TerminalSizeContext } from './TerminalSizeContext.js'; + +// Platforms that support Unix-style process suspension (SIGSTOP/SIGCONT) +const SUPPORTS_SUSPEND = process.platform !== 'win32'; + +// After this many milliseconds of stdin silence, the next chunk triggers +// a terminal mode re-assert (mouse tracking). Catches tmux detach→attach, +// ssh reconnect, and laptop wake — the terminal resets DEC private modes +// but no signal reaches us. 5s is well above normal inter-keystroke gaps +// but short enough that the first scroll after reattach works. +const STDIN_RESUME_GAP_MS = 5000; +type Props = { + readonly children: ReactNode; + readonly stdin: NodeJS.ReadStream; + readonly stdout: NodeJS.WriteStream; + readonly stderr: NodeJS.WriteStream; + readonly exitOnCtrlC: boolean; + readonly onExit: (error?: Error) => void; + readonly terminalColumns: number; + readonly terminalRows: number; + // Text selection state. App mutates this directly from mouse events + // and calls onSelectionChange to trigger a repaint. Mouse events only + // arrive when (or similar) enables mouse tracking, + // so the handler is always wired but dormant until tracking is on. + readonly selection: SelectionState; + readonly onSelectionChange: () => void; + // Dispatch a click at (col, row) — hit-tests the DOM tree and bubbles + // onClick handlers. Returns true if a DOM handler consumed the click. + // No-op (returns false) outside fullscreen mode (Ink.dispatchClick + // gates on altScreenActive). + readonly onClickAt: (col: number, row: number) => boolean; + // Dispatch hover (onMouseEnter/onMouseLeave) as the pointer moves over + // DOM elements. Called for mode-1003 motion events with no button held. + // No-op outside fullscreen (Ink.dispatchHover gates on altScreenActive). + readonly onHoverAt: (col: number, row: number) => void; + // Look up the OSC 8 hyperlink at (col, row) synchronously at click + // time. Returns the URL or undefined. The browser-open is deferred by + // MULTI_CLICK_TIMEOUT_MS so double-click can cancel it. + readonly getHyperlinkAt: (col: number, row: number) => string | undefined; + // Open a hyperlink URL in the browser. Called after the timer fires. + readonly onOpenHyperlink: (url: string) => void; + // Called on double/triple-click PRESS at (col, row). count=2 selects + // the word under the cursor; count=3 selects the line. Ink reads the + // screen buffer to find word/line boundaries and mutates selection, + // setting isDragging=true so a subsequent drag extends by word/line. + readonly onMultiClick: (col: number, row: number, count: 2 | 3) => void; + // Called on drag-motion. Mode-aware: char mode updates focus to the + // exact cell; word/line mode snaps to word/line boundaries. Needs + // screen-buffer access (word boundaries) so lives on Ink, not here. + readonly onSelectionDrag: (col: number, row: number) => void; + // Called when stdin data arrives after a >STDIN_RESUME_GAP_MS gap. + // Ink re-asserts terminal modes: extended key reporting, and (when in + // fullscreen) re-enters alt-screen + mouse tracking. Idempotent on the + // terminal side. Optional so testing.tsx doesn't need to stub it. + readonly onStdinResume?: () => void; + // Receives the declared native-cursor position from useDeclaredCursor + // so ink.tsx can park the terminal cursor there after each frame. + // Enables IME composition at the input caret and lets screen readers / + // magnifiers track the input. Optional so testing.tsx doesn't stub it. + readonly onCursorDeclaration?: CursorDeclarationSetter; + // Dispatch a keyboard event through the DOM tree. Called for each + // parsed key alongside the legacy EventEmitter path. + readonly dispatchKeyboardEvent: (parsedKey: ParsedKey) => void; +}; + +// Multi-click detection thresholds. 500ms is the macOS default; a small +// position tolerance allows for trackpad jitter between clicks. +const MULTI_CLICK_TIMEOUT_MS = 500; +const MULTI_CLICK_DISTANCE = 1; +type State = { + readonly error?: Error; +}; + +// Root component for all Ink apps +// It renders stdin and stdout contexts, so that children can access them if needed +// It also handles Ctrl+C exiting and cursor visibility +export default class App extends PureComponent { + static displayName = 'InternalApp'; + static getDerivedStateFromError(error: Error) { + return { + error + }; + } + override state = { + error: undefined + }; + + // Count how many components enabled raw mode to avoid disabling + // raw mode until all components don't need it anymore + rawModeEnabledCount = 0; + internal_eventEmitter = new EventEmitter(); + keyParseState = INITIAL_STATE; + // Timer for flushing incomplete escape sequences + incompleteEscapeTimer: NodeJS.Timeout | null = null; + // Timeout durations for incomplete sequences (ms) + readonly NORMAL_TIMEOUT = 50; // Short timeout for regular esc sequences + readonly PASTE_TIMEOUT = 500; // Longer timeout for paste operations + + // Terminal query/response dispatch. Responses arrive on stdin (parsed + // out by parse-keypress) and are routed to pending promise resolvers. + querier = new TerminalQuerier(this.props.stdout); + + // Multi-click tracking for double/triple-click text selection. A click + // within MULTI_CLICK_TIMEOUT_MS and MULTI_CLICK_DISTANCE of the previous + // click increments clickCount; otherwise it resets to 1. + lastClickTime = 0; + lastClickCol = -1; + lastClickRow = -1; + clickCount = 0; + // Deferred hyperlink-open timer — cancelled if a second click arrives + // within MULTI_CLICK_TIMEOUT_MS (so double-clicking a hyperlink selects + // the word without also opening the browser). DOM onClick dispatch is + // NOT deferred — it returns true from onClickAt and skips this timer. + pendingHyperlinkTimer: ReturnType | null = null; + // Last mode-1003 motion position. Terminals already dedupe to cell + // granularity but this also lets us skip dispatchHover entirely on + // repeat events (drag-then-release at same cell, etc.). + lastHoverCol = -1; + lastHoverRow = -1; + + // Timestamp of last stdin chunk. Used to detect long gaps (tmux attach, + // ssh reconnect, laptop wake) and trigger terminal mode re-assert. + // Initialized to now so startup doesn't false-trigger. + lastStdinTime = Date.now(); + + // Determines if TTY is supported on the provided stdin + isRawModeSupported(): boolean { + return this.props.stdin.isTTY; + } + override render() { + return + + + + + {})}> + {this.state.error ? : this.props.children} + + + + + + ; + } + override componentDidMount() { + // In accessibility mode, keep the native cursor visible for screen magnifiers and other tools + if (this.props.stdout.isTTY && !isEnvTruthy(process.env.CLAUDE_CODE_ACCESSIBILITY)) { + this.props.stdout.write(HIDE_CURSOR); + } + } + override componentWillUnmount() { + if (this.props.stdout.isTTY) { + this.props.stdout.write(SHOW_CURSOR); + } + + // Clear any pending timers + if (this.incompleteEscapeTimer) { + clearTimeout(this.incompleteEscapeTimer); + this.incompleteEscapeTimer = null; + } + if (this.pendingHyperlinkTimer) { + clearTimeout(this.pendingHyperlinkTimer); + this.pendingHyperlinkTimer = null; + } + // ignore calling setRawMode on an handle stdin it cannot be called + if (this.isRawModeSupported()) { + this.handleSetRawMode(false); + } + } + override componentDidCatch(error: Error) { + this.handleExit(error); + } + handleSetRawMode = (isEnabled: boolean): void => { + const { + stdin + } = this.props; + if (!this.isRawModeSupported()) { + if (stdin === process.stdin) { + throw new Error('Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported'); + } else { + throw new Error('Raw mode is not supported on the stdin provided to Ink.\nRead about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported'); + } + } + stdin.setEncoding('utf8'); + if (isEnabled) { + // Ensure raw mode is enabled only once + if (this.rawModeEnabledCount === 0) { + // Stop early input capture right before we add our own readable handler. + // Both use the same stdin 'readable' + read() pattern, so they can't + // coexist -- our handler would drain stdin before Ink's can see it. + // The buffered text is preserved for REPL.tsx via consumeEarlyInput(). + stopCapturingEarlyInput(); + stdin.ref(); + stdin.setRawMode(true); + stdin.addListener('readable', this.handleReadable); + // Enable bracketed paste mode + this.props.stdout.write(EBP); + // Enable terminal focus reporting (DECSET 1004) + this.props.stdout.write(EFE); + // Enable extended key reporting so ctrl+shift+ is + // distinguishable from ctrl+. We write both the kitty stack + // push (CSI >1u) and xterm modifyOtherKeys level 2 (CSI >4;2m) — + // terminals honor whichever they implement (tmux only accepts the + // latter). + if (supportsExtendedKeys()) { + this.props.stdout.write(ENABLE_KITTY_KEYBOARD); + this.props.stdout.write(ENABLE_MODIFY_OTHER_KEYS); + } + // Probe terminal identity. XTVERSION survives SSH (query/reply goes + // through the pty), unlike TERM_PROGRAM. Used for wheel-scroll base + // detection when env vars are absent. Fire-and-forget: the DA1 + // sentinel bounds the round-trip, and if the terminal ignores the + // query, flush() still resolves and name stays undefined. + // Deferred to next tick so it fires AFTER the current synchronous + // init sequence completes — avoids interleaving with alt-screen/mouse + // tracking enable writes that may happen in the same render cycle. + setImmediate(() => { + void Promise.all([this.querier.send(xtversion()), this.querier.flush()]).then(([r]) => { + if (r) { + setXtversionName(r.name); + logForDebugging(`XTVERSION: terminal identified as "${r.name}"`); + } else { + logForDebugging('XTVERSION: no reply (terminal ignored query)'); + } + }); + }); + } + this.rawModeEnabledCount++; + return; + } + + // Disable raw mode only when no components left that are using it + if (--this.rawModeEnabledCount === 0) { + this.props.stdout.write(DISABLE_MODIFY_OTHER_KEYS); + this.props.stdout.write(DISABLE_KITTY_KEYBOARD); + // Disable terminal focus reporting (DECSET 1004) + this.props.stdout.write(DFE); + // Disable bracketed paste mode + this.props.stdout.write(DBP); + stdin.setRawMode(false); + stdin.removeListener('readable', this.handleReadable); + stdin.unref(); + } + }; + + // Helper to flush incomplete escape sequences + flushIncomplete = (): void => { + // Clear the timer reference + this.incompleteEscapeTimer = null; + + // Only proceed if we have incomplete sequences + if (!this.keyParseState.incomplete) return; + + // Fullscreen: if stdin has data waiting, it's almost certainly the + // continuation of the buffered sequence (e.g. `[<64;74;16M` after a + // lone ESC). Node's event loop runs the timers phase before the poll + // phase, so when a heavy render blocks the loop past 50ms, this timer + // fires before the queued readable event even though the bytes are + // already buffered. Re-arm instead of flushing: handleReadable will + // drain stdin next and clear this timer. Prevents both the spurious + // Escape key and the lost scroll event. + if (this.props.stdin.readableLength > 0) { + this.incompleteEscapeTimer = setTimeout(this.flushIncomplete, this.NORMAL_TIMEOUT); + return; + } + + // Process incomplete as a flush operation (input=null) + // This reuses all existing parsing logic + this.processInput(null); + }; + + // Process input through the parser and handle the results + processInput = (input: string | Buffer | null): void => { + // Parse input using our state machine + const [keys, newState] = parseMultipleKeypresses(this.keyParseState, input); + this.keyParseState = newState; + + // Process ALL keys in a SINGLE discreteUpdates call to prevent + // "Maximum update depth exceeded" error when many keys arrive at once + // (e.g., from paste operations or holding keys rapidly). + // This batches all state updates from handleInput and all useInput + // listeners together within one high-priority update context. + if (keys.length > 0) { + reconciler.discreteUpdates(processKeysInBatch, this, keys, undefined, undefined); + } + + // If we have incomplete escape sequences, set a timer to flush them + if (this.keyParseState.incomplete) { + // Cancel any existing timer first + if (this.incompleteEscapeTimer) { + clearTimeout(this.incompleteEscapeTimer); + } + this.incompleteEscapeTimer = setTimeout(this.flushIncomplete, this.keyParseState.mode === 'IN_PASTE' ? this.PASTE_TIMEOUT : this.NORMAL_TIMEOUT); + } + }; + handleReadable = (): void => { + // Detect long stdin gaps (tmux attach, ssh reconnect, laptop wake). + // The terminal may have reset DEC private modes; re-assert mouse + // tracking. Checked before the read loop so one Date.now() covers + // all chunks in this readable event. + const now = Date.now(); + if (now - this.lastStdinTime > STDIN_RESUME_GAP_MS) { + this.props.onStdinResume?.(); + } + this.lastStdinTime = now; + try { + let chunk; + while ((chunk = this.props.stdin.read() as string | null) !== null) { + // Process the input chunk + this.processInput(chunk); + } + } catch (error) { + // In Bun, an uncaught throw inside a stream 'readable' handler can + // permanently wedge the stream: data stays buffered and 'readable' + // never re-emits. Catching here ensures the stream stays healthy so + // subsequent keystrokes are still delivered. + logError(error); + + // Re-attach the listener in case the exception detached it. + // Bun may remove the listener after an error; without this, + // the session freezes permanently (stdin reader dead, event loop alive). + const { + stdin + } = this.props; + if (this.rawModeEnabledCount > 0 && !stdin.listeners('readable').includes(this.handleReadable)) { + logForDebugging('handleReadable: re-attaching stdin readable listener after error recovery', { + level: 'warn' + }); + stdin.addListener('readable', this.handleReadable); + } + } + }; + handleInput = (input: string | undefined): void => { + // Exit on Ctrl+C + if (input === '\x03' && this.props.exitOnCtrlC) { + this.handleExit(); + } + + // Note: Ctrl+Z (suspend) is now handled in processKeysInBatch using the + // parsed key to support both raw (\x1a) and CSI u format from Kitty + // keyboard protocol terminals (Ghostty, iTerm2, kitty, WezTerm) + }; + handleExit = (error?: Error): void => { + if (this.isRawModeSupported()) { + this.handleSetRawMode(false); + } + this.props.onExit(error); + }; + handleTerminalFocus = (isFocused: boolean): void => { + // setTerminalFocused notifies subscribers: TerminalFocusProvider (context) + // and Clock (interval speed) — no App setState needed. + setTerminalFocused(isFocused); + }; + handleSuspend = (): void => { + if (!this.isRawModeSupported()) { + return; + } + + // Store the exact raw mode count to restore it properly + const rawModeCountBeforeSuspend = this.rawModeEnabledCount; + + // Completely disable raw mode before suspending + while (this.rawModeEnabledCount > 0) { + this.handleSetRawMode(false); + } + + // Show cursor, disable focus reporting, and disable mouse tracking + // before suspending. DISABLE_MOUSE_TRACKING is a no-op if tracking + // wasn't enabled, so it's safe to emit unconditionally — without + // it, SGR mouse sequences would appear as garbled text at the + // shell prompt while suspended. + if (this.props.stdout.isTTY) { + this.props.stdout.write(SHOW_CURSOR + DFE + DISABLE_MOUSE_TRACKING); + } + + // Emit suspend event for Claude Code to handle. Mostly just has a notification + this.internal_eventEmitter.emit('suspend'); + + // Set up resume handler + const resumeHandler = () => { + // Restore raw mode to exact previous state + for (let i = 0; i < rawModeCountBeforeSuspend; i++) { + if (this.isRawModeSupported()) { + this.handleSetRawMode(true); + } + } + + // Hide cursor (unless in accessibility mode) and re-enable focus reporting after resuming + if (this.props.stdout.isTTY) { + if (!isEnvTruthy(process.env.CLAUDE_CODE_ACCESSIBILITY)) { + this.props.stdout.write(HIDE_CURSOR); + } + // Re-enable focus reporting to restore terminal state + this.props.stdout.write(EFE); + } + + // Emit resume event for Claude Code to handle + this.internal_eventEmitter.emit('resume'); + process.removeListener('SIGCONT', resumeHandler); + }; + process.on('SIGCONT', resumeHandler); + process.kill(process.pid, 'SIGSTOP'); + }; +} + +// Helper to process all keys within a single discrete update context. +// discreteUpdates expects (fn, a, b, c, d) -> fn(a, b, c, d) +function processKeysInBatch(app: App, items: ParsedInput[], _unused1: undefined, _unused2: undefined): void { + // Update interaction time for notification timeout tracking. + // This is called from the central input handler to avoid having multiple + // stdin listeners that can cause race conditions and dropped input. + // Terminal responses (kind: 'response') are automated, not user input. + // Mode-1003 no-button motion is also excluded — passive cursor drift is + // not engagement (would suppress idle notifications + defer housekeeping). + if (items.some(i => i.kind === 'key' || i.kind === 'mouse' && !((i.button & 0x20) !== 0 && (i.button & 0x03) === 3))) { + updateLastInteractionTime(); + } + for (const item of items) { + // Terminal responses (DECRPM, DA1, OSC replies, etc.) are not user + // input — route them to the querier to resolve pending promises. + if (item.kind === 'response') { + app.querier.onResponse(item.response); + continue; + } + + // Mouse click/drag events update selection state (fullscreen only). + // Terminal sends 1-indexed col/row; convert to 0-indexed for the + // screen buffer. Button bit 0x20 = drag (motion while button held). + if (item.kind === 'mouse') { + handleMouseEvent(app, item); + continue; + } + const sequence = item.sequence; + + // Handle terminal focus events (DECSET 1004) + if (sequence === FOCUS_IN) { + app.handleTerminalFocus(true); + const event = new TerminalFocusEvent('terminalfocus'); + app.internal_eventEmitter.emit('terminalfocus', event); + continue; + } + if (sequence === FOCUS_OUT) { + app.handleTerminalFocus(false); + // Defensive: if we lost the release event (mouse released outside + // terminal window — some emulators drop it rather than capturing the + // pointer), focus-out is the next observable signal that the drag is + // over. Without this, drag-to-scroll's timer runs until the scroll + // boundary is hit. + if (app.props.selection.isDragging) { + finishSelection(app.props.selection); + app.props.onSelectionChange(); + } + const event = new TerminalFocusEvent('terminalblur'); + app.internal_eventEmitter.emit('terminalblur', event); + continue; + } + + // Failsafe: if we receive input, the terminal must be focused + if (!getTerminalFocused()) { + setTerminalFocused(true); + } + + // Handle Ctrl+Z (suspend) using parsed key to support both raw (\x1a) and + // CSI u format (\x1b[122;5u) from Kitty keyboard protocol terminals + if (item.name === 'z' && item.ctrl && SUPPORTS_SUSPEND) { + app.handleSuspend(); + continue; + } + app.handleInput(sequence); + const event = new InputEvent(item); + app.internal_eventEmitter.emit('input', event); + + // Also dispatch through the DOM tree so onKeyDown handlers fire. + app.props.dispatchKeyboardEvent(item); + } +} + +/** Exported for testing. Mutates app.props.selection and click/hover state. */ +export function handleMouseEvent(app: App, m: ParsedMouse): void { + // Allow disabling click handling while keeping wheel scroll (which goes + // through the keybinding system as 'wheelup'/'wheeldown', not here). + if (isMouseClicksDisabled()) return; + const sel = app.props.selection; + // Terminal coords are 1-indexed; screen buffer is 0-indexed + const col = m.col - 1; + const row = m.row - 1; + const baseButton = m.button & 0x03; + if (m.action === 'press') { + if ((m.button & 0x20) !== 0 && baseButton === 3) { + // Mode-1003 motion with no button held. Dispatch hover; skip the + // rest of this handler (no selection, no click-count side effects). + // Lost-release recovery: no-button motion while isDragging=true means + // the release happened outside the terminal window (iTerm2 doesn't + // capture the pointer past window bounds, so the SGR 'm' never + // arrives). Finish the selection here so copy-on-select fires. The + // FOCUS_OUT handler covers the "switched apps" case but not "released + // past the edge, came back" — and tmux drops focus events unless + // `focus-events on` is set, so this is the more reliable signal. + if (sel.isDragging) { + finishSelection(sel); + app.props.onSelectionChange(); + } + if (col === app.lastHoverCol && row === app.lastHoverRow) return; + app.lastHoverCol = col; + app.lastHoverRow = row; + app.props.onHoverAt(col, row); + return; + } + if (baseButton !== 0) { + // Non-left press breaks the multi-click chain. + app.clickCount = 0; + return; + } + if ((m.button & 0x20) !== 0) { + // Drag motion: mode-aware extension (char/word/line). onSelectionDrag + // calls notifySelectionChange internally — no extra onSelectionChange. + app.props.onSelectionDrag(col, row); + return; + } + // Lost-release fallback for mode-1002-only terminals: a fresh press + // while isDragging=true means the previous release was dropped (cursor + // left the window). Finish that selection so copy-on-select fires + // before startSelection/onMultiClick clobbers it. Mode-1003 terminals + // hit the no-button-motion recovery above instead, so this is rare. + if (sel.isDragging) { + finishSelection(sel); + app.props.onSelectionChange(); + } + // Fresh left press. Detect multi-click HERE (not on release) so the + // word/line highlight appears immediately and a subsequent drag can + // extend by word/line like native macOS. Previously detected on + // release, which meant (a) visible latency before the word highlights + // and (b) double-click+drag fell through to char-mode selection. + const now = Date.now(); + const nearLast = now - app.lastClickTime < MULTI_CLICK_TIMEOUT_MS && Math.abs(col - app.lastClickCol) <= MULTI_CLICK_DISTANCE && Math.abs(row - app.lastClickRow) <= MULTI_CLICK_DISTANCE; + app.clickCount = nearLast ? app.clickCount + 1 : 1; + app.lastClickTime = now; + app.lastClickCol = col; + app.lastClickRow = row; + if (app.clickCount >= 2) { + // Cancel any pending hyperlink-open from the first click — this is + // a double-click, not a single-click on a link. + if (app.pendingHyperlinkTimer) { + clearTimeout(app.pendingHyperlinkTimer); + app.pendingHyperlinkTimer = null; + } + // Cap at 3 (line select) for quadruple+ clicks. + const count = app.clickCount === 2 ? 2 : 3; + app.props.onMultiClick(col, row, count); + return; + } + startSelection(sel, col, row); + // SGR bit 0x08 = alt (xterm.js wires altKey here, not metaKey — see + // comment at the hyperlink-open guard below). On macOS xterm.js, + // receiving alt means macOptionClickForcesSelection is OFF (otherwise + // xterm.js would have consumed the event for native selection). + sel.lastPressHadAlt = (m.button & 0x08) !== 0; + app.props.onSelectionChange(); + return; + } + + // Release: end the drag even for non-zero button codes. Some terminals + // encode release with the motion bit or button=3 "no button" (carried + // over from pre-SGR X10 encoding) — filtering those would orphan + // isDragging=true and leave drag-to-scroll's timer running until the + // scroll boundary. Only act on non-left releases when we ARE dragging + // (so an unrelated middle/right click-release doesn't touch selection). + if (baseButton !== 0) { + if (!sel.isDragging) return; + finishSelection(sel); + app.props.onSelectionChange(); + return; + } + finishSelection(sel); + // NOTE: unlike the old release-based detection we do NOT reset clickCount + // on release-after-drag. This aligns with NSEvent.clickCount semantics: + // an intervening drag doesn't break the click chain. Practical upside: + // trackpad jitter during an intended double-click (press→wobble→release + // →press) now correctly resolves to word-select instead of breaking to a + // fresh single click. The nearLast window (500ms, 1 cell) bounds the + // effect — a deliberate drag past that just starts a fresh chain. + // A press+release with no drag in char mode is a click: anchor set, + // focus null → hasSelection false. In word/line mode the press already + // set anchor+focus (hasSelection true), so release just keeps the + // highlight. The anchor check guards against an orphaned release (no + // prior press — e.g. button was held when mouse tracking was enabled). + if (!hasSelection(sel) && sel.anchor) { + // Single click: dispatch DOM click immediately (cursor repositioning + // etc. are latency-sensitive). If no DOM handler consumed it, defer + // the hyperlink check so a second click can cancel it. + if (!app.props.onClickAt(col, row)) { + // Resolve the hyperlink URL synchronously while the screen buffer + // still reflects what the user clicked — deferring only the + // browser-open so double-click can cancel it. + const url = app.props.getHyperlinkAt(col, row); + // xterm.js (VS Code, Cursor, Windsurf, etc.) has its own OSC 8 link + // handler that fires on Cmd+click *without consuming the mouse event* + // (Linkifier._handleMouseUp calls link.activate() but never + // preventDefault/stopPropagation). The click is also forwarded to the + // pty as SGR, so both VS Code's terminalLinkManager AND our handler + // here would open the URL — twice. We can't filter on Cmd: xterm.js + // drops metaKey before SGR encoding (ICoreMouseEvent has no meta + // field; the SGR bit we call 'meta' is wired to alt). Let xterm.js + // own link-opening; Cmd+click is the native UX there anyway. + // TERM_PROGRAM is the sync fast-path; isXtermJs() is the XTVERSION + // probe result (catches SSH + non-VS Code embedders like Hyper). + if (url && process.env.TERM_PROGRAM !== 'vscode' && !isXtermJs()) { + // Clear any prior pending timer — clicking a second link + // supersedes the first (only the latest click opens). + if (app.pendingHyperlinkTimer) { + clearTimeout(app.pendingHyperlinkTimer); + } + app.pendingHyperlinkTimer = setTimeout((app, url) => { + app.pendingHyperlinkTimer = null; + app.props.onOpenHyperlink(url); + }, MULTI_CLICK_TIMEOUT_MS, app, url); + } + } + } + app.props.onSelectionChange(); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlB1cmVDb21wb25lbnQiLCJSZWFjdE5vZGUiLCJ1cGRhdGVMYXN0SW50ZXJhY3Rpb25UaW1lIiwibG9nRm9yRGVidWdnaW5nIiwic3RvcENhcHR1cmluZ0Vhcmx5SW5wdXQiLCJpc0VudlRydXRoeSIsImlzTW91c2VDbGlja3NEaXNhYmxlZCIsImxvZ0Vycm9yIiwiRXZlbnRFbWl0dGVyIiwiSW5wdXRFdmVudCIsIlRlcm1pbmFsRm9jdXNFdmVudCIsIklOSVRJQUxfU1RBVEUiLCJQYXJzZWRJbnB1dCIsIlBhcnNlZEtleSIsIlBhcnNlZE1vdXNlIiwicGFyc2VNdWx0aXBsZUtleXByZXNzZXMiLCJyZWNvbmNpbGVyIiwiZmluaXNoU2VsZWN0aW9uIiwiaGFzU2VsZWN0aW9uIiwiU2VsZWN0aW9uU3RhdGUiLCJzdGFydFNlbGVjdGlvbiIsImlzWHRlcm1KcyIsInNldFh0dmVyc2lvbk5hbWUiLCJzdXBwb3J0c0V4dGVuZGVkS2V5cyIsImdldFRlcm1pbmFsRm9jdXNlZCIsInNldFRlcm1pbmFsRm9jdXNlZCIsIlRlcm1pbmFsUXVlcmllciIsInh0dmVyc2lvbiIsIkRJU0FCTEVfS0lUVFlfS0VZQk9BUkQiLCJESVNBQkxFX01PRElGWV9PVEhFUl9LRVlTIiwiRU5BQkxFX0tJVFRZX0tFWUJPQVJEIiwiRU5BQkxFX01PRElGWV9PVEhFUl9LRVlTIiwiRk9DVVNfSU4iLCJGT0NVU19PVVQiLCJEQlAiLCJERkUiLCJESVNBQkxFX01PVVNFX1RSQUNLSU5HIiwiRUJQIiwiRUZFIiwiSElERV9DVVJTT1IiLCJTSE9XX0NVUlNPUiIsIkFwcENvbnRleHQiLCJDbG9ja1Byb3ZpZGVyIiwiQ3Vyc29yRGVjbGFyYXRpb25Db250ZXh0IiwiQ3Vyc29yRGVjbGFyYXRpb25TZXR0ZXIiLCJFcnJvck92ZXJ2aWV3IiwiU3RkaW5Db250ZXh0IiwiVGVybWluYWxGb2N1c1Byb3ZpZGVyIiwiVGVybWluYWxTaXplQ29udGV4dCIsIlNVUFBPUlRTX1NVU1BFTkQiLCJwcm9jZXNzIiwicGxhdGZvcm0iLCJTVERJTl9SRVNVTUVfR0FQX01TIiwiUHJvcHMiLCJjaGlsZHJlbiIsInN0ZGluIiwiTm9kZUpTIiwiUmVhZFN0cmVhbSIsInN0ZG91dCIsIldyaXRlU3RyZWFtIiwic3RkZXJyIiwiZXhpdE9uQ3RybEMiLCJvbkV4aXQiLCJlcnJvciIsIkVycm9yIiwidGVybWluYWxDb2x1bW5zIiwidGVybWluYWxSb3dzIiwic2VsZWN0aW9uIiwib25TZWxlY3Rpb25DaGFuZ2UiLCJvbkNsaWNrQXQiLCJjb2wiLCJyb3ciLCJvbkhvdmVyQXQiLCJnZXRIeXBlcmxpbmtBdCIsIm9uT3Blbkh5cGVybGluayIsInVybCIsIm9uTXVsdGlDbGljayIsImNvdW50Iiwib25TZWxlY3Rpb25EcmFnIiwib25TdGRpblJlc3VtZSIsIm9uQ3Vyc29yRGVjbGFyYXRpb24iLCJkaXNwYXRjaEtleWJvYXJkRXZlbnQiLCJwYXJzZWRLZXkiLCJNVUxUSV9DTElDS19USU1FT1VUX01TIiwiTVVMVElfQ0xJQ0tfRElTVEFOQ0UiLCJTdGF0ZSIsIkFwcCIsImRpc3BsYXlOYW1lIiwiZ2V0RGVyaXZlZFN0YXRlRnJvbUVycm9yIiwic3RhdGUiLCJ1bmRlZmluZWQiLCJyYXdNb2RlRW5hYmxlZENvdW50IiwiaW50ZXJuYWxfZXZlbnRFbWl0dGVyIiwia2V5UGFyc2VTdGF0ZSIsImluY29tcGxldGVFc2NhcGVUaW1lciIsIlRpbWVvdXQiLCJOT1JNQUxfVElNRU9VVCIsIlBBU1RFX1RJTUVPVVQiLCJxdWVyaWVyIiwicHJvcHMiLCJsYXN0Q2xpY2tUaW1lIiwibGFzdENsaWNrQ29sIiwibGFzdENsaWNrUm93IiwiY2xpY2tDb3VudCIsInBlbmRpbmdIeXBlcmxpbmtUaW1lciIsIlJldHVyblR5cGUiLCJzZXRUaW1lb3V0IiwibGFzdEhvdmVyQ29sIiwibGFzdEhvdmVyUm93IiwibGFzdFN0ZGluVGltZSIsIkRhdGUiLCJub3ciLCJpc1Jhd01vZGVTdXBwb3J0ZWQiLCJpc1RUWSIsInJlbmRlciIsImNvbHVtbnMiLCJyb3dzIiwiZXhpdCIsImhhbmRsZUV4aXQiLCJzZXRSYXdNb2RlIiwiaGFuZGxlU2V0UmF3TW9kZSIsImludGVybmFsX2V4aXRPbkN0cmxDIiwiaW50ZXJuYWxfcXVlcmllciIsImNvbXBvbmVudERpZE1vdW50IiwiZW52IiwiQ0xBVURFX0NPREVfQUNDRVNTSUJJTElUWSIsIndyaXRlIiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJjbGVhclRpbWVvdXQiLCJjb21wb25lbnREaWRDYXRjaCIsImlzRW5hYmxlZCIsInNldEVuY29kaW5nIiwicmVmIiwiYWRkTGlzdGVuZXIiLCJoYW5kbGVSZWFkYWJsZSIsInNldEltbWVkaWF0ZSIsIlByb21pc2UiLCJhbGwiLCJzZW5kIiwiZmx1c2giLCJ0aGVuIiwiciIsIm5hbWUiLCJyZW1vdmVMaXN0ZW5lciIsInVucmVmIiwiZmx1c2hJbmNvbXBsZXRlIiwiaW5jb21wbGV0ZSIsInJlYWRhYmxlTGVuZ3RoIiwicHJvY2Vzc0lucHV0IiwiaW5wdXQiLCJCdWZmZXIiLCJrZXlzIiwibmV3U3RhdGUiLCJsZW5ndGgiLCJkaXNjcmV0ZVVwZGF0ZXMiLCJwcm9jZXNzS2V5c0luQmF0Y2giLCJtb2RlIiwiY2h1bmsiLCJyZWFkIiwibGlzdGVuZXJzIiwiaW5jbHVkZXMiLCJsZXZlbCIsImhhbmRsZUlucHV0IiwiaGFuZGxlVGVybWluYWxGb2N1cyIsImlzRm9jdXNlZCIsImhhbmRsZVN1c3BlbmQiLCJyYXdNb2RlQ291bnRCZWZvcmVTdXNwZW5kIiwiZW1pdCIsInJlc3VtZUhhbmRsZXIiLCJpIiwib24iLCJraWxsIiwicGlkIiwiYXBwIiwiaXRlbXMiLCJfdW51c2VkMSIsIl91bnVzZWQyIiwic29tZSIsImtpbmQiLCJidXR0b24iLCJpdGVtIiwib25SZXNwb25zZSIsInJlc3BvbnNlIiwiaGFuZGxlTW91c2VFdmVudCIsInNlcXVlbmNlIiwiZXZlbnQiLCJpc0RyYWdnaW5nIiwiY3RybCIsIm0iLCJzZWwiLCJiYXNlQnV0dG9uIiwiYWN0aW9uIiwibmVhckxhc3QiLCJNYXRoIiwiYWJzIiwibGFzdFByZXNzSGFkQWx0IiwiYW5jaG9yIiwiVEVSTV9QUk9HUkFNIl0sInNvdXJjZXMiOlsiQXBwLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHsgUHVyZUNvbXBvbmVudCwgdHlwZSBSZWFjdE5vZGUgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVwZGF0ZUxhc3RJbnRlcmFjdGlvblRpbWUgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgeyBsb2dGb3JEZWJ1Z2dpbmcgfSBmcm9tICcuLi8uLi91dGlscy9kZWJ1Zy5qcydcbmltcG9ydCB7IHN0b3BDYXB0dXJpbmdFYXJseUlucHV0IH0gZnJvbSAnLi4vLi4vdXRpbHMvZWFybHlJbnB1dC5qcydcbmltcG9ydCB7IGlzRW52VHJ1dGh5IH0gZnJvbSAnLi4vLi4vdXRpbHMvZW52VXRpbHMuanMnXG5pbXBvcnQgeyBpc01vdXNlQ2xpY2tzRGlzYWJsZWQgfSBmcm9tICcuLi8uLi91dGlscy9mdWxsc2NyZWVuLmpzJ1xuaW1wb3J0IHsgbG9nRXJyb3IgfSBmcm9tICcuLi8uLi91dGlscy9sb2cuanMnXG5pbXBvcnQgeyBFdmVudEVtaXR0ZXIgfSBmcm9tICcuLi9ldmVudHMvZW1pdHRlci5qcydcbmltcG9ydCB7IElucHV0RXZlbnQgfSBmcm9tICcuLi9ldmVudHMvaW5wdXQtZXZlbnQuanMnXG5pbXBvcnQgeyBUZXJtaW5hbEZvY3VzRXZlbnQgfSBmcm9tICcuLi9ldmVudHMvdGVybWluYWwtZm9jdXMtZXZlbnQuanMnXG5pbXBvcnQge1xuICBJTklUSUFMX1NUQVRFLFxuICB0eXBlIFBhcnNlZElucHV0LFxuICB0eXBlIFBhcnNlZEtleSxcbiAgdHlwZSBQYXJzZWRNb3VzZSxcbiAgcGFyc2VNdWx0aXBsZUtleXByZXNzZXMsXG59IGZyb20gJy4uL3BhcnNlLWtleXByZXNzLmpzJ1xuaW1wb3J0IHJlY29uY2lsZXIgZnJvbSAnLi4vcmVjb25jaWxlci5qcydcbmltcG9ydCB7XG4gIGZpbmlzaFNlbGVjdGlvbixcbiAgaGFzU2VsZWN0aW9uLFxuICB0eXBlIFNlbGVjdGlvblN0YXRlLFxuICBzdGFydFNlbGVjdGlvbixcbn0gZnJvbSAnLi4vc2VsZWN0aW9uLmpzJ1xuaW1wb3J0IHtcbiAgaXNYdGVybUpzLFxuICBzZXRYdHZlcnNpb25OYW1lLFxuICBzdXBwb3J0c0V4dGVuZGVkS2V5cyxcbn0gZnJvbSAnLi4vdGVybWluYWwuanMnXG5pbXBvcnQge1xuICBnZXRUZXJtaW5hbEZvY3VzZWQsXG4gIHNldFRlcm1pbmFsRm9jdXNlZCxcbn0gZnJvbSAnLi4vdGVybWluYWwtZm9jdXMtc3RhdGUuanMnXG5pbXBvcnQgeyBUZXJtaW5hbFF1ZXJpZXIsIHh0dmVyc2lvbiB9IGZyb20gJy4uL3Rlcm1pbmFsLXF1ZXJpZXIuanMnXG5pbXBvcnQge1xuICBESVNBQkxFX0tJVFRZX0tFWUJPQVJELFxuICBESVNBQkxFX01PRElGWV9PVEhFUl9LRVlTLFxuICBFTkFCTEVfS0lUVFlfS0VZQk9BUkQsXG4gIEVOQUJMRV9NT0RJRllfT1RIRVJfS0VZUyxcbiAgRk9DVVNfSU4sXG4gIEZPQ1VTX09VVCxcbn0gZnJvbSAnLi4vdGVybWlvL2NzaS5qcydcbmltcG9ydCB7XG4gIERCUCxcbiAgREZFLFxuICBESVNBQkxFX01PVVNFX1RSQUNLSU5HLFxuICBFQlAsXG4gIEVGRSxcbiAgSElERV9DVVJTT1IsXG4gIFNIT1dfQ1VSU09SLFxufSBmcm9tICcuLi90ZXJtaW8vZGVjLmpzJ1xuaW1wb3J0IEFwcENvbnRleHQgZnJvbSAnLi9BcHBDb250ZXh0LmpzJ1xuaW1wb3J0IHsgQ2xvY2tQcm92aWRlciB9IGZyb20gJy4vQ2xvY2tDb250ZXh0LmpzJ1xuaW1wb3J0IEN1cnNvckRlY2xhcmF0aW9uQ29udGV4dCwge1xuICB0eXBlIEN1cnNvckRlY2xhcmF0aW9uU2V0dGVyLFxufSBmcm9tICcuL0N1cnNvckRlY2xhcmF0aW9uQ29udGV4dC5qcydcbmltcG9ydCBFcnJvck92ZXJ2aWV3IGZyb20gJy4vRXJyb3JPdmVydmlldy5qcydcbmltcG9ydCBTdGRpbkNvbnRleHQgZnJvbSAnLi9TdGRpbkNvbnRleHQuanMnXG5pbXBvcnQgeyBUZXJtaW5hbEZvY3VzUHJvdmlkZXIgfSBmcm9tICcuL1Rlcm1pbmFsRm9jdXNDb250ZXh0LmpzJ1xuaW1wb3J0IHsgVGVybWluYWxTaXplQ29udGV4dCB9IGZyb20gJy4vVGVybWluYWxTaXplQ29udGV4dC5qcydcblxuLy8gUGxhdGZvcm1zIHRoYXQgc3VwcG9ydCBVbml4LXN0eWxlIHByb2Nlc3Mgc3VzcGVuc2lvbiAoU0lHU1RPUC9TSUdDT05UKVxuY29uc3QgU1VQUE9SVFNfU1VTUEVORCA9IHByb2Nlc3MucGxhdGZvcm0gIT09ICd3aW4zMidcblxuLy8gQWZ0ZXIgdGhpcyBtYW55IG1pbGxpc2Vjb25kcyBvZiBzdGRpbiBzaWxlbmNlLCB0aGUgbmV4dCBjaHVuayB0cmlnZ2Vyc1xuLy8gYSB0ZXJtaW5hbCBtb2RlIHJlLWFzc2VydCAobW91c2UgdHJhY2tpbmcpLiBDYXRjaGVzIHRtdXggZGV0YWNo4oaSYXR0YWNoLFxuLy8gc3NoIHJlY29ubmVjdCwgYW5kIGxhcHRvcCB3YWtlIOKAlCB0aGUgdGVybWluYWwgcmVzZXRzIERFQyBwcml2YXRlIG1vZGVzXG4vLyBidXQgbm8gc2lnbmFsIHJlYWNoZXMgdXMuIDVzIGlzIHdlbGwgYWJvdmUgbm9ybWFsIGludGVyLWtleXN0cm9rZSBnYXBzXG4vLyBidXQgc2hvcnQgZW5vdWdoIHRoYXQgdGhlIGZpcnN0IHNjcm9sbCBhZnRlciByZWF0dGFjaCB3b3Jrcy5cbmNvbnN0IFNURElOX1JFU1VNRV9HQVBfTVMgPSA1MDAwXG5cbnR5cGUgUHJvcHMgPSB7XG4gIHJlYWRvbmx5IGNoaWxkcmVuOiBSZWFjdE5vZGVcbiAgcmVhZG9ubHkgc3RkaW46IE5vZGVKUy5SZWFkU3RyZWFtXG4gIHJlYWRvbmx5IHN0ZG91dDogTm9kZUpTLldyaXRlU3RyZWFtXG4gIHJlYWRvbmx5IHN0ZGVycjogTm9kZUpTLldyaXRlU3RyZWFtXG4gIHJlYWRvbmx5IGV4aXRPbkN0cmxDOiBib29sZWFuXG4gIHJlYWRvbmx5IG9uRXhpdDogKGVycm9yPzogRXJyb3IpID0+IHZvaWRcbiAgcmVhZG9ubHkgdGVybWluYWxDb2x1bW5zOiBudW1iZXJcbiAgcmVhZG9ubHkgdGVybWluYWxSb3dzOiBudW1iZXJcbiAgLy8gVGV4dCBzZWxlY3Rpb24gc3RhdGUuIEFwcCBtdXRhdGVzIHRoaXMgZGlyZWN0bHkgZnJvbSBtb3VzZSBldmVudHNcbiAgLy8gYW5kIGNhbGxzIG9uU2VsZWN0aW9uQ2hhbmdlIHRvIHRyaWdnZXIgYSByZXBhaW50LiBNb3VzZSBldmVudHMgb25seVxuICAvLyBhcnJpdmUgd2hlbiA8QWx0ZXJuYXRlU2NyZWVuPiAob3Igc2ltaWxhcikgZW5hYmxlcyBtb3VzZSB0cmFja2luZyxcbiAgLy8gc28gdGhlIGhhbmRsZXIgaXMgYWx3YXlzIHdpcmVkIGJ1dCBkb3JtYW50IHVudGlsIHRyYWNraW5nIGlzIG9uLlxuICByZWFkb25seSBzZWxlY3Rpb246IFNlbGVjdGlvblN0YXRlXG4gIHJlYWRvbmx5IG9uU2VsZWN0aW9uQ2hhbmdlOiAoKSA9PiB2b2lkXG4gIC8vIERpc3BhdGNoIGEgY2xpY2sgYXQgKGNvbCwgcm93KSDigJQgaGl0LXRlc3RzIHRoZSBET00gdHJlZSBhbmQgYnViYmxlc1xuICAvLyBvbkNsaWNrIGhhbmRsZXJzLiBSZXR1cm5zIHRydWUgaWYgYSBET00gaGFuZGxlciBjb25zdW1lZCB0aGUgY2xpY2suXG4gIC8vIE5vLW9wIChyZXR1cm5zIGZhbHNlKSBvdXRzaWRlIGZ1bGxzY3JlZW4gbW9kZSAoSW5rLmRpc3BhdGNoQ2xpY2tcbiAgLy8gZ2F0ZXMgb24gYWx0U2NyZWVuQWN0aXZlKS5cbiAgcmVhZG9ubHkgb25DbGlja0F0OiAoY29sOiBudW1iZXIsIHJvdzogbnVtYmVyKSA9PiBib29sZWFuXG4gIC8vIERpc3BhdGNoIGhvdmVyIChvbk1vdXNlRW50ZXIvb25Nb3VzZUxlYXZlKSBhcyB0aGUgcG9pbnRlciBtb3ZlcyBvdmVyXG4gIC8vIERPTSBlbGVtZW50cy4gQ2FsbGVkIGZvciBtb2RlLTEwMDMgbW90aW9uIGV2ZW50cyB3aXRoIG5vIGJ1dHRvbiBoZWxkLlxuICAvLyBOby1vcCBvdXRzaWRlIGZ1bGxzY3JlZW4gKEluay5kaXNwYXRjaEhvdmVyIGdhdGVzIG9uIGFsdFNjcmVlbkFjdGl2ZSkuXG4gIHJlYWRvbmx5IG9uSG92ZXJBdDogKGNvbDogbnVtYmVyLCByb3c6IG51bWJlcikgPT4gdm9pZFxuICAvLyBMb29rIHVwIHRoZSBPU0MgOCBoeXBlcmxpbmsgYXQgKGNvbCwgcm93KSBzeW5jaHJvbm91c2x5IGF0IGNsaWNrXG4gIC8vIHRpbWUuIFJldHVybnMgdGhlIFVSTCBvciB1bmRlZmluZWQuIFRoZSBicm93c2VyLW9wZW4gaXMgZGVmZXJyZWQgYnlcbiAgLy8gTVVMVElfQ0xJQ0tfVElNRU9VVF9NUyBzbyBkb3VibGUtY2xpY2sgY2FuIGNhbmNlbCBpdC5cbiAgcmVhZG9ubHkgZ2V0SHlwZXJsaW5rQXQ6IChjb2w6IG51bWJlciwgcm93OiBudW1iZXIpID0+IHN0cmluZyB8IHVuZGVmaW5lZFxuICAvLyBPcGVuIGEgaHlwZXJsaW5rIFVSTCBpbiB0aGUgYnJvd3Nlci4gQ2FsbGVkIGFmdGVyIHRoZSB0aW1lciBmaXJlcy5cbiAgcmVhZG9ubHkgb25PcGVuSHlwZXJsaW5rOiAodXJsOiBzdHJpbmcpID0+IHZvaWRcbiAgLy8gQ2FsbGVkIG9uIGRvdWJsZS90cmlwbGUtY2xpY2sgUFJFU1MgYXQgKGNvbCwgcm93KS4gY291bnQ9MiBzZWxlY3RzXG4gIC8vIHRoZSB3b3JkIHVuZGVyIHRoZSBjdXJzb3I7IGNvdW50PTMgc2VsZWN0cyB0aGUgbGluZS4gSW5rIHJlYWRzIHRoZVxuICAvLyBzY3JlZW4gYnVmZmVyIHRvIGZpbmQgd29yZC9saW5lIGJvdW5kYXJpZXMgYW5kIG11dGF0ZXMgc2VsZWN0aW9uLFxuICAvLyBzZXR0aW5nIGlzRHJhZ2dpbmc9dHJ1ZSBzbyBhIHN1YnNlcXVlbnQgZHJhZyBleHRlbmRzIGJ5IHdvcmQvbGluZS5cbiAgcmVhZG9ubHkgb25NdWx0aUNsaWNrOiAoY29sOiBudW1iZXIsIHJvdzogbnVtYmVyLCBjb3VudDogMiB8IDMpID0+IHZvaWRcbiAgLy8gQ2FsbGVkIG9uIGRyYWctbW90aW9uLiBNb2RlLWF3YXJlOiBjaGFyIG1vZGUgdXBkYXRlcyBmb2N1cyB0byB0aGVcbiAgLy8gZXhhY3QgY2VsbDsgd29yZC9saW5lIG1vZGUgc25hcHMgdG8gd29yZC9saW5lIGJvdW5kYXJpZXMuIE5lZWRzXG4gIC8vIHNjcmVlbi1idWZmZXIgYWNjZXNzICh3b3JkIGJvdW5kYXJpZXMpIHNvIGxpdmVzIG9uIEluaywgbm90IGhlcmUuXG4gIHJlYWRvbmx5IG9uU2VsZWN0aW9uRHJhZzogKGNvbDogbnVtYmVyLCByb3c6IG51bWJlcikgPT4gdm9pZFxuICAvLyBDYWxsZWQgd2hlbiBzdGRpbiBkYXRhIGFycml2ZXMgYWZ0ZXIgYSA+U1RESU5fUkVTVU1FX0dBUF9NUyBnYXAuXG4gIC8vIEluayByZS1hc3NlcnRzIHRlcm1pbmFsIG1vZGVzOiBleHRlbmRlZCBrZXkgcmVwb3J0aW5nLCBhbmQgKHdoZW4gaW5cbiAgLy8gZnVsbHNjcmVlbikgcmUtZW50ZXJzIGFsdC1zY3JlZW4gKyBtb3VzZSB0cmFja2luZy4gSWRlbXBvdGVudCBvbiB0aGVcbiAgLy8gdGVybWluYWwgc2lkZS4gT3B0aW9uYWwgc28gdGVzdGluZy50c3ggZG9lc24ndCBuZWVkIHRvIHN0dWIgaXQuXG4gIHJlYWRvbmx5IG9uU3RkaW5SZXN1bWU/OiAoKSA9PiB2b2lkXG4gIC8vIFJlY2VpdmVzIHRoZSBkZWNsYXJlZCBuYXRpdmUtY3Vyc29yIHBvc2l0aW9uIGZyb20gdXNlRGVjbGFyZWRDdXJzb3JcbiAgLy8gc28gaW5rLnRzeCBjYW4gcGFyayB0aGUgdGVybWluYWwgY3Vyc29yIHRoZXJlIGFmdGVyIGVhY2ggZnJhbWUuXG4gIC8vIEVuYWJsZXMgSU1FIGNvbXBvc2l0aW9uIGF0IHRoZSBpbnB1dCBjYXJldCBhbmQgbGV0cyBzY3JlZW4gcmVhZGVycyAvXG4gIC8vIG1hZ25pZmllcnMgdHJhY2sgdGhlIGlucHV0LiBPcHRpb25hbCBzbyB0ZXN0aW5nLnRzeCBkb2Vzbid0IHN0dWIgaXQuXG4gIHJlYWRvbmx5IG9uQ3Vyc29yRGVjbGFyYXRpb24/OiBDdXJzb3JEZWNsYXJhdGlvblNldHRlclxuICAvLyBEaXNwYXRjaCBhIGtleWJvYXJkIGV2ZW50IHRocm91Z2ggdGhlIERPTSB0cmVlLiBDYWxsZWQgZm9yIGVhY2hcbiAgLy8gcGFyc2VkIGtleSBhbG9uZ3NpZGUgdGhlIGxlZ2FjeSBFdmVudEVtaXR0ZXIgcGF0aC5cbiAgcmVhZG9ubHkgZGlzcGF0Y2hLZXlib2FyZEV2ZW50OiAocGFyc2VkS2V5OiBQYXJzZWRLZXkpID0+IHZvaWRcbn1cblxuLy8gTXVsdGktY2xpY2sgZGV0ZWN0aW9uIHRocmVzaG9sZHMuIDUwMG1zIGlzIHRoZSBtYWNPUyBkZWZhdWx0OyBhIHNtYWxsXG4vLyBwb3NpdGlvbiB0b2xlcmFuY2UgYWxsb3dzIGZvciB0cmFja3BhZCBqaXR0ZXIgYmV0d2VlbiBjbGlja3MuXG5jb25zdCBNVUxUSV9DTElDS19USU1FT1VUX01TID0gNTAwXG5jb25zdCBNVUxUSV9DTElDS19ESVNUQU5DRSA9IDFcblxudHlwZSBTdGF0ZSA9IHtcbiAgcmVhZG9ubHkgZXJyb3I/OiBFcnJvclxufVxuXG4vLyBSb290IGNvbXBvbmVudCBmb3IgYWxsIEluayBhcHBzXG4vLyBJdCByZW5kZXJzIHN0ZGluIGFuZCBzdGRvdXQgY29udGV4dHMsIHNvIHRoYXQgY2hpbGRyZW4gY2FuIGFjY2VzcyB0aGVtIGlmIG5lZWRlZFxuLy8gSXQgYWxzbyBoYW5kbGVzIEN0cmwrQyBleGl0aW5nIGFuZCBjdXJzb3IgdmlzaWJpbGl0eVxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQXBwIGV4dGVuZHMgUHVyZUNvbXBvbmVudDxQcm9wcywgU3RhdGU+IHtcbiAgc3RhdGljIGRpc3BsYXlOYW1lID0gJ0ludGVybmFsQXBwJ1xuXG4gIHN0YXRpYyBnZXREZXJpdmVkU3RhdGVGcm9tRXJyb3IoZXJyb3I6IEVycm9yKSB7XG4gICAgcmV0dXJuIHsgZXJyb3IgfVxuICB9XG5cbiAgb3ZlcnJpZGUgc3RhdGUgPSB7XG4gICAgZXJyb3I6IHVuZGVmaW5lZCxcbiAgfVxuXG4gIC8vIENvdW50IGhvdyBtYW55IGNvbXBvbmVudHMgZW5hYmxlZCByYXcgbW9kZSB0byBhdm9pZCBkaXNhYmxpbmdcbiAgLy8gcmF3IG1vZGUgdW50aWwgYWxsIGNvbXBvbmVudHMgZG9uJ3QgbmVlZCBpdCBhbnltb3JlXG4gIHJhd01vZGVFbmFibGVkQ291bnQgPSAwXG5cbiAgaW50ZXJuYWxfZXZlbnRFbWl0dGVyID0gbmV3IEV2ZW50RW1pdHRlcigpXG4gIGtleVBhcnNlU3RhdGUgPSBJTklUSUFMX1NUQVRFXG4gIC8vIFRpbWVyIGZvciBmbHVzaGluZyBpbmNvbXBsZXRlIGVzY2FwZSBzZXF1ZW5jZXNcbiAgaW5jb21wbGV0ZUVzY2FwZVRpbWVyOiBOb2RlSlMuVGltZW91dCB8IG51bGwgPSBudWxsXG4gIC8vIFRpbWVvdXQgZHVyYXRpb25zIGZvciBpbmNvbXBsZXRlIHNlcXVlbmNlcyAobXMpXG4gIHJlYWRvbmx5IE5PUk1BTF9USU1FT1VUID0gNTAgLy8gU2hvcnQgdGltZW91dCBmb3IgcmVndWxhciBlc2Mgc2VxdWVuY2VzXG4gIHJlYWRvbmx5IFBBU1RFX1RJTUVPVVQgPSA1MDAgLy8gTG9uZ2VyIHRpbWVvdXQgZm9yIHBhc3RlIG9wZXJhdGlvbnNcblxuICAvLyBUZXJtaW5hbCBxdWVyeS9yZXNwb25zZSBkaXNwYXRjaC4gUmVzcG9uc2VzIGFycml2ZSBvbiBzdGRpbiAocGFyc2VkXG4gIC8vIG91dCBieSBwYXJzZS1rZXlwcmVzcykgYW5kIGFyZSByb3V0ZWQgdG8gcGVuZGluZyBwcm9taXNlIHJlc29sdmVycy5cbiAgcXVlcmllciA9IG5ldyBUZXJtaW5hbFF1ZXJpZXIodGhpcy5wcm9wcy5zdGRvdXQpXG5cbiAgLy8gTXVsdGktY2xpY2sgdHJhY2tpbmcgZm9yIGRvdWJsZS90cmlwbGUtY2xpY2sgdGV4dCBzZWxlY3Rpb24uIEEgY2xpY2tcbiAgLy8gd2l0aGluIE1VTFRJX0NMSUNLX1RJTUVPVVRfTVMgYW5kIE1VTFRJX0NMSUNLX0RJU1RBTkNFIG9mIHRoZSBwcmV2aW91c1xuICAvLyBjbGljayBpbmNyZW1lbnRzIGNsaWNrQ291bnQ7IG90aGVyd2lzZSBpdCByZXNldHMgdG8gMS5cbiAgbGFzdENsaWNrVGltZSA9IDBcbiAgbGFzdENsaWNrQ29sID0gLTFcbiAgbGFzdENsaWNrUm93ID0gLTFcbiAgY2xpY2tDb3VudCA9IDBcbiAgLy8gRGVmZXJyZWQgaHlwZXJsaW5rLW9wZW4gdGltZXIg4oCUIGNhbmNlbGxlZCBpZiBhIHNlY29uZCBjbGljayBhcnJpdmVzXG4gIC8vIHdpdGhpbiBNVUxUSV9DTElDS19USU1FT1VUX01TIChzbyBkb3VibGUtY2xpY2tpbmcgYSBoeXBlcmxpbmsgc2VsZWN0c1xuICAvLyB0aGUgd29yZCB3aXRob3V0IGFsc28gb3BlbmluZyB0aGUgYnJvd3NlcikuIERPTSBvbkNsaWNrIGRpc3BhdGNoIGlzXG4gIC8vIE5PVCBkZWZlcnJlZCDigJQgaXQgcmV0dXJucyB0cnVlIGZyb20gb25DbGlja0F0IGFuZCBza2lwcyB0aGlzIHRpbWVyLlxuICBwZW5kaW5nSHlwZXJsaW5rVGltZXI6IFJldHVyblR5cGU8dHlwZW9mIHNldFRpbWVvdXQ+IHwgbnVsbCA9IG51bGxcbiAgLy8gTGFzdCBtb2RlLTEwMDMgbW90aW9uIHBvc2l0aW9uLiBUZXJtaW5hbHMgYWxyZWFkeSBkZWR1cGUgdG8gY2VsbFxuICAvLyBncmFudWxhcml0eSBidXQgdGhpcyBhbHNvIGxldHMgdXMgc2tpcCBkaXNwYXRjaEhvdmVyIGVudGlyZWx5IG9uXG4gIC8vIHJlcGVhdCBldmVudHMgKGRyYWctdGhlbi1yZWxlYXNlIGF0IHNhbWUgY2VsbCwgZXRjLikuXG4gIGxhc3RIb3ZlckNvbCA9IC0xXG4gIGxhc3RIb3ZlclJvdyA9IC0xXG5cbiAgLy8gVGltZXN0YW1wIG9mIGxhc3Qgc3RkaW4gY2h1bmsuIFVzZWQgdG8gZGV0ZWN0IGxvbmcgZ2FwcyAodG11eCBhdHRhY2gsXG4gIC8vIHNzaCByZWNvbm5lY3QsIGxhcHRvcCB3YWtlKSBhbmQgdHJpZ2dlciB0ZXJtaW5hbCBtb2RlIHJlLWFzc2VydC5cbiAgLy8gSW5pdGlhbGl6ZWQgdG8gbm93IHNvIHN0YXJ0dXAgZG9lc24ndCBmYWxzZS10cmlnZ2VyLlxuICBsYXN0U3RkaW5UaW1lID0gRGF0ZS5ub3coKVxuXG4gIC8vIERldGVybWluZXMgaWYgVFRZIGlzIHN1cHBvcnRlZCBvbiB0aGUgcHJvdmlkZWQgc3RkaW5cbiAgaXNSYXdNb2RlU3VwcG9ydGVkKCk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLnByb3BzLnN0ZGluLmlzVFRZXG4gIH1cblxuICBvdmVycmlkZSByZW5kZXIoKSB7XG4gICAgcmV0dXJuIChcbiAgICAgIDxUZXJtaW5hbFNpemVDb250ZXh0LlByb3ZpZGVyXG4gICAgICAgIHZhbHVlPXt7XG4gICAgICAgICAgY29sdW1uczogdGhpcy5wcm9wcy50ZXJtaW5hbENvbHVtbnMsXG4gICAgICAgICAgcm93czogdGhpcy5wcm9wcy50ZXJtaW5hbFJvd3MsXG4gICAgICAgIH19XG4gICAgICA+XG4gICAgICAgIDxBcHBDb250ZXh0LlByb3ZpZGVyXG4gICAgICAgICAgdmFsdWU9e3tcbiAgICAgICAgICAgIGV4aXQ6IHRoaXMuaGFuZGxlRXhpdCxcbiAgICAgICAgICB9fVxuICAgICAgICA+XG4gICAgICAgICAgPFN0ZGluQ29udGV4dC5Qcm92aWRlclxuICAgICAgICAgICAgdmFsdWU9e3tcbiAgICAgICAgICAgICAgc3RkaW46IHRoaXMucHJvcHMuc3RkaW4sXG4gICAgICAgICAgICAgIHNldFJhd01vZGU6IHRoaXMuaGFuZGxlU2V0UmF3TW9kZSxcbiAgICAgICAgICAgICAgaXNSYXdNb2RlU3VwcG9ydGVkOiB0aGlzLmlzUmF3TW9kZVN1cHBvcnRlZCgpLFxuXG4gICAgICAgICAgICAgIGludGVybmFsX2V4aXRPbkN0cmxDOiB0aGlzLnByb3BzLmV4aXRPbkN0cmxDLFxuXG4gICAgICAgICAgICAgIGludGVybmFsX2V2ZW50RW1pdHRlcjogdGhpcy5pbnRlcm5hbF9ldmVudEVtaXR0ZXIsXG4gICAgICAgICAgICAgIGludGVybmFsX3F1ZXJpZXI6IHRoaXMucXVlcmllcixcbiAgICAgICAgICAgIH19XG4gICAgICAgICAgPlxuICAgICAgICAgICAgPFRlcm1pbmFsRm9jdXNQcm92aWRlcj5cbiAgICAgICAgICAgICAgPENsb2NrUHJvdmlkZXI+XG4gICAgICAgICAgICAgICAgPEN1cnNvckRlY2xhcmF0aW9uQ29udGV4dC5Qcm92aWRlclxuICAgICAgICAgICAgICAgICAgdmFsdWU9e3RoaXMucHJvcHMub25DdXJzb3JEZWNsYXJhdGlvbiA/PyAoKCkgPT4ge30pfVxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgIHt0aGlzLnN0YXRlLmVycm9yID8gKFxuICAgICAgICAgICAgICAgICAgICA8RXJyb3JPdmVydmlldyBlcnJvcj17dGhpcy5zdGF0ZS5lcnJvciBhcyBFcnJvcn0gLz5cbiAgICAgICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgICAgIHRoaXMucHJvcHMuY2hpbGRyZW5cbiAgICAgICAgICAgICAgICAgICl9XG4gICAgICAgICAgICAgICAgPC9DdXJzb3JEZWNsYXJhdGlvbkNvbnRleHQuUHJvdmlkZXI+XG4gICAgICAgICAgICAgIDwvQ2xvY2tQcm92aWRlcj5cbiAgICAgICAgICAgIDwvVGVybWluYWxGb2N1c1Byb3ZpZGVyPlxuICAgICAgICAgIDwvU3RkaW5Db250ZXh0LlByb3ZpZGVyPlxuICAgICAgICA8L0FwcENvbnRleHQuUHJvdmlkZXI+XG4gICAgICA8L1Rlcm1pbmFsU2l6ZUNvbnRleHQuUHJvdmlkZXI+XG4gICAgKVxuICB9XG5cbiAgb3ZlcnJpZGUgY29tcG9uZW50RGlkTW91bnQoKSB7XG4gICAgLy8gSW4gYWNjZXNzaWJpbGl0eSBtb2RlLCBrZWVwIHRoZSBuYXRpdmUgY3Vyc29yIHZpc2libGUgZm9yIHNjcmVlbiBtYWduaWZpZXJzIGFuZCBvdGhlciB0b29sc1xuICAgIGlmIChcbiAgICAgIHRoaXMucHJvcHMuc3Rkb3V0LmlzVFRZICYmXG4gICAgICAhaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfQUNDRVNTSUJJTElUWSlcbiAgICApIHtcbiAgICAgIHRoaXMucHJvcHMuc3Rkb3V0LndyaXRlKEhJREVfQ1VSU09SKVxuICAgIH1cbiAgfVxuXG4gIG92ZXJyaWRlIGNvbXBvbmVudFdpbGxVbm1vdW50KCkge1xuICAgIGlmICh0aGlzLnByb3BzLnN0ZG91dC5pc1RUWSkge1xuICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoU0hPV19DVVJTT1IpXG4gICAgfVxuXG4gICAgLy8gQ2xlYXIgYW55IHBlbmRpbmcgdGltZXJzXG4gICAgaWYgKHRoaXMuaW5jb21wbGV0ZUVzY2FwZVRpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIpXG4gICAgICB0aGlzLmluY29tcGxldGVFc2NhcGVUaW1lciA9IG51bGxcbiAgICB9XG4gICAgaWYgKHRoaXMucGVuZGluZ0h5cGVybGlua1RpbWVyKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5wZW5kaW5nSHlwZXJsaW5rVGltZXIpXG4gICAgICB0aGlzLnBlbmRpbmdIeXBlcmxpbmtUaW1lciA9IG51bGxcbiAgICB9XG4gICAgLy8gaWdub3JlIGNhbGxpbmcgc2V0UmF3TW9kZSBvbiBhbiBoYW5kbGUgc3RkaW4gaXQgY2Fubm90IGJlIGNhbGxlZFxuICAgIGlmICh0aGlzLmlzUmF3TW9kZVN1cHBvcnRlZCgpKSB7XG4gICAgICB0aGlzLmhhbmRsZVNldFJhd01vZGUoZmFsc2UpXG4gICAgfVxuICB9XG5cbiAgb3ZlcnJpZGUgY29tcG9uZW50RGlkQ2F0Y2goZXJyb3I6IEVycm9yKSB7XG4gICAgdGhpcy5oYW5kbGVFeGl0KGVycm9yKVxuICB9XG5cbiAgaGFuZGxlU2V0UmF3TW9kZSA9IChpc0VuYWJsZWQ6IGJvb2xlYW4pOiB2b2lkID0+IHtcbiAgICBjb25zdCB7IHN0ZGluIH0gPSB0aGlzLnByb3BzXG5cbiAgICBpZiAoIXRoaXMuaXNSYXdNb2RlU3VwcG9ydGVkKCkpIHtcbiAgICAgIGlmIChzdGRpbiA9PT0gcHJvY2Vzcy5zdGRpbikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ1JhdyBtb2RlIGlzIG5vdCBzdXBwb3J0ZWQgb24gdGhlIGN1cnJlbnQgcHJvY2Vzcy5zdGRpbiwgd2hpY2ggSW5rIHVzZXMgYXMgaW5wdXQgc3RyZWFtIGJ5IGRlZmF1bHQuXFxuUmVhZCBhYm91dCBob3cgdG8gcHJldmVudCB0aGlzIGVycm9yIG9uIGh0dHBzOi8vZ2l0aHViLmNvbS92YWRpbWRlbWVkZXMvaW5rLyNpc3Jhd21vZGVzdXBwb3J0ZWQnLFxuICAgICAgICApXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgJ1JhdyBtb2RlIGlzIG5vdCBzdXBwb3J0ZWQgb24gdGhlIHN0ZGluIHByb3ZpZGVkIHRvIEluay5cXG5SZWFkIGFib3V0IGhvdyB0byBwcmV2ZW50IHRoaXMgZXJyb3Igb24gaHR0cHM6Ly9naXRodWIuY29tL3ZhZGltZGVtZWRlcy9pbmsvI2lzcmF3bW9kZXN1cHBvcnRlZCcsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG5cbiAgICBzdGRpbi5zZXRFbmNvZGluZygndXRmOCcpXG5cbiAgICBpZiAoaXNFbmFibGVkKSB7XG4gICAgICAvLyBFbnN1cmUgcmF3IG1vZGUgaXMgZW5hYmxlZCBvbmx5IG9uY2VcbiAgICAgIGlmICh0aGlzLnJhd01vZGVFbmFibGVkQ291bnQgPT09IDApIHtcbiAgICAgICAgLy8gU3RvcCBlYXJseSBpbnB1dCBjYXB0dXJlIHJpZ2h0IGJlZm9yZSB3ZSBhZGQgb3VyIG93biByZWFkYWJsZSBoYW5kbGVyLlxuICAgICAgICAvLyBCb3RoIHVzZSB0aGUgc2FtZSBzdGRpbiAncmVhZGFibGUnICsgcmVhZCgpIHBhdHRlcm4sIHNvIHRoZXkgY2FuJ3RcbiAgICAgICAgLy8gY29leGlzdCAtLSBvdXIgaGFuZGxlciB3b3VsZCBkcmFpbiBzdGRpbiBiZWZvcmUgSW5rJ3MgY2FuIHNlZSBpdC5cbiAgICAgICAgLy8gVGhlIGJ1ZmZlcmVkIHRleHQgaXMgcHJlc2VydmVkIGZvciBSRVBMLnRzeCB2aWEgY29uc3VtZUVhcmx5SW5wdXQoKS5cbiAgICAgICAgc3RvcENhcHR1cmluZ0Vhcmx5SW5wdXQoKVxuICAgICAgICBzdGRpbi5yZWYoKVxuICAgICAgICBzdGRpbi5zZXRSYXdNb2RlKHRydWUpXG4gICAgICAgIHN0ZGluLmFkZExpc3RlbmVyKCdyZWFkYWJsZScsIHRoaXMuaGFuZGxlUmVhZGFibGUpXG4gICAgICAgIC8vIEVuYWJsZSBicmFja2V0ZWQgcGFzdGUgbW9kZVxuICAgICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShFQlApXG4gICAgICAgIC8vIEVuYWJsZSB0ZXJtaW5hbCBmb2N1cyByZXBvcnRpbmcgKERFQ1NFVCAxMDA0KVxuICAgICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShFRkUpXG4gICAgICAgIC8vIEVuYWJsZSBleHRlbmRlZCBrZXkgcmVwb3J0aW5nIHNvIGN0cmwrc2hpZnQrPGxldHRlcj4gaXNcbiAgICAgICAgLy8gZGlzdGluZ3Vpc2hhYmxlIGZyb20gY3RybCs8bGV0dGVyPi4gV2Ugd3JpdGUgYm90aCB0aGUga2l0dHkgc3RhY2tcbiAgICAgICAgLy8gcHVzaCAoQ1NJID4xdSkgYW5kIHh0ZXJtIG1vZGlmeU90aGVyS2V5cyBsZXZlbCAyIChDU0kgPjQ7Mm0pIOKAlFxuICAgICAgICAvLyB0ZXJtaW5hbHMgaG9ub3Igd2hpY2hldmVyIHRoZXkgaW1wbGVtZW50ICh0bXV4IG9ubHkgYWNjZXB0cyB0aGVcbiAgICAgICAgLy8gbGF0dGVyKS5cbiAgICAgICAgaWYgKHN1cHBvcnRzRXh0ZW5kZWRLZXlzKCkpIHtcbiAgICAgICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShFTkFCTEVfS0lUVFlfS0VZQk9BUkQpXG4gICAgICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoRU5BQkxFX01PRElGWV9PVEhFUl9LRVlTKVxuICAgICAgICB9XG4gICAgICAgIC8vIFByb2JlIHRlcm1pbmFsIGlkZW50aXR5LiBYVFZFUlNJT04gc3Vydml2ZXMgU1NIIChxdWVyeS9yZXBseSBnb2VzXG4gICAgICAgIC8vIHRocm91Z2ggdGhlIHB0eSksIHVubGlrZSBURVJNX1BST0dSQU0uIFVzZWQgZm9yIHdoZWVsLXNjcm9sbCBiYXNlXG4gICAgICAgIC8vIGRldGVjdGlvbiB3aGVuIGVudiB2YXJzIGFyZSBhYnNlbnQuIEZpcmUtYW5kLWZvcmdldDogdGhlIERBMVxuICAgICAgICAvLyBzZW50aW5lbCBib3VuZHMgdGhlIHJvdW5kLXRyaXAsIGFuZCBpZiB0aGUgdGVybWluYWwgaWdub3JlcyB0aGVcbiAgICAgICAgLy8gcXVlcnksIGZsdXNoKCkgc3RpbGwgcmVzb2x2ZXMgYW5kIG5hbWUgc3RheXMgdW5kZWZpbmVkLlxuICAgICAgICAvLyBEZWZlcnJlZCB0byBuZXh0IHRpY2sgc28gaXQgZmlyZXMgQUZURVIgdGhlIGN1cnJlbnQgc3luY2hyb25vdXNcbiAgICAgICAgLy8gaW5pdCBzZXF1ZW5jZSBjb21wbGV0ZXMg4oCUIGF2b2lkcyBpbnRlcmxlYXZpbmcgd2l0aCBhbHQtc2NyZWVuL21vdXNlXG4gICAgICAgIC8vIHRyYWNraW5nIGVuYWJsZSB3cml0ZXMgdGhhdCBtYXkgaGFwcGVuIGluIHRoZSBzYW1lIHJlbmRlciBjeWNsZS5cbiAgICAgICAgc2V0SW1tZWRpYXRlKCgpID0+IHtcbiAgICAgICAgICB2b2lkIFByb21pc2UuYWxsKFtcbiAgICAgICAgICAgIHRoaXMucXVlcmllci5zZW5kKHh0dmVyc2lvbigpKSxcbiAgICAgICAgICAgIHRoaXMucXVlcmllci5mbHVzaCgpLFxuICAgICAgICAgIF0pLnRoZW4oKFtyXSkgPT4ge1xuICAgICAgICAgICAgaWYgKHIpIHtcbiAgICAgICAgICAgICAgc2V0WHR2ZXJzaW9uTmFtZShyLm5hbWUpXG4gICAgICAgICAgICAgIGxvZ0ZvckRlYnVnZ2luZyhgWFRWRVJTSU9OOiB0ZXJtaW5hbCBpZGVudGlmaWVkIGFzIFwiJHtyLm5hbWV9XCJgKVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgbG9nRm9yRGVidWdnaW5nKCdYVFZFUlNJT046IG5vIHJlcGx5ICh0ZXJtaW5hbCBpZ25vcmVkIHF1ZXJ5KScpXG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSlcbiAgICAgICAgfSlcbiAgICAgIH1cblxuICAgICAgdGhpcy5yYXdNb2RlRW5hYmxlZENvdW50KytcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIERpc2FibGUgcmF3IG1vZGUgb25seSB3aGVuIG5vIGNvbXBvbmVudHMgbGVmdCB0aGF0IGFyZSB1c2luZyBpdFxuICAgIGlmICgtLXRoaXMucmF3TW9kZUVuYWJsZWRDb3VudCA9PT0gMCkge1xuICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoRElTQUJMRV9NT0RJRllfT1RIRVJfS0VZUylcbiAgICAgIHRoaXMucHJvcHMuc3Rkb3V0LndyaXRlKERJU0FCTEVfS0lUVFlfS0VZQk9BUkQpXG4gICAgICAvLyBEaXNhYmxlIHRlcm1pbmFsIGZvY3VzIHJlcG9ydGluZyAoREVDU0VUIDEwMDQpXG4gICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShERkUpXG4gICAgICAvLyBEaXNhYmxlIGJyYWNrZXRlZCBwYXN0ZSBtb2RlXG4gICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShEQlApXG4gICAgICBzdGRpbi5zZXRSYXdNb2RlKGZhbHNlKVxuICAgICAgc3RkaW4ucmVtb3ZlTGlzdGVuZXIoJ3JlYWRhYmxlJywgdGhpcy5oYW5kbGVSZWFkYWJsZSlcbiAgICAgIHN0ZGluLnVucmVmKClcbiAgICB9XG4gIH1cblxuICAvLyBIZWxwZXIgdG8gZmx1c2ggaW5jb21wbGV0ZSBlc2NhcGUgc2VxdWVuY2VzXG4gIGZsdXNoSW5jb21wbGV0ZSA9ICgpOiB2b2lkID0+IHtcbiAgICAvLyBDbGVhciB0aGUgdGltZXIgcmVmZXJlbmNlXG4gICAgdGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIgPSBudWxsXG5cbiAgICAvLyBPbmx5IHByb2NlZWQgaWYgd2UgaGF2ZSBpbmNvbXBsZXRlIHNlcXVlbmNlc1xuICAgIGlmICghdGhpcy5rZXlQYXJzZVN0YXRlLmluY29tcGxldGUpIHJldHVyblxuXG4gICAgLy8gRnVsbHNjcmVlbjogaWYgc3RkaW4gaGFzIGRhdGEgd2FpdGluZywgaXQncyBhbG1vc3QgY2VydGFpbmx5IHRoZVxuICAgIC8vIGNvbnRpbnVhdGlvbiBvZiB0aGUgYnVmZmVyZWQgc2VxdWVuY2UgKGUuZy4gYFs8NjQ7NzQ7MTZNYCBhZnRlciBhXG4gICAgLy8gbG9uZSBFU0MpLiBOb2RlJ3MgZXZlbnQgbG9vcCBydW5zIHRoZSB0aW1lcnMgcGhhc2UgYmVmb3JlIHRoZSBwb2xsXG4gICAgLy8gcGhhc2UsIHNvIHdoZW4gYSBoZWF2eSByZW5kZXIgYmxvY2tzIHRoZSBsb29wIHBhc3QgNTBtcywgdGhpcyB0aW1lclxuICAgIC8vIGZpcmVzIGJlZm9yZSB0aGUgcXVldWVkIHJlYWRhYmxlIGV2ZW50IGV2ZW4gdGhvdWdoIHRoZSBieXRlcyBhcmVcbiAgICAvLyBhbHJlYWR5IGJ1ZmZlcmVkLiBSZS1hcm0gaW5zdGVhZCBvZiBmbHVzaGluZzogaGFuZGxlUmVhZGFibGUgd2lsbFxuICAgIC8vIGRyYWluIHN0ZGluIG5leHQgYW5kIGNsZWFyIHRoaXMgdGltZXIuIFByZXZlbnRzIGJvdGggdGhlIHNwdXJpb3VzXG4gICAgLy8gRXNjYXBlIGtleSBhbmQgdGhlIGxvc3Qgc2Nyb2xsIGV2ZW50LlxuICAgIGlmICh0aGlzLnByb3BzLnN0ZGluLnJlYWRhYmxlTGVuZ3RoID4gMCkge1xuICAgICAgdGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIgPSBzZXRUaW1lb3V0KFxuICAgICAgICB0aGlzLmZsdXNoSW5jb21wbGV0ZSxcbiAgICAgICAgdGhpcy5OT1JNQUxfVElNRU9VVCxcbiAgICAgIClcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIC8vIFByb2Nlc3MgaW5jb21wbGV0ZSBhcyBhIGZsdXNoIG9wZXJhdGlvbiAoaW5wdXQ9bnVsbClcbiAgICAvLyBUaGlzIHJldXNlcyBhbGwgZXhpc3RpbmcgcGFyc2luZyBsb2dpY1xuICAgIHRoaXMucHJvY2Vzc0lucHV0KG51bGwpXG4gIH1cblxuICAvLyBQcm9jZXNzIGlucHV0IHRocm91Z2ggdGhlIHBhcnNlciBhbmQgaGFuZGxlIHRoZSByZXN1bHRzXG4gIHByb2Nlc3NJbnB1dCA9IChpbnB1dDogc3RyaW5nIHwgQnVmZmVyIHwgbnVsbCk6IHZvaWQgPT4ge1xuICAgIC8vIFBhcnNlIGlucHV0IHVzaW5nIG91ciBzdGF0ZSBtYWNoaW5lXG4gICAgY29uc3QgW2tleXMsIG5ld1N0YXRlXSA9IHBhcnNlTXVsdGlwbGVLZXlwcmVzc2VzKHRoaXMua2V5UGFyc2VTdGF0ZSwgaW5wdXQpXG4gICAgdGhpcy5rZXlQYXJzZVN0YXRlID0gbmV3U3RhdGVcblxuICAgIC8vIFByb2Nlc3MgQUxMIGtleXMgaW4gYSBTSU5HTEUgZGlzY3JldGVVcGRhdGVzIGNhbGwgdG8gcHJldmVudFxuICAgIC8vIFwiTWF4aW11bSB1cGRhdGUgZGVwdGggZXhjZWVkZWRcIiBlcnJvciB3aGVuIG1hbnkga2V5cyBhcnJpdmUgYXQgb25jZVxuICAgIC8vIChlLmcuLCBmcm9tIHBhc3RlIG9wZXJhdGlvbnMgb3IgaG9sZGluZyBrZXlzIHJhcGlkbHkpLlxuICAgIC8vIFRoaXMgYmF0Y2hlcyBhbGwgc3RhdGUgdXBkYXRlcyBmcm9tIGhhbmRsZUlucHV0IGFuZCBhbGwgdXNlSW5wdXRcbiAgICAvLyBsaXN0ZW5lcnMgdG9nZXRoZXIgd2l0aGluIG9uZSBoaWdoLXByaW9yaXR5IHVwZGF0ZSBjb250ZXh0LlxuICAgIGlmIChrZXlzLmxlbmd0aCA+IDApIHtcbiAgICAgIHJlY29uY2lsZXIuZGlzY3JldGVVcGRhdGVzKFxuICAgICAgICBwcm9jZXNzS2V5c0luQmF0Y2gsXG4gICAgICAgIHRoaXMsXG4gICAgICAgIGtleXMsXG4gICAgICAgIHVuZGVmaW5lZCxcbiAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgKVxuICAgIH1cblxuICAgIC8vIElmIHdlIGhhdmUgaW5jb21wbGV0ZSBlc2NhcGUgc2VxdWVuY2VzLCBzZXQgYSB0aW1lciB0byBmbHVzaCB0aGVtXG4gICAgaWYgKHRoaXMua2V5UGFyc2VTdGF0ZS5pbmNvbXBsZXRlKSB7XG4gICAgICAvLyBDYW5jZWwgYW55IGV4aXN0aW5nIHRpbWVyIGZpcnN0XG4gICAgICBpZiAodGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIpIHtcbiAgICAgICAgY2xlYXJUaW1lb3V0KHRoaXMuaW5jb21wbGV0ZUVzY2FwZVRpbWVyKVxuICAgICAgfVxuICAgICAgdGhpcy5pbmNvbXBsZXRlRXNjYXBlVGltZXIgPSBzZXRUaW1lb3V0KFxuICAgICAgICB0aGlzLmZsdXNoSW5jb21wbGV0ZSxcbiAgICAgICAgdGhpcy5rZXlQYXJzZVN0YXRlLm1vZGUgPT09ICdJTl9QQVNURSdcbiAgICAgICAgICA/IHRoaXMuUEFTVEVfVElNRU9VVFxuICAgICAgICAgIDogdGhpcy5OT1JNQUxfVElNRU9VVCxcbiAgICAgIClcbiAgICB9XG4gIH1cblxuICBoYW5kbGVSZWFkYWJsZSA9ICgpOiB2b2lkID0+IHtcbiAgICAvLyBEZXRlY3QgbG9uZyBzdGRpbiBnYXBzICh0bXV4IGF0dGFjaCwgc3NoIHJlY29ubmVjdCwgbGFwdG9wIHdha2UpLlxuICAgIC8vIFRoZSB0ZXJtaW5hbCBtYXkgaGF2ZSByZXNldCBERUMgcHJpdmF0ZSBtb2RlczsgcmUtYXNzZXJ0IG1vdXNlXG4gICAgLy8gdHJhY2tpbmcuIENoZWNrZWQgYmVmb3JlIHRoZSByZWFkIGxvb3Agc28gb25lIERhdGUubm93KCkgY292ZXJzXG4gICAgLy8gYWxsIGNodW5rcyBpbiB0aGlzIHJlYWRhYmxlIGV2ZW50LlxuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KClcbiAgICBpZiAobm93IC0gdGhpcy5sYXN0U3RkaW5UaW1lID4gU1RESU5fUkVTVU1FX0dBUF9NUykge1xuICAgICAgdGhpcy5wcm9wcy5vblN0ZGluUmVzdW1lPy4oKVxuICAgIH1cbiAgICB0aGlzLmxhc3RTdGRpblRpbWUgPSBub3dcbiAgICB0cnkge1xuICAgICAgbGV0IGNodW5rXG4gICAgICB3aGlsZSAoKGNodW5rID0gdGhpcy5wcm9wcy5zdGRpbi5yZWFkKCkgYXMgc3RyaW5nIHwgbnVsbCkgIT09IG51bGwpIHtcbiAgICAgICAgLy8gUHJvY2VzcyB0aGUgaW5wdXQgY2h1bmtcbiAgICAgICAgdGhpcy5wcm9jZXNzSW5wdXQoY2h1bmspXG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIC8vIEluIEJ1biwgYW4gdW5jYXVnaHQgdGhyb3cgaW5zaWRlIGEgc3RyZWFtICdyZWFkYWJsZScgaGFuZGxlciBjYW5cbiAgICAgIC8vIHBlcm1hbmVudGx5IHdlZGdlIHRoZSBzdHJlYW06IGRhdGEgc3RheXMgYnVmZmVyZWQgYW5kICdyZWFkYWJsZSdcbiAgICAgIC8vIG5ldmVyIHJlLWVtaXRzLiBDYXRjaGluZyBoZXJlIGVuc3VyZXMgdGhlIHN0cmVhbSBzdGF5cyBoZWFsdGh5IHNvXG4gICAgICAvLyBzdWJzZXF1ZW50IGtleXN0cm9rZXMgYXJlIHN0aWxsIGRlbGl2ZXJlZC5cbiAgICAgIGxvZ0Vycm9yKGVycm9yKVxuXG4gICAgICAvLyBSZS1hdHRhY2ggdGhlIGxpc3RlbmVyIGluIGNhc2UgdGhlIGV4Y2VwdGlvbiBkZXRhY2hlZCBpdC5cbiAgICAgIC8vIEJ1biBtYXkgcmVtb3ZlIHRoZSBsaXN0ZW5lciBhZnRlciBhbiBlcnJvcjsgd2l0aG91dCB0aGlzLFxuICAgICAgLy8gdGhlIHNlc3Npb24gZnJlZXplcyBwZXJtYW5lbnRseSAoc3RkaW4gcmVhZGVyIGRlYWQsIGV2ZW50IGxvb3AgYWxpdmUpLlxuICAgICAgY29uc3QgeyBzdGRpbiB9ID0gdGhpcy5wcm9wc1xuICAgICAgaWYgKFxuICAgICAgICB0aGlzLnJhd01vZGVFbmFibGVkQ291bnQgPiAwICYmXG4gICAgICAgICFzdGRpbi5saXN0ZW5lcnMoJ3JlYWRhYmxlJykuaW5jbHVkZXModGhpcy5oYW5kbGVSZWFkYWJsZSlcbiAgICAgICkge1xuICAgICAgICBsb2dGb3JEZWJ1Z2dpbmcoXG4gICAgICAgICAgJ2hhbmRsZVJlYWRhYmxlOiByZS1hdHRhY2hpbmcgc3RkaW4gcmVhZGFibGUgbGlzdGVuZXIgYWZ0ZXIgZXJyb3IgcmVjb3ZlcnknLFxuICAgICAgICAgIHsgbGV2ZWw6ICd3YXJuJyB9LFxuICAgICAgICApXG4gICAgICAgIHN0ZGluLmFkZExpc3RlbmVyKCdyZWFkYWJsZScsIHRoaXMuaGFuZGxlUmVhZGFibGUpXG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgaGFuZGxlSW5wdXQgPSAoaW5wdXQ6IHN0cmluZyB8IHVuZGVmaW5lZCk6IHZvaWQgPT4ge1xuICAgIC8vIEV4aXQgb24gQ3RybCtDXG4gICAgaWYgKGlucHV0ID09PSAnXFx4MDMnICYmIHRoaXMucHJvcHMuZXhpdE9uQ3RybEMpIHtcbiAgICAgIHRoaXMuaGFuZGxlRXhpdCgpXG4gICAgfVxuXG4gICAgLy8gTm90ZTogQ3RybCtaIChzdXNwZW5kKSBpcyBub3cgaGFuZGxlZCBpbiBwcm9jZXNzS2V5c0luQmF0Y2ggdXNpbmcgdGhlXG4gICAgLy8gcGFyc2VkIGtleSB0byBzdXBwb3J0IGJvdGggcmF3IChcXHgxYSkgYW5kIENTSSB1IGZvcm1hdCBmcm9tIEtpdHR5XG4gICAgLy8ga2V5Ym9hcmQgcHJvdG9jb2wgdGVybWluYWxzIChHaG9zdHR5LCBpVGVybTIsIGtpdHR5LCBXZXpUZXJtKVxuICB9XG5cbiAgaGFuZGxlRXhpdCA9IChlcnJvcj86IEVycm9yKTogdm9pZCA9PiB7XG4gICAgaWYgKHRoaXMuaXNSYXdNb2RlU3VwcG9ydGVkKCkpIHtcbiAgICAgIHRoaXMuaGFuZGxlU2V0UmF3TW9kZShmYWxzZSlcbiAgICB9XG5cbiAgICB0aGlzLnByb3BzLm9uRXhpdChlcnJvcilcbiAgfVxuXG4gIGhhbmRsZVRlcm1pbmFsRm9jdXMgPSAoaXNGb2N1c2VkOiBib29sZWFuKTogdm9pZCA9PiB7XG4gICAgLy8gc2V0VGVybWluYWxGb2N1c2VkIG5vdGlmaWVzIHN1YnNjcmliZXJzOiBUZXJtaW5hbEZvY3VzUHJvdmlkZXIgKGNvbnRleHQpXG4gICAgLy8gYW5kIENsb2NrIChpbnRlcnZhbCBzcGVlZCkg4oCUIG5vIEFwcCBzZXRTdGF0ZSBuZWVkZWQuXG4gICAgc2V0VGVybWluYWxGb2N1c2VkKGlzRm9jdXNlZClcbiAgfVxuXG4gIGhhbmRsZVN1c3BlbmQgPSAoKTogdm9pZCA9PiB7XG4gICAgaWYgKCF0aGlzLmlzUmF3TW9kZVN1cHBvcnRlZCgpKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICAvLyBTdG9yZSB0aGUgZXhhY3QgcmF3IG1vZGUgY291bnQgdG8gcmVzdG9yZSBpdCBwcm9wZXJseVxuICAgIGNvbnN0IHJhd01vZGVDb3VudEJlZm9yZVN1c3BlbmQgPSB0aGlzLnJhd01vZGVFbmFibGVkQ291bnRcblxuICAgIC8vIENvbXBsZXRlbHkgZGlzYWJsZSByYXcgbW9kZSBiZWZvcmUgc3VzcGVuZGluZ1xuICAgIHdoaWxlICh0aGlzLnJhd01vZGVFbmFibGVkQ291bnQgPiAwKSB7XG4gICAgICB0aGlzLmhhbmRsZVNldFJhd01vZGUoZmFsc2UpXG4gICAgfVxuXG4gICAgLy8gU2hvdyBjdXJzb3IsIGRpc2FibGUgZm9jdXMgcmVwb3J0aW5nLCBhbmQgZGlzYWJsZSBtb3VzZSB0cmFja2luZ1xuICAgIC8vIGJlZm9yZSBzdXNwZW5kaW5nLiBESVNBQkxFX01PVVNFX1RSQUNLSU5HIGlzIGEgbm8tb3AgaWYgdHJhY2tpbmdcbiAgICAvLyB3YXNuJ3QgZW5hYmxlZCwgc28gaXQncyBzYWZlIHRvIGVtaXQgdW5jb25kaXRpb25hbGx5IOKAlCB3aXRob3V0XG4gICAgLy8gaXQsIFNHUiBtb3VzZSBzZXF1ZW5jZXMgd291bGQgYXBwZWFyIGFzIGdhcmJsZWQgdGV4dCBhdCB0aGVcbiAgICAvLyBzaGVsbCBwcm9tcHQgd2hpbGUgc3VzcGVuZGVkLlxuICAgIGlmICh0aGlzLnByb3BzLnN0ZG91dC5pc1RUWSkge1xuICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoU0hPV19DVVJTT1IgKyBERkUgKyBESVNBQkxFX01PVVNFX1RSQUNLSU5HKVxuICAgIH1cblxuICAgIC8vIEVtaXQgc3VzcGVuZCBldmVudCBmb3IgQ2xhdWRlIENvZGUgdG8gaGFuZGxlLiBNb3N0bHkganVzdCBoYXMgYSBub3RpZmljYXRpb25cbiAgICB0aGlzLmludGVybmFsX2V2ZW50RW1pdHRlci5lbWl0KCdzdXNwZW5kJylcblxuICAgIC8vIFNldCB1cCByZXN1bWUgaGFuZGxlclxuICAgIGNvbnN0IHJlc3VtZUhhbmRsZXIgPSAoKSA9PiB7XG4gICAgICAvLyBSZXN0b3JlIHJhdyBtb2RlIHRvIGV4YWN0IHByZXZpb3VzIHN0YXRlXG4gICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJhd01vZGVDb3VudEJlZm9yZVN1c3BlbmQ7IGkrKykge1xuICAgICAgICBpZiAodGhpcy5pc1Jhd01vZGVTdXBwb3J0ZWQoKSkge1xuICAgICAgICAgIHRoaXMuaGFuZGxlU2V0UmF3TW9kZSh0cnVlKVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIEhpZGUgY3Vyc29yICh1bmxlc3MgaW4gYWNjZXNzaWJpbGl0eSBtb2RlKSBhbmQgcmUtZW5hYmxlIGZvY3VzIHJlcG9ydGluZyBhZnRlciByZXN1bWluZ1xuICAgICAgaWYgKHRoaXMucHJvcHMuc3Rkb3V0LmlzVFRZKSB7XG4gICAgICAgIGlmICghaXNFbnZUcnV0aHkocHJvY2Vzcy5lbnYuQ0xBVURFX0NPREVfQUNDRVNTSUJJTElUWSkpIHtcbiAgICAgICAgICB0aGlzLnByb3BzLnN0ZG91dC53cml0ZShISURFX0NVUlNPUilcbiAgICAgICAgfVxuICAgICAgICAvLyBSZS1lbmFibGUgZm9jdXMgcmVwb3J0aW5nIHRvIHJlc3RvcmUgdGVybWluYWwgc3RhdGVcbiAgICAgICAgdGhpcy5wcm9wcy5zdGRvdXQud3JpdGUoRUZFKVxuICAgICAgfVxuXG4gICAgICAvLyBFbWl0IHJlc3VtZSBldmVudCBmb3IgQ2xhdWRlIENvZGUgdG8gaGFuZGxlXG4gICAgICB0aGlzLmludGVybmFsX2V2ZW50RW1pdHRlci5lbWl0KCdyZXN1bWUnKVxuXG4gICAgICBwcm9jZXNzLnJlbW92ZUxpc3RlbmVyKCdTSUdDT05UJywgcmVzdW1lSGFuZGxlcilcbiAgICB9XG5cbiAgICBwcm9jZXNzLm9uKCdTSUdDT05UJywgcmVzdW1lSGFuZGxlcilcbiAgICBwcm9jZXNzLmtpbGwocHJvY2Vzcy5waWQsICdTSUdTVE9QJylcbiAgfVxufVxuXG4vLyBIZWxwZXIgdG8gcHJvY2VzcyBhbGwga2V5cyB3aXRoaW4gYSBzaW5nbGUgZGlzY3JldGUgdXBkYXRlIGNvbnRleHQuXG4vLyBkaXNjcmV0ZVVwZGF0ZXMgZXhwZWN0cyAoZm4sIGEsIGIsIGMsIGQpIC0+IGZuKGEsIGIsIGMsIGQpXG5mdW5jdGlvbiBwcm9jZXNzS2V5c0luQmF0Y2goXG4gIGFwcDogQXBwLFxuICBpdGVtczogUGFyc2VkSW5wdXRbXSxcbiAgX3VudXNlZDE6IHVuZGVmaW5lZCxcbiAgX3VudXNlZDI6IHVuZGVmaW5lZCxcbik6IHZvaWQge1xuICAvLyBVcGRhdGUgaW50ZXJhY3Rpb24gdGltZSBmb3Igbm90aWZpY2F0aW9uIHRpbWVvdXQgdHJhY2tpbmcuXG4gIC8vIFRoaXMgaXMgY2FsbGVkIGZyb20gdGhlIGNlbnRyYWwgaW5wdXQgaGFuZGxlciB0byBhdm9pZCBoYXZpbmcgbXVsdGlwbGVcbiAgLy8gc3RkaW4gbGlzdGVuZXJzIHRoYXQgY2FuIGNhdXNlIHJhY2UgY29uZGl0aW9ucyBhbmQgZHJvcHBlZCBpbnB1dC5cbiAgLy8gVGVybWluYWwgcmVzcG9uc2VzIChraW5kOiAncmVzcG9uc2UnKSBhcmUgYXV0b21hdGVkLCBub3QgdXNlciBpbnB1dC5cbiAgLy8gTW9kZS0xMDAzIG5vLWJ1dHRvbiBtb3Rpb24gaXMgYWxzbyBleGNsdWRlZCDigJQgcGFzc2l2ZSBjdXJzb3IgZHJpZnQgaXNcbiAgLy8gbm90IGVuZ2FnZW1lbnQgKHdvdWxkIHN1cHByZXNzIGlkbGUgbm90aWZpY2F0aW9ucyArIGRlZmVyIGhvdXNla2VlcGluZykuXG4gIGlmIChcbiAgICBpdGVtcy5zb21lKFxuICAgICAgaSA9PlxuICAgICAgICBpLmtpbmQgPT09ICdrZXknIHx8XG4gICAgICAgIChpLmtpbmQgPT09ICdtb3VzZScgJiZcbiAgICAgICAgICAhKChpLmJ1dHRvbiAmIDB4MjApICE9PSAwICYmIChpLmJ1dHRvbiAmIDB4MDMpID09PSAzKSksXG4gICAgKVxuICApIHtcbiAgICB1cGRhdGVMYXN0SW50ZXJhY3Rpb25UaW1lKClcbiAgfVxuXG4gIGZvciAoY29uc3QgaXRlbSBvZiBpdGVtcykge1xuICAgIC8vIFRlcm1pbmFsIHJlc3BvbnNlcyAoREVDUlBNLCBEQTEsIE9TQyByZXBsaWVzLCBldGMuKSBhcmUgbm90IHVzZXJcbiAgICAvLyBpbnB1dCDigJQgcm91dGUgdGhlbSB0byB0aGUgcXVlcmllciB0byByZXNvbHZlIHBlbmRpbmcgcHJvbWlzZXMuXG4gICAgaWYgKGl0ZW0ua2luZCA9PT0gJ3Jlc3BvbnNlJykge1xuICAgICAgYXBwLnF1ZXJpZXIub25SZXNwb25zZShpdGVtLnJlc3BvbnNlKVxuICAgICAgY29udGludWVcbiAgICB9XG5cbiAgICAvLyBNb3VzZSBjbGljay9kcmFnIGV2ZW50cyB1cGRhdGUgc2VsZWN0aW9uIHN0YXRlIChmdWxsc2NyZWVuIG9ubHkpLlxuICAgIC8vIFRlcm1pbmFsIHNlbmRzIDEtaW5kZXhlZCBjb2wvcm93OyBjb252ZXJ0IHRvIDAtaW5kZXhlZCBmb3IgdGhlXG4gICAgLy8gc2NyZWVuIGJ1ZmZlci4gQnV0dG9uIGJpdCAweDIwID0gZHJhZyAobW90aW9uIHdoaWxlIGJ1dHRvbiBoZWxkKS5cbiAgICBpZiAoaXRlbS5raW5kID09PSAnbW91c2UnKSB7XG4gICAgICBoYW5kbGVNb3VzZUV2ZW50KGFwcCwgaXRlbSlcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgY29uc3Qgc2VxdWVuY2UgPSBpdGVtLnNlcXVlbmNlXG5cbiAgICAvLyBIYW5kbGUgdGVybWluYWwgZm9jdXMgZXZlbnRzIChERUNTRVQgMTAwNClcbiAgICBpZiAoc2VxdWVuY2UgPT09IEZPQ1VTX0lOKSB7XG4gICAgICBhcHAuaGFuZGxlVGVybWluYWxGb2N1cyh0cnVlKVxuICAgICAgY29uc3QgZXZlbnQgPSBuZXcgVGVybWluYWxGb2N1c0V2ZW50KCd0ZXJtaW5hbGZvY3VzJylcbiAgICAgIGFwcC5pbnRlcm5hbF9ldmVudEVtaXR0ZXIuZW1pdCgndGVybWluYWxmb2N1cycsIGV2ZW50KVxuICAgICAgY29udGludWVcbiAgICB9XG4gICAgaWYgKHNlcXVlbmNlID09PSBGT0NVU19PVVQpIHtcbiAgICAgIGFwcC5oYW5kbGVUZXJtaW5hbEZvY3VzKGZhbHNlKVxuICAgICAgLy8gRGVmZW5zaXZlOiBpZiB3ZSBsb3N0IHRoZSByZWxlYXNlIGV2ZW50IChtb3VzZSByZWxlYXNlZCBvdXRzaWRlXG4gICAgICAvLyB0ZXJtaW5hbCB3aW5kb3cg4oCUIHNvbWUgZW11bGF0b3JzIGRyb3AgaXQgcmF0aGVyIHRoYW4gY2FwdHVyaW5nIHRoZVxuICAgICAgLy8gcG9pbnRlciksIGZvY3VzLW91dCBpcyB0aGUgbmV4dCBvYnNlcnZhYmxlIHNpZ25hbCB0aGF0IHRoZSBkcmFnIGlzXG4gICAgICAvLyBvdmVyLiBXaXRob3V0IHRoaXMsIGRyYWctdG8tc2Nyb2xsJ3MgdGltZXIgcnVucyB1bnRpbCB0aGUgc2Nyb2xsXG4gICAgICAvLyBib3VuZGFyeSBpcyBoaXQuXG4gICAgICBpZiAoYXBwLnByb3BzLnNlbGVjdGlvbi5pc0RyYWdnaW5nKSB7XG4gICAgICAgIGZpbmlzaFNlbGVjdGlvbihhcHAucHJvcHMuc2VsZWN0aW9uKVxuICAgICAgICBhcHAucHJvcHMub25TZWxlY3Rpb25DaGFuZ2UoKVxuICAgICAgfVxuICAgICAgY29uc3QgZXZlbnQgPSBuZXcgVGVybWluYWxGb2N1c0V2ZW50KCd0ZXJtaW5hbGJsdXInKVxuICAgICAgYXBwLmludGVybmFsX2V2ZW50RW1pdHRlci5lbWl0KCd0ZXJtaW5hbGJsdXInLCBldmVudClcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgLy8gRmFpbHNhZmU6IGlmIHdlIHJlY2VpdmUgaW5wdXQsIHRoZSB0ZXJtaW5hbCBtdXN0IGJlIGZvY3VzZWRcbiAgICBpZiAoIWdldFRlcm1pbmFsRm9jdXNlZCgpKSB7XG4gICAgICBzZXRUZXJtaW5hbEZvY3VzZWQodHJ1ZSlcbiAgICB9XG5cbiAgICAvLyBIYW5kbGUgQ3RybCtaIChzdXNwZW5kKSB1c2luZyBwYXJzZWQga2V5IHRvIHN1cHBvcnQgYm90aCByYXcgKFxceDFhKSBhbmRcbiAgICAvLyBDU0kgdSBmb3JtYXQgKFxceDFiWzEyMjs1dSkgZnJvbSBLaXR0eSBrZXlib2FyZCBwcm90b2NvbCB0ZXJtaW5hbHNcbiAgICBpZiAoaXRlbS5uYW1lID09PSAneicgJiYgaXRlbS5jdHJsICYmIFNVUFBPUlRTX1NVU1BFTkQpIHtcbiAgICAgIGFwcC5oYW5kbGVTdXNwZW5kKClcbiAgICAgIGNvbnRpbnVlXG4gICAgfVxuXG4gICAgYXBwLmhhbmRsZUlucHV0KHNlcXVlbmNlKVxuICAgIGNvbnN0IGV2ZW50ID0gbmV3IElucHV0RXZlbnQoaXRlbSlcbiAgICBhcHAuaW50ZXJuYWxfZXZlbnRFbWl0dGVyLmVtaXQoJ2lucHV0JywgZXZlbnQpXG5cbiAgICAvLyBBbHNvIGRpc3BhdGNoIHRocm91Z2ggdGhlIERPTSB0cmVlIHNvIG9uS2V5RG93biBoYW5kbGVycyBmaXJlLlxuICAgIGFwcC5wcm9wcy5kaXNwYXRjaEtleWJvYXJkRXZlbnQoaXRlbSlcbiAgfVxufVxuXG4vKiogRXhwb3J0ZWQgZm9yIHRlc3RpbmcuIE11dGF0ZXMgYXBwLnByb3BzLnNlbGVjdGlvbiBhbmQgY2xpY2svaG92ZXIgc3RhdGUuICovXG5leHBvcnQgZnVuY3Rpb24gaGFuZGxlTW91c2VFdmVudChhcHA6IEFwcCwgbTogUGFyc2VkTW91c2UpOiB2b2lkIHtcbiAgLy8gQWxsb3cgZGlzYWJsaW5nIGNsaWNrIGhhbmRsaW5nIHdoaWxlIGtlZXBpbmcgd2hlZWwgc2Nyb2xsICh3aGljaCBnb2VzXG4gIC8vIHRocm91Z2ggdGhlIGtleWJpbmRpbmcgc3lzdGVtIGFzICd3aGVlbHVwJy8nd2hlZWxkb3duJywgbm90IGhlcmUpLlxuICBpZiAoaXNNb3VzZUNsaWNrc0Rpc2FibGVkKCkpIHJldHVyblxuXG4gIGNvbnN0IHNlbCA9IGFwcC5wcm9wcy5zZWxlY3Rpb25cbiAgLy8gVGVybWluYWwgY29vcmRzIGFyZSAxLWluZGV4ZWQ7IHNjcmVlbiBidWZmZXIgaXMgMC1pbmRleGVkXG4gIGNvbnN0IGNvbCA9IG0uY29sIC0gMVxuICBjb25zdCByb3cgPSBtLnJvdyAtIDFcbiAgY29uc3QgYmFzZUJ1dHRvbiA9IG0uYnV0dG9uICYgMHgwM1xuXG4gIGlmIChtLmFjdGlvbiA9PT0gJ3ByZXNzJykge1xuICAgIGlmICgobS5idXR0b24gJiAweDIwKSAhPT0gMCAmJiBiYXNlQnV0dG9uID09PSAzKSB7XG4gICAgICAvLyBNb2RlLTEwMDMgbW90aW9uIHdpdGggbm8gYnV0dG9uIGhlbGQuIERpc3BhdGNoIGhvdmVyOyBza2lwIHRoZVxuICAgICAgLy8gcmVzdCBvZiB0aGlzIGhhbmRsZXIgKG5vIHNlbGVjdGlvbiwgbm8gY2xpY2stY291bnQgc2lkZSBlZmZlY3RzKS5cbiAgICAgIC8vIExvc3QtcmVsZWFzZSByZWNvdmVyeTogbm8tYnV0dG9uIG1vdGlvbiB3aGlsZSBpc0RyYWdnaW5nPXRydWUgbWVhbnNcbiAgICAgIC8vIHRoZSByZWxlYXNlIGhhcHBlbmVkIG91dHNpZGUgdGhlIHRlcm1pbmFsIHdpbmRvdyAoaVRlcm0yIGRvZXNuJ3RcbiAgICAgIC8vIGNhcHR1cmUgdGhlIHBvaW50ZXIgcGFzdCB3aW5kb3cgYm91bmRzLCBzbyB0aGUgU0dSICdtJyBuZXZlclxuICAgICAgLy8gYXJyaXZlcykuIEZpbmlzaCB0aGUgc2VsZWN0aW9uIGhlcmUgc28gY29weS1vbi1zZWxlY3QgZmlyZXMuIFRoZVxuICAgICAgLy8gRk9DVVNfT1VUIGhhbmRsZXIgY292ZXJzIHRoZSBcInN3aXRjaGVkIGFwcHNcIiBjYXNlIGJ1dCBub3QgXCJyZWxlYXNlZFxuICAgICAgLy8gcGFzdCB0aGUgZWRnZSwgY2FtZSBiYWNrXCIg4oCUIGFuZCB0bXV4IGRyb3BzIGZvY3VzIGV2ZW50cyB1bmxlc3NcbiAgICAgIC8vIGBmb2N1cy1ldmVudHMgb25gIGlzIHNldCwgc28gdGhpcyBpcyB0aGUgbW9yZSByZWxpYWJsZSBzaWduYWwuXG4gICAgICBpZiAoc2VsLmlzRHJhZ2dpbmcpIHtcbiAgICAgICAgZmluaXNoU2VsZWN0aW9uKHNlbClcbiAgICAgICAgYXBwLnByb3BzLm9uU2VsZWN0aW9uQ2hhbmdlKClcbiAgICAgIH1cbiAgICAgIGlmIChjb2wgPT09IGFwcC5sYXN0SG92ZXJDb2wgJiYgcm93ID09PSBhcHAubGFzdEhvdmVyUm93KSByZXR1cm5cbiAgICAgIGFwcC5sYXN0SG92ZXJDb2wgPSBjb2xcbiAgICAgIGFwcC5sYXN0SG92ZXJSb3cgPSByb3dcbiAgICAgIGFwcC5wcm9wcy5vbkhvdmVyQXQoY29sLCByb3cpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgaWYgKGJhc2VCdXR0b24gIT09IDApIHtcbiAgICAgIC8vIE5vbi1sZWZ0IHByZXNzIGJyZWFrcyB0aGUgbXVsdGktY2xpY2sgY2hhaW4uXG4gICAgICBhcHAuY2xpY2tDb3VudCA9IDBcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBpZiAoKG0uYnV0dG9uICYgMHgyMCkgIT09IDApIHtcbiAgICAgIC8vIERyYWcgbW90aW9uOiBtb2RlLWF3YXJlIGV4dGVuc2lvbiAoY2hhci93b3JkL2xpbmUpLiBvblNlbGVjdGlvbkRyYWdcbiAgICAgIC8vIGNhbGxzIG5vdGlmeVNlbGVjdGlvbkNoYW5nZSBpbnRlcm5hbGx5IOKAlCBubyBleHRyYSBvblNlbGVjdGlvbkNoYW5nZS5cbiAgICAgIGFwcC5wcm9wcy5vblNlbGVjdGlvbkRyYWcoY29sLCByb3cpXG4gICAgICByZXR1cm5cbiAgICB9XG4gICAgLy8gTG9zdC1yZWxlYXNlIGZhbGxiYWNrIGZvciBtb2RlLTEwMDItb25seSB0ZXJtaW5hbHM6IGEgZnJlc2ggcHJlc3NcbiAgICAvLyB3aGlsZSBpc0RyYWdnaW5nPXRydWUgbWVhbnMgdGhlIHByZXZpb3VzIHJlbGVhc2Ugd2FzIGRyb3BwZWQgKGN1cnNvclxuICAgIC8vIGxlZnQgdGhlIHdpbmRvdykuIEZpbmlzaCB0aGF0IHNlbGVjdGlvbiBzbyBjb3B5LW9uLXNlbGVjdCBmaXJlc1xuICAgIC8vIGJlZm9yZSBzdGFydFNlbGVjdGlvbi9vbk11bHRpQ2xpY2sgY2xvYmJlcnMgaXQuIE1vZGUtMTAwMyB0ZXJtaW5hbHNcbiAgICAvLyBoaXQgdGhlIG5vLWJ1dHRvbi1tb3Rpb24gcmVjb3ZlcnkgYWJvdmUgaW5zdGVhZCwgc28gdGhpcyBpcyByYXJlLlxuICAgIGlmIChzZWwuaXNEcmFnZ2luZykge1xuICAgICAgZmluaXNoU2VsZWN0aW9uKHNlbClcbiAgICAgIGFwcC5wcm9wcy5vblNlbGVjdGlvbkNoYW5nZSgpXG4gICAgfVxuICAgIC8vIEZyZXNoIGxlZnQgcHJlc3MuIERldGVjdCBtdWx0aS1jbGljayBIRVJFIChub3Qgb24gcmVsZWFzZSkgc28gdGhlXG4gICAgLy8gd29yZC9saW5lIGhpZ2hsaWdodCBhcHBlYXJzIGltbWVkaWF0ZWx5IGFuZCBhIHN1YnNlcXVlbnQgZHJhZyBjYW5cbiAgICAvLyBleHRlbmQgYnkgd29yZC9saW5lIGxpa2UgbmF0aXZlIG1hY09TLiBQcmV2aW91c2x5IGRldGVjdGVkIG9uXG4gICAgLy8gcmVsZWFzZSwgd2hpY2ggbWVhbnQgKGEpIHZpc2libGUgbGF0ZW5jeSBiZWZvcmUgdGhlIHdvcmQgaGlnaGxpZ2h0c1xuICAgIC8vIGFuZCAoYikgZG91YmxlLWNsaWNrK2RyYWcgZmVsbCB0aHJvdWdoIHRvIGNoYXItbW9kZSBzZWxlY3Rpb24uXG4gICAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKVxuICAgIGNvbnN0IG5lYXJMYXN0ID1cbiAgICAgIG5vdyAtIGFwcC5sYXN0Q2xpY2tUaW1lIDwgTVVMVElfQ0xJQ0tfVElNRU9VVF9NUyAmJlxuICAgICAgTWF0aC5hYnMoY29sIC0gYXBwLmxhc3RDbGlja0NvbCkgPD0gTVVMVElfQ0xJQ0tfRElTVEFOQ0UgJiZcbiAgICAgIE1hdGguYWJzKHJvdyAtIGFwcC5sYXN0Q2xpY2tSb3cpIDw9IE1VTFRJX0NMSUNLX0RJU1RBTkNFXG4gICAgYXBwLmNsaWNrQ291bnQgPSBuZWFyTGFzdCA/IGFwcC5jbGlja0NvdW50ICsgMSA6IDFcbiAgICBhcHAubGFzdENsaWNrVGltZSA9IG5vd1xuICAgIGFwcC5sYXN0Q2xpY2tDb2wgPSBjb2xcbiAgICBhcHAubGFzdENsaWNrUm93ID0gcm93XG4gICAgaWYgKGFwcC5jbGlja0NvdW50ID49IDIpIHtcbiAgICAgIC8vIENhbmNlbCBhbnkgcGVuZGluZyBoeXBlcmxpbmstb3BlbiBmcm9tIHRoZSBmaXJzdCBjbGljayDigJQgdGhpcyBpc1xuICAgICAgLy8gYSBkb3VibGUtY2xpY2ssIG5vdCBhIHNpbmdsZS1jbGljayBvbiBhIGxpbmsuXG4gICAgICBpZiAoYXBwLnBlbmRpbmdIeXBlcmxpbmtUaW1lcikge1xuICAgICAgICBjbGVhclRpbWVvdXQoYXBwLnBlbmRpbmdIeXBlcmxpbmtUaW1lcilcbiAgICAgICAgYXBwLnBlbmRpbmdIeXBlcmxpbmtUaW1lciA9IG51bGxcbiAgICAgIH1cbiAgICAgIC8vIENhcCBhdCAzIChsaW5lIHNlbGVjdCkgZm9yIHF1YWRydXBsZSsgY2xpY2tzLlxuICAgICAgY29uc3QgY291bnQgPSBhcHAuY2xpY2tDb3VudCA9PT0gMiA/IDIgOiAzXG4gICAgICBhcHAucHJvcHMub25NdWx0aUNsaWNrKGNvbCwgcm93LCBjb3VudClcbiAgICAgIHJldHVyblxuICAgIH1cbiAgICBzdGFydFNlbGVjdGlvbihzZWwsIGNvbCwgcm93KVxuICAgIC8vIFNHUiBiaXQgMHgwOCA9IGFsdCAoeHRlcm0uanMgd2lyZXMgYWx0S2V5IGhlcmUsIG5vdCBtZXRhS2V5IOKAlCBzZWVcbiAgICAvLyBjb21tZW50IGF0IHRoZSBoeXBlcmxpbmstb3BlbiBndWFyZCBiZWxvdykuIE9uIG1hY09TIHh0ZXJtLmpzLFxuICAgIC8vIHJlY2VpdmluZyBhbHQgbWVhbnMgbWFjT3B0aW9uQ2xpY2tGb3JjZXNTZWxlY3Rpb24gaXMgT0ZGIChvdGhlcndpc2VcbiAgICAvLyB4dGVybS5qcyB3b3VsZCBoYXZlIGNvbnN1bWVkIHRoZSBldmVudCBmb3IgbmF0aXZlIHNlbGVjdGlvbikuXG4gICAgc2VsLmxhc3RQcmVzc0hhZEFsdCA9IChtLmJ1dHRvbiAmIDB4MDgpICE9PSAwXG4gICAgYXBwLnByb3BzLm9uU2VsZWN0aW9uQ2hhbmdlKClcbiAgICByZXR1cm5cbiAgfVxuXG4gIC8vIFJlbGVhc2U6IGVuZCB0aGUgZHJhZyBldmVuIGZvciBub24temVybyBidXR0b24gY29kZXMuIFNvbWUgdGVybWluYWxzXG4gIC8vIGVuY29kZSByZWxlYXNlIHdpdGggdGhlIG1vdGlvbiBiaXQgb3IgYnV0dG9uPTMgXCJubyBidXR0b25cIiAoY2FycmllZFxuICAvLyBvdmVyIGZyb20gcHJlLVNHUiBYMTAgZW5jb2RpbmcpIOKAlCBmaWx0ZXJpbmcgdGhvc2Ugd291bGQgb3JwaGFuXG4gIC8vIGlzRHJhZ2dpbmc9dHJ1ZSBhbmQgbGVhdmUgZHJhZy10by1zY3JvbGwncyB0aW1lciBydW5uaW5nIHVudGlsIHRoZVxuICAvLyBzY3JvbGwgYm91bmRhcnkuIE9ubHkgYWN0IG9uIG5vbi1sZWZ0IHJlbGVhc2VzIHdoZW4gd2UgQVJFIGRyYWdnaW5nXG4gIC8vIChzbyBhbiB1bnJlbGF0ZWQgbWlkZGxlL3JpZ2h0IGNsaWNrLXJlbGVhc2UgZG9lc24ndCB0b3VjaCBzZWxlY3Rpb24pLlxuICBpZiAoYmFzZUJ1dHRvbiAhPT0gMCkge1xuICAgIGlmICghc2VsLmlzRHJhZ2dpbmcpIHJldHVyblxuICAgIGZpbmlzaFNlbGVjdGlvbihzZWwpXG4gICAgYXBwLnByb3BzLm9uU2VsZWN0aW9uQ2hhbmdlKClcbiAgICByZXR1cm5cbiAgfVxuICBmaW5pc2hTZWxlY3Rpb24oc2VsKVxuICAvLyBOT1RFOiB1bmxpa2UgdGhlIG9sZCByZWxlYXNlLWJhc2VkIGRldGVjdGlvbiB3ZSBkbyBOT1QgcmVzZXQgY2xpY2tDb3VudFxuICAvLyBvbiByZWxlYXNlLWFmdGVyLWRyYWcuIFRoaXMgYWxpZ25zIHdpdGggTlNFdmVudC5jbGlja0NvdW50IHNlbWFudGljczpcbiAgLy8gYW4gaW50ZXJ2ZW5pbmcgZHJhZyBkb2Vzbid0IGJyZWFrIHRoZSBjbGljayBjaGFpbi4gUHJhY3RpY2FsIHVwc2lkZTpcbiAgLy8gdHJhY2twYWQgaml0dGVyIGR1cmluZyBhbiBpbnRlbmRlZCBkb3VibGUtY2xpY2sgKHByZXNz4oaSd29iYmxl4oaScmVsZWFzZVxuICAvLyDihpJwcmVzcykgbm93IGNvcnJlY3RseSByZXNvbHZlcyB0byB3b3JkLXNlbGVjdCBpbnN0ZWFkIG9mIGJyZWFraW5nIHRvIGFcbiAgLy8gZnJlc2ggc2luZ2xlIGNsaWNrLiBUaGUgbmVhckxhc3Qgd2luZG93ICg1MDBtcywgMSBjZWxsKSBib3VuZHMgdGhlXG4gIC8vIGVmZmVjdCDigJQgYSBkZWxpYmVyYXRlIGRyYWcgcGFzdCB0aGF0IGp1c3Qgc3RhcnRzIGEgZnJlc2ggY2hhaW4uXG4gIC8vIEEgcHJlc3MrcmVsZWFzZSB3aXRoIG5vIGRyYWcgaW4gY2hhciBtb2RlIGlzIGEgY2xpY2s6IGFuY2hvciBzZXQsXG4gIC8vIGZvY3VzIG51bGwg4oaSIGhhc1NlbGVjdGlvbiBmYWxzZS4gSW4gd29yZC9saW5lIG1vZGUgdGhlIHByZXNzIGFscmVhZHlcbiAgLy8gc2V0IGFuY2hvcitmb2N1cyAoaGFzU2VsZWN0aW9uIHRydWUpLCBzbyByZWxlYXNlIGp1c3Qga2VlcHMgdGhlXG4gIC8vIGhpZ2hsaWdodC4gVGhlIGFuY2hvciBjaGVjayBndWFyZHMgYWdhaW5zdCBhbiBvcnBoYW5lZCByZWxlYXNlIChub1xuICAvLyBwcmlvciBwcmVzcyDigJQgZS5nLiBidXR0b24gd2FzIGhlbGQgd2hlbiBtb3VzZSB0cmFja2luZyB3YXMgZW5hYmxlZCkuXG4gIGlmICghaGFzU2VsZWN0aW9uKHNlbCkgJiYgc2VsLmFuY2hvcikge1xuICAgIC8vIFNpbmdsZSBjbGljazogZGlzcGF0Y2ggRE9NIGNsaWNrIGltbWVkaWF0ZWx5IChjdXJzb3IgcmVwb3NpdGlvbmluZ1xuICAgIC8vIGV0Yy4gYXJlIGxhdGVuY3ktc2Vuc2l0aXZlKS4gSWYgbm8gRE9NIGhhbmRsZXIgY29uc3VtZWQgaXQsIGRlZmVyXG4gICAgLy8gdGhlIGh5cGVybGluayBjaGVjayBzbyBhIHNlY29uZCBjbGljayBjYW4gY2FuY2VsIGl0LlxuICAgIGlmICghYXBwLnByb3BzLm9uQ2xpY2tBdChjb2wsIHJvdykpIHtcbiAgICAgIC8vIFJlc29sdmUgdGhlIGh5cGVybGluayBVUkwgc3luY2hyb25vdXNseSB3aGlsZSB0aGUgc2NyZWVuIGJ1ZmZlclxuICAgICAgLy8gc3RpbGwgcmVmbGVjdHMgd2hhdCB0aGUgdXNlciBjbGlja2VkIOKAlCBkZWZlcnJpbmcgb25seSB0aGVcbiAgICAgIC8vIGJyb3dzZXItb3BlbiBzbyBkb3VibGUtY2xpY2sgY2FuIGNhbmNlbCBpdC5cbiAgICAgIGNvbnN0IHVybCA9IGFwcC5wcm9wcy5nZXRIeXBlcmxpbmtBdChjb2wsIHJvdylcbiAgICAgIC8vIHh0ZXJtLmpzIChWUyBDb2RlLCBDdXJzb3IsIFdpbmRzdXJmLCBldGMuKSBoYXMgaXRzIG93biBPU0MgOCBsaW5rXG4gICAgICAvLyBoYW5kbGVyIHRoYXQgZmlyZXMgb24gQ21kK2NsaWNrICp3aXRob3V0IGNvbnN1bWluZyB0aGUgbW91c2UgZXZlbnQqXG4gICAgICAvLyAoTGlua2lmaWVyLl9oYW5kbGVNb3VzZVVwIGNhbGxzIGxpbmsuYWN0aXZhdGUoKSBidXQgbmV2ZXJcbiAgICAgIC8vIHByZXZlbnREZWZhdWx0L3N0b3BQcm9wYWdhdGlvbikuIFRoZSBjbGljayBpcyBhbHNvIGZvcndhcmRlZCB0byB0aGVcbiAgICAgIC8vIHB0eSBhcyBTR1IsIHNvIGJvdGggVlMgQ29kZSdzIHRlcm1pbmFsTGlua01hbmFnZXIgQU5EIG91ciBoYW5kbGVyXG4gICAgICAvLyBoZXJlIHdvdWxkIG9wZW4gdGhlIFVSTCDigJQgdHdpY2UuIFdlIGNhbid0IGZpbHRlciBvbiBDbWQ6IHh0ZXJtLmpzXG4gICAgICAvLyBkcm9wcyBtZXRhS2V5IGJlZm9yZSBTR1IgZW5jb2RpbmcgKElDb3JlTW91c2VFdmVudCBoYXMgbm8gbWV0YVxuICAgICAgLy8gZmllbGQ7IHRoZSBTR1IgYml0IHdlIGNhbGwgJ21ldGEnIGlzIHdpcmVkIHRvIGFsdCkuIExldCB4dGVybS5qc1xuICAgICAgLy8gb3duIGxpbmstb3BlbmluZzsgQ21kK2NsaWNrIGlzIHRoZSBuYXRpdmUgVVggdGhlcmUgYW55d2F5LlxuICAgICAgLy8gVEVSTV9QUk9HUkFNIGlzIHRoZSBzeW5jIGZhc3QtcGF0aDsgaXNYdGVybUpzKCkgaXMgdGhlIFhUVkVSU0lPTlxuICAgICAgLy8gcHJvYmUgcmVzdWx0IChjYXRjaGVzIFNTSCArIG5vbi1WUyBDb2RlIGVtYmVkZGVycyBsaWtlIEh5cGVyKS5cbiAgICAgIGlmICh1cmwgJiYgcHJvY2Vzcy5lbnYuVEVSTV9QUk9HUkFNICE9PSAndnNjb2RlJyAmJiAhaXNYdGVybUpzKCkpIHtcbiAgICAgICAgLy8gQ2xlYXIgYW55IHByaW9yIHBlbmRpbmcgdGltZXIg4oCUIGNsaWNraW5nIGEgc2Vjb25kIGxpbmtcbiAgICAgICAgLy8gc3VwZXJzZWRlcyB0aGUgZmlyc3QgKG9ubHkgdGhlIGxhdGVzdCBjbGljayBvcGVucykuXG4gICAgICAgIGlmIChhcHAucGVuZGluZ0h5cGVybGlua1RpbWVyKSB7XG4gICAgICAgICAgY2xlYXJUaW1lb3V0KGFwcC5wZW5kaW5nSHlwZXJsaW5rVGltZXIpXG4gICAgICAgIH1cbiAgICAgICAgYXBwLnBlbmRpbmdIeXBlcmxpbmtUaW1lciA9IHNldFRpbWVvdXQoXG4gICAgICAgICAgKGFwcCwgdXJsKSA9PiB7XG4gICAgICAgICAgICBhcHAucGVuZGluZ0h5cGVybGlua1RpbWVyID0gbnVsbFxuICAgICAgICAgICAgYXBwLnByb3BzLm9uT3Blbkh5cGVybGluayh1cmwpXG4gICAgICAgICAgfSxcbiAgICAgICAgICBNVUxUSV9DTElDS19USU1FT1VUX01TLFxuICAgICAgICAgIGFwcCxcbiAgICAgICAgICB1cmwsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgYXBwLnByb3BzLm9uU2VsZWN0aW9uQ2hhbmdlKClcbn1cbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxJQUFJQyxhQUFhLEVBQUUsS0FBS0MsU0FBUyxRQUFRLE9BQU87QUFDNUQsU0FBU0MseUJBQXlCLFFBQVEsMEJBQTBCO0FBQ3BFLFNBQVNDLGVBQWUsUUFBUSxzQkFBc0I7QUFDdEQsU0FBU0MsdUJBQXVCLFFBQVEsMkJBQTJCO0FBQ25FLFNBQVNDLFdBQVcsUUFBUSx5QkFBeUI7QUFDckQsU0FBU0MscUJBQXFCLFFBQVEsMkJBQTJCO0FBQ2pFLFNBQVNDLFFBQVEsUUFBUSxvQkFBb0I7QUFDN0MsU0FBU0MsWUFBWSxRQUFRLHNCQUFzQjtBQUNuRCxTQUFTQyxVQUFVLFFBQVEsMEJBQTBCO0FBQ3JELFNBQVNDLGtCQUFrQixRQUFRLG1DQUFtQztBQUN0RSxTQUNFQyxhQUFhLEVBQ2IsS0FBS0MsV0FBVyxFQUNoQixLQUFLQyxTQUFTLEVBQ2QsS0FBS0MsV0FBVyxFQUNoQkMsdUJBQXVCLFFBQ2xCLHNCQUFzQjtBQUM3QixPQUFPQyxVQUFVLE1BQU0sa0JBQWtCO0FBQ3pDLFNBQ0VDLGVBQWUsRUFDZkMsWUFBWSxF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 \ No newline at end of file diff --git a/src/ink/components/Box.tsx b/src/ink/components/Box.tsx new file mode 100644 index 0000000000000000000000000000000000000000..67f2500ff84220940f4c40e14c06afa2fd6e10f1 --- /dev/null +++ b/src/ink/components/Box.tsx @@ -0,0 +1,214 @@ +import { c as _c } from "react/compiler-runtime"; +import '../global.d.ts'; +import React, { type PropsWithChildren, type Ref } from 'react'; +import type { Except } from 'type-fest'; +import type { DOMElement } from '../dom.js'; +import type { ClickEvent } from '../events/click-event.js'; +import type { FocusEvent } from '../events/focus-event.js'; +import type { KeyboardEvent } from '../events/keyboard-event.js'; +import type { Styles } from '../styles.js'; +import * as warn from '../warn.js'; +export type Props = Except & { + ref?: Ref; + /** + * Tab order index. Nodes with `tabIndex >= 0` participate in + * Tab/Shift+Tab cycling; `-1` means programmatically focusable only. + */ + tabIndex?: number; + /** + * Focus this element when it mounts. Like the HTML `autofocus` + * attribute — the FocusManager calls `focus(node)` during the + * reconciler's `commitMount` phase. + */ + autoFocus?: boolean; + /** + * Fired on left-button click (press + release without drag). Only works + * inside `` where mouse tracking is enabled — no-op + * otherwise. The event bubbles from the deepest hit Box up through + * ancestors; call `event.stopImmediatePropagation()` to stop bubbling. + */ + onClick?: (event: ClickEvent) => void; + onFocus?: (event: FocusEvent) => void; + onFocusCapture?: (event: FocusEvent) => void; + onBlur?: (event: FocusEvent) => void; + onBlurCapture?: (event: FocusEvent) => void; + onKeyDown?: (event: KeyboardEvent) => void; + onKeyDownCapture?: (event: KeyboardEvent) => void; + /** + * Fired when the mouse moves into this Box's rendered rect. Like DOM + * `mouseenter`, does NOT bubble — moving between children does not + * re-fire on the parent. Only works inside `` where + * mode-1003 mouse tracking is enabled. + */ + onMouseEnter?: () => void; + /** Fired when the mouse moves out of this Box's rendered rect. */ + onMouseLeave?: () => void; +}; + +/** + * `` is an essential Ink component to build your layout. It's like `
` in the browser. + */ +function Box(t0) { + const $ = _c(42); + let autoFocus; + let children; + let flexDirection; + let flexGrow; + let flexShrink; + let flexWrap; + let onBlur; + let onBlurCapture; + let onClick; + let onFocus; + let onFocusCapture; + let onKeyDown; + let onKeyDownCapture; + let onMouseEnter; + let onMouseLeave; + let ref; + let style; + let tabIndex; + if ($[0] !== t0) { + const { + children: t1, + flexWrap: t2, + flexDirection: t3, + flexGrow: t4, + flexShrink: t5, + ref: t6, + tabIndex: t7, + autoFocus: t8, + onClick: t9, + onFocus: t10, + onFocusCapture: t11, + onBlur: t12, + onBlurCapture: t13, + onMouseEnter: t14, + onMouseLeave: t15, + onKeyDown: t16, + onKeyDownCapture: t17, + ...t18 + } = t0; + children = t1; + ref = t6; + tabIndex = t7; + autoFocus = t8; + onClick = t9; + onFocus = t10; + onFocusCapture = t11; + onBlur = t12; + onBlurCapture = t13; + onMouseEnter = t14; + onMouseLeave = t15; + onKeyDown = t16; + onKeyDownCapture = t17; + style = t18; + flexWrap = t2 === undefined ? "nowrap" : t2; + flexDirection = t3 === undefined ? "row" : t3; + flexGrow = t4 === undefined ? 0 : t4; + flexShrink = t5 === undefined ? 1 : t5; + warn.ifNotInteger(style.margin, "margin"); + warn.ifNotInteger(style.marginX, "marginX"); + warn.ifNotInteger(style.marginY, "marginY"); + warn.ifNotInteger(style.marginTop, "marginTop"); + warn.ifNotInteger(style.marginBottom, "marginBottom"); + warn.ifNotInteger(style.marginLeft, "marginLeft"); + warn.ifNotInteger(style.marginRight, "marginRight"); + warn.ifNotInteger(style.padding, "padding"); + warn.ifNotInteger(style.paddingX, "paddingX"); + warn.ifNotInteger(style.paddingY, "paddingY"); + warn.ifNotInteger(style.paddingTop, "paddingTop"); + warn.ifNotInteger(style.paddingBottom, "paddingBottom"); + warn.ifNotInteger(style.paddingLeft, "paddingLeft"); + warn.ifNotInteger(style.paddingRight, "paddingRight"); + warn.ifNotInteger(style.gap, "gap"); + warn.ifNotInteger(style.columnGap, "columnGap"); + warn.ifNotInteger(style.rowGap, "rowGap"); + $[0] = t0; + $[1] = autoFocus; + $[2] = children; + $[3] = flexDirection; + $[4] = flexGrow; + $[5] = flexShrink; + $[6] = flexWrap; + $[7] = onBlur; + $[8] = onBlurCapture; + $[9] = onClick; + $[10] = onFocus; + $[11] = onFocusCapture; + $[12] = onKeyDown; + $[13] = onKeyDownCapture; + $[14] = onMouseEnter; + $[15] = onMouseLeave; + $[16] = ref; + $[17] = style; + $[18] = tabIndex; + } else { + autoFocus = $[1]; + children = $[2]; + flexDirection = $[3]; + flexGrow = $[4]; + flexShrink = $[5]; + flexWrap = $[6]; + onBlur = $[7]; + onBlurCapture = $[8]; + onClick = $[9]; + onFocus = $[10]; + onFocusCapture = $[11]; + onKeyDown = $[12]; + onKeyDownCapture = $[13]; + onMouseEnter = $[14]; + onMouseLeave = $[15]; + ref = $[16]; + style = $[17]; + tabIndex = $[18]; + } + const t1 = style.overflowX ?? style.overflow ?? "visible"; + const t2 = style.overflowY ?? style.overflow ?? "visible"; + let t3; + if ($[19] !== flexDirection || $[20] !== flexGrow || $[21] !== flexShrink || $[22] !== flexWrap || $[23] !== style || $[24] !== t1 || $[25] !== t2) { + t3 = { + flexWrap, + flexDirection, + flexGrow, + flexShrink, + ...style, + overflowX: t1, + overflowY: t2 + }; + $[19] = flexDirection; + $[20] = flexGrow; + $[21] = flexShrink; + $[22] = flexWrap; + $[23] = style; + $[24] = t1; + $[25] = t2; + $[26] = t3; + } else { + t3 = $[26]; + } + let t4; + if ($[27] !== autoFocus || $[28] !== children || $[29] !== onBlur || $[30] !== onBlurCapture || $[31] !== onClick || $[32] !== onFocus || $[33] !== onFocusCapture || $[34] !== onKeyDown || $[35] !== onKeyDownCapture || $[36] !== onMouseEnter || $[37] !== onMouseLeave || $[38] !== ref || $[39] !== t3 || $[40] !== tabIndex) { + t4 = {children}; + $[27] = autoFocus; + $[28] = children; + $[29] = onBlur; + $[30] = onBlurCapture; + $[31] = onClick; + $[32] = onFocus; + $[33] = onFocusCapture; + $[34] = onKeyDown; + $[35] = onKeyDownCapture; + $[36] = onMouseEnter; + $[37] = onMouseLeave; + $[38] = ref; + $[39] = t3; + $[40] = tabIndex; + $[41] = t4; + } else { + t4 = $[41]; + } + return t4; +} +export default Box; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzV2l0aENoaWxkcmVuIiwiUmVmIiwiRXhjZXB0IiwiRE9NRWxlbWVudCIsIkNsaWNrRXZlbnQiLCJGb2N1c0V2ZW50IiwiS2V5Ym9hcmRFdmVudCIsIlN0eWxlcyIsIndhcm4iLCJQcm9wcyIsInJlZiIsInRhYkluZGV4IiwiYXV0b0ZvY3VzIiwib25DbGljayIsImV2ZW50Iiwib25Gb2N1cyIsIm9uRm9jdXNDYXB0dXJlIiwib25CbHVyIiwib25CbHVyQ2FwdHVyZSIsIm9uS2V5RG93biIsIm9uS2V5RG93bkNhcHR1cmUiLCJvbk1vdXNlRW50ZXIiLCJvbk1vdXNlTGVhdmUiLCJCb3giLCJ0MCIsIiQiLCJfYyIsImNoaWxkcmVuIiwiZmxleERpcmVjdGlvbiIsImZsZXhHcm93IiwiZmxleFNocmluayIsImZsZXhXcmFwIiwic3R5bGUiLCJ0MSIsInQyIiwidDMiLCJ0NCIsInQ1IiwidDYiLCJ0NyIsInQ4IiwidDkiLCJ0MTAiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJ0MTQiLCJ0MTUiLCJ0MTYiLCJ0MTciLCJ0MTgiLCJ1bmRlZmluZWQiLCJpZk5vdEludGVnZXIiLCJtYXJnaW4iLCJtYXJnaW5YIiwibWFyZ2luWSIsIm1hcmdpblRvcCIsIm1hcmdpbkJvdHRvbSIsIm1hcmdpbkxlZnQiLCJtYXJnaW5SaWdodCIsInBhZGRpbmciLCJwYWRkaW5nWCIsInBhZGRpbmdZIiwicGFkZGluZ1RvcCIsInBhZGRpbmdCb3R0b20iLCJwYWRkaW5nTGVmdCIsInBhZGRpbmdSaWdodCIsImdhcCIsImNvbHVtbkdhcCIsInJvd0dhcCIsIm92ZXJmbG93WCIsIm92ZXJmbG93Iiwib3ZlcmZsb3dZIl0sInNvdXJjZXMiOlsiQm94LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgJy4uL2dsb2JhbC5kLnRzJ1xuaW1wb3J0IFJlYWN0LCB7IHR5cGUgUHJvcHNXaXRoQ2hpbGRyZW4sIHR5cGUgUmVmIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IEV4Y2VwdCB9IGZyb20gJ3R5cGUtZmVzdCdcbmltcG9ydCB0eXBlIHsgRE9NRWxlbWVudCB9IGZyb20gJy4uL2RvbS5qcydcbmltcG9ydCB0eXBlIHsgQ2xpY2tFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9jbGljay1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgRm9jdXNFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9mb2N1cy1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgU3R5bGVzIH0gZnJvbSAnLi4vc3R5bGVzLmpzJ1xuaW1wb3J0ICogYXMgd2FybiBmcm9tICcuLi93YXJuLmpzJ1xuXG5leHBvcnQgdHlwZSBQcm9wcyA9IEV4Y2VwdDxTdHlsZXMsICd0ZXh0V3JhcCc+ICYge1xuICByZWY/OiBSZWY8RE9NRWxlbWVudD5cbiAgLyoqXG4gICAqIFRhYiBvcmRlciBpbmRleC4gTm9kZXMgd2l0aCBgdGFiSW5kZXggPj0gMGAgcGFydGljaXBhdGUgaW5cbiAgICogVGFiL1NoaWZ0K1RhYiBjeWNsaW5nOyBgLTFgIG1lYW5zIHByb2dyYW1tYXRpY2FsbHkgZm9jdXNhYmxlIG9ubHkuXG4gICAqL1xuICB0YWJJbmRleD86IG51bWJlclxuICAvKipcbiAgICogRm9jdXMgdGhpcyBlbGVtZW50IHdoZW4gaXQgbW91bnRzLiBMaWtlIHRoZSBIVE1MIGBhdXRvZm9jdXNgXG4gICAqIGF0dHJpYnV0ZSDigJQgdGhlIEZvY3VzTWFuYWdlciBjYWxscyBgZm9jdXMobm9kZSlgIGR1cmluZyB0aGVcbiAgICogcmVjb25jaWxlcidzIGBjb21taXRNb3VudGAgcGhhc2UuXG4gICAqL1xuICBhdXRvRm9jdXM/OiBib29sZWFuXG4gIC8qKlxuICAgKiBGaXJlZCBvbiBsZWZ0LWJ1dHRvbiBjbGljayAocHJlc3MgKyByZWxlYXNlIHdpdGhvdXQgZHJhZykuIE9ubHkgd29ya3NcbiAgICogaW5zaWRlIGA8QWx0ZXJuYXRlU2NyZWVuPmAgd2hlcmUgbW91c2UgdHJhY2tpbmcgaXMgZW5hYmxlZCDigJQgbm8tb3BcbiAgICogb3RoZXJ3aXNlLiBUaGUgZXZlbnQgYnViYmxlcyBmcm9tIHRoZSBkZWVwZXN0IGhpdCBCb3ggdXAgdGhyb3VnaFxuICAgKiBhbmNlc3RvcnM7IGNhbGwgYGV2ZW50LnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpYCB0byBzdG9wIGJ1YmJsaW5nLlxuICAgKi9cbiAgb25DbGljaz86IChldmVudDogQ2xpY2tFdmVudCkgPT4gdm9pZFxuICBvbkZvY3VzPzogKGV2ZW50OiBGb2N1c0V2ZW50KSA9PiB2b2lkXG4gIG9uRm9jdXNDYXB0dXJlPzogKGV2ZW50OiBGb2N1c0V2ZW50KSA9PiB2b2lkXG4gIG9uQmx1cj86IChldmVudDogRm9jdXNFdmVudCkgPT4gdm9pZFxuICBvbkJsdXJDYXB0dXJlPzogKGV2ZW50OiBGb2N1c0V2ZW50KSA9PiB2b2lkXG4gIG9uS2V5RG93bj86IChldmVudDogS2V5Ym9hcmRFdmVudCkgPT4gdm9pZFxuICBvbktleURvd25DYXB0dXJlPzogKGV2ZW50OiBLZXlib2FyZEV2ZW50KSA9PiB2b2lkXG4gIC8qKlxuICAgKiBGaXJlZCB3aGVuIHRoZSBtb3VzZSBtb3ZlcyBpbnRvIHRoaXMgQm94J3MgcmVuZGVyZWQgcmVjdC4gTGlrZSBET01cbiAgICogYG1vdXNlZW50ZXJgLCBkb2VzIE5PVCBidWJibGUg4oCUIG1vdmluZyBiZXR3ZWVuIGNoaWxkcmVuIGRvZXMgbm90XG4gICAqIHJlLWZpcmUgb24gdGhlIHBhcmVudC4gT25seSB3b3JrcyBpbnNpZGUgYDxBbHRlcm5hdGVTY3JlZW4+YCB3aGVyZVxuICAgKiBtb2RlLTEwMDMgbW91c2UgdHJhY2tpbmcgaXMgZW5hYmxlZC5cbiAgICovXG4gIG9uTW91c2VFbnRlcj86ICgpID0+IHZvaWRcbiAgLyoqIEZpcmVkIHdoZW4gdGhlIG1vdXNlIG1vdmVzIG91dCBvZiB0aGlzIEJveCdzIHJlbmRlcmVkIHJlY3QuICovXG4gIG9uTW91c2VMZWF2ZT86ICgpID0+IHZvaWRcbn1cblxuLyoqXG4gKiBgPEJveD5gIGlzIGFuIGVzc2VudGlhbCBJbmsgY29tcG9uZW50IHRvIGJ1aWxkIHlvdXIgbGF5b3V0LiBJdCdzIGxpa2UgYDxkaXYgc3R5bGU9XCJkaXNwbGF5OiBmbGV4XCI+YCBpbiB0aGUgYnJvd3Nlci5cbiAqL1xuZnVuY3Rpb24gQm94KHtcbiAgY2hpbGRyZW4sXG4gIGZsZXhXcmFwID0gJ25vd3JhcCcsXG4gIGZsZXhEaXJlY3Rpb24gPSAncm93JyxcbiAgZmxleEdyb3cgPSAwLFxuICBmbGV4U2hyaW5rID0gMSxcbiAgcmVmLFxuICB0YWJJbmRleCxcbiAgYXV0b0ZvY3VzLFxuICBvbkNsaWNrLFxuICBvbkZvY3VzLFxuICBvbkZvY3VzQ2FwdHVyZSxcbiAgb25CbHVyLFxuICBvbkJsdXJDYXB0dXJlLFxuICBvbk1vdXNlRW50ZXIsXG4gIG9uTW91c2VMZWF2ZSxcbiAgb25LZXlEb3duLFxuICBvbktleURvd25DYXB0dXJlLFxuICAuLi5zdHlsZVxufTogUHJvcHNXaXRoQ2hpbGRyZW48UHJvcHM+KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgLy8gV2FybiBpZiBzcGFjaW5nIHZhbHVlcyBhcmUgbm90IGludGVnZXJzIHRvIHByZXZlbnQgZnJhY3Rpb25hbCBsYXlvdXQgZGltZW5zaW9uc1xuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5tYXJnaW4sICdtYXJnaW4nKVxuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5tYXJnaW5YLCAnbWFyZ2luWCcpXG4gIHdhcm4uaWZOb3RJbnRlZ2VyKHN0eWxlLm1hcmdpblksICdtYXJnaW5ZJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUubWFyZ2luVG9wLCAnbWFyZ2luVG9wJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUubWFyZ2luQm90dG9tLCAnbWFyZ2luQm90dG9tJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUubWFyZ2luTGVmdCwgJ21hcmdpbkxlZnQnKVxuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5tYXJnaW5SaWdodCwgJ21hcmdpblJpZ2h0JylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucGFkZGluZywgJ3BhZGRpbmcnKVxuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5wYWRkaW5nWCwgJ3BhZGRpbmdYJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucGFkZGluZ1ksICdwYWRkaW5nWScpXG4gIHdhcm4uaWZOb3RJbnRlZ2VyKHN0eWxlLnBhZGRpbmdUb3AsICdwYWRkaW5nVG9wJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucGFkZGluZ0JvdHRvbSwgJ3BhZGRpbmdCb3R0b20nKVxuICB3YXJuLmlmTm90SW50ZWdlcihzdHlsZS5wYWRkaW5nTGVmdCwgJ3BhZGRpbmdMZWZ0JylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucGFkZGluZ1JpZ2h0LCAncGFkZGluZ1JpZ2h0JylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUuZ2FwLCAnZ2FwJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUuY29sdW1uR2FwLCAnY29sdW1uR2FwJylcbiAgd2Fybi5pZk5vdEludGVnZXIoc3R5bGUucm93R2FwLCAncm93R2FwJylcblxuICByZXR1cm4gKFxuICAgIDxpbmstYm94XG4gICAgICByZWY9e3JlZn1cbiAgICAgIHRhYkluZGV4PXt0YWJJbmRleH1cbiAgICAgIGF1dG9Gb2N1cz17YXV0b0ZvY3VzfVxuICAgICAgb25DbGljaz17b25DbGlja31cbiAgICAgIG9uRm9jdXM9e29uRm9jdXN9XG4gICAgICBvbkZvY3VzQ2FwdHVyZT17b25Gb2N1c0NhcHR1cmV9XG4gICAgICBvbkJsdXI9e29uQmx1cn1cbiAgICAgIG9uQmx1ckNhcHR1cmU9e29uQmx1ckNhcHR1cmV9XG4gICAgICBvbk1vdXNlRW50ZXI9e29uTW91c2VFbnRlcn1cbiAgICAgIG9uTW91c2VMZWF2ZT17b25Nb3VzZUxlYXZlfVxuICAgICAgb25LZXlEb3duPXtvbktleURvd259XG4gICAgICBvbktleURvd25DYXB0dXJlPXtvbktleURvd25DYXB0dXJlfVxuICAgICAgc3R5bGU9e3tcbiAgICAgICAgZmxleFdyYXAsXG4gICAgICAgIGZsZXhEaXJlY3Rpb24sXG4gICAgICAgIGZsZXhHcm93LFxuICAgICAgICBmbGV4U2hyaW5rLFxuICAgICAgICAuLi5zdHlsZSxcbiAgICAgICAgb3ZlcmZsb3dYOiBzdHlsZS5vdmVyZmxvd1ggPz8gc3R5bGUub3ZlcmZsb3cgPz8gJ3Zpc2libGUnLFxuICAgICAgICBvdmVyZmxvd1k6IHN0eWxlLm92ZXJmbG93WSA/PyBzdHlsZS5vdmVyZmxvdyA/PyAndmlzaWJsZScsXG4gICAgICB9fVxuICAgID5cbiAgICAgIHtjaGlsZHJlbn1cbiAgICA8L2luay1ib3g+XG4gIClcbn1cblxuZXhwb3J0IGRlZmF1bHQgQm94XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPLGdCQUFnQjtBQUN2QixPQUFPQSxLQUFLLElBQUksS0FBS0MsaUJBQWlCLEVBQUUsS0FBS0MsR0FBRyxRQUFRLE9BQU87QUFDL0QsY0FBY0MsTUFBTSxRQUFRLFdBQVc7QUFDdkMsY0FBY0MsVUFBVSxRQUFRLFdBQVc7QUFDM0MsY0FBY0MsVUFBVSxRQUFRLDBCQUEwQjtBQUMxRCxjQUFjQyxVQUFVLFFBQVEsMEJBQTBCO0FBQzFELGNBQWNDLGFBQWEsUUFBUSw2QkFBNkI7QUFDaEUsY0FBY0MsTUFBTSxRQUFRLGNBQWM7QUFDMUMsT0FBTyxLQUFLQyxJQUFJLE1BQU0sWUFBWTtBQUVsQyxPQUFPLEtBQUtDLEtBQUssR0FBR1AsTUFBTSxDQUFDSyxNQUFNLEVBQUUsVUFBVSxDQUFDLEdBQUc7RUFDL0NHLEdBQUcsQ0FBQyxFQUFFVCxHQUFHLENBQUNFLFVBQVUsQ0FBQztFQUNyQjtBQUNGO0FBQ0E7QUFDQTtFQUNFUSxRQUFRLENBQUMsRUFBRSxNQUFNO0VBQ2pCO0FBQ0Y7QUFDQTtBQUNBO0FBQ0E7RUFDRUMsU0FBUyxDQUFDLEVBQUUsT0FBTztFQUNu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 \ No newline at end of file diff --git a/src/ink/components/Button.tsx b/src/ink/components/Button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8dc35f06cb6e1eeda93d1d3d4f298120b75ccfa1 --- /dev/null +++ b/src/ink/components/Button.tsx @@ -0,0 +1,192 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { type Ref, useCallback, useEffect, useRef, useState } from 'react'; +import type { Except } from 'type-fest'; +import type { DOMElement } from '../dom.js'; +import type { ClickEvent } from '../events/click-event.js'; +import type { FocusEvent } from '../events/focus-event.js'; +import type { KeyboardEvent } from '../events/keyboard-event.js'; +import type { Styles } from '../styles.js'; +import Box from './Box.js'; +type ButtonState = { + focused: boolean; + hovered: boolean; + active: boolean; +}; +export type Props = Except & { + ref?: Ref; + /** + * Called when the button is activated via Enter, Space, or click. + */ + onAction: () => void; + /** + * Tab order index. Defaults to 0 (in tab order). + * Set to -1 for programmatically focusable only. + */ + tabIndex?: number; + /** + * Focus this button when it mounts. + */ + autoFocus?: boolean; + /** + * Render prop receiving the interactive state. Use this to + * style children based on focus/hover/active — Button itself + * is intentionally unstyled. + * + * If not provided, children render as-is (no state-dependent styling). + */ + children: ((state: ButtonState) => React.ReactNode) | React.ReactNode; +}; +function Button(t0) { + const $ = _c(30); + let autoFocus; + let children; + let onAction; + let ref; + let style; + let t1; + if ($[0] !== t0) { + ({ + onAction, + tabIndex: t1, + autoFocus, + children, + ref, + ...style + } = t0); + $[0] = t0; + $[1] = autoFocus; + $[2] = children; + $[3] = onAction; + $[4] = ref; + $[5] = style; + $[6] = t1; + } else { + autoFocus = $[1]; + children = $[2]; + onAction = $[3]; + ref = $[4]; + style = $[5]; + t1 = $[6]; + } + const tabIndex = t1 === undefined ? 0 : t1; + const [isFocused, setIsFocused] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const [isActive, setIsActive] = useState(false); + const activeTimer = useRef(null); + let t2; + let t3; + if ($[7] === Symbol.for("react.memo_cache_sentinel")) { + t2 = () => () => { + if (activeTimer.current) { + clearTimeout(activeTimer.current); + } + }; + t3 = []; + $[7] = t2; + $[8] = t3; + } else { + t2 = $[7]; + t3 = $[8]; + } + useEffect(t2, t3); + let t4; + if ($[9] !== onAction) { + t4 = e => { + if (e.key === "return" || e.key === " ") { + e.preventDefault(); + setIsActive(true); + onAction(); + if (activeTimer.current) { + clearTimeout(activeTimer.current); + } + activeTimer.current = setTimeout(_temp, 100, setIsActive); + } + }; + $[9] = onAction; + $[10] = t4; + } else { + t4 = $[10]; + } + const handleKeyDown = t4; + let t5; + if ($[11] !== onAction) { + t5 = _e => { + onAction(); + }; + $[11] = onAction; + $[12] = t5; + } else { + t5 = $[12]; + } + const handleClick = t5; + let t6; + if ($[13] === Symbol.for("react.memo_cache_sentinel")) { + t6 = _e_0 => setIsFocused(true); + $[13] = t6; + } else { + t6 = $[13]; + } + const handleFocus = t6; + let t7; + if ($[14] === Symbol.for("react.memo_cache_sentinel")) { + t7 = _e_1 => setIsFocused(false); + $[14] = t7; + } else { + t7 = $[14]; + } + const handleBlur = t7; + let t8; + if ($[15] === Symbol.for("react.memo_cache_sentinel")) { + t8 = () => setIsHovered(true); + $[15] = t8; + } else { + t8 = $[15]; + } + const handleMouseEnter = t8; + let t9; + if ($[16] === Symbol.for("react.memo_cache_sentinel")) { + t9 = () => setIsHovered(false); + $[16] = t9; + } else { + t9 = $[16]; + } + const handleMouseLeave = t9; + let t10; + if ($[17] !== children || $[18] !== isActive || $[19] !== isFocused || $[20] !== isHovered) { + const state = { + focused: isFocused, + hovered: isHovered, + active: isActive + }; + t10 = typeof children === "function" ? children(state) : children; + $[17] = children; + $[18] = isActive; + $[19] = isFocused; + $[20] = isHovered; + $[21] = t10; + } else { + t10 = $[21]; + } + const content = t10; + let t11; + if ($[22] !== autoFocus || $[23] !== content || $[24] !== handleClick || $[25] !== handleKeyDown || $[26] !== ref || $[27] !== style || $[28] !== tabIndex) { + t11 = {content}; + $[22] = autoFocus; + $[23] = content; + $[24] = handleClick; + $[25] = handleKeyDown; + $[26] = ref; + $[27] = style; + $[28] = tabIndex; + $[29] = t11; + } else { + t11 = $[29]; + } + return t11; +} +function _temp(setter) { + return setter(false); +} +export default Button; +export type { ButtonState }; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlJlZiIsInVzZUNhbGxiYWNrIiwidXNlRWZmZWN0IiwidXNlUmVmIiwidXNlU3RhdGUiLCJFeGNlcHQiLCJET01FbGVtZW50IiwiQ2xpY2tFdmVudCIsIkZvY3VzRXZlbnQiLCJLZXlib2FyZEV2ZW50IiwiU3R5bGVzIiwiQm94IiwiQnV0dG9uU3RhdGUiLCJmb2N1c2VkIiwiaG92ZXJlZCIsImFjdGl2ZSIsIlByb3BzIiwicmVmIiwib25BY3Rpb24iLCJ0YWJJbmRleCIsImF1dG9Gb2N1cyIsImNoaWxkcmVuIiwic3RhdGUiLCJSZWFjdE5vZGUiLCJCdXR0b24iLCJ0MCIsIiQiLCJfYyIsInN0eWxlIiwidDEiLCJ1bmRlZmluZWQiLCJpc0ZvY3VzZWQiLCJzZXRJc0ZvY3VzZWQiLCJpc0hvdmVyZWQiLCJzZXRJc0hvdmVyZWQiLCJpc0FjdGl2ZSIsInNldElzQWN0aXZlIiwiYWN0aXZlVGltZXIiLCJ0MiIsInQzIiwiU3ltYm9sIiwiZm9yIiwiY3VycmVudCIsImNsZWFyVGltZW91dCIsInQ0IiwiZSIsImtleSIsInByZXZlbnREZWZhdWx0Iiwic2V0VGltZW91dCIsIl90ZW1wIiwiaGFuZGxlS2V5RG93biIsInQ1IiwiX2UiLCJoYW5kbGVDbGljayIsInQ2IiwiX2VfMCIsImhhbmRsZUZvY3VzIiwidDciLCJfZV8xIiwiaGFuZGxlQmx1ciIsInQ4IiwiaGFuZGxlTW91c2VFbnRlciIsInQ5IiwiaGFuZGxlTW91c2VMZWF2ZSIsInQxMCIsImNvbnRlbnQiLCJ0MTEiLCJzZXR0ZXIiXSwic291cmNlcyI6WyJCdXR0b24udHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwge1xuICB0eXBlIFJlZixcbiAgdXNlQ2FsbGJhY2ssXG4gIHVzZUVmZmVjdCxcbiAgdXNlUmVmLFxuICB1c2VTdGF0ZSxcbn0gZnJvbSAncmVhY3QnXG5pbXBvcnQgdHlwZSB7IEV4Y2VwdCB9IGZyb20gJ3R5cGUtZmVzdCdcbmltcG9ydCB0eXBlIHsgRE9NRWxlbWVudCB9IGZyb20gJy4uL2RvbS5qcydcbmltcG9ydCB0eXBlIHsgQ2xpY2tFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9jbGljay1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgRm9jdXNFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9mb2N1cy1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgS2V5Ym9hcmRFdmVudCB9IGZyb20gJy4uL2V2ZW50cy9rZXlib2FyZC1ldmVudC5qcydcbmltcG9ydCB0eXBlIHsgU3R5bGVzIH0gZnJvbSAnLi4vc3R5bGVzLmpzJ1xuaW1wb3J0IEJveCBmcm9tICcuL0JveC5qcydcblxudHlwZSBCdXR0b25TdGF0ZSA9IHtcbiAgZm9jdXNlZDogYm9vbGVhblxuICBob3ZlcmVkOiBib29sZWFuXG4gIGFjdGl2ZTogYm9vbGVhblxufVxuXG5leHBvcnQgdHlwZSBQcm9wcyA9IEV4Y2VwdDxTdHlsZXMsICd0ZXh0V3JhcCc+ICYge1xuICByZWY/OiBSZWY8RE9NRWxlbWVudD5cbiAgLyoqXG4gICAqIENhbGxlZCB3aGVuIHRoZSBidXR0b24gaXMgYWN0aXZhdGVkIHZpYSBFbnRlciwgU3BhY2UsIG9yIGNsaWNrLlxuICAgKi9cbiAgb25BY3Rpb246ICgpID0+IHZvaWRcbiAgLyoqXG4gICAqIFRhYiBvcmRlciBpbmRleC4gRGVmYXVsdHMgdG8gMCAoaW4gdGFiIG9yZGVyKS5cbiAgICogU2V0IHRvIC0xIGZvciBwcm9ncmFtbWF0aWNhbGx5IGZvY3VzYWJsZSBvbmx5LlxuICAgKi9cbiAgdGFiSW5kZXg/OiBudW1iZXJcbiAgLyoqXG4gICAqIEZvY3VzIHRoaXMgYnV0dG9uIHdoZW4gaXQgbW91bnRzLlxuICAgKi9cbiAgYXV0b0ZvY3VzPzogYm9vbGVhblxuICAvKipcbiAgICogUmVuZGVyIHByb3AgcmVjZWl2aW5nIHRoZSBpbnRlcmFjdGl2ZSBzdGF0ZS4gVXNlIHRoaXMgdG9cbiAgICogc3R5bGUgY2hpbGRyZW4gYmFzZWQgb24gZm9jdXMvaG92ZXIvYWN0aXZlIOKAlCBCdXR0b24gaXRzZWxmXG4gICAqIGlzIGludGVudGlvbmFsbHkgdW5zdHlsZWQuXG4gICAqXG4gICAqIElmIG5vdCBwcm92aWRlZCwgY2hpbGRyZW4gcmVuZGVyIGFzLWlzIChubyBzdGF0ZS1kZXBlbmRlbnQgc3R5bGluZykuXG4gICAqL1xuICBjaGlsZHJlbjogKChzdGF0ZTogQnV0dG9uU3RhdGUpID0+IFJlYWN0LlJlYWN0Tm9kZSkgfCBSZWFjdC5SZWFjdE5vZGVcbn1cblxuZnVuY3Rpb24gQnV0dG9uKHtcbiAgb25BY3Rpb24sXG4gIHRhYkluZGV4ID0gMCxcbiAgYXV0b0ZvY3VzLFxuICBjaGlsZHJlbixcbiAgcmVmLFxuICAuLi5zdHlsZVxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBbaXNGb2N1c2VkLCBzZXRJc0ZvY3VzZWRdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtpc0hvdmVyZWQsIHNldElzSG92ZXJlZF0gPSB1c2VTdGF0ZShmYWxzZSlcbiAgY29uc3QgW2lzQWN0aXZlLCBzZXRJc0FjdGl2ZV0gPSB1c2VTdGF0ZShmYWxzZSlcblxuICBjb25zdCBhY3RpdmVUaW1lciA9IHVzZVJlZjxSZXR1cm5UeXBlPHR5cGVvZiBzZXRUaW1lb3V0PiB8IG51bGw+KG51bGwpXG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgaWYgKGFjdGl2ZVRpbWVyLmN1cnJlbnQpIGNsZWFyVGltZW91dChhY3RpdmVUaW1lci5jdXJyZW50KVxuICAgIH1cbiAgfSwgW10pXG5cbiAgY29uc3QgaGFuZGxlS2V5RG93biA9IHVzZUNhbGxiYWNrKFxuICAgIChlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgICBpZiAoZS5rZXkgPT09ICdyZXR1cm4nIHx8IGUua2V5ID09PSAnICcpIHtcbiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpXG4gICAgICAgIHNldElzQWN0aXZlKHRydWUpXG4gICAgICAgIG9uQWN0aW9uKClcbiAgICAgICAgaWYgKGFjdGl2ZVRpbWVyLmN1cnJlbnQpIGNsZWFyVGltZW91dChhY3RpdmVUaW1lci5jdXJyZW50KVxuICAgICAgICBhY3RpdmVUaW1lci5jdXJyZW50ID0gc2V0VGltZW91dChcbiAgICAgICAgICBzZXR0ZXIgPT4gc2V0dGVyKGZhbHNlKSxcbiAgICAgICAgICAxMDAsXG4gICAgICAgICAgc2V0SXNBY3RpdmUsXG4gICAgICAgIClcbiAgICAgIH1cbiAgICB9LFxuICAgIFtvbkFjdGlvbl0sXG4gIClcblxuICBjb25zdCBoYW5kbGVDbGljayA9IHVzZUNhbGxiYWNrKFxuICAgIChfZTogQ2xpY2tFdmVudCkgPT4ge1xuICAgICAgb25BY3Rpb24oKVxuICAgIH0sXG4gICAgW29uQWN0aW9uXSxcbiAgKVxuXG4gIGNvbnN0IGhhbmRsZUZvY3VzID0gdXNlQ2FsbGJhY2soKF9lOiBGb2N1c0V2ZW50KSA9PiBzZXRJc0ZvY3VzZWQodHJ1ZSksIFtdKVxuICBjb25zdCBoYW5kbGVCbHVyID0gdXNlQ2FsbGJhY2soKF9lOiBGb2N1c0V2ZW50KSA9PiBzZXRJc0ZvY3VzZWQoZmFsc2UpLCBbXSlcbiAgY29uc3QgaGFuZGxlTW91c2VFbnRlciA9IHVzZUNhbGxiYWNrKCgpID0+IHNldElzSG92ZXJlZCh0cnVlKSwgW10pXG4gIGNvbnN0IGhhbmRsZU1vdXNlTGVhdmUgPSB1c2VDYWxsYmFjaygoKSA9PiBzZXRJc0hvdmVyZWQoZmFsc2UpLCBbXSlcblxuICBjb25zdCBzdGF0ZTogQnV0dG9uU3RhdGUgPSB7XG4gICAgZm9jdXNlZDogaXNGb2N1c2VkLFxuICAgIGhvdmVyZWQ6IGlzSG92ZXJlZCxcbiAgICBhY3RpdmU6IGlzQWN0aXZlLFxuICB9XG4gIGNvbnN0IGNvbnRlbnQgPSB0eXBlb2YgY2hpbGRyZW4gPT09ICdmdW5jdGlvbicgPyBjaGlsZHJlbihzdGF0ZSkgOiBjaGlsZHJlblxuXG4gIHJldHVybiAoXG4gICAgPEJveFxuICAgICAgcmVmPXtyZWZ9XG4gICAgICB0YWJJbmRleD17dGFiSW5kZXh9XG4gICAgICBhdXRvRm9jdXM9e2F1dG9Gb2N1c31cbiAgICAgIG9uS2V5RG93bj17aGFuZGxlS2V5RG93bn1cbiAgICAgIG9uQ2xpY2s9e2hhbmRsZUNsaWNrfVxuICAgICAgb25Gb2N1cz17aGFuZGxlRm9jdXN9XG4gICAgICBvbkJsdXI9e2hhbmRsZUJsdXJ9XG4gICAgICBvbk1vdXNlRW50ZXI9e2hhbmRsZU1vdXNlRW50ZXJ9XG4gICAgICBvbk1vdXNlTGVhdmU9e2hhbmRsZU1vdXNlTGVhdmV9XG4gICAgICB7Li4uc3R5bGV9XG4gICAgPlxuICAgICAge2NvbnRlbnR9XG4gICAgPC9Cb3g+XG4gIClcbn1cblxuZXhwb3J0IGRlZmF1bHQgQnV0dG9uXG5leHBvcnQgdHlwZSB7IEJ1dHRvblN0YXRlIH1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU9BLEtBQUssSUFDVixLQUFLQyxHQUFHLEVBQ1JDLFdBQVcsRUFDWEMsU0FBUyxFQUNUQyxNQUFNLEVBQ05DLFFBQVEsUUFDSCxPQUFPO0FBQ2QsY0FBY0MsTUFBTSxRQUFRLFdBQVc7QUFDdkMsY0FBY0MsVUFBVSxRQUFRLFdBQVc7QUFDM0MsY0FBY0MsVUFBVSxRQUFRLDBCQUEwQjtBQUMxRCxjQUFjQyxVQUFVLFFBQVEsMEJBQTBCO0FBQzFELGNBQWNDLGFBQWEsUUFBUSw2QkFBNkI7QUFDaEUsY0FBY0MsTUFBTSxRQUFRLGNBQWM7QUFDMUMsT0FBT0MsR0FBRyxNQUFNLFVBQVU7QUFFMUIsS0FBS0MsV0FBVyxHQUFHO0VBQ2pCQyxPQUFPLEVBQUUsT0FBTztFQUNoQkMsT0FBTyxFQUFFLE9BQU87RUFDaEJDLE1BQU0sRUFBRSxPQUFPO0FBQ2pCLENBQUM7QUFFRCxPQUFPLEtBQUtDLEtBQUssR0FBR1gsTUFBTSxDQUFDSyxNQUFNLEVBQUUsVUFBVSxDQUFDLEdBQUc7RUFDL0NPLEdBQUcsQ0FBQyxFQUFFakIsR0FBRyxDQUFDTSxVQUFVLENBQUM7RUFDckI7QUFDRjtBQUNBO0VBQ0VZLFFBQVEsRUFBRSxHQUFHLEdBQUcsSUFBSTtFQUNwQjtB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 \ No newline at end of file diff --git a/src/ink/components/ClockContext.tsx b/src/ink/components/ClockContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0f24839dfce619809cb3b4b79a3d436d5ea4baa4 --- /dev/null +++ b/src/ink/components/ClockContext.tsx @@ -0,0 +1,112 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { createContext, useEffect, useState } from 'react'; +import { FRAME_INTERVAL_MS } from '../constants.js'; +import { useTerminalFocus } from '../hooks/use-terminal-focus.js'; +export type Clock = { + subscribe: (onChange: () => void, keepAlive: boolean) => () => void; + now: () => number; + setTickInterval: (ms: number) => void; +}; +export function createClock(tickIntervalMs: number): Clock { + const subscribers = new Map<() => void, boolean>(); + let interval: ReturnType | null = null; + let currentTickIntervalMs = tickIntervalMs; + let startTime = 0; + // Snapshot of the current tick's time, ensuring all subscribers in the same + // tick see the same value (keeps animations synchronized) + let tickTime = 0; + function tick(): void { + tickTime = Date.now() - startTime; + for (const onChange of subscribers.keys()) { + onChange(); + } + } + function updateInterval(): void { + const anyKeepAlive = [...subscribers.values()].some(Boolean); + if (anyKeepAlive) { + if (interval) { + clearInterval(interval); + interval = null; + } + if (startTime === 0) { + startTime = Date.now(); + } + interval = setInterval(tick, currentTickIntervalMs); + } else if (interval) { + clearInterval(interval); + interval = null; + } + } + return { + subscribe(onChange, keepAlive) { + subscribers.set(onChange, keepAlive); + updateInterval(); + return () => { + subscribers.delete(onChange); + updateInterval(); + }; + }, + now() { + if (startTime === 0) { + startTime = Date.now(); + } + // When the clock interval is running, return the synchronized tickTime + // so all subscribers in the same tick see the same value. + // When paused (no keepAlive subscribers), return real-time to avoid + // returning a stale tickTime from the last tick before the pause. + if (interval && tickTime) { + return tickTime; + } + return Date.now() - startTime; + }, + setTickInterval(ms) { + if (ms === currentTickIntervalMs) return; + currentTickIntervalMs = ms; + updateInterval(); + } + }; +} +export const ClockContext = createContext(null); +const BLURRED_TICK_INTERVAL_MS = FRAME_INTERVAL_MS * 2; + +// Own component so App.tsx doesn't re-render when the clock is created. +// The clock value is stable (created once via useState), so the provider +// never causes consumer re-renders on its own. +export function ClockProvider(t0) { + const $ = _c(7); + const { + children + } = t0; + const [clock] = useState(_temp); + const focused = useTerminalFocus(); + let t1; + let t2; + if ($[0] !== clock || $[1] !== focused) { + t1 = () => { + clock.setTickInterval(focused ? FRAME_INTERVAL_MS : BLURRED_TICK_INTERVAL_MS); + }; + t2 = [clock, focused]; + $[0] = clock; + $[1] = focused; + $[2] = t1; + $[3] = t2; + } else { + t1 = $[2]; + t2 = $[3]; + } + useEffect(t1, t2); + let t3; + if ($[4] !== children || $[5] !== clock) { + t3 = {children}; + $[4] = children; + $[5] = clock; + $[6] = t3; + } else { + t3 = $[6]; + } + return t3; +} +function _temp() { + return createClock(FRAME_INTERVAL_MS); +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImNyZWF0ZUNvbnRleHQiLCJ1c2VFZmZlY3QiLCJ1c2VTdGF0ZSIsIkZSQU1FX0lOVEVSVkFMX01TIiwidXNlVGVybWluYWxGb2N1cyIsIkNsb2NrIiwic3Vic2NyaWJlIiwib25DaGFuZ2UiLCJrZWVwQWxpdmUiLCJub3ciLCJzZXRUaWNrSW50ZXJ2YWwiLCJtcyIsImNyZWF0ZUNsb2NrIiwidGlja0ludGVydmFsTXMiLCJzdWJzY3JpYmVycyIsIk1hcCIsImludGVydmFsIiwiUmV0dXJuVHlwZSIsInNldEludGVydmFsIiwiY3VycmVudFRpY2tJbnRlcnZhbE1zIiwic3RhcnRUaW1lIiwidGlja1RpbWUiLCJ0aWNrIiwiRGF0ZSIsImtleXMiLCJ1cGRhdGVJbnRlcnZhbCIsImFueUtlZXBBbGl2ZSIsInZhbHVlcyIsInNvbWUiLCJCb29sZWFuIiwiY2xlYXJJbnRlcnZhbCIsInNldCIsImRlbGV0ZSIsIkNsb2NrQ29udGV4dCIsIkJMVVJSRURfVElDS19JTlRFUlZBTF9NUyIsIkNsb2NrUHJvdmlkZXIiLCJ0MCIsIiQiLCJfYyIsImNoaWxkcmVuIiwiY2xvY2siLCJfdGVtcCIsImZvY3VzZWQiLCJ0MSIsInQyIiwidDMiXSwic291cmNlcyI6WyJDbG9ja0NvbnRleHQudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyBjcmVhdGVDb250ZXh0LCB1c2VFZmZlY3QsIHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBGUkFNRV9JTlRFUlZBTF9NUyB9IGZyb20gJy4uL2NvbnN0YW50cy5qcydcbmltcG9ydCB7IHVzZVRlcm1pbmFsRm9jdXMgfSBmcm9tICcuLi9ob29rcy91c2UtdGVybWluYWwtZm9jdXMuanMnXG5cbmV4cG9ydCB0eXBlIENsb2NrID0ge1xuICBzdWJzY3JpYmU6IChvbkNoYW5nZTogKCkgPT4gdm9pZCwga2VlcEFsaXZlOiBib29sZWFuKSA9PiAoKSA9PiB2b2lkXG4gIG5vdzogKCkgPT4gbnVtYmVyXG4gIHNldFRpY2tJbnRlcnZhbDogKG1zOiBudW1iZXIpID0+IHZvaWRcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZUNsb2NrKHRpY2tJbnRlcnZhbE1zOiBudW1iZXIpOiBDbG9jayB7XG4gIGNvbnN0IHN1YnNjcmliZXJzID0gbmV3IE1hcDwoKSA9PiB2b2lkLCBib29sZWFuPigpXG4gIGxldCBpbnRlcnZhbDogUmV0dXJuVHlwZTx0eXBlb2Ygc2V0SW50ZXJ2YWw+IHwgbnVsbCA9IG51bGxcbiAgbGV0IGN1cnJlbnRUaWNrSW50ZXJ2YWxNcyA9IHRpY2tJbnRlcnZhbE1zXG4gIGxldCBzdGFydFRpbWUgPSAwXG4gIC8vIFNuYXBzaG90IG9mIHRoZSBjdXJyZW50IHRpY2sncyB0aW1lLCBlbnN1cmluZyBhbGwgc3Vic2NyaWJlcnMgaW4gdGhlIHNhbWVcbiAgLy8gdGljayBzZWUgdGhlIHNhbWUgdmFsdWUgKGtlZXBzIGFuaW1hdGlvbnMgc3luY2hyb25pemVkKVxuICBsZXQgdGlja1RpbWUgPSAwXG5cbiAgZnVuY3Rpb24gdGljaygpOiB2b2lkIHtcbiAgICB0aWNrVGltZSA9IERhdGUubm93KCkgLSBzdGFydFRpbWVcbiAgICBmb3IgKGNvbnN0IG9uQ2hhbmdlIG9mIHN1YnNjcmliZXJzLmtleXMoKSkge1xuICAgICAgb25DaGFuZ2UoKVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHVwZGF0ZUludGVydmFsKCk6IHZvaWQge1xuICAgIGNvbnN0IGFueUtlZXBBbGl2ZSA9IFsuLi5zdWJzY3JpYmVycy52YWx1ZXMoKV0uc29tZShCb29sZWFuKVxuXG4gICAgaWYgKGFueUtlZXBBbGl2ZSkge1xuICAgICAgaWYgKGludGVydmFsKSB7XG4gICAgICAgIGNsZWFySW50ZXJ2YWwoaW50ZXJ2YWwpXG4gICAgICAgIGludGVydmFsID0gbnVsbFxuICAgICAgfVxuICAgICAgaWYgKHN0YXJ0VGltZSA9PT0gMCkge1xuICAgICAgICBzdGFydFRpbWUgPSBEYXRlLm5vdygpXG4gICAgICB9XG4gICAgICBpbnRlcnZhbCA9IHNldEludGVydmFsKHRpY2ssIGN1cnJlbnRUaWNrSW50ZXJ2YWxNcylcbiAgICB9IGVsc2UgaWYgKGludGVydmFsKSB7XG4gICAgICBjbGVhckludGVydmFsKGludGVydmFsKVxuICAgICAgaW50ZXJ2YWwgPSBudWxsXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHtcbiAgICBzdWJzY3JpYmUob25DaGFuZ2UsIGtlZXBBbGl2ZSkge1xuICAgICAgc3Vic2NyaWJlcnMuc2V0KG9uQ2hhbmdlLCBrZWVwQWxpdmUpXG4gICAgICB1cGRhdGVJbnRlcnZhbCgpXG4gICAgICByZXR1cm4gKCkgPT4ge1xuICAgICAgICBzdWJzY3JpYmVycy5kZWxldGUob25DaGFuZ2UpXG4gICAgICAgIHVwZGF0ZUludGVydmFsKClcbiAgICAgIH1cbiAgICB9LFxuXG4gICAgbm93KCkge1xuICAgICAgaWYgKHN0YXJ0VGltZSA9PT0gMCkge1xuICAgICAgICBzdGFydFRpbWUgPSBEYXRlLm5vdygpXG4gICAgICB9XG4gICAgICAvLyBXaGVuIHRoZSBjbG9jayBpbnRlcnZhbCBpcyBydW5uaW5nLCByZXR1cm4gdGhlIHN5bmNocm9uaXplZCB0aWNrVGltZVxuICAgICAgLy8gc28gYWxsIHN1YnNjcmliZXJzIGluIHRoZSBzYW1lIHRpY2sgc2VlIHRoZSBzYW1lIHZhbHVlLlxuICAgICAgLy8gV2hlbiBwYXVzZWQgKG5vIGtlZXBBbGl2ZSBzdWJzY3JpYmVycyksIHJldHVybiByZWFsLXRpbWUgdG8gYXZvaWRcbiAgICAgIC8vIHJldHVybmluZyBhIHN0YWxlIHRpY2tUaW1lIGZyb20gdGhlIGxhc3QgdGljayBiZWZvcmUgdGhlIHBhdXNlLlxuICAgICAgaWYgKGludGVydmFsICYmIHRpY2tUaW1lKSB7XG4gICAgICAgIHJldHVybiB0aWNrVGltZVxuICAgICAgfVxuICAgICAgcmV0dXJuIERhdGUubm93KCkgLSBzdGFydFRpbWVcbiAgICB9LFxuXG4gICAgc2V0VGlja0ludGVydmFsKG1zKSB7XG4gICAgICBpZiAobXMgPT09IGN1cnJlbnRUaWNrSW50ZXJ2YWxNcykgcmV0dXJuXG4gICAgICBjdXJyZW50VGlja0ludGVydmFsTXMgPSBtc1xuICAgICAgdXBkYXRlSW50ZXJ2YWwoKVxuICAgIH0sXG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IENsb2NrQ29udGV4dCA9IGNyZWF0ZUNvbnRleHQ8Q2xvY2sgfCBudWxsPihudWxsKVxuXG5jb25zdCBCTFVSUkVEX1RJQ0tfSU5URVJWQUxfTVMgPSBGUkFNRV9JTlRFUlZBTF9NUyAqIDJcblxuLy8gT3duIGNvbXBvbmVudCBzbyBBcHAudHN4IGRvZXNuJ3QgcmUtcmVuZGVyIHdoZW4gdGhlIGNsb2NrIGlzIGNyZWF0ZWQuXG4vLyBUaGUgY2xvY2sgdmFsdWUgaXMgc3RhYmxlIChjcmVhdGVkIG9uY2UgdmlhIHVzZVN0YXRlKSwgc28gdGhlIHByb3ZpZGVyXG4vLyBuZXZlciBjYXVzZXMgY29uc3VtZXIgcmUtcmVuZGVycyBvbiBpdHMgb3duLlxuZXhwb3J0IGZ1bmN0aW9uIENsb2NrUHJvdmlkZXIoe1xuICBjaGlsZHJlbixcbn06IHtcbiAgY2hpbGRyZW46IFJlYWN0LlJlYWN0Tm9kZVxufSk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtjbG9ja10gPSB1c2VTdGF0ZSgoKSA9PiBjcmVhdGVDbG9jayhGUkFNRV9JTlRFUlZBTF9NUykpXG4gIGNvbnN0IGZvY3VzZWQgPSB1c2VUZXJtaW5hbEZvY3VzKClcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNsb2NrLnNldFRpY2tJbnRlcnZhbChcbiAgICAgIGZvY3VzZWQgPyBGUkFNRV9JTlRFUlZBTF9NUyA6IEJMVVJSRURfVElDS19JTlRFUlZBTF9NUyxcbiAgICApXG4gIH0sIFtjbG9jaywgZm9jdXNlZF0pXG5cbiAgcmV0dXJuIDxDbG9ja0NvbnRleHQuUHJvdmlkZXIgdmFsdWU9e2Nsb2NrfT57Y2hpbGRyZW59PC9DbG9ja0NvbnRleHQuUHJvdmlkZXI+XG59XG4iXSwibWFwcGluZ3MiOiI7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QUtRLHFCQUFxQixFQUFFO01BQ2xDQSxxQkFBcUIsR0FBR1IsRUFBRTtNQUMxQmMsY0FBYyxDQUFDLENBQUM7SUFDbEI7RUFDRixDQUFDO0FBQ0g7QUFFQSxPQUFPLE1BQU1RLFlBQVksR0FBR2pDLGFBQWEsQ0FBQ0ssS0FBSyxHQUFHLElBQUksQ0FBQyxDQUFDLElBQUksQ0FBQztBQUU3RCxNQUFNNkIsd0JBQXdCLEdBQUcvQixpQkFBaUIsR0FBRyxDQUFDOztBQUV0RDtBQUNBO0FBQ0E7QUFDQSxPQUFPLFNBQUFnQyxjQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQXVCO0lBQUFDO0VBQUEsSUFBQUgsRUFJN0I7RUFDQyxPQUFBSSxLQUFBLElBQWdCdEMsUUFBUSxDQUFDdUMsS0FBb0MsQ0FBQztFQUM5RCxNQUFBQyxPQUFBLEdBQWdCdEMsZ0JBQWdCLENBQUMsQ0FBQztFQUFBLElBQUF1QyxFQUFBO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFQLENBQUEsUUFBQUcsS0FBQSxJQUFBSCxDQUFBLFFBQUFLLE9BQUE7SUFFeEJDLEVBQUEsR0FBQUEsQ0FBQTtNQUNSSCxLQUFLLENBQUE5QixlQUFnQixDQUNuQmdDLE9BQU8sR0FBUHZDLGlCQUFzRCxHQUF0RCtCLHdCQUNGLENBQUM7SUFBQSxDQUNGO0lBQUVVLEVBQUEsSUFBQ0osS0FBSyxFQUFFRSxPQUFPLENBQUM7SUFBQUwsQ0FBQSxNQUFBRyxLQUFBO0lBQUFILENBQUEsTUFBQUssT0FBQTtJQUFBTCxDQUFBLE1BQUFNLEVBQUE7SUFBQU4sQ0FBQSxNQUFBTyxFQUFBO0VBQUE7SUFBQUQsRUFBQSxHQUFBTixDQUFBO0lBQUFPLEVBQUEsR0FBQVAsQ0FBQTtFQUFBO0VBSm5CcEMsU0FBUyxDQUFDMEMsRUFJVCxFQUFFQyxFQUFnQixDQUFDO0VBQUEsSUFBQUMsRUFBQTtFQUFBLElBQUFSLENBQUEsUUFBQUUsUUFBQSxJQUFBRixDQUFBLFFBQUFHLEtBQUE7SUFFYkssRUFBQSwwQkFBOEJMLEtBQUssQ0FBTEEsTUFBSSxDQUFDLENBQUdELFNBQU8sQ0FBRSx3QkFBd0I7SUFBQUYsQ0FBQSxNQUFBRSxRQUFBO0lBQUFGLENBQUEsTUFBQUcsS0FBQTtJQUFBSCxDQUFBLE1BQUFRLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFSLENBQUE7RUFBQTtFQUFBLE9BQXZFUSxFQUF1RTtBQUFBO0FBZHpFLFNBQUFKLE1BQUE7RUFBQSxPQUswQjdCLFdBQVcsQ0FBQ1QsaUJBQWlCLENBQUM7QUFBQSIsImlnbm9yZUxpc3QiOltdfQ== \ No newline at end of file diff --git a/src/ink/components/CursorDeclarationContext.ts b/src/ink/components/CursorDeclarationContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..358c80409a6bc2dd808edfc52fe419024846f5d6 --- /dev/null +++ b/src/ink/components/CursorDeclarationContext.ts @@ -0,0 +1,32 @@ +import { createContext } from 'react' +import type { DOMElement } from '../dom.js' + +export type CursorDeclaration = { + /** Display column (terminal cell width) within the declared node */ + readonly relativeX: number + /** Line number within the declared node */ + readonly relativeY: number + /** The ink-box DOMElement whose yoga layout provides the absolute origin */ + readonly node: DOMElement +} + +/** + * Setter for the declared cursor position. + * + * The optional second argument makes `null` a conditional clear: the + * declaration is only cleared if the currently-declared node matches + * `clearIfNode`. This makes the hook safe for sibling components + * (e.g. list items) that transfer focus among themselves — without the + * node check, a newly-unfocused item's clear could clobber a + * newly-focused sibling's set depending on layout-effect order. + */ +export type CursorDeclarationSetter = ( + declaration: CursorDeclaration | null, + clearIfNode?: DOMElement | null, +) => void + +const CursorDeclarationContext = createContext( + () => {}, +) + +export default CursorDeclarationContext diff --git a/src/ink/components/Link.tsx b/src/ink/components/Link.tsx new file mode 100644 index 0000000000000000000000000000000000000000..82341db6489298c975d28e52d3ebe19b8b90eb6e --- /dev/null +++ b/src/ink/components/Link.tsx @@ -0,0 +1,42 @@ +import { c as _c } from "react/compiler-runtime"; +import type { ReactNode } from 'react'; +import React from 'react'; +import { supportsHyperlinks } from '../supports-hyperlinks.js'; +import Text from './Text.js'; +export type Props = { + readonly children?: ReactNode; + readonly url: string; + readonly fallback?: ReactNode; +}; +export default function Link(t0) { + const $ = _c(5); + const { + children, + url, + fallback + } = t0; + const content = children ?? url; + if (supportsHyperlinks()) { + let t1; + if ($[0] !== content || $[1] !== url) { + t1 = {content}; + $[0] = content; + $[1] = url; + $[2] = t1; + } else { + t1 = $[2]; + } + return t1; + } + const t1 = fallback ?? content; + let t2; + if ($[3] !== t1) { + t2 = {t1}; + $[3] = t1; + $[4] = t2; + } else { + t2 = $[4]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdE5vZGUiLCJSZWFjdCIsInN1cHBvcnRzSHlwZXJsaW5rcyIsIlRleHQiLCJQcm9wcyIsImNoaWxkcmVuIiwidXJsIiwiZmFsbGJhY2siLCJMaW5rIiwidDAiLCIkIiwiX2MiLCJjb250ZW50IiwidDEiLCJ0MiJdLCJzb3VyY2VzIjpbIkxpbmsudHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgUmVhY3ROb2RlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgeyBzdXBwb3J0c0h5cGVybGlua3MgfSBmcm9tICcuLi9zdXBwb3J0cy1oeXBlcmxpbmtzLmpzJ1xuaW1wb3J0IFRleHQgZnJvbSAnLi9UZXh0LmpzJ1xuXG5leHBvcnQgdHlwZSBQcm9wcyA9IHtcbiAgcmVhZG9ubHkgY2hpbGRyZW4/OiBSZWFjdE5vZGVcbiAgcmVhZG9ubHkgdXJsOiBzdHJpbmdcbiAgcmVhZG9ubHkgZmFsbGJhY2s/OiBSZWFjdE5vZGVcbn1cblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gTGluayh7XG4gIGNoaWxkcmVuLFxuICB1cmwsXG4gIGZhbGxiYWNrLFxufTogUHJvcHMpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBVc2UgY2hpbGRyZW4gaWYgcHJvdmlkZWQsIG90aGVyd2lzZSBkaXNwbGF5IHRoZSBVUkxcbiAgY29uc3QgY29udGVudCA9IGNoaWxkcmVuID8/IHVybFxuXG4gIGlmIChzdXBwb3J0c0h5cGVybGlua3MoKSkge1xuICAgIC8vIFdyYXAgaW4gVGV4dCB0byBlbnN1cmUgd2UncmUgaW4gYSB0ZXh0IGNvbnRleHRcbiAgICAvLyAoaW5rLWxpbmsgaXMgYSB0ZXh0IGVsZW1lbnQgbGlrZSBpbmstdGV4dClcbiAgICByZXR1cm4gKFxuICAgICAgPFRleHQ+XG4gICAgICAgIDxpbmstbGluayBocmVmPXt1cmx9Pntjb250ZW50fTwvaW5rLWxpbms+XG4gICAgICA8L1RleHQ+XG4gICAgKVxuICB9XG5cbiAgcmV0dXJuIDxUZXh0PntmYWxsYmFjayA/PyBjb250ZW50fTwvVGV4dD5cbn1cbiJdLCJtYXBwaW5ncyI6IjtBQUFBLGNBQWNBLFNBQVMsUUFBUSxPQUFPO0FBQ3RDLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLFNBQVNDLGtCQUFrQixRQUFRLDJCQUEyQjtBQUM5RCxPQUFPQyxJQUFJLE1BQU0sV0FBVztBQUU1QixPQUFPLEtBQUtDLEtBQUssR0FBRztFQUNsQixTQUFTQyxRQUFRLENBQUMsRUFBRUwsU0FBUztFQUM3QixTQUFTTSxHQUFHLEVBQUUsTUFBTTtFQUNwQixTQUFTQyxRQUFRLENBQUMsRUFBRVAsU0FBUztBQUMvQixDQUFDO0FBRUQsZUFBZSxTQUFBUSxLQUFBQyxFQUFBO0VBQUEsTUFBQUMsQ0FBQSxHQUFBQyxFQUFBO0VBQWM7SUFBQU4sUUFBQTtJQUFBQyxHQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFJckI7RUFFTixNQUFBRyxPQUFBLEdBQWdCUCxRQUFlLElBQWZDLEdBQWU7RUFFL0IsSUFBSUosa0JBQWtCLENBQUMsQ0FBQztJQUFBLElBQUFXLEVBQUE7SUFBQSxJQUFBSCxDQUFBLFFBQUFFLE9BQUEsSUFBQUYsQ0FBQSxRQUFBSixHQUFBO01BSXBCTyxFQUFBLElBQUMsSUFBSSxDQUNILFNBQXlDLENBQXpCUCxJQUFHLENBQUhBLElBQUUsQ0FBQyxDQUFHTSxRQUFNLENBQUUsRUFBOUIsUUFBeUMsQ0FDM0MsRUFGQyxJQUFJLENBRUU7TUFBQUYsQ0FBQSxNQUFBRSxPQUFBO01BQUFGLENBQUEsTUFBQUosR0FBQTtNQUFBSSxDQUFBLE1BQUFHLEVBQUE7SUFBQTtNQUFBQSxFQUFBLEdBQUFILENBQUE7SUFBQTtJQUFBLE9BRlBHLEVBRU87RUFBQTtFQUlHLE1BQUFBLEVBQUEsR0FBQU4sUUFBbUIsSUFBbkJLLE9BQW1CO0VBQUEsSUFBQUUsRUFBQTtFQUFBLElBQUFKLENBQUEsUUFBQUcsRUFBQTtJQUExQkMsRUFBQSxJQUFDLElBQUksQ0FBRSxDQUFBRCxFQUFrQixDQUFFLEVBQTFCLElBQUksQ0FBNkI7SUFBQUgsQ0FBQSxNQUFBRyxFQUFBO0lBQUFILENBQUEsTUFBQUksRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUosQ0FBQTtFQUFBO0VBQUEsT0FBbENJLEVBQWtDO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/src/ink/components/Newline.tsx b/src/ink/components/Newline.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5edf61882f36bdda97243879485d57ab25e09136 --- /dev/null +++ b/src/ink/components/Newline.tsx @@ -0,0 +1,39 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +export type Props = { + /** + * Number of newlines to insert. + * + * @default 1 + */ + readonly count?: number; +}; + +/** + * Adds one or more newline (\n) characters. Must be used within components. + */ +export default function Newline(t0) { + const $ = _c(4); + const { + count: t1 + } = t0; + const count = t1 === undefined ? 1 : t1; + let t2; + if ($[0] !== count) { + t2 = "\n".repeat(count); + $[0] = count; + $[1] = t2; + } else { + t2 = $[1]; + } + let t3; + if ($[2] !== t2) { + t3 = {t2}; + $[2] = t2; + $[3] = t3; + } else { + t3 = $[3]; + } + return t3; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzIiwiY291bnQiLCJOZXdsaW5lIiwidDAiLCIkIiwiX2MiLCJ0MSIsInVuZGVmaW5lZCIsInQyIiwicmVwZWF0IiwidDMiXSwic291cmNlcyI6WyJOZXdsaW5lLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5cbmV4cG9ydCB0eXBlIFByb3BzID0ge1xuICAvKipcbiAgICogTnVtYmVyIG9mIG5ld2xpbmVzIHRvIGluc2VydC5cbiAgICpcbiAgICogQGRlZmF1bHQgMVxuICAgKi9cbiAgcmVhZG9ubHkgY291bnQ/OiBudW1iZXJcbn1cblxuLyoqXG4gKiBBZGRzIG9uZSBvciBtb3JlIG5ld2xpbmUgKFxcbikgY2hhcmFjdGVycy4gTXVzdCBiZSB1c2VkIHdpdGhpbiA8VGV4dD4gY29tcG9uZW50cy5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gTmV3bGluZSh7IGNvdW50ID0gMSB9OiBQcm9wcykge1xuICByZXR1cm4gPGluay10ZXh0PnsnXFxuJy5yZXBlYXQoY291bnQpfTwvaW5rLXRleHQ+XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUV6QixPQUFPLEtBQUtDLEtBQUssR0FBRztFQUNsQjtBQUNGO0FBQ0E7QUFDQTtBQUNBO0VBQ0UsU0FBU0MsS0FBSyxDQUFDLEVBQUUsTUFBTTtBQUN6QixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBLGVBQWUsU0FBQUMsUUFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFpQjtJQUFBSixLQUFBLEVBQUFLO0VBQUEsSUFBQUgsRUFBb0I7RUFBbEIsTUFBQUYsS0FBQSxHQUFBSyxFQUFTLEtBQVRDLFNBQVMsR0FBVCxDQUFTLEdBQVRELEVBQVM7RUFBQSxJQUFBRSxFQUFBO0VBQUEsSUFBQUosQ0FBQSxRQUFBSCxLQUFBO0lBQ3ZCTyxFQUFBLE9BQUksQ0FBQUMsTUFBTyxDQUFDUixLQUFLLENBQUM7SUFBQUcsQ0FBQSxNQUFBSCxLQUFBO0lBQUFHLENBQUEsTUFBQUksRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUosQ0FBQTtFQUFBO0VBQUEsSUFBQU0sRUFBQTtFQUFBLElBQUFOLENBQUEsUUFBQUksRUFBQTtJQUE3QkUsRUFBQSxZQUF5QyxDQUE5QixDQUFBRixFQUFpQixDQUFFLEVBQTlCLFFBQXlDO0lBQUFKLENBQUEsTUFBQUksRUFBQTtJQUFBSixDQUFBLE1BQUFNLEVBQUE7RUFBQTtJQUFBQSxFQUFBLEdBQUFOLENBQUE7RUFBQTtFQUFBLE9BQXpDTSxFQUF5QztBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/src/ink/components/RawAnsi.tsx b/src/ink/components/RawAnsi.tsx new file mode 100644 index 0000000000000000000000000000000000000000..919e453b283412a51fcea3a4804e7045ca8c54e3 --- /dev/null +++ b/src/ink/components/RawAnsi.tsx @@ -0,0 +1,57 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +type Props = { + /** + * Pre-rendered ANSI lines. Each element must be exactly one terminal row + * (already wrapped to `width` by the producer) with ANSI escape codes inline. + */ + lines: string[]; + /** Column width the producer wrapped to. Sent to Yoga as the fixed leaf width. */ + width: number; +}; + +/** + * Bypass the → React tree → Yoga → squash → re-serialize roundtrip for + * content that is already terminal-ready. + * + * Use this when an external renderer (e.g. the ColorDiff NAPI module) has + * already produced ANSI-escaped, width-wrapped output. A normal mount + * reparses that output into one React per style span, lays out each + * span as a Yoga flex child, then walks the tree to re-emit the same escape + * codes it was given. For a long transcript full of syntax-highlighted diffs + * that roundtrip is the dominant cost of the render. + * + * This component emits a single Yoga leaf with a constant-time measure func + * (width × lines.length) and hands the joined string straight to output.write(), + * which already splits on '\n' and parses ANSI into the screen buffer. + */ +export function RawAnsi(t0) { + const $ = _c(6); + const { + lines, + width + } = t0; + if (lines.length === 0) { + return null; + } + let t1; + if ($[0] !== lines) { + t1 = lines.join("\n"); + $[0] = lines; + $[1] = t1; + } else { + t1 = $[1]; + } + let t2; + if ($[2] !== lines.length || $[3] !== t1 || $[4] !== width) { + t2 = ; + $[2] = lines.length; + $[3] = t1; + $[4] = width; + $[5] = t2; + } else { + t2 = $[5]; + } + return t2; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzIiwibGluZXMiLCJ3aWR0aCIsIlJhd0Fuc2kiLCJ0MCIsIiQiLCJfYyIsImxlbmd0aCIsInQxIiwiam9pbiIsInQyIl0sInNvdXJjZXMiOlsiUmF3QW5zaS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0IGZyb20gJ3JlYWN0J1xuXG50eXBlIFByb3BzID0ge1xuICAvKipcbiAgICogUHJlLXJlbmRlcmVkIEFOU0kgbGluZXMuIEVhY2ggZWxlbWVudCBtdXN0IGJlIGV4YWN0bHkgb25lIHRlcm1pbmFsIHJvd1xuICAgKiAoYWxyZWFkeSB3cmFwcGVkIHRvIGB3aWR0aGAgYnkgdGhlIHByb2R1Y2VyKSB3aXRoIEFOU0kgZXNjYXBlIGNvZGVzIGlubGluZS5cbiAgICovXG4gIGxpbmVzOiBzdHJpbmdbXVxuICAvKiogQ29sdW1uIHdpZHRoIHRoZSBwcm9kdWNlciB3cmFwcGVkIHRvLiBTZW50IHRvIFlvZ2EgYXMgdGhlIGZpeGVkIGxlYWYgd2lkdGguICovXG4gIHdpZHRoOiBudW1iZXJcbn1cblxuLyoqXG4gKiBCeXBhc3MgdGhlIDxBbnNpPiDihpIgUmVhY3QgdHJlZSDihpIgWW9nYSDihpIgc3F1YXNoIOKGkiByZS1zZXJpYWxpemUgcm91bmR0cmlwIGZvclxuICogY29udGVudCB0aGF0IGlzIGFscmVhZHkgdGVybWluYWwtcmVhZHkuXG4gKlxuICogVXNlIHRoaXMgd2hlbiBhbiBleHRlcm5hbCByZW5kZXJlciAoZS5nLiB0aGUgQ29sb3JEaWZmIE5BUEkgbW9kdWxlKSBoYXNcbiAqIGFscmVhZHkgcHJvZHVjZWQgQU5TSS1lc2NhcGVkLCB3aWR0aC13cmFwcGVkIG91dHB1dC4gQSBub3JtYWwgPEFuc2k+IG1vdW50XG4gKiByZXBhcnNlcyB0aGF0IG91dHB1dCBpbnRvIG9uZSBSZWFjdCA8VGV4dD4gcGVyIHN0eWxlIHNwYW4sIGxheXMgb3V0IGVhY2hcbiAqIHNwYW4gYXMgYSBZb2dhIGZsZXggY2hpbGQsIHRoZW4gd2Fsa3MgdGhlIHRyZWUgdG8gcmUtZW1pdCB0aGUgc2FtZSBlc2NhcGVcbiAqIGNvZGVzIGl0IHdhcyBnaXZlbi4gRm9yIGEgbG9uZyB0cmFuc2NyaXB0IGZ1bGwgb2Ygc3ludGF4LWhpZ2hsaWdodGVkIGRpZmZzXG4gKiB0aGF0IHJvdW5kdHJpcCBpcyB0aGUgZG9taW5hbnQgY29zdCBvZiB0aGUgcmVuZGVyLlxuICpcbiAqIFRoaXMgY29tcG9uZW50IGVtaXRzIGEgc2luZ2xlIFlvZ2EgbGVhZiB3aXRoIGEgY29uc3RhbnQtdGltZSBtZWFzdXJlIGZ1bmNcbiAqICh3aWR0aCDDlyBsaW5lcy5sZW5ndGgpIGFuZCBoYW5kcyB0aGUgam9pbmVkIHN0cmluZyBzdHJhaWdodCB0byBvdXRwdXQud3JpdGUoKSxcbiAqIHdoaWNoIGFscmVhZHkgc3BsaXRzIG9uICdcXG4nIGFuZCBwYXJzZXMgQU5TSSBpbnRvIHRoZSBzY3JlZW4gYnVmZmVyLlxuICovXG5leHBvcnQgZnVuY3Rpb24gUmF3QW5zaSh7IGxpbmVzLCB3aWR0aCB9OiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGlmIChsaW5lcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG4gIHJldHVybiAoXG4gICAgPGluay1yYXctYW5zaVxuICAgICAgcmF3VGV4dD17bGluZXMuam9pbignXFxuJyl9XG4gICAgICByYXdXaWR0aD17d2lkdGh9XG4gICAgICByYXdIZWlnaHQ9e2xpbmVzLmxlbmd0aH1cbiAgICAvPlxuICApXG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUV6QixLQUFLQyxLQUFLLEdBQUc7RUFDWDtBQUNGO0FBQ0E7QUFDQTtFQUNFQyxLQUFLLEVBQUUsTUFBTSxFQUFFO0VBQ2Y7RUFDQUMsS0FBSyxFQUFFLE1BQU07QUFDZixDQUFDOztBQUVEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU8sU0FBQUMsUUFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUFpQjtJQUFBTCxLQUFBO0lBQUFDO0VBQUEsSUFBQUUsRUFBdUI7RUFDN0MsSUFBSUgsS0FBSyxDQUFBTSxNQUFPLEtBQUssQ0FBQztJQUFBLE9BQ2IsSUFBSTtFQUFBO0VBQ1osSUFBQUMsRUFBQTtFQUFBLElBQUFILENBQUEsUUFBQUosS0FBQTtJQUdZTyxFQUFBLEdBQUFQLEtBQUssQ0FBQVEsSUFBSyxDQUFDLElBQUksQ0FBQztJQUFBSixDQUFBLE1BQUFKLEtBQUE7SUFBQUksQ0FBQSxNQUFBRyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBSCxDQUFBO0VBQUE7RUFBQSxJQUFBSyxFQUFBO0VBQUEsSUFBQUwsQ0FBQSxRQUFBSixLQUFBLENBQUFNLE1BQUEsSUFBQUYsQ0FBQSxRQUFBRyxFQUFBLElBQUFILENBQUEsUUFBQUgsS0FBQTtJQUQzQlEsRUFBQSxnQkFJRSxDQUhTLE9BQWdCLENBQWhCLENBQUFGLEVBQWUsQ0FBQyxDQUNmTixRQUFLLENBQUxBLE1BQUksQ0FBQyxDQUNKLFNBQVksQ0FBWixDQUFBRCxLQUFLLENBQUFNLE1BQU0sQ0FBQyxHQUN2QjtJQUFBRixDQUFBLE1BQUFKLEtBQUEsQ0FBQU0sTUFBQTtJQUFBRixDQUFBLE1BQUFHLEVBQUE7SUFBQUgsQ0FBQSxNQUFBSCxLQUFBO0lBQUFHLENBQUEsTUFBQUssRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUwsQ0FBQTtFQUFBO0VBQUEsT0FKRkssRUFJRTtBQUFBIiwiaWdub3JlTGlzdCI6W119 \ No newline at end of file diff --git a/src/ink/components/ScrollBox.tsx b/src/ink/components/ScrollBox.tsx new file mode 100644 index 0000000000000000000000000000000000000000..03e4a31cb1dab9818a259f89aa46cd83d6c9309b --- /dev/null +++ b/src/ink/components/ScrollBox.tsx @@ -0,0 +1,237 @@ +import React, { type PropsWithChildren, type Ref, useImperativeHandle, useRef, useState } from 'react'; +import type { Except } from 'type-fest'; +import { markScrollActivity } from '../../bootstrap/state.js'; +import type { DOMElement } from '../dom.js'; +import { markDirty, scheduleRenderFrom } from '../dom.js'; +import { markCommitStart } from '../reconciler.js'; +import type { Styles } from '../styles.js'; +import '../global.d.ts'; +import Box from './Box.js'; +export type ScrollBoxHandle = { + scrollTo: (y: number) => void; + scrollBy: (dy: number) => void; + /** + * Scroll so `el`'s top is at the viewport top (plus `offset`). Unlike + * scrollTo which bakes a number that's stale by the time the throttled + * render fires, this defers the position read to render time — + * render-node-to-output reads `el.yogaNode.getComputedTop()` in the + * SAME Yoga pass that computes scrollHeight. Deterministic. One-shot. + */ + scrollToElement: (el: DOMElement, offset?: number) => void; + scrollToBottom: () => void; + getScrollTop: () => number; + getPendingDelta: () => number; + getScrollHeight: () => number; + /** + * Like getScrollHeight, but reads Yoga directly instead of the cached + * value written by render-node-to-output (throttled, up to 16ms stale). + * Use when you need a fresh value in useLayoutEffect after a React commit + * that grew content. Slightly more expensive (native Yoga call). + */ + getFreshScrollHeight: () => number; + getViewportHeight: () => number; + /** + * Absolute screen-buffer row of the first visible content line (inside + * padding). Used for drag-to-scroll edge detection. + */ + getViewportTop: () => number; + /** + * True when scroll is pinned to the bottom. Set by scrollToBottom, the + * initial stickyScroll attribute, and by the renderer when positional + * follow fires (scrollTop at prevMax, content grows). Cleared by + * scrollTo/scrollBy. Stable signal for "at bottom" that doesn't depend on + * layout values (unlike scrollTop+viewportH >= scrollHeight). + */ + isSticky: () => boolean; + /** + * Subscribe to imperative scroll changes (scrollTo/scrollBy/scrollToBottom). + * Does NOT fire for stickyScroll updates done by the Ink renderer — those + * happen during Ink's render phase after React has committed. Callers that + * care about the sticky case should treat "at bottom" as a fallback. + */ + subscribe: (listener: () => void) => () => void; + /** + * Set the render-time scrollTop clamp to the currently-mounted children's + * coverage span. Called by useVirtualScroll after computing its range; + * render-node-to-output clamps scrollTop to [min, max] so burst scrollTo + * calls that race past React's async re-render show the edge of mounted + * content instead of blank spacer. Pass undefined to disable (sticky, + * cold start). + */ + setClampBounds: (min: number | undefined, max: number | undefined) => void; +}; +export type ScrollBoxProps = Except & { + ref?: Ref; + /** + * When true, automatically pins scroll position to the bottom when content + * grows. Unset manually via scrollTo/scrollBy to break the stickiness. + */ + stickyScroll?: boolean; +}; + +/** + * A Box with `overflow: scroll` and an imperative scroll API. + * + * Children are laid out at their full Yoga-computed height inside a + * constrained container. At render time, only children intersecting the + * visible window (scrollTop..scrollTop+height) are rendered (viewport + * culling). Content is translated by -scrollTop and clipped to the box bounds. + * + * Works best inside a fullscreen (constrained-height root) Ink tree. + */ +function ScrollBox({ + children, + ref, + stickyScroll, + ...style +}: PropsWithChildren): React.ReactNode { + const domRef = useRef(null); + // scrollTo/scrollBy bypass React: they mutate scrollTop on the DOM node, + // mark it dirty, and call the root's throttled scheduleRender directly. + // The Ink renderer reads scrollTop from the node — no React state needed, + // no reconciler overhead per wheel event. The microtask defer coalesces + // multiple scrollBy calls in one input batch (discreteUpdates) into one + // render — otherwise scheduleRender's leading edge fires on the FIRST + // event before subsequent events mutate scrollTop. scrollToBottom still + // forces a React render: sticky is attribute-observed, no DOM-only path. + const [, forceRender] = useState(0); + const listenersRef = useRef(new Set<() => void>()); + const renderQueuedRef = useRef(false); + const notify = () => { + for (const l of listenersRef.current) l(); + }; + function scrollMutated(el: DOMElement): void { + // Signal background intervals (IDE poll, LSP poll, GCS fetch, orphan + // check) to skip their next tick — they compete for the event loop and + // contributed to 1402ms max frame gaps during scroll drain. + markScrollActivity(); + markDirty(el); + markCommitStart(); + notify(); + if (renderQueuedRef.current) return; + renderQueuedRef.current = true; + queueMicrotask(() => { + renderQueuedRef.current = false; + scheduleRenderFrom(el); + }); + } + useImperativeHandle(ref, (): ScrollBoxHandle => ({ + scrollTo(y: number) { + const el = domRef.current; + if (!el) return; + // Explicit false overrides the DOM attribute so manual scroll + // breaks stickiness. Render code checks ?? precedence. + el.stickyScroll = false; + el.pendingScrollDelta = undefined; + el.scrollAnchor = undefined; + el.scrollTop = Math.max(0, Math.floor(y)); + scrollMutated(el); + }, + scrollToElement(el: DOMElement, offset = 0) { + const box = domRef.current; + if (!box) return; + box.stickyScroll = false; + box.pendingScrollDelta = undefined; + box.scrollAnchor = { + el, + offset + }; + scrollMutated(box); + }, + scrollBy(dy: number) { + const el = domRef.current; + if (!el) return; + el.stickyScroll = false; + // Wheel input cancels any in-flight anchor seek — user override. + el.scrollAnchor = undefined; + // Accumulate in pendingScrollDelta; renderer drains it at a capped + // rate so fast flicks show intermediate frames. Pure accumulator: + // scroll-up followed by scroll-down naturally cancels. + el.pendingScrollDelta = (el.pendingScrollDelta ?? 0) + Math.floor(dy); + scrollMutated(el); + }, + scrollToBottom() { + const el = domRef.current; + if (!el) return; + el.pendingScrollDelta = undefined; + el.stickyScroll = true; + markDirty(el); + notify(); + forceRender(n => n + 1); + }, + getScrollTop() { + return domRef.current?.scrollTop ?? 0; + }, + getPendingDelta() { + // Accumulated-but-not-yet-drained delta. useVirtualScroll needs + // this to mount the union [committed, committed+pending] range — + // otherwise intermediate drain frames find no children (blank). + return domRef.current?.pendingScrollDelta ?? 0; + }, + getScrollHeight() { + return domRef.current?.scrollHeight ?? 0; + }, + getFreshScrollHeight() { + const content = domRef.current?.childNodes[0] as DOMElement | undefined; + return content?.yogaNode?.getComputedHeight() ?? domRef.current?.scrollHeight ?? 0; + }, + getViewportHeight() { + return domRef.current?.scrollViewportHeight ?? 0; + }, + getViewportTop() { + return domRef.current?.scrollViewportTop ?? 0; + }, + isSticky() { + const el = domRef.current; + if (!el) return false; + return el.stickyScroll ?? Boolean(el.attributes['stickyScroll']); + }, + subscribe(listener: () => void) { + listenersRef.current.add(listener); + return () => listenersRef.current.delete(listener); + }, + setClampBounds(min, max) { + const el = domRef.current; + if (!el) return; + el.scrollClampMin = min; + el.scrollClampMax = max; + } + }), + // notify/scrollMutated are inline (no useCallback) but only close over + // refs + imports — stable. Empty deps avoids rebuilding the handle on + // every render (which re-registers the ref = churn). + // eslint-disable-next-line react-hooks/exhaustive-deps + []); + + // Structure: outer viewport (overflow:scroll, constrained height) > + // inner content (flexGrow:1, flexShrink:0 — fills at least the viewport + // but grows beyond it for tall content). flexGrow:1 lets children use + // spacers to pin elements to the bottom of the scroll area. Yoga's + // Overflow.Scroll prevents the viewport from growing to fit the content. + // The renderer computes scrollHeight from the content box and culls + // content's children based on scrollTop. + // + // stickyScroll is passed as a DOM attribute (via ink-box directly) so it's + // available on the first render — ref callbacks fire after the initial + // commit, which is too late for the first frame. + return { + domRef.current = el; + if (el) el.scrollTop ??= 0; + }} style={{ + flexWrap: 'nowrap', + flexDirection: style.flexDirection ?? 'row', + flexGrow: style.flexGrow ?? 0, + flexShrink: style.flexShrink ?? 1, + ...style, + overflowX: 'scroll', + overflowY: 'scroll' + }} {...stickyScroll ? { + stickyScroll: true + } : {}}> + + {children} + + ; +} +export default ScrollBox; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlByb3BzV2l0aENoaWxkcmVuIiwiUmVmIiwidXNlSW1wZXJhdGl2ZUhhbmRsZSIsInVzZVJlZiIsInVzZVN0YXRlIiwiRXhjZXB0IiwibWFya1Njcm9sbEFjdGl2aXR5IiwiRE9NRWxlbWVudCIsIm1hcmtEaXJ0eSIsInNjaGVkdWxlUmVuZGVyRnJvbSIsIm1hcmtDb21taXRTdGFydCIsIlN0eWxlcyIsIkJveCIsIlNjcm9sbEJveEhhbmRsZSIsInNjcm9sbFRvIiwieSIsInNjcm9sbEJ5IiwiZHkiLCJzY3JvbGxUb0VsZW1lbnQiLCJlbCIsIm9mZnNldCIsInNjcm9sbFRvQm90dG9tIiwiZ2V0U2Nyb2xsVG9wIiwiZ2V0UGVuZGluZ0RlbHRhIiwiZ2V0U2Nyb2xsSGVpZ2h0IiwiZ2V0RnJlc2hTY3JvbGxIZWlnaHQiLCJnZXRWaWV3cG9ydEhlaWdodCIsImdldFZpZXdwb3J0VG9wIiwiaXNTdGlja3kiLCJzdWJzY3JpYmUiLCJsaXN0ZW5lciIsInNldENsYW1wQm91bmRzIiwibWluIiwibWF4IiwiU2Nyb2xsQm94UHJvcHMiLCJyZWYiLCJzdGlja3lTY3JvbGwiLCJTY3JvbGxCb3giLCJjaGlsZHJlbiIsInN0eWxlIiwiUmVhY3ROb2RlIiwiZG9tUmVmIiwiZm9yY2VSZW5kZXIiLCJsaXN0ZW5lcnNSZWYiLCJTZXQiLCJyZW5kZXJRdWV1ZWRSZWYiLCJub3RpZnkiLCJsIiwiY3VycmVudCIsInNjcm9sbE11dGF0ZWQiLCJxdWV1ZU1pY3JvdGFzayIsInBlbmRpbmdTY3JvbGxEZWx0YSIsInVuZGVmaW5lZCIsInNjcm9sbEFuY2hvciIsInNjcm9sbFRvcCIsIk1hdGgiLCJmbG9vciIsImJveCIsIm4iLCJzY3JvbGxIZWlnaHQiLCJjb250ZW50IiwiY2hpbGROb2RlcyIsInlvZ2FOb2RlIiwiZ2V0Q29tcHV0ZWRIZWlnaHQiLCJzY3JvbGxWaWV3cG9ydEhlaWdodCIsInNjcm9sbFZpZXdwb3J0VG9wIiwiQm9vbGVhbiIsImF0dHJpYnV0ZXMiLCJhZGQiLCJkZWxldGUiLCJzY3JvbGxDbGFtcE1pbiIsInNjcm9sbENsYW1wTWF4IiwiZmxleFdyYXAiLCJmbGV4RGlyZWN0aW9uIiwiZmxleEdyb3ciLCJmbGV4U2hyaW5rIiwib3ZlcmZsb3dYIiwib3ZlcmZsb3dZIl0sInNvdXJjZXMiOlsiU2Nyb2xsQm94LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QsIHtcbiAgdHlwZSBQcm9wc1dpdGhDaGlsZHJlbixcbiAgdHlwZSBSZWYsXG4gIHVzZUltcGVyYXRpdmVIYW5kbGUsXG4gIHVzZVJlZixcbiAgdXNlU3RhdGUsXG59IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBFeGNlcHQgfSBmcm9tICd0eXBlLWZlc3QnXG5pbXBvcnQgeyBtYXJrU2Nyb2xsQWN0aXZpdHkgfSBmcm9tICcuLi8uLi9ib290c3RyYXAvc3RhdGUuanMnXG5pbXBvcnQgdHlwZSB7IERPTUVsZW1lbnQgfSBmcm9tICcuLi9kb20uanMnXG5pbXBvcnQgeyBtYXJrRGlydHksIHNjaGVkdWxlUmVuZGVyRnJvbSB9IGZyb20gJy4uL2RvbS5qcydcbmltcG9ydCB7IG1hcmtDb21taXRTdGFydCB9IGZyb20gJy4uL3JlY29uY2lsZXIuanMnXG5pbXBvcnQgdHlwZSB7IFN0eWxlcyB9IGZyb20gJy4uL3N0eWxlcy5qcydcbmltcG9ydCAnLi4vZ2xvYmFsLmQudHMnXG5pbXBvcnQgQm94IGZyb20gJy4vQm94LmpzJ1xuXG5leHBvcnQgdHlwZSBTY3JvbGxCb3hIYW5kbGUgPSB7XG4gIHNjcm9sbFRvOiAoeTogbnVtYmVyKSA9PiB2b2lkXG4gIHNjcm9sbEJ5OiAoZHk6IG51bWJlcikgPT4gdm9pZFxuICAvKipcbiAgICogU2Nyb2xsIHNvIGBlbGAncyB0b3AgaXMgYXQgdGhlIHZpZXdwb3J0IHRvcCAocGx1cyBgb2Zmc2V0YCkuIFVubGlrZVxuICAgKiBzY3JvbGxUbyB3aGljaCBiYWtlcyBhIG51bWJlciB0aGF0J3Mgc3RhbGUgYnkgdGhlIHRpbWUgdGhlIHRocm90dGxlZFxuICAgKiByZW5kZXIgZmlyZXMsIHRoaXMgZGVmZXJzIHRoZSBwb3NpdGlvbiByZWFkIHRvIHJlbmRlciB0aW1lIOKAlFxuICAgKiByZW5kZXItbm9kZS10by1vdXRwdXQgcmVhZHMgYGVsLnlvZ2FOb2RlLmdldENvbXB1dGVkVG9wKClgIGluIHRoZVxuICAgKiBTQU1FIFlvZ2EgcGFzcyB0aGF0IGNvbXB1dGVzIHNjcm9sbEhlaWdodC4gRGV0ZXJtaW5pc3RpYy4gT25lLXNob3QuXG4gICAqL1xuICBzY3JvbGxUb0VsZW1lbnQ6IChlbDogRE9NRWxlbWVudCwgb2Zmc2V0PzogbnVtYmVyKSA9PiB2b2lkXG4gIHNjcm9sbFRvQm90dG9tOiAoKSA9PiB2b2lkXG4gIGdldFNjcm9sbFRvcDogKCkgPT4gbnVtYmVyXG4gIGdldFBlbmRpbmdEZWx0YTogKCkgPT4gbnVtYmVyXG4gIGdldFNjcm9sbEhlaWdodDogKCkgPT4gbnVtYmVyXG4gIC8qKlxuICAgKiBMaWtlIGdldFNjcm9sbEhlaWdodCwgYnV0IHJlYWRzIFlvZ2EgZGlyZWN0bHkgaW5zdGVhZCBvZiB0aGUgY2FjaGVkXG4gICAqIHZhbHVlIHdyaXR0ZW4gYnkgcmVuZGVyLW5vZGUtdG8tb3V0cHV0ICh0aHJvdHRsZWQsIHVwIHRvIDE2bXMgc3RhbGUpLlxuICAgKiBVc2Ugd2hlbiB5b3UgbmVlZCBhIGZyZXNoIHZhbHVlIGluIHVzZUxheW91dEVmZmVjdCBhZnRlciBhIFJlYWN0IGNvbW1pdFxuICAgKiB0aGF0IGdyZXcgY29udGVudC4gU2xpZ2h0bHkgbW9yZSBleHBlbnNpdmUgKG5hdGl2ZSBZb2dhIGNhbGwpLlxuICAgKi9cbiAgZ2V0RnJlc2hTY3JvbGxIZWlnaHQ6ICgpID0+IG51bWJlclxuICBnZXRWaWV3cG9ydEhlaWdodDogKCkgPT4gbnVtYmVyXG4gIC8qKlxuICAgKiBBYnNvbHV0ZSBzY3JlZW4tYnVmZmVyIHJvdyBvZiB0aGUgZmlyc3QgdmlzaWJsZSBjb250ZW50IGxpbmUgKGluc2lkZVxuICAgKiBwYWRkaW5nKS4gVXNlZCBmb3IgZHJhZy10by1zY3JvbGwgZWRnZSBkZXRlY3Rpb24uXG4gICAqL1xuICBnZXRWaWV3cG9ydFRvcDogKCkgPT4gbnVtYmVyXG4gIC8qKlxuICAgKiBUcnVlIHdoZW4gc2Nyb2xsIGlzIHBpbm5lZCB0byB0aGUgYm90dG9tLiBTZXQgYnkgc2Nyb2xsVG9Cb3R0b20sIHRoZVxuICAgKiBpbml0aWFsIHN0aWNreVNjcm9sbCBhdHRyaWJ1dGUsIGFuZCBieSB0aGUgcmVuZGVyZXIgd2hlbiBwb3NpdGlvbmFsXG4gICAqIGZvbGxvdyBmaXJlcyAoc2Nyb2xsVG9wIGF0IHByZXZNYXgsIGNvbnRlbnQgZ3Jvd3MpLiBDbGVhcmVkIGJ5XG4gICAqIHNjcm9sbFRvL3Njcm9sbEJ5LiBTdGFibGUgc2lnbmFsIGZvciBcImF0IGJvdHRvbVwiIHRoYXQgZG9lc24ndCBkZXBlbmQgb25cbiAgICogbGF5b3V0IHZhbHVlcyAodW5saWtlIHNjcm9sbFRvcCt2aWV3cG9ydEggPj0gc2Nyb2xsSGVpZ2h0KS5cbiAgICovXG4gIGlzU3RpY2t5OiAoKSA9PiBib29sZWFuXG4gIC8qKlxuICAgKiBTdWJzY3JpYmUgdG8gaW1wZXJhdGl2ZSBzY3JvbGwgY2hhbmdlcyAoc2Nyb2xsVG8vc2Nyb2xsQnkvc2Nyb2xsVG9Cb3R0b20pLlxuICAgKiBEb2VzIE5PVCBmaXJlIGZvciBzdGlja3lTY3JvbGwgdXBkYXRlcyBkb25lIGJ5IHRoZSBJbmsgcmVuZGVyZXIg4oCUIHRob3NlXG4gICAqIGhhcHBlbiBkdXJpbmcgSW5rJ3MgcmVuZGVyIHBoYXNlIGFmdGVyIFJlYWN0IGhhcyBjb21taXR0ZWQuIENhbGxlcnMgdGhhdFxuICAgKiBjYXJlIGFib3V0IHRoZSBzdGlja3kgY2FzZSBzaG91bGQgdHJlYXQgXCJhdCBib3R0b21cIiBhcyBhIGZhbGxiYWNrLlxuICAgKi9cbiAgc3Vic2NyaWJlOiAobGlzdGVuZXI6ICgpID0+IHZvaWQpID0+ICgpID0+IHZvaWRcbiAgLyoqXG4gICAqIFNldCB0aGUgcmVuZGVyLXRpbWUgc2Nyb2xsVG9wIGNsYW1wIHRvIHRoZSBjdXJyZW50bHktbW91bnRlZCBjaGlsZHJlbidzXG4gICAqIGNvdmVyYWdlIHNwYW4uIENhbGxlZCBieSB1c2VWaXJ0dWFsU2Nyb2xsIGFmdGVyIGNvbXB1dGluZyBpdHMgcmFuZ2U7XG4gICAqIHJlbmRlci1ub2RlLXRvLW91dHB1dCBjbGFtcHMgc2Nyb2xsVG9wIHRvIFttaW4sIG1heF0gc28gYnVyc3Qgc2Nyb2xsVG9cbiAgICogY2FsbHMgdGhhdCByYWNlIHBhc3QgUmVhY3QncyBhc3luYyByZS1yZW5kZXIgc2hvdyB0aGUgZWRnZSBvZiBtb3VudGVkXG4gICAqIGNvbnRlbnQgaW5zdGVhZCBvZiBibGFuayBzcGFjZXIuIFBhc3MgdW5kZWZpbmVkIHRvIGRpc2FibGUgKHN0aWNreSxcbiAgICogY29sZCBzdGFydCkuXG4gICAqL1xuICBzZXRDbGFtcEJvdW5kczogKG1pbjogbnVtYmVyIHwgdW5kZWZpbmVkLCBtYXg6IG51bWJlciB8IHVuZGVmaW5lZCkgPT4gdm9pZFxufVxuXG5leHBvcnQgdHlwZSBTY3JvbGxCb3hQcm9wcyA9IEV4Y2VwdDxcbiAgU3R5bGVzLFxuICAndGV4dFdyYXAnIHwgJ292ZXJmbG93JyB8ICdvdmVyZmxvd1gnIHwgJ292ZXJmbG93WSdcbj4gJiB7XG4gIHJlZj86IFJlZjxTY3JvbGxCb3hIYW5kbGU+XG4gIC8qKlxuICAgKiBXaGVuIHRydWUsIGF1dG9tYXRpY2FsbHkgcGlucyBzY3JvbGwgcG9zaXRpb24gdG8gdGhlIGJvdHRvbSB3aGVuIGNvbnRlbnRcbiAgICogZ3Jvd3MuIFVuc2V0IG1hbnVhbGx5IHZpYSBzY3JvbGxUby9zY3JvbGxCeSB0byBicmVhayB0aGUgc3RpY2tpbmVzcy5cbiAgICovXG4gIHN0aWNreVNjcm9sbD86IGJvb2xlYW5cbn1cblxuLyoqXG4gKiBBIEJveCB3aXRoIGBvdmVyZmxvdzogc2Nyb2xsYCBhbmQgYW4gaW1wZXJhdGl2ZSBzY3JvbGwgQVBJLlxuICpcbiAqIENoaWxkcmVuIGFyZSBsYWlkIG91dCBhdCB0aGVpciBmdWxsIFlvZ2EtY29tcHV0ZWQgaGVpZ2h0IGluc2lkZSBhXG4gKiBjb25zdHJhaW5lZCBjb250YWluZXIuIEF0IHJlbmRlciB0aW1lLCBvbmx5IGNoaWxkcmVuIGludGVyc2VjdGluZyB0aGVcbiAqIHZpc2libGUgd2luZG93IChzY3JvbGxUb3AuLnNjcm9sbFRvcCtoZWlnaHQpIGFyZSByZW5kZXJlZCAodmlld3BvcnRcbiAqIGN1bGxpbmcpLiBDb250ZW50IGlzIHRyYW5zbGF0ZWQgYnkgLXNjcm9sbFRvcCBhbmQgY2xpcHBlZCB0byB0aGUgYm94IGJvdW5kcy5cbiAqXG4gKiBXb3JrcyBiZXN0IGluc2lkZSBhIGZ1bGxzY3JlZW4gKGNvbnN0cmFpbmVkLWhlaWdodCByb290KSBJbmsgdHJlZS5cbiAqL1xuZnVuY3Rpb24gU2Nyb2xsQm94KHtcbiAgY2hpbGRyZW4sXG4gIHJlZixcbiAgc3RpY2t5U2Nyb2xsLFxuICAuLi5zdHlsZVxufTogUHJvcHNXaXRoQ2hpbGRyZW48U2Nyb2xsQm94UHJvcHM+KTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgY29uc3QgZG9tUmVmID0gdXNlUmVmPERPTUVsZW1lbnQ+KG51bGwpXG4gIC8vIHNjcm9sbFRvL3Njcm9sbEJ5IGJ5cGFzcyBSZWFjdDogdGhleSBtdXRhdGUgc2Nyb2xsVG9wIG9uIHRoZSBET00gbm9kZSxcbiAgLy8gbWFyayBpdCBkaXJ0eSwgYW5kIGNhbGwgdGhlIHJvb3QncyB0aHJvdHRsZWQgc2NoZWR1bGVSZW5kZXIgZGlyZWN0bHkuXG4gIC8vIFRoZSBJbmsgcmVuZGVyZXIgcmVhZHMgc2Nyb2xsVG9wIGZyb20gdGhlIG5vZGUg4oCUIG5vIFJlYWN0IHN0YXRlIG5lZWRlZCxcbiAgLy8gbm8gcmVjb25jaWxlciBvdmVyaGVhZCBwZXIgd2hlZWwgZXZlbnQuIFRoZSBtaWNyb3Rhc2sgZGVmZXIgY29hbGVzY2VzXG4gIC8vIG11bHRpcGxlIHNjcm9sbEJ5IGNhbGxzIGluIG9uZSBpbnB1dCBiYXRjaCAoZGlzY3JldGVVcGRhdGVzKSBpbnRvIG9uZVxuICAvLyByZW5kZXIg4oCUIG90aGVyd2lzZSBzY2hlZHVsZVJlbmRlcidzIGxlYWRpbmcgZWRnZSBmaXJlcyBvbiB0aGUgRklSU1RcbiAgLy8gZXZlbnQgYmVmb3JlIHN1YnNlcXVlbnQgZXZlbnRzIG11dGF0ZSBzY3JvbGxUb3AuIHNjcm9sbFRvQm90dG9tIHN0aWxsXG4gIC8vIGZvcmNlcyBhIFJlYWN0IHJlbmRlcjogc3RpY2t5IGlzIGF0dHJpYnV0ZS1vYnNlcnZlZCwgbm8gRE9NLW9ubHkgcGF0aC5cbiAgY29uc3QgWywgZm9yY2VSZW5kZXJdID0gdXNlU3RhdGUoMClcbiAgY29uc3QgbGlzdGVuZXJzUmVmID0gdXNlUmVmKG5ldyBTZXQ8KCkgPT4gdm9pZD4oKSlcbiAgY29uc3QgcmVuZGVyUXVldWVkUmVmID0gdXNlUmVmKGZhbHNlKVxuXG4gIGNvbnN0IG5vdGlmeSA9ICgpID0+IHtcbiAgICBmb3IgKGNvbnN0IGwgb2YgbGlzdGVuZXJzUmVmLmN1cnJlbnQpIGwoKVxuICB9XG5cbiAgZnVuY3Rpb24gc2Nyb2xsTXV0YXRlZChlbDogRE9NRWxlbWVudCk6IHZvaWQge1xuICAgIC8vIFNpZ25hbCBiYWNrZ3JvdW5kIGludGVydmFscyAoSURFIHBvbGwsIExTUCBwb2xsLCBHQ1MgZmV0Y2gsIG9ycGhhblxuICAgIC8vIGNoZWNrKSB0byBza2lwIHRoZWlyIG5leHQgdGljayDigJQgdGhleSBjb21wZXRlIGZvciB0aGUgZXZlbnQgbG9vcCBhbmRcbiAgICAvLyBjb250cmlidXRlZCB0byAxNDAybXMgbWF4IGZyYW1lIGdhcHMgZHVyaW5nIHNjcm9sbCBkcmFpbi5cbiAgICBtYXJrU2Nyb2xsQWN0aXZpdHkoKVxuICAgIG1hcmtEaXJ0eShlbClcbiAgICBtYXJrQ29tbWl0U3RhcnQoKVxuICAgIG5vdGlmeSgpXG4gICAgaWYgKHJlbmRlclF1ZXVlZFJlZi5jdXJyZW50KSByZXR1cm5cbiAgICByZW5kZXJRdWV1ZWRSZWYuY3VycmVudCA9IHRydWVcbiAgICBxdWV1ZU1pY3JvdGFzaygoKSA9PiB7XG4gICAgICByZW5kZXJRdWV1ZWRSZWYuY3VycmVudCA9IGZhbHNlXG4gICAgICBzY2hlZHVsZVJlbmRlckZyb20oZWwpXG4gICAgfSlcbiAgfVxuXG4gIHVzZUltcGVyYXRpdmVIYW5kbGUoXG4gICAgcmVmLFxuICAgICgpOiBTY3JvbGxCb3hIYW5kbGUgPT4gKHtcbiAgICAgIHNjcm9sbFRvKHk6IG51bWJlcikge1xuICAgICAgICBjb25zdCBlbCA9IGRvbVJlZi5jdXJyZW50XG4gICAgICAgIGlmICghZWwpIHJldHVyblxuICAgICAgICAvLyBFeHBsaWNpdCBmYWxzZSBvdmVycmlkZXMgdGhlIERPTSBhdHRyaWJ1dGUgc28gbWFudWFsIHNjcm9sbFxuICAgICAgICAvLyBicmVha3Mgc3RpY2tpbmVzcy4gUmVuZGVyIGNvZGUgY2hlY2tzID8/IHByZWNlZGVuY2UuXG4gICAgICAgIGVsLnN0aWNreVNjcm9sbCA9IGZhbHNlXG4gICAgICAgIGVsLnBlbmRpbmdTY3JvbGxEZWx0YSA9IHVuZGVmaW5lZFxuICAgICAgICBlbC5zY3JvbGxBbmNob3IgPSB1bmRlZmluZWRcbiAgICAgICAgZWwuc2Nyb2xsVG9wID0gTWF0aC5tYXgoMCwgTWF0aC5mbG9vcih5KSlcbiAgICAgICAgc2Nyb2xsTXV0YXRlZChlbClcbiAgICAgIH0sXG4gICAgICBzY3JvbGxUb0VsZW1lbnQoZWw6IERPTUVsZW1lbnQsIG9mZnNldCA9IDApIHtcbiAgICAgICAgY29uc3QgYm94ID0gZG9tUmVmLmN1cnJlbnRcbiAgICAgICAgaWYgKCFib3gpIHJldHVyblxuICAgICAgICBib3guc3RpY2t5U2Nyb2xsID0gZmFsc2VcbiAgICAgICAgYm94LnBlbmRpbmdTY3JvbGxEZWx0YSA9IHVuZGVmaW5lZFxuICAgICAgICBib3guc2Nyb2xsQW5jaG9yID0geyBlbCwgb2Zmc2V0IH1cbiAgICAgICAgc2Nyb2xsTXV0YXRlZChib3gpXG4gICAgICB9LFxuICAgICAgc2Nyb2xsQnkoZHk6IG51bWJlcikge1xuICAgICAgICBjb25zdCBlbCA9IGRvbVJlZi5jdXJyZW50XG4gICAgICAgIGlmICghZWwpIHJldHVyblxuICAgICAgICBlbC5zdGlja3lTY3JvbGwgPSBmYWxzZVxuICAgICAgICAvLyBXaGVlbCBpbnB1dCBjYW5jZWxzIGFueSBpbi1mbGlnaHQgYW5jaG9yIHNlZWsg4oCUIHVzZXIgb3ZlcnJpZGUuXG4gICAgICAgIGVsLnNjcm9sbEFuY2hvciA9IHVuZGVmaW5lZFxuICAgICAgICAvLyBBY2N1bXVsYXRlIGluIHBlbmRpbmdTY3JvbGxEZWx0YTsgcmVuZGVyZXIgZHJhaW5zIGl0IGF0IGEgY2FwcGVkXG4gICAgICAgIC8vIHJhdGUgc28gZmFzdCBmbGlja3Mgc2hvdyBpbnRlcm1lZGlhdGUgZnJhbWVzLiBQdXJlIGFjY3VtdWxhdG9yOlxuICAgICAgICAvLyBzY3JvbGwtdXAgZm9sbG93ZWQgYnkgc2Nyb2xsLWRvd24gbmF0dXJhbGx5IGNhbmNlbHMuXG4gICAgICAgIGVsLnBlbmRpbmdTY3JvbGxEZWx0YSA9IChlbC5wZW5kaW5nU2Nyb2xsRGVsdGEgPz8gMCkgKyBNYXRoLmZsb29yKGR5KVxuICAgICAgICBzY3JvbGxNdXRhdGVkKGVsKVxuICAgICAgfSxcbiAgICAgIHNjcm9sbFRvQm90dG9tKCkge1xuICAgICAgICBjb25zdCBlbCA9IGRvbVJlZi5jdXJyZW50XG4gICAgICAgIGlmICghZWwpIHJldHVyblxuICAgICAgICBlbC5wZW5kaW5nU2Nyb2xsRGVsdGEgPSB1bmRlZmluZWRcbiAgICAgICAgZWwuc3RpY2t5U2Nyb2xsID0gdHJ1ZVxuICAgICAgICBtYXJrRGlydHkoZWwpXG4gICAgICAgIG5vdGlmeSgpXG4gICAgICAgIGZvcmNlUmVuZGVyKG4gPT4gbiArIDEpXG4gICAgICB9LFxuICAgICAgZ2V0U2Nyb2xsVG9wKCkge1xuICAgICAgICByZXR1cm4gZG9tUmVmLmN1cnJlbnQ/LnNjcm9sbFRvcCA/PyAwXG4gICAgICB9LFxuICAgICAgZ2V0UGVuZGluZ0RlbHRhKCkge1xuICAgICAgICAvLyBBY2N1bXVsYXRlZC1idXQtbm90LXlldC1kcmFpbmVkIGRlbHRhLiB1c2VWaXJ0dWFsU2Nyb2xsIG5lZWRzXG4gICAgICAgIC8vIHRoaXMgdG8gbW91bnQgdGhlIHVuaW9uIFtjb21taXR0ZWQsIGNvbW1pdHRlZCtwZW5kaW5nXSByYW5nZSDigJRcbiAgICAgICAgLy8gb3RoZXJ3aXNlIGludGVybWVkaWF0ZSBkcmFpbiBmcmFtZXMgZmluZCBubyBjaGlsZHJlbiAoYmxhbmspLlxuICAgICAgICByZXR1cm4gZG9tUmVmLmN1cnJlbnQ/LnBlbmRpbmdTY3JvbGxEZWx0YSA/PyAwXG4gICAgICB9LFxuICAgICAgZ2V0U2Nyb2xsSGVpZ2h0KCkge1xuICAgICAgICByZXR1cm4gZG9tUmVmLmN1cnJlbnQ/LnNjcm9sbEhlaWdodCA/PyAwXG4gICAgICB9LFxuICAgICAgZ2V0RnJlc2hTY3JvbGxIZWlnaHQoKSB7XG4gICAgICAgIGNvbnN0IGNvbnRlbnQgPSBkb21SZWYuY3VycmVudD8uY2hpbGROb2Rlc1swXSBhcyBET01FbGVtZW50IHwgdW5kZWZpbmVkXG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgY29udGVudD8ueW9nYU5vZGU/LmdldENvbXB1dGVkSGVpZ2h0KCkgPz9cbiAgICAgICAgICBkb21SZWYuY3VycmVudD8uc2Nyb2xsSGVpZ2h0ID8/XG4gICAgICAgICAgMFxuICAgICAgICApXG4gICAgICB9LFxuICAgICAgZ2V0Vmlld3BvcnRIZWlnaHQoKSB7XG4gICAgICAgIHJldHVybiBkb21SZWYuY3VycmVudD8uc2Nyb2xsVmlld3BvcnRIZWlnaHQgPz8gMFxuICAgICAgfSxcbiAgICAgIGdldFZpZXdwb3J0VG9wKCkge1xuICAgICAgICByZXR1cm4gZG9tUmVmLmN1cnJlbnQ/LnNjcm9sbFZpZXdwb3J0VG9wID8/IDBcbiAgICAgIH0sXG4gICAgICBpc1N0aWNreSgpIHtcbiAgICAgICAgY29uc3QgZWwgPSBkb21SZWYuY3VycmVudFxuICAgICAgICBpZiAoIWVsKSByZXR1cm4gZmFsc2VcbiAgICAgICAgcmV0dXJuIGVsLnN0aWNreVNjcm9sbCA/PyBCb29sZWFuKGVsLmF0dHJpYnV0ZXNbJ3N0aWNreVNjcm9sbCddKVxuICAgICAgfSxcbiAgICAgIHN1YnNjcmliZShsaXN0ZW5lcjogKCkgPT4gdm9pZCkge1xuICAgICAgICBsaXN0ZW5lcnNSZWYuY3VycmVudC5hZGQobGlzdGVuZXIpXG4gICAgICAgIHJldHVybiAoKSA9PiBsaXN0ZW5lcnNSZWYuY3VycmVudC5kZWxldGUobGlzdGVuZXIpXG4gICAgICB9LFxuICAgICAgc2V0Q2xhbXBCb3VuZHMobWluLCBtYXgpIHtcbiAgICAgICAgY29uc3QgZWwgPSBkb21SZWYuY3VycmVudFxuICAgICAgICBpZiAoIWVsKSByZXR1cm5cbiAgICAgICAgZWwuc2Nyb2xsQ2xhbXBNaW4gPSBtaW5cbiAgICAgICAgZWwuc2Nyb2xsQ2xhbXBNYXggPSBtYXhcbiAgICAgIH0sXG4gICAgfSksXG4gICAgLy8gbm90aWZ5L3Njcm9sbE11dGF0ZWQgYXJlIGlubGluZSAobm8gdXNlQ2FsbGJhY2spIGJ1dCBvbmx5IGNsb3NlIG92ZXJcbiAgICAvLyByZWZzICsgaW1wb3J0cyDigJQgc3RhYmxlLiBFbXB0eSBkZXBzIGF2b2lkcyByZWJ1aWxkaW5nIHRoZSBoYW5kbGUgb25cbiAgICAvLyBldmVyeSByZW5kZXIgKHdoaWNoIHJlLXJlZ2lzdGVycyB0aGUgcmVmID0gY2h1cm4pLlxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSByZWFjdC1ob29rcy9leGhhdXN0aXZlLWRlcHNcbiAgICBbXSxcbiAgKVxuXG4gIC8vIFN0cnVjdHVyZTogb3V0ZXIgdmlld3BvcnQgKG92ZXJmbG93OnNjcm9sbCwgY29uc3RyYWluZWQgaGVpZ2h0KSA+XG4gIC8vIGlubmVyIGNvbnRlbnQgKGZsZXhHcm93OjEsIGZsZXhTaHJpbms6MCDigJQgZmlsbHMgYXQgbGVhc3QgdGhlIHZpZXdwb3J0XG4gIC8vIGJ1dCBncm93cyBiZXlvbmQgaXQgZm9yIHRhbGwgY29udGVudCkuIGZsZXhHcm93OjEgbGV0cyBjaGlsZHJlbiB1c2VcbiAgLy8gc3BhY2VycyB0byBwaW4gZWxlbWVudHMgdG8gdGhlIGJvdHRvbSBvZiB0aGUgc2Nyb2xsIGFyZWEuIFlvZ2Enc1xuICAvLyBPdmVyZmxvdy5TY3JvbGwgcHJldmVudHMgdGhlIHZpZXdwb3J0IGZyb20gZ3Jvd2luZyB0byBmaXQgdGhlIGNvbnRlbnQuXG4gIC8vIFRoZSByZW5kZXJlciBjb21wdXRlcyBzY3JvbGxIZWlnaHQgZnJvbSB0aGUgY29udGVudCBib3ggYW5kIGN1bGxzXG4gIC8vIGNvbnRlbnQncyBjaGlsZHJlbiBiYXNlZCBvbiBzY3JvbGxUb3AuXG4gIC8vXG4gIC8vIHN0aWNreVNjcm9sbCBpcyBwYXNzZWQgYXMgYSBET00gYXR0cmlidXRlICh2aWEgaW5rLWJveCBkaXJlY3RseSkgc28gaXQnc1xuICAvLyBhdmFpbGFibGUgb24gdGhlIGZpcnN0IHJlbmRlciDigJQgcmVmIGNhbGxiYWNrcyBmaXJlIGFmdGVyIHRoZSBpbml0aWFsXG4gIC8vIGNvbW1pdCwgd2hpY2ggaXMgdG9vIGxhdGUgZm9yIHRoZSBmaXJzdCBmcmFtZS5cbiAgcmV0dXJuIChcbiAgICA8aW5rLWJveFxuICAgICAgcmVmPXtlbCA9PiB7XG4gICAgICAgIGRvbVJlZi5jdXJyZW50ID0gZWxcbiAgICAgICAgaWYgKGVsKSBlbC5zY3JvbGxUb3AgPz89IDBcbiAgICAgIH19XG4gICAgICBzdHlsZT17e1xuICAgICAgICBmbGV4V3JhcDogJ25vd3JhcCcsXG4gICAgICAgIGZsZXhEaXJlY3Rpb246IHN0eWxlLmZsZXhEaXJlY3Rpb24gPz8gJ3JvdycsXG4gICAgICAgIGZsZXhHcm93OiBzdHlsZS5mbGV4R3JvdyA/PyAwLFxuICAgICAgICBmbGV4U2hyaW5rOiBzdHlsZS5mbGV4U2hyaW5rID8/IDEsXG4gICAgICAgIC4uLnN0eWxlLFxuICAgICAgICBvdmVyZmxvd1g6ICdzY3JvbGwnLFxuICAgICAgICBvdmVyZmxvd1k6ICdzY3JvbGwnLFxuICAgICAgfX1cbiAgICAgIHsuLi4oc3RpY2t5U2Nyb2xsID8geyBzdGlja3lTY3JvbGw6IHRydWUgfSA6IHt9KX1cbiAgICA+XG4gICAgICA8Qm94IGZsZXhEaXJlY3Rpb249XCJjb2x1bW5cIiBmbGV4R3Jvdz17MX0gZmxleFNocmluaz17MH0gd2lkdGg9XCIxMDAlXCI+XG4gICAgICAgIHtjaGlsZHJlbn1cbiAgICAgIDwvQm94PlxuICAgIDwvaW5rLWJveD5cbiAgKVxufVxuXG5leHBvcnQgZGVmYXVsdCBTY3JvbGxCb3hcbiJdLCJtYXBwaW5ncyI6IkFBQUEsT0FBT0EsS0FBSyxJQUNWLEtBQUtDLGlCQUFpQixFQUN0QixLQUFLQyxHQUFHLEVBQ1JDLG1CQUFtQixFQUNuQkMsTUFBTSxFQUNOQyxRQUFRLFFBQ0gsT0FBTztBQUNkLGNBQWNDLE1BQU0sUUFBUSxXQUFXO0FBQ3ZDLFNBQVNDLGtCQUFrQixRQUFRLDBCQUEwQjtBQUM3RCxjQUFjQyxVQUFVLFFBQVEsV0FBVztBQUMzQyxTQUFTQyxTQUFTLEVBQUVDLGtCQUFrQixRQUFRLFdBQVc7QUFDekQsU0FBU0MsZUFBZSxRQUFRLGtCQUFrQjtBQUNsRCxjQUFjQyxNQUFNLFFBQVEsY0FBYztBQUMxQyxPQUFPLGdCQUFnQjtBQUN2QixPQUFPQyxHQUFHLE1BQU0sVUFBVTtBQUUxQixPQUFPLEtBQUtDLGVBQWUsR0FBRztFQUM1QkMsUUFBUSxFQUFFLENBQUNDLENBQUMsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQzdCQyxRQUFRLEVBQUUsQ0FBQ0MsRUFBRSxFQUFFLE1BQU0sRUFBRSxHQUFHLElBQUk7RUFDOUI7QUFDRjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7RUFDRUMsZUFBZSxFQUFFLENBQUNDLEVBQUUsRUFBRVosVUFBVSxFQUFFYSxNQUFlLENBQVIsRUFBRSxNQUFNLEVBQUUsR0FBRyxJQUFJO0VBQzFEQyxjQUFjLEVBQUUsR0FBRyxHQUFHLElBQUk7RUFDMUJDLFlBQVksRUFBRSxHQUFHLEdBQUcsTUFBTTtFQUMxQkMsZUFBZSxFQUFFLEdB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 \ No newline at end of file diff --git a/src/ink/components/Spacer.tsx b/src/ink/components/Spacer.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4d0af40f66646de8dec1f7d88dc4560f22ed68f6 --- /dev/null +++ b/src/ink/components/Spacer.tsx @@ -0,0 +1,20 @@ +import { c as _c } from "react/compiler-runtime"; +import React from 'react'; +import Box from './Box.js'; + +/** + * A flexible space that expands along the major axis of its containing layout. + * It's useful as a shortcut for filling all the available spaces between elements. + */ +export default function Spacer() { + const $ = _c(1); + let t0; + if ($[0] === Symbol.for("react.memo_cache_sentinel")) { + t0 = ; + $[0] = t0; + } else { + t0 = $[0]; + } + return t0; +} +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIkJveCIsIlNwYWNlciIsIiQiLCJfYyIsInQwIiwiU3ltYm9sIiwiZm9yIl0sInNvdXJjZXMiOlsiU3BhY2VyLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnXG5pbXBvcnQgQm94IGZyb20gJy4vQm94LmpzJ1xuXG4vKipcbiAqIEEgZmxleGlibGUgc3BhY2UgdGhhdCBleHBhbmRzIGFsb25nIHRoZSBtYWpvciBheGlzIG9mIGl0cyBjb250YWluaW5nIGxheW91dC5cbiAqIEl0J3MgdXNlZnVsIGFzIGEgc2hvcnRjdXQgZm9yIGZpbGxpbmcgYWxsIHRoZSBhdmFpbGFibGUgc3BhY2VzIGJldHdlZW4gZWxlbWVudHMuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIFNwYWNlcigpIHtcbiAgcmV0dXJuIDxCb3ggZmxleEdyb3c9ezF9IC8+XG59XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLE1BQU0sT0FBTztBQUN6QixPQUFPQyxHQUFHLE1BQU0sVUFBVTs7QUFFMUI7QUFDQTtBQUNBO0FBQ0E7QUFDQSxlQUFlLFNBQUFDLE9BQUE7RUFBQSxNQUFBQyxDQUFBLEdBQUFDLEVBQUE7RUFBQSxJQUFBQyxFQUFBO0VBQUEsSUFBQUYsQ0FBQSxRQUFBRyxNQUFBLENBQUFDLEdBQUE7SUFDTkYsRUFBQSxJQUFDLEdBQUcsQ0FBVyxRQUFDLENBQUQsR0FBQyxHQUFJO0lBQUFGLENBQUEsTUFBQUUsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUYsQ0FBQTtFQUFBO0VBQUEsT0FBcEJFLEVBQW9CO0FBQUEiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/src/ink/components/StdinContext.ts b/src/ink/components/StdinContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b1a49717c1d4922f4c5917d30c86c54056dde4f --- /dev/null +++ b/src/ink/components/StdinContext.ts @@ -0,0 +1,49 @@ +import { createContext } from 'react' +import { EventEmitter } from '../events/emitter.js' +import type { TerminalQuerier } from '../terminal-querier.js' + +export type Props = { + /** + * Stdin stream passed to `render()` in `options.stdin` or `process.stdin` by default. Useful if your app needs to handle user input. + */ + readonly stdin: NodeJS.ReadStream + + /** + * Ink exposes this function via own `` to be able to handle Ctrl+C, that's why you should use Ink's `setRawMode` instead of `process.stdin.setRawMode`. + * If the `stdin` stream passed to Ink does not support setRawMode, this function does nothing. + */ + readonly setRawMode: (value: boolean) => void + + /** + * A boolean flag determining if the current `stdin` supports `setRawMode`. A component using `setRawMode` might want to use `isRawModeSupported` to nicely fall back in environments where raw mode is not supported. + */ + readonly isRawModeSupported: boolean + + readonly internal_exitOnCtrlC: boolean + + readonly internal_eventEmitter: EventEmitter + + /** Query the terminal and await responses (DECRQM, OSC 11, etc.). + * Null only in the never-reached default context value. */ + readonly internal_querier: TerminalQuerier | null +} + +/** + * `StdinContext` is a React context, which exposes input stream. + */ + +const StdinContext = createContext({ + stdin: process.stdin, + + internal_eventEmitter: new EventEmitter(), + setRawMode() {}, + isRawModeSupported: false, + + internal_exitOnCtrlC: true, + internal_querier: null, +}) + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +StdinContext.displayName = 'InternalStdinContext' + +export default StdinContext diff --git a/src/ink/components/TerminalFocusContext.tsx b/src/ink/components/TerminalFocusContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e017b64ab18e01a8cb53561641c3439652c25efc --- /dev/null +++ b/src/ink/components/TerminalFocusContext.tsx @@ -0,0 +1,52 @@ +import { c as _c } from "react/compiler-runtime"; +import React, { createContext, useMemo, useSyncExternalStore } from 'react'; +import { getTerminalFocused, getTerminalFocusState, subscribeTerminalFocus, type TerminalFocusState } from '../terminal-focus-state.js'; +export type { TerminalFocusState }; +export type TerminalFocusContextProps = { + readonly isTerminalFocused: boolean; + readonly terminalFocusState: TerminalFocusState; +}; +const TerminalFocusContext = createContext({ + isTerminalFocused: true, + terminalFocusState: 'unknown' +}); + +// eslint-disable-next-line custom-rules/no-top-level-side-effects +TerminalFocusContext.displayName = 'TerminalFocusContext'; + +// Separate component so App.tsx doesn't re-render on focus changes. +// Children are a stable prop reference, so they don't re-render either — +// only components that consume the context will re-render. +export function TerminalFocusProvider(t0) { + const $ = _c(6); + const { + children + } = t0; + const isTerminalFocused = useSyncExternalStore(subscribeTerminalFocus, getTerminalFocused); + const terminalFocusState = useSyncExternalStore(subscribeTerminalFocus, getTerminalFocusState); + let t1; + if ($[0] !== isTerminalFocused || $[1] !== terminalFocusState) { + t1 = { + isTerminalFocused, + terminalFocusState + }; + $[0] = isTerminalFocused; + $[1] = terminalFocusState; + $[2] = t1; + } else { + t1 = $[2]; + } + const value = t1; + let t2; + if ($[3] !== children || $[4] !== value) { + t2 = {children}; + $[3] = children; + $[4] = value; + $[5] = t2; + } else { + t2 = $[5]; + } + return t2; +} +export default TerminalFocusContext; +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsImNyZWF0ZUNvbnRleHQiLCJ1c2VNZW1vIiwidXNlU3luY0V4dGVybmFsU3RvcmUiLCJnZXRUZXJtaW5hbEZvY3VzZWQiLCJnZXRUZXJtaW5hbEZvY3VzU3RhdGUiLCJzdWJzY3JpYmVUZXJtaW5hbEZvY3VzIiwiVGVybWluYWxGb2N1c1N0YXRlIiwiVGVybWluYWxGb2N1c0NvbnRleHRQcm9wcyIsImlzVGVybWluYWxGb2N1c2VkIiwidGVybWluYWxGb2N1c1N0YXRlIiwiVGVybWluYWxGb2N1c0NvbnRleHQiLCJkaXNwbGF5TmFtZSIsIlRlcm1pbmFsRm9jdXNQcm92aWRlciIsInQwIiwiJCIsIl9jIiwiY2hpbGRyZW4iLCJ0MSIsInZhbHVlIiwidDIiXSwic291cmNlcyI6WyJUZXJtaW5hbEZvY3VzQ29udGV4dC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFJlYWN0LCB7IGNyZWF0ZUNvbnRleHQsIHVzZU1lbW8sIHVzZVN5bmNFeHRlcm5hbFN0b3JlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICBnZXRUZXJtaW5hbEZvY3VzZWQsXG4gIGdldFRlcm1pbmFsRm9jdXNTdGF0ZSxcbiAgc3Vic2NyaWJlVGVybWluYWxGb2N1cyxcbiAgdHlwZSBUZXJtaW5hbEZvY3VzU3RhdGUsXG59IGZyb20gJy4uL3Rlcm1pbmFsLWZvY3VzLXN0YXRlLmpzJ1xuXG5leHBvcnQgdHlwZSB7IFRlcm1pbmFsRm9jdXNTdGF0ZSB9XG5cbmV4cG9ydCB0eXBlIFRlcm1pbmFsRm9jdXNDb250ZXh0UHJvcHMgPSB7XG4gIHJlYWRvbmx5IGlzVGVybWluYWxGb2N1c2VkOiBib29sZWFuXG4gIHJlYWRvbmx5IHRlcm1pbmFsRm9jdXNTdGF0ZTogVGVybWluYWxGb2N1c1N0YXRlXG59XG5cbmNvbnN0IFRlcm1pbmFsRm9jdXNDb250ZXh0ID0gY3JlYXRlQ29udGV4dDxUZXJtaW5hbEZvY3VzQ29udGV4dFByb3BzPih7XG4gIGlzVGVybWluYWxGb2N1c2VkOiB0cnVlLFxuICB0ZXJtaW5hbEZvY3VzU3RhdGU6ICd1bmtub3duJyxcbn0pXG5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjdXN0b20tcnVsZXMvbm8tdG9wLWxldmVsLXNpZGUtZWZmZWN0c1xuVGVybWluYWxGb2N1c0NvbnRleHQuZGlzcGxheU5hbWUgPSAnVGVybWluYWxGb2N1c0NvbnRleHQnXG5cbi8vIFNlcGFyYXRlIGNvbXBvbmVudCBzbyBBcHAudHN4IGRvZXNuJ3QgcmUtcmVuZGVyIG9uIGZvY3VzIGNoYW5nZXMuXG4vLyBDaGlsZHJlbiBhcmUgYSBzdGFibGUgcHJvcCByZWZlcmVuY2UsIHNvIHRoZXkgZG9uJ3QgcmUtcmVuZGVyIGVpdGhlciDigJRcbi8vIG9ubHkgY29tcG9uZW50cyB0aGF0IGNvbnN1bWUgdGhlIGNvbnRleHQgd2lsbCByZS1yZW5kZXIuXG5leHBvcnQgZnVuY3Rpb24gVGVybWluYWxGb2N1c1Byb3ZpZGVyKHtcbiAgY2hpbGRyZW4sXG59OiB7XG4gIGNoaWxkcmVuOiBSZWFjdC5SZWFjdE5vZGVcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBpc1Rlcm1pbmFsRm9jdXNlZCA9IHVzZVN5bmNFeHRlcm5hbFN0b3JlKFxuICAgIHN1YnNjcmliZVRlcm1pbmFsRm9jdXMsXG4gICAgZ2V0VGVybWluYWxGb2N1c2VkLFxuICApXG4gIGNvbnN0IHRlcm1pbmFsRm9jdXNTdGF0ZSA9IHVzZVN5bmNFeHRlcm5hbFN0b3JlKFxuICAgIHN1YnNjcmliZVRlcm1pbmFsRm9jdXMsXG4gICAgZ2V0VGVybWluYWxGb2N1c1N0YXRlLFxuICApXG5cbiAgY29uc3QgdmFsdWUgPSB1c2VNZW1vKFxuICAgICgpID0+ICh7IGlzVGVybWluYWxGb2N1c2VkLCB0ZXJtaW5hbEZvY3VzU3RhdGUgfSksXG4gICAgW2lzVGVybWluYWxGb2N1c2VkLCB0ZXJtaW5hbEZvY3VzU3RhdGVdLFxuICApXG5cbiAgcmV0dXJuIChcbiAgICA8VGVybWluYWxGb2N1c0NvbnRleHQuUHJvdmlkZXIgdmFsdWU9e3ZhbHVlfT5cbiAgICAgIHtjaGlsZHJlbn1cbiAgICA8L1Rlcm1pbmFsRm9jdXNDb250ZXh0LlByb3ZpZGVyPlxuICApXG59XG5cbmV4cG9ydCBkZWZhdWx0IFRlcm1pbmFsRm9jdXNDb250ZXh0XG4iXSwibWFwcGluZ3MiOiI7QUFBQSxPQUFPQSxLQUFLLElBQUlDLGFBQWEsRUFBRUMsT0FBTyxFQUFFQyxvQkFBb0IsUUFBUSxPQUFPO0FBQzNFLFNBQ0VDLGtCQUFrQixFQUNsQkMscUJBQXFCLEVBQ3JCQyxzQkFBc0IsRUFDdEIsS0FBS0Msa0JBQWtCLFFBQ2xCLDRCQUE0QjtBQUVuQyxjQUFjQSxrQkFBa0I7QUFFaEMsT0FBTyxLQUFLQyx5QkFBeUIsR0FBRztFQUN0QyxTQUFTQyxpQkFBaUIsRUFBRSxPQUFPO0VBQ25DLFNBQVNDLGtCQUFrQixFQUFFSCxrQkFBa0I7QUFDakQsQ0FBQztBQUVELE1BQU1JLG9CQUFvQixHQUFHVixhQUFhLENBQUNPLHlCQUF5QixDQUFDLENBQUM7RUFDcEVDLGlCQUFpQixFQUFFLElBQUk7RUFDdkJDLGtCQUFrQixFQUFFO0FBQ3RCLENBQUMsQ0FBQzs7QUFFRjtBQUNBQyxvQkFBb0IsQ0FBQ0MsV0FBVyxHQUFHLHNCQUFzQjs7QUFFekQ7QUFDQTtBQUNBO0FBQ0EsT0FBTyxTQUFBQyxzQkFBQUMsRUFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUErQjtJQUFBQztFQUFBLElBQUFILEVBSXJDO0VBQ0MsTUFBQUwsaUJBQUEsR0FBMEJOLG9CQUFvQixDQUM1Q0csc0JBQXNCLEVBQ3RCRixrQkFDRixDQUFDO0VBQ0QsTUFBQU0sa0JBQUEsR0FBMkJQLG9CQUFvQixDQUM3Q0csc0JBQXNCLEVBQ3RCRCxxQkFDRixDQUFDO0VBQUEsSUFBQWEsRUFBQTtFQUFBLElBQUFILENBQUEsUUFBQU4saUJBQUEsSUFBQU0sQ0FBQSxRQUFBTCxrQkFBQTtJQUdRUSxFQUFBO01BQUFULGlCQUFBO01BQUFDO0lBQXdDLENBQUM7SUFBQUssQ0FBQSxNQUFBTixpQkFBQTtJQUFBTSxDQUFBLE1BQUFMLGtCQUFBO0lBQUFLLENBQUEsTUFBQUcsRUFBQTtFQUFBO0lBQUFBLEVBQUEsR0FBQUgsQ0FBQTtFQUFBO0VBRGxELE1BQUFJLEtBQUEsR0FDU0QsRUFBeUM7RUFFakQsSUFBQUUsRUFBQTtFQUFBLElBQUFMLENBQUEsUUFBQUUsUUFBQSxJQUFBRixDQUFBLFFBQUFJLEtBQUE7SUFHQ0MsRUFBQSxrQ0FBc0NELEtBQUssQ0FBTEEsTUFBSSxDQUFDLENBQ3hDRixTQUFPLENBQ1YsZ0NBQWdDO0lBQUFGLENBQUEsTUFBQUUsUUFBQTtJQUFBRixDQUFBLE1BQUFJLEtBQUE7SUFBQUosQ0FBQSxNQUFBSyxFQUFBO0VBQUE7SUFBQUEsRUFBQSxHQUFBTCxDQUFBO0VBQUE7RUFBQSxPQUZoQ0ssRUFFZ0M7QUFBQTtBQUlwQyxlQUFlVCxvQkFBb0IiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/src/ink/components/TerminalSizeContext.tsx b/src/ink/components/TerminalSizeContext.tsx new file mode 100644 index 0000000000000000000000000000000000000000..8ca447ec4e96834aa20cdf4cba1f81943357874f --- /dev/null +++ b/src/ink/components/TerminalSizeContext.tsx @@ -0,0 +1,7 @@ +import { createContext } from 'react'; +export type TerminalSize = { + columns: number; + rows: number; +}; +export const TerminalSizeContext = createContext(null); +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJjcmVhdGVDb250ZXh0IiwiVGVybWluYWxTaXplIiwiY29sdW1ucyIsInJvd3MiLCJUZXJtaW5hbFNpemVDb250ZXh0Il0sInNvdXJjZXMiOlsiVGVybWluYWxTaXplQ29udGV4dC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRlQ29udGV4dCB9IGZyb20gJ3JlYWN0J1xuXG5leHBvcnQgdHlwZSBUZXJtaW5hbFNpemUgPSB7XG4gIGNvbHVtbnM6IG51bWJlclxuICByb3dzOiBudW1iZXJcbn1cblxuZXhwb3J0IGNvbnN0IFRlcm1pbmFsU2l6ZUNvbnRleHQgPSBjcmVhdGVDb250ZXh0PFRlcm1pbmFsU2l6ZSB8IG51bGw+KG51bGwpXG4iXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLGFBQWEsUUFBUSxPQUFPO0FBRXJDLE9BQU8sS0FBS0MsWUFBWSxHQUFHO0VBQ3pCQyxPQUFPLEVBQUUsTUFBTTtFQUNmQyxJQUFJLEVBQUUsTUFBTTtBQUNkLENBQUM7QUFFRCxPQUFPLE1BQU1DLG1CQUFtQixHQUFHSixhQUFhLENBQUNDLFlBQVksR0FBRyxJQUFJLENBQUMsQ0FBQyxJQUFJLENBQUMiLCJpZ25vcmVMaXN0IjpbXX0= \ No newline at end of file diff --git a/src/ink/events/click-event.ts b/src/ink/events/click-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f58659a89bb7c5d7e8e24f875426ca959edc550 --- /dev/null +++ b/src/ink/events/click-event.ts @@ -0,0 +1,38 @@ +import { Event } from './event.js' + +/** + * Mouse click event. Fired on left-button release without drag, only when + * mouse tracking is enabled (i.e. inside ). + * + * Bubbles from the deepest hit node up through parentNode. Call + * stopImmediatePropagation() to prevent ancestors' onClick from firing. + */ +export class ClickEvent extends Event { + /** 0-indexed screen column of the click */ + readonly col: number + /** 0-indexed screen row of the click */ + readonly row: number + /** + * Click column relative to the current handler's Box (col - box.x). + * Recomputed by dispatchClick before each handler fires, so an onClick + * on a container sees coords relative to that container, not to any + * child the click landed on. + */ + localCol = 0 + /** Click row relative to the current handler's Box (row - box.y). */ + localRow = 0 + /** + * True if the clicked cell has no visible content (unwritten in the + * screen buffer — both packed words are 0). Handlers can check this to + * ignore clicks on blank space to the right of text, so accidental + * clicks on empty terminal space don't toggle state. + */ + readonly cellIsBlank: boolean + + constructor(col: number, row: number, cellIsBlank: boolean) { + super() + this.col = col + this.row = row + this.cellIsBlank = cellIsBlank + } +} diff --git a/src/ink/events/dispatcher.ts b/src/ink/events/dispatcher.ts new file mode 100644 index 0000000000000000000000000000000000000000..2d42884939a667e97ad83df35821d9e9daf28a25 --- /dev/null +++ b/src/ink/events/dispatcher.ts @@ -0,0 +1,235 @@ +import { + ContinuousEventPriority, + DefaultEventPriority, + DiscreteEventPriority, +} from 'react-reconciler/constants.js' + +// NoEventPriority doesn't exist in react-reconciler 0.29, use 0 as fallback +const NoEventPriority = 0; +import { logError } from '../../utils/log.js' +import { HANDLER_FOR_EVENT } from './event-handlers.js' +import type { EventTarget, TerminalEvent } from './terminal-event.js' + +// -- + +type DispatchListener = { + node: EventTarget + handler: (event: TerminalEvent) => void + phase: 'capturing' | 'at_target' | 'bubbling' +} + +function getHandler( + node: EventTarget, + eventType: string, + capture: boolean, +): ((event: TerminalEvent) => void) | undefined { + const handlers = node._eventHandlers + if (!handlers) return undefined + + const mapping = HANDLER_FOR_EVENT[eventType] + if (!mapping) return undefined + + const propName = capture ? mapping.capture : mapping.bubble + if (!propName) return undefined + + return handlers[propName] as ((event: TerminalEvent) => void) | undefined +} + +/** + * Collect all listeners for an event in dispatch order. + * + * Uses react-dom's two-phase accumulation pattern: + * - Walk from target to root + * - Capture handlers are prepended (unshift) → root-first + * - Bubble handlers are appended (push) → target-first + * + * Result: [root-cap, ..., parent-cap, target-cap, target-bub, parent-bub, ..., root-bub] + */ +function collectListeners( + target: EventTarget, + event: TerminalEvent, +): DispatchListener[] { + const listeners: DispatchListener[] = [] + + let node: EventTarget | undefined = target + while (node) { + const isTarget = node === target + + const captureHandler = getHandler(node, event.type, true) + const bubbleHandler = getHandler(node, event.type, false) + + if (captureHandler) { + listeners.unshift({ + node, + handler: captureHandler, + phase: isTarget ? 'at_target' : 'capturing', + }) + } + + if (bubbleHandler && (event.bubbles || isTarget)) { + listeners.push({ + node, + handler: bubbleHandler, + phase: isTarget ? 'at_target' : 'bubbling', + }) + } + + node = node.parentNode + } + + return listeners +} + +/** + * Execute collected listeners with propagation control. + * + * Before each handler, calls event._prepareForTarget(node) so event + * subclasses can do per-node setup. + */ +function processDispatchQueue( + listeners: DispatchListener[], + event: TerminalEvent, +): void { + let previousNode: EventTarget | undefined + + for (const { node, handler, phase } of listeners) { + if (event._isImmediatePropagationStopped()) { + break + } + + if (event._isPropagationStopped() && node !== previousNode) { + break + } + + event._setEventPhase(phase) + event._setCurrentTarget(node) + event._prepareForTarget(node) + + try { + handler(event) + } catch (error) { + logError(error) + } + + previousNode = node + } +} + +// -- + +/** + * Map terminal event types to React scheduling priorities. + * Mirrors react-dom's getEventPriority() switch. + */ +function getEventPriority(eventType: string): number { + switch (eventType) { + case 'keydown': + case 'keyup': + case 'click': + case 'focus': + case 'blur': + case 'paste': + return DiscreteEventPriority as number + case 'resize': + case 'scroll': + case 'mousemove': + return ContinuousEventPriority as number + default: + return DefaultEventPriority as number + } +} + +// -- + +type DiscreteUpdates = ( + fn: (a: A, b: B) => boolean, + a: A, + b: B, + c: undefined, + d: undefined, +) => boolean + +/** + * Owns event dispatch state and the capture/bubble dispatch loop. + * + * The reconciler host config reads currentEvent and currentUpdatePriority + * to implement resolveUpdatePriority, resolveEventType, and + * resolveEventTimeStamp — mirroring how react-dom's host config reads + * ReactDOMSharedInternals and window.event. + * + * discreteUpdates is injected after construction (by InkReconciler) + * to break the import cycle. + */ +export class Dispatcher { + currentEvent: TerminalEvent | null = null + currentUpdatePriority: number = DefaultEventPriority as number + discreteUpdates: DiscreteUpdates | null = null + + /** + * Infer event priority from the currently-dispatching event. + * Called by the reconciler host config's resolveUpdatePriority + * when no explicit priority has been set. + */ + resolveEventPriority(): number { + if (this.currentUpdatePriority !== (NoEventPriority as number)) { + return this.currentUpdatePriority + } + if (this.currentEvent) { + return getEventPriority(this.currentEvent.type) + } + return DefaultEventPriority as number + } + + /** + * Dispatch an event through capture and bubble phases. + * Returns true if preventDefault() was NOT called. + */ + dispatch(target: EventTarget, event: TerminalEvent): boolean { + const previousEvent = this.currentEvent + this.currentEvent = event + try { + event._setTarget(target) + + const listeners = collectListeners(target, event) + processDispatchQueue(listeners, event) + + event._setEventPhase('none') + event._setCurrentTarget(null) + + return !event.defaultPrevented + } finally { + this.currentEvent = previousEvent + } + } + + /** + * Dispatch with discrete (sync) priority. + * For user-initiated events: keyboard, click, focus, paste. + */ + dispatchDiscrete(target: EventTarget, event: TerminalEvent): boolean { + if (!this.discreteUpdates) { + return this.dispatch(target, event) + } + return this.discreteUpdates( + (t, e) => this.dispatch(t, e), + target, + event, + undefined, + undefined, + ) + } + + /** + * Dispatch with continuous priority. + * For high-frequency events: resize, scroll, mouse move. + */ + dispatchContinuous(target: EventTarget, event: TerminalEvent): boolean { + const previousPriority = this.currentUpdatePriority + try { + this.currentUpdatePriority = ContinuousEventPriority as number + return this.dispatch(target, event) + } finally { + this.currentUpdatePriority = previousPriority + } + } +} diff --git a/src/ink/events/emitter.ts b/src/ink/events/emitter.ts new file mode 100644 index 0000000000000000000000000000000000000000..56a4b0dc69a0c4af3b6cb3a3421ff901a4075200 --- /dev/null +++ b/src/ink/events/emitter.ts @@ -0,0 +1,39 @@ +import { EventEmitter as NodeEventEmitter } from 'events' +import { Event } from './event.js' + +// Similar to node's builtin EventEmitter, but is also aware of our `Event` +// class, and so `emit` respects `stopImmediatePropagation()`. +export class EventEmitter extends NodeEventEmitter { + constructor() { + super() + // Disable the default maxListeners warning. In React, many components + // can legitimately listen to the same event (e.g., useInput hooks). + // The default limit of 10 causes spurious warnings. + this.setMaxListeners(0) + } + + override emit(type: string | symbol, ...args: unknown[]): boolean { + // Delegate to node for `error`, since it's not treated like a normal event + if (type === 'error') { + return super.emit(type, ...args) + } + + const listeners = this.rawListeners(type) + + if (listeners.length === 0) { + return false + } + + const ccEvent = args[0] instanceof Event ? args[0] : null + + for (const listener of listeners) { + listener.apply(this, args) + + if (ccEvent?.didStopImmediatePropagation()) { + break + } + } + + return true + } +} diff --git a/src/ink/events/event-handlers.ts b/src/ink/events/event-handlers.ts new file mode 100644 index 0000000000000000000000000000000000000000..7865f5b3c721f75525d4ec351f9558ded7d60b40 --- /dev/null +++ b/src/ink/events/event-handlers.ts @@ -0,0 +1,73 @@ +import type { ClickEvent } from './click-event.js' +import type { FocusEvent } from './focus-event.js' +import type { KeyboardEvent } from './keyboard-event.js' +import type { PasteEvent } from './paste-event.js' +import type { ResizeEvent } from './resize-event.js' + +type KeyboardEventHandler = (event: KeyboardEvent) => void +type FocusEventHandler = (event: FocusEvent) => void +type PasteEventHandler = (event: PasteEvent) => void +type ResizeEventHandler = (event: ResizeEvent) => void +type ClickEventHandler = (event: ClickEvent) => void +type HoverEventHandler = () => void + +/** + * Props for event handlers on Box and other host components. + * + * Follows the React/DOM naming convention: + * - onEventName: handler for bubble phase + * - onEventNameCapture: handler for capture phase + */ +export type EventHandlerProps = { + onKeyDown?: KeyboardEventHandler + onKeyDownCapture?: KeyboardEventHandler + + onFocus?: FocusEventHandler + onFocusCapture?: FocusEventHandler + onBlur?: FocusEventHandler + onBlurCapture?: FocusEventHandler + + onPaste?: PasteEventHandler + onPasteCapture?: PasteEventHandler + + onResize?: ResizeEventHandler + + onClick?: ClickEventHandler + onMouseEnter?: HoverEventHandler + onMouseLeave?: HoverEventHandler +} + +/** + * Reverse lookup: event type string → handler prop names. + * Used by the dispatcher for O(1) handler lookup per node. + */ +export const HANDLER_FOR_EVENT: Record< + string, + { bubble?: keyof EventHandlerProps; capture?: keyof EventHandlerProps } +> = { + keydown: { bubble: 'onKeyDown', capture: 'onKeyDownCapture' }, + focus: { bubble: 'onFocus', capture: 'onFocusCapture' }, + blur: { bubble: 'onBlur', capture: 'onBlurCapture' }, + paste: { bubble: 'onPaste', capture: 'onPasteCapture' }, + resize: { bubble: 'onResize' }, + click: { bubble: 'onClick' }, +} + +/** + * Set of all event handler prop names, for the reconciler to detect + * event props and store them in _eventHandlers instead of attributes. + */ +export const EVENT_HANDLER_PROPS = new Set([ + 'onKeyDown', + 'onKeyDownCapture', + 'onFocus', + 'onFocusCapture', + 'onBlur', + 'onBlurCapture', + 'onPaste', + 'onPasteCapture', + 'onResize', + 'onClick', + 'onMouseEnter', + 'onMouseLeave', +]) diff --git a/src/ink/events/event.ts b/src/ink/events/event.ts new file mode 100644 index 0000000000000000000000000000000000000000..61874002ebbdb476ffe9a2a0db8001ec6aefb178 --- /dev/null +++ b/src/ink/events/event.ts @@ -0,0 +1,11 @@ +export class Event { + private _didStopImmediatePropagation = false + + didStopImmediatePropagation(): boolean { + return this._didStopImmediatePropagation + } + + stopImmediatePropagation(): void { + this._didStopImmediatePropagation = true + } +} diff --git a/src/ink/events/focus-event.ts b/src/ink/events/focus-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..a552e54561162cb97469e1160d1529e30d17ed48 --- /dev/null +++ b/src/ink/events/focus-event.ts @@ -0,0 +1,21 @@ +import { type EventTarget, TerminalEvent } from './terminal-event.js' + +/** + * Focus event for component focus changes. + * + * Dispatched when focus moves between elements. 'focus' fires on the + * newly focused element, 'blur' fires on the previously focused one. + * Both bubble, matching react-dom's use of focusin/focusout semantics + * so parent components can observe descendant focus changes. + */ +export class FocusEvent extends TerminalEvent { + readonly relatedTarget: EventTarget | null + + constructor( + type: 'focus' | 'blur', + relatedTarget: EventTarget | null = null, + ) { + super(type, { bubbles: true, cancelable: false }) + this.relatedTarget = relatedTarget + } +} diff --git a/src/ink/events/input-event.ts b/src/ink/events/input-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..490502811fefaa513b133f9c840904c7ce63a907 --- /dev/null +++ b/src/ink/events/input-event.ts @@ -0,0 +1,205 @@ +import { nonAlphanumericKeys, type ParsedKey } from '../parse-keypress.js' +import { Event } from './event.js' + +export type Key = { + upArrow: boolean + downArrow: boolean + leftArrow: boolean + rightArrow: boolean + pageDown: boolean + pageUp: boolean + wheelUp: boolean + wheelDown: boolean + home: boolean + end: boolean + return: boolean + escape: boolean + ctrl: boolean + shift: boolean + fn: boolean + tab: boolean + backspace: boolean + delete: boolean + meta: boolean + super: boolean +} + +function parseKey(keypress: ParsedKey): [Key, string] { + const key: Key = { + upArrow: keypress.name === 'up', + downArrow: keypress.name === 'down', + leftArrow: keypress.name === 'left', + rightArrow: keypress.name === 'right', + pageDown: keypress.name === 'pagedown', + pageUp: keypress.name === 'pageup', + wheelUp: keypress.name === 'wheelup', + wheelDown: keypress.name === 'wheeldown', + home: keypress.name === 'home', + end: keypress.name === 'end', + return: keypress.name === 'return', + escape: keypress.name === 'escape', + fn: keypress.fn, + ctrl: keypress.ctrl, + shift: keypress.shift, + tab: keypress.name === 'tab', + backspace: keypress.name === 'backspace', + delete: keypress.name === 'delete', + // `parseKeypress` parses \u001B\u001B[A (meta + up arrow) as meta = false + // but with option = true, so we need to take this into account here + // to avoid breaking changes in Ink. + // TODO(vadimdemedes): consider removing this in the next major version. + meta: keypress.meta || keypress.name === 'escape' || keypress.option, + // Super (Cmd on macOS / Win key) — only arrives via kitty keyboard + // protocol CSI u sequences. Distinct from meta (Alt/Option) so + // bindings like cmd+c can be expressed separately from opt+c. + super: keypress.super, + } + + let input = keypress.ctrl ? keypress.name : keypress.sequence + + // Handle undefined input case + if (input === undefined) { + input = '' + } + + // When ctrl is set, keypress.name for space is the literal word "space". + // Convert to actual space character for consistency with the CSI u branch + // (which maps 'space' → ' '). Without this, ctrl+space leaks the literal + // word "space" into text input. + if (keypress.ctrl && input === 'space') { + input = ' ' + } + + // Suppress unrecognized escape sequences that were parsed as function keys + // (matched by FN_KEY_RE) but have no name in the keyName map. + // Examples: ESC[25~ (F13/Right Alt on Windows), ESC[26~ (F14), etc. + // Without this, the ESC prefix is stripped below and the remainder (e.g., + // "[25~") leaks into the input as literal text. + if (keypress.code && !keypress.name) { + input = '' + } + + // Suppress ESC-less SGR mouse fragments. When a heavy React commit blocks + // the event loop past App's 50ms NORMAL_TIMEOUT flush, a CSI split across + // stdin chunks gets its buffered ESC flushed as a lone Escape key, and the + // continuation arrives as a text token with name='' — which falls through + // all of parseKeypress's ESC-anchored regexes and the nonAlphanumericKeys + // clear below (name is falsy). The fragment then leaks into the prompt as + // literal `[<64;74;16M`. This is the same defensive sink as the F13 guard + // above; the underlying tokenizer-flush race is upstream of this layer. + if (!keypress.name && /^\[<\d+;\d+;\d+[Mm]/.test(input)) { + input = '' + } + + // Strip meta if it's still remaining after `parseKeypress` + // TODO(vadimdemedes): remove this in the next major version. + if (input.startsWith('\u001B')) { + input = input.slice(1) + } + + // Track whether we've already processed this as a special sequence + // that converted input to the key name (CSI u or application keypad mode). + // For these, we don't want to clear input with nonAlphanumericKeys check. + let processedAsSpecialSequence = false + + // Handle CSI u sequences (Kitty keyboard protocol): after stripping ESC, + // we're left with "[codepoint;modifieru" (e.g., "[98;3u" for Alt+b). + // Use the parsed key name instead for input handling. Require a digit + // after [ — real CSI u is always […u, and a bare startsWith('[') + // false-matches X10 mouse at row 85 (Cy = 85+32 = 'u'), leaking the + // literal text "mouse" into the prompt via processedAsSpecialSequence. + if (/^\[\d/.test(input) && input.endsWith('u')) { + if (!keypress.name) { + // Unmapped Kitty functional key (Caps Lock 57358, F13–F35, KP nav, + // bare modifiers, etc.) — keycodeToName() returned undefined. Swallow + // so the raw "[57358u" doesn't leak into the prompt. See #38781. + input = '' + } else { + // 'space' → ' '; 'escape' → '' (key.escape carries it; + // processedAsSpecialSequence bypasses the nonAlphanumericKeys + // clear below, so we must handle it explicitly here); + // otherwise use key name. + input = + keypress.name === 'space' + ? ' ' + : keypress.name === 'escape' + ? '' + : keypress.name + } + processedAsSpecialSequence = true + } + + // Handle xterm modifyOtherKeys sequences: after stripping ESC, we're left + // with "[27;modifier;keycode~" (e.g., "[27;3;98~" for Alt+b). Same + // extraction as CSI u — without this, printable-char keycodes (single-letter + // names) skip the nonAlphanumericKeys clear and leak "[27;..." as input. + if (input.startsWith('[27;') && input.endsWith('~')) { + if (!keypress.name) { + // Unmapped modifyOtherKeys keycode — swallow for consistency with + // the CSI u handler above. Practically untriggerable today (xterm + // modifyOtherKeys only sends ASCII keycodes, all mapped), but + // guards against future terminal behavior. + input = '' + } else { + input = + keypress.name === 'space' + ? ' ' + : keypress.name === 'escape' + ? '' + : keypress.name + } + processedAsSpecialSequence = true + } + + // Handle application keypad mode sequences: after stripping ESC, + // we're left with "O" (e.g., "Op" for numpad 0, "Oy" for numpad 9). + // Use the parsed key name (the digit character) for input handling. + if ( + input.startsWith('O') && + input.length === 2 && + keypress.name && + keypress.name.length === 1 + ) { + input = keypress.name + processedAsSpecialSequence = true + } + + // Clear input for non-alphanumeric keys (arrows, function keys, etc.) + // Skip this for CSI u and application keypad mode sequences since + // those were already converted to their proper input characters. + if ( + !processedAsSpecialSequence && + keypress.name && + nonAlphanumericKeys.includes(keypress.name) + ) { + input = '' + } + + // Set shift=true for uppercase letters (A-Z) + // Must check it's actually a letter, not just any char unchanged by toUpperCase + if ( + input.length === 1 && + typeof input[0] === 'string' && + input[0] >= 'A' && + input[0] <= 'Z' + ) { + key.shift = true + } + + return [key, input] +} + +export class InputEvent extends Event { + readonly keypress: ParsedKey + readonly key: Key + readonly input: string + + constructor(keypress: ParsedKey) { + super() + const [key, input] = parseKey(keypress) + + this.keypress = keypress + this.key = key + this.input = input + } +} diff --git a/src/ink/events/keyboard-event.ts b/src/ink/events/keyboard-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..1210efd13da400294fbf47a09358632f80e6cd80 --- /dev/null +++ b/src/ink/events/keyboard-event.ts @@ -0,0 +1,51 @@ +import type { ParsedKey } from '../parse-keypress.js' +import { TerminalEvent } from './terminal-event.js' + +/** + * Keyboard event dispatched through the DOM tree via capture/bubble. + * + * Follows browser KeyboardEvent semantics: `key` is the literal character + * for printable keys ('a', '3', ' ', '/') and a multi-char name for + * special keys ('down', 'return', 'escape', 'f1'). The idiomatic + * printable-char check is `e.key.length === 1`. + */ +export class KeyboardEvent extends TerminalEvent { + readonly key: string + readonly ctrl: boolean + readonly shift: boolean + readonly meta: boolean + readonly superKey: boolean + readonly fn: boolean + + constructor(parsedKey: ParsedKey) { + super('keydown', { bubbles: true, cancelable: true }) + + this.key = keyFromParsed(parsedKey) + this.ctrl = parsedKey.ctrl + this.shift = parsedKey.shift + this.meta = parsedKey.meta || parsedKey.option + this.superKey = parsedKey.super + this.fn = parsedKey.fn + } +} + +function keyFromParsed(parsed: ParsedKey): string { + const seq = parsed.sequence ?? '' + const name = parsed.name ?? '' + + // Ctrl combos: sequence is a control byte (\x03 for ctrl+c), name is the + // letter. Browsers report e.key === 'c' with e.ctrlKey === true. + if (parsed.ctrl) return name + + // Single printable char (space through ~, plus anything above ASCII): + // use the literal char. Browsers report e.key === '3', not 'Digit3'. + if (seq.length === 1) { + const code = seq.charCodeAt(0) + if (code >= 0x20 && code !== 0x7f) return seq + } + + // Special keys (arrows, F-keys, return, tab, escape, etc.): sequence is + // either an escape sequence (\x1b[B) or a control byte (\r, \t), so use + // the parsed name. Browsers report e.key === 'ArrowDown'. + return name || seq +} diff --git a/src/ink/events/terminal-event.ts b/src/ink/events/terminal-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..9a86bf8b296dac649b632ff172a470933d1f8493 --- /dev/null +++ b/src/ink/events/terminal-event.ts @@ -0,0 +1,107 @@ +import { Event } from './event.js' + +type EventPhase = 'none' | 'capturing' | 'at_target' | 'bubbling' + +type TerminalEventInit = { + bubbles?: boolean + cancelable?: boolean +} + +/** + * Base class for all terminal events with DOM-style propagation. + * + * Extends Event so existing event types (ClickEvent, InputEvent, + * TerminalFocusEvent) share a common ancestor and can migrate later. + * + * Mirrors the browser's Event API: target, currentTarget, eventPhase, + * stopPropagation(), preventDefault(), timeStamp. + */ +export class TerminalEvent extends Event { + readonly type: string + readonly timeStamp: number + readonly bubbles: boolean + readonly cancelable: boolean + + private _target: EventTarget | null = null + private _currentTarget: EventTarget | null = null + private _eventPhase: EventPhase = 'none' + private _propagationStopped = false + private _defaultPrevented = false + + constructor(type: string, init?: TerminalEventInit) { + super() + this.type = type + this.timeStamp = performance.now() + this.bubbles = init?.bubbles ?? true + this.cancelable = init?.cancelable ?? true + } + + get target(): EventTarget | null { + return this._target + } + + get currentTarget(): EventTarget | null { + return this._currentTarget + } + + get eventPhase(): EventPhase { + return this._eventPhase + } + + get defaultPrevented(): boolean { + return this._defaultPrevented + } + + stopPropagation(): void { + this._propagationStopped = true + } + + override stopImmediatePropagation(): void { + super.stopImmediatePropagation() + this._propagationStopped = true + } + + preventDefault(): void { + if (this.cancelable) { + this._defaultPrevented = true + } + } + + // -- Internal setters used by the Dispatcher + + /** @internal */ + _setTarget(target: EventTarget): void { + this._target = target + } + + /** @internal */ + _setCurrentTarget(target: EventTarget | null): void { + this._currentTarget = target + } + + /** @internal */ + _setEventPhase(phase: EventPhase): void { + this._eventPhase = phase + } + + /** @internal */ + _isPropagationStopped(): boolean { + return this._propagationStopped + } + + /** @internal */ + _isImmediatePropagationStopped(): boolean { + return this.didStopImmediatePropagation() + } + + /** + * Hook for subclasses to do per-node setup before each handler fires. + * Default is a no-op. + */ + _prepareForTarget(_target: EventTarget): void {} +} + +export type EventTarget = { + parentNode: EventTarget | undefined + _eventHandlers?: Record +} diff --git a/src/ink/events/terminal-focus-event.ts b/src/ink/events/terminal-focus-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d0303fdb48c824d9ca7b104d0180ec7f9a5b0e8 --- /dev/null +++ b/src/ink/events/terminal-focus-event.ts @@ -0,0 +1,19 @@ +import { Event } from './event.js' + +export type TerminalFocusEventType = 'terminalfocus' | 'terminalblur' + +/** + * Event fired when the terminal window gains or loses focus. + * + * Uses DECSET 1004 focus reporting - the terminal sends: + * - CSI I (\x1b[I) when the terminal gains focus + * - CSI O (\x1b[O) when the terminal loses focus + */ +export class TerminalFocusEvent extends Event { + readonly type: TerminalFocusEventType + + constructor(type: TerminalFocusEventType) { + super() + this.type = type + } +} diff --git a/src/ink/hooks/use-animation-frame.ts b/src/ink/hooks/use-animation-frame.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4dd38a1657f8f4ccff70211f269353291da7ce6 --- /dev/null +++ b/src/ink/hooks/use-animation-frame.ts @@ -0,0 +1,57 @@ +import { useContext, useEffect, useState } from 'react' +import { ClockContext } from '../components/ClockContext.js' +import type { DOMElement } from '../dom.js' +import { useTerminalViewport } from './use-terminal-viewport.js' + +/** + * Hook for synchronized animations that pause when offscreen. + * + * Returns a ref to attach to the animated element and the current animation time. + * All instances share the same clock, so animations stay in sync. + * The clock only runs when at least one keepAlive subscriber exists. + * + * Pass `null` to pause — unsubscribes from the clock so no ticks fire. + * Time freezes at the last value and resumes from the current clock time + * when a number is passed again. + * + * @param intervalMs - How often to update, or null to pause + * @returns [ref, time] - Ref to attach to element, elapsed time in ms + * + * @example + * function Spinner() { + * const [ref, time] = useAnimationFrame(120) + * const frame = Math.floor(time / 120) % FRAMES.length + * return {FRAMES[frame]} + * } + * + * The clock automatically slows when the terminal is blurred, + * so consumers don't need to handle focus state. + */ +export function useAnimationFrame( + intervalMs: number | null = 16, +): [ref: (element: DOMElement | null) => void, time: number] { + const clock = useContext(ClockContext) + const [viewportRef, { isVisible }] = useTerminalViewport() + const [time, setTime] = useState(() => clock?.now() ?? 0) + + const active = isVisible && intervalMs !== null + + useEffect(() => { + if (!clock || !active) return + + let lastUpdate = clock.now() + + const onChange = (): void => { + const now = clock.now() + if (now - lastUpdate >= intervalMs!) { + lastUpdate = now + setTime(now) + } + } + + // keepAlive: true — visible animations drive the clock + return clock.subscribe(onChange, true) + }, [clock, intervalMs, active]) + + return [viewportRef, time] +} diff --git a/src/ink/hooks/use-app.ts b/src/ink/hooks/use-app.ts new file mode 100644 index 0000000000000000000000000000000000000000..5545f35f7bf25ffdf858658243495f5bc9aca346 --- /dev/null +++ b/src/ink/hooks/use-app.ts @@ -0,0 +1,8 @@ +import { useContext } from 'react' +import AppContext from '../components/AppContext.js' + +/** + * `useApp` is a React hook, which exposes a method to manually exit the app (unmount). + */ +const useApp = () => useContext(AppContext) +export default useApp diff --git a/src/ink/hooks/use-declared-cursor.ts b/src/ink/hooks/use-declared-cursor.ts new file mode 100644 index 0000000000000000000000000000000000000000..e49668b3885ba68a29e433f0370a6a24152cd760 --- /dev/null +++ b/src/ink/hooks/use-declared-cursor.ts @@ -0,0 +1,73 @@ +import { useCallback, useContext, useLayoutEffect, useRef } from 'react' +import CursorDeclarationContext from '../components/CursorDeclarationContext.js' +import type { DOMElement } from '../dom.js' + +/** + * Declares where the terminal cursor should be parked after each frame. + * + * Terminal emulators render IME preedit text at the physical cursor + * position, and screen readers / screen magnifiers track the native + * cursor — so parking it at the text input's caret makes CJK input + * appear inline and lets accessibility tools follow the input. + * + * Returns a ref callback to attach to the Box that contains the input. + * The declared (line, column) is interpreted relative to that Box's + * nodeCache rect (populated by renderNodeToOutput). + * + * Timing: Both ref attach and useLayoutEffect fire in React's layout + * phase — after resetAfterCommit calls scheduleRender. scheduleRender + * defers onRender via queueMicrotask, so onRender runs AFTER layout + * effects commit and reads the fresh declaration on the first frame + * (no one-keystroke lag). Test env uses onImmediateRender (synchronous, + * no microtask), so tests compensate by calling ink.onRender() + * explicitly after render. + */ +export function useDeclaredCursor({ + line, + column, + active, +}: { + line: number + column: number + active: boolean +}): (element: DOMElement | null) => void { + const setCursorDeclaration = useContext(CursorDeclarationContext) + const nodeRef = useRef(null) + + const setNode = useCallback((node: DOMElement | null) => { + nodeRef.current = node + }, []) + + // When active, set unconditionally. When inactive, clear conditionally + // (only if the currently-declared node is ours). The node-identity check + // handles two hazards: + // 1. A memo()ized active instance elsewhere (e.g. the search input in + // a memo'd Footer) doesn't re-render this commit — an inactive + // instance re-rendering here must not clobber it. + // 2. Sibling handoff (menu focus moving between list items) — when + // focus moves opposite to sibling order, the newly-inactive item's + // effect runs AFTER the newly-active item's set. Without the node + // check it would clobber. + // No dep array: must re-declare every commit so the active instance + // re-claims the declaration after another instance's unmount-cleanup or + // sibling handoff nulls it. + useLayoutEffect(() => { + const node = nodeRef.current + if (active && node) { + setCursorDeclaration({ relativeX: column, relativeY: line, node }) + } else { + setCursorDeclaration(null, node) + } + }) + + // Clear on unmount (conditionally — another instance may own by then). + // Separate effect with empty deps so cleanup only fires once — not on + // every line/column change, which would transiently null between commits. + useLayoutEffect(() => { + return () => { + setCursorDeclaration(null, nodeRef.current) + } + }, [setCursorDeclaration]) + + return setNode +} diff --git a/src/ink/hooks/use-input.ts b/src/ink/hooks/use-input.ts new file mode 100644 index 0000000000000000000000000000000000000000..7cf75b3114227891dde2babb6552182891bceabd --- /dev/null +++ b/src/ink/hooks/use-input.ts @@ -0,0 +1,92 @@ +import { useEffect, useLayoutEffect } from 'react' +import { useEventCallback } from 'usehooks-ts' +import type { InputEvent, Key } from '../events/input-event.js' +import useStdin from './use-stdin.js' + +type Handler = (input: string, key: Key, event: InputEvent) => void + +type Options = { + /** + * Enable or disable capturing of user input. + * Useful when there are multiple useInput hooks used at once to avoid handling the same input several times. + * + * @default true + */ + isActive?: boolean +} + +/** + * This hook is used for handling user input. + * It's a more convenient alternative to using `StdinContext` and listening to `data` events. + * The callback you pass to `useInput` is called for each character when user enters any input. + * However, if user pastes text and it's more than one character, the callback will be called only once and the whole string will be passed as `input`. + * + * ``` + * import {useInput} from 'ink'; + * + * const UserInput = () => { + * useInput((input, key) => { + * if (input === 'q') { + * // Exit program + * } + * + * if (key.leftArrow) { + * // Left arrow key pressed + * } + * }); + * + * return … + * }; + * ``` + */ +const useInput = (inputHandler: Handler, options: Options = {}) => { + const { setRawMode, internal_exitOnCtrlC, internal_eventEmitter } = useStdin() + + // useLayoutEffect (not useEffect) so that raw mode is enabled synchronously + // during React's commit phase, before render() returns. With useEffect, raw + // mode setup is deferred to the next event loop tick via React's scheduler, + // leaving the terminal in cooked mode — keystrokes echo and the cursor is + // visible until the effect fires. + useLayoutEffect(() => { + if (options.isActive === false) { + return + } + + setRawMode(true) + + return () => { + setRawMode(false) + } + }, [options.isActive, setRawMode]) + + // Register the listener once on mount so its slot in the EventEmitter's + // listener array is stable. If isActive were in the effect's deps, the + // listener would re-append on false→true, moving it behind listeners + // that registered while it was inactive — breaking + // stopImmediatePropagation() ordering. useEventCallback keeps the + // reference stable while reading latest isActive/inputHandler from + // closure (it syncs via useLayoutEffect, so it's compiler-safe). + const handleData = useEventCallback((event: InputEvent) => { + if (options.isActive === false) { + return + } + const { input, key } = event + + // If app is not supposed to exit on Ctrl+C, then let input listener handle it + // Note: discreteUpdates is called at the App level when emitting events, + // so all listeners are already within a high-priority update context. + if (!(input === 'c' && key.ctrl) || !internal_exitOnCtrlC) { + inputHandler(input, key, event) + } + }) + + useEffect(() => { + internal_eventEmitter?.on('input', handleData) + + return () => { + internal_eventEmitter?.removeListener('input', handleData) + } + }, [internal_eventEmitter, handleData]) +} + +export default useInput diff --git a/src/ink/hooks/use-interval.ts b/src/ink/hooks/use-interval.ts new file mode 100644 index 0000000000000000000000000000000000000000..49c3ee6279b119b0ebea5e103944961dc1fe0f27 --- /dev/null +++ b/src/ink/hooks/use-interval.ts @@ -0,0 +1,67 @@ +import { useContext, useEffect, useRef, useState } from 'react' +import { ClockContext } from '../components/ClockContext.js' + +/** + * Returns the clock time, updating at the given interval. + * Subscribes as non-keepAlive — won't keep the clock alive on its own, + * but updates whenever a keepAlive subscriber (e.g. the spinner) + * is driving the clock. + * + * Use this to drive pure time-based computations (shimmer position, + * frame index) from the shared clock. + */ +export function useAnimationTimer(intervalMs: number): number { + const clock = useContext(ClockContext) + const [time, setTime] = useState(() => clock?.now() ?? 0) + + useEffect(() => { + if (!clock) return + + let lastUpdate = clock.now() + + const onChange = (): void => { + const now = clock.now() + if (now - lastUpdate >= intervalMs) { + lastUpdate = now + setTime(now) + } + } + + return clock.subscribe(onChange, false) + }, [clock, intervalMs]) + + return time +} + +/** + * Interval hook backed by the shared Clock. + * + * Unlike `useInterval` from `usehooks-ts` (which creates its own setInterval), + * this piggybacks on the single shared clock so all timers consolidate into + * one wake-up. Pass `null` for intervalMs to pause. + */ +export function useInterval( + callback: () => void, + intervalMs: number | null, +): void { + const callbackRef = useRef(callback) + callbackRef.current = callback + + const clock = useContext(ClockContext) + + useEffect(() => { + if (!clock || intervalMs === null) return + + let lastUpdate = clock.now() + + const onChange = (): void => { + const now = clock.now() + if (now - lastUpdate >= intervalMs) { + lastUpdate = now + callbackRef.current() + } + } + + return clock.subscribe(onChange, false) + }, [clock, intervalMs]) +} diff --git a/src/ink/hooks/use-search-highlight.ts b/src/ink/hooks/use-search-highlight.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce9fc364dccc0c6f675402dc672e1b8fb8d53807 --- /dev/null +++ b/src/ink/hooks/use-search-highlight.ts @@ -0,0 +1,53 @@ +import { useContext, useMemo } from 'react' +import StdinContext from '../components/StdinContext.js' +import type { DOMElement } from '../dom.js' +import instances from '../instances.js' +import type { MatchPosition } from '../render-to-screen.js' + +/** + * Set the search highlight query on the Ink instance. Non-empty → all + * visible occurrences are inverted on the next frame (SGR 7, screen-buffer + * overlay, same damage machinery as selection). Empty → clears. + * + * This is a screen-space highlight — it matches the RENDERED text, not the + * source message text. Works for anything visible (bash output, file paths, + * error messages) regardless of where it came from in the message tree. A + * query that matched in source but got truncated/ellipsized in rendering + * won't highlight; that's acceptable — we highlight what you see. + */ +export function useSearchHighlight(): { + setQuery: (query: string) => void + /** Paint an existing DOM subtree (from the MAIN tree) to a fresh + * Screen at its natural height, scan. Element-relative positions + * (row 0 = element top). Zero context duplication — the element + * IS the one built with all real providers. */ + scanElement: (el: DOMElement) => MatchPosition[] + /** Position-based CURRENT highlight. Every frame writes yellow at + * positions[currentIdx] + rowOffset. The scan-highlight (inverse on + * all matches) still runs — this overlays on top. rowOffset tracks + * scroll; positions stay stable (message-relative). null clears. */ + setPositions: ( + state: { + positions: MatchPosition[] + rowOffset: number + currentIdx: number + } | null, + ) => void +} { + useContext(StdinContext) // anchor to App subtree for hook rules + const ink = instances.get(process.stdout) + return useMemo(() => { + if (!ink) { + return { + setQuery: () => {}, + scanElement: () => [], + setPositions: () => {}, + } + } + return { + setQuery: (query: string) => ink.setSearchHighlight(query), + scanElement: (el: DOMElement) => ink.scanElementSubtree(el), + setPositions: state => ink.setSearchPositions(state), + } + }, [ink]) +} diff --git a/src/ink/hooks/use-selection.ts b/src/ink/hooks/use-selection.ts new file mode 100644 index 0000000000000000000000000000000000000000..f7e1d45717c18282bca8c307c60f7d9038dc7471 --- /dev/null +++ b/src/ink/hooks/use-selection.ts @@ -0,0 +1,104 @@ +import { useContext, useMemo, useSyncExternalStore } from 'react' +import StdinContext from '../components/StdinContext.js' +import instances from '../instances.js' +import { + type FocusMove, + type SelectionState, + shiftAnchor, +} from '../selection.js' + +/** + * Access to text selection operations on the Ink instance (fullscreen only). + * Returns no-op functions when fullscreen mode is disabled. + */ +export function useSelection(): { + copySelection: () => string + /** Copy without clearing the highlight (for copy-on-select). */ + copySelectionNoClear: () => string + clearSelection: () => void + hasSelection: () => boolean + /** Read the raw mutable selection state (for drag-to-scroll). */ + getState: () => SelectionState | null + /** Subscribe to selection mutations (start/update/finish/clear). */ + subscribe: (cb: () => void) => () => void + /** Shift the anchor row by dRow, clamped to [minRow, maxRow]. */ + shiftAnchor: (dRow: number, minRow: number, maxRow: number) => void + /** Shift anchor AND focus by dRow (keyboard scroll: whole selection + * tracks content). Clamped points get col reset to the full-width edge + * since their content was captured by captureScrolledRows. Reads + * screen.width from the ink instance for the col-reset boundary. */ + shiftSelection: (dRow: number, minRow: number, maxRow: number) => void + /** Keyboard selection extension (shift+arrow): move focus, anchor fixed. + * Left/right wrap across rows; up/down clamp at viewport edges. */ + moveFocus: (move: FocusMove) => void + /** Capture text from rows about to scroll out of the viewport (call + * BEFORE scrollBy so the screen buffer still has the outgoing rows). */ + captureScrolledRows: ( + firstRow: number, + lastRow: number, + side: 'above' | 'below', + ) => void + /** Set the selection highlight bg color (theme-piping; solid bg + * replaces the old SGR-7 inverse so syntax highlighting stays readable + * under selection). Call once on mount + whenever theme changes. */ + setSelectionBgColor: (color: string) => void +} { + // Look up the Ink instance via stdout — same pattern as instances map. + // StdinContext is available (it's always provided), and the Ink instance + // is keyed by stdout which we can get from process.stdout since there's + // only one Ink instance per process in practice. + useContext(StdinContext) // anchor to App subtree for hook rules + const ink = instances.get(process.stdout) + // Memoize so callers can safely use the return value in dependency arrays. + // ink is a singleton per stdout — stable across renders. + return useMemo(() => { + if (!ink) { + return { + copySelection: () => '', + copySelectionNoClear: () => '', + clearSelection: () => {}, + hasSelection: () => false, + getState: () => null, + subscribe: () => () => {}, + shiftAnchor: () => {}, + shiftSelection: () => {}, + moveFocus: () => {}, + captureScrolledRows: () => {}, + setSelectionBgColor: () => {}, + } + } + return { + copySelection: () => ink.copySelection(), + copySelectionNoClear: () => ink.copySelectionNoClear(), + clearSelection: () => ink.clearTextSelection(), + hasSelection: () => ink.hasTextSelection(), + getState: () => ink.selection, + subscribe: (cb: () => void) => ink.subscribeToSelectionChange(cb), + shiftAnchor: (dRow: number, minRow: number, maxRow: number) => + shiftAnchor(ink.selection, dRow, minRow, maxRow), + shiftSelection: (dRow, minRow, maxRow) => + ink.shiftSelectionForScroll(dRow, minRow, maxRow), + moveFocus: (move: FocusMove) => ink.moveSelectionFocus(move), + captureScrolledRows: (firstRow, lastRow, side) => + ink.captureScrolledRows(firstRow, lastRow, side), + setSelectionBgColor: (color: string) => ink.setSelectionBgColor(color), + } + }, [ink]) +} + +const NO_SUBSCRIBE = () => () => {} +const ALWAYS_FALSE = () => false + +/** + * Reactive selection-exists state. Re-renders the caller when a text + * selection is created or cleared. Always returns false outside + * fullscreen mode (selection is only available in alt-screen). + */ +export function useHasSelection(): boolean { + useContext(StdinContext) + const ink = instances.get(process.stdout) + return useSyncExternalStore( + ink ? ink.subscribeToSelectionChange : NO_SUBSCRIBE, + ink ? ink.hasTextSelection : ALWAYS_FALSE, + ) +} diff --git a/src/ink/hooks/use-stdin.ts b/src/ink/hooks/use-stdin.ts new file mode 100644 index 0000000000000000000000000000000000000000..997f3c3883f1c787befdc640cd730471487db9c5 --- /dev/null +++ b/src/ink/hooks/use-stdin.ts @@ -0,0 +1,8 @@ +import { useContext } from 'react' +import StdinContext from '../components/StdinContext.js' + +/** + * `useStdin` is a React hook, which exposes stdin stream. + */ +const useStdin = () => useContext(StdinContext) +export default useStdin diff --git a/src/ink/hooks/use-terminal-focus.ts b/src/ink/hooks/use-terminal-focus.ts new file mode 100644 index 0000000000000000000000000000000000000000..b717f7b469550cbe649fab4b913a4e023ecb67e5 --- /dev/null +++ b/src/ink/hooks/use-terminal-focus.ts @@ -0,0 +1,16 @@ +import { useContext } from 'react' +import TerminalFocusContext from '../components/TerminalFocusContext.js' + +/** + * Hook to check if the terminal has focus. + * + * Uses DECSET 1004 focus reporting - the terminal sends escape sequences + * when it gains or loses focus. These are handled automatically + * by Ink and filtered from useInput. + * + * @returns true if the terminal is focused (or focus state is unknown) + */ +export function useTerminalFocus(): boolean { + const { isTerminalFocused } = useContext(TerminalFocusContext) + return isTerminalFocused +} diff --git a/src/ink/hooks/use-terminal-title.ts b/src/ink/hooks/use-terminal-title.ts new file mode 100644 index 0000000000000000000000000000000000000000..d820cd7ae94a0f4a69c8188cc3a5966e72e6b8f5 --- /dev/null +++ b/src/ink/hooks/use-terminal-title.ts @@ -0,0 +1,31 @@ +import { useContext, useEffect } from 'react' +import stripAnsi from 'strip-ansi' +import { OSC, osc } from '../termio/osc.js' +import { TerminalWriteContext } from '../useTerminalNotification.js' + +/** + * Declaratively set the terminal tab/window title. + * + * Pass a string to set the title. ANSI escape sequences are stripped + * automatically so callers don't need to know about terminal encoding. + * Pass `null` to opt out — the hook becomes a no-op and leaves the + * terminal title untouched. + * + * On Windows, uses `process.title` (classic conhost doesn't support OSC). + * Elsewhere, writes OSC 0 (set title+icon) via Ink's stdout. + */ +export function useTerminalTitle(title: string | null): void { + const writeRaw = useContext(TerminalWriteContext) + + useEffect(() => { + if (title === null || !writeRaw) return + + const clean = stripAnsi(title) + + if (process.platform === 'win32') { + process.title = clean + } else { + writeRaw(osc(OSC.SET_TITLE_AND_ICON, clean)) + } + }, [title, writeRaw]) +} diff --git a/src/ink/hooks/use-terminal-viewport.ts b/src/ink/hooks/use-terminal-viewport.ts new file mode 100644 index 0000000000000000000000000000000000000000..91193bf73f62b8af0f7bba8044284e9951bb4faf --- /dev/null +++ b/src/ink/hooks/use-terminal-viewport.ts @@ -0,0 +1,96 @@ +import { useCallback, useContext, useLayoutEffect, useRef } from 'react' +import { TerminalSizeContext } from '../components/TerminalSizeContext.js' +import type { DOMElement } from '../dom.js' + +type ViewportEntry = { + /** + * Whether the element is currently within the terminal viewport + */ + isVisible: boolean +} + +/** + * Hook to detect if a component is within the terminal viewport. + * + * Returns a callback ref and a viewport entry object. + * Attach the ref to the component you want to track. + * + * The entry is updated during the layout phase (useLayoutEffect) so callers + * always read fresh values during render. Visibility changes do NOT trigger + * re-renders on their own — callers that re-render for other reasons (e.g. + * animation ticks, state changes) will pick up the latest value naturally. + * This avoids infinite update loops when combined with other layout effects + * that also call setState. + * + * @example + * const [ref, entry] = useTerminalViewport() + * return ... + */ +export function useTerminalViewport(): [ + ref: (element: DOMElement | null) => void, + entry: ViewportEntry, +] { + const terminalSize = useContext(TerminalSizeContext) + const elementRef = useRef(null) + const entryRef = useRef({ isVisible: true }) + + const setElement = useCallback((el: DOMElement | null) => { + elementRef.current = el + }, []) + + // Runs on every render because yoga layout values can change + // without React being aware. Only updates the ref — no setState + // to avoid cascading re-renders during the commit phase. + // Walks the DOM ancestor chain fresh each time to avoid holding stale + // references after yoga tree rebuilds. + useLayoutEffect(() => { + const element = elementRef.current + if (!element?.yogaNode || !terminalSize) { + return + } + + const height = element.yogaNode.getComputedHeight() + const rows = terminalSize.rows + + // Walk the DOM parent chain (not yoga.getParent()) so we can detect + // scroll containers and subtract their scrollTop. Yoga computes layout + // positions without scroll offset — scrollTop is applied at render time. + // Without this, an element inside a ScrollBox whose yoga position exceeds + // terminalRows would be considered offscreen even when scrolled into view + // (e.g., the spinner in fullscreen mode after enough messages accumulate). + let absoluteTop = element.yogaNode.getComputedTop() + let parent: DOMElement | undefined = element.parentNode + let root = element.yogaNode + while (parent) { + if (parent.yogaNode) { + absoluteTop += parent.yogaNode.getComputedTop() + root = parent.yogaNode + } + // scrollTop is only ever set on scroll containers (by ScrollBox + renderer). + // Non-scroll nodes have undefined scrollTop → falsy fast-path. + if (parent.scrollTop) absoluteTop -= parent.scrollTop + parent = parent.parentNode + } + + // Only the root's height matters + const screenHeight = root.getComputedHeight() + + const bottom = absoluteTop + height + // When content overflows the viewport (screenHeight > rows), the + // cursor-restore at frame end scrolls one extra row into scrollback. + // log-update.ts accounts for this with scrollbackRows = viewportY + 1. + // We must match, otherwise an element at the boundary is considered + // "visible" here (animation keeps ticking) but its row is treated as + // scrollback by log-update (content change → full reset → flicker). + const cursorRestoreScroll = screenHeight > rows ? 1 : 0 + const viewportY = Math.max(0, screenHeight - rows) + cursorRestoreScroll + const viewportBottom = viewportY + rows + const visible = bottom > viewportY && absoluteTop < viewportBottom + + if (visible !== entryRef.current.isVisible) { + entryRef.current = { isVisible: visible } + } + }) + + return [setElement, entryRef.current] +} diff --git a/src/ink/layout/engine.ts b/src/ink/layout/engine.ts new file mode 100644 index 0000000000000000000000000000000000000000..38f6dcb0fb5614d423f9a59cedb31d696ddeeef6 --- /dev/null +++ b/src/ink/layout/engine.ts @@ -0,0 +1,6 @@ +import type { LayoutNode } from './node.js' +import { createYogaLayoutNode } from './yoga.js' + +export function createLayoutNode(): LayoutNode { + return createYogaLayoutNode() +} diff --git a/src/ink/layout/geometry.ts b/src/ink/layout/geometry.ts new file mode 100644 index 0000000000000000000000000000000000000000..e586f8ecf11df704babb9ad283f80e1c818b4477 --- /dev/null +++ b/src/ink/layout/geometry.ts @@ -0,0 +1,97 @@ +export type Point = { + x: number + y: number +} + +export type Size = { + width: number + height: number +} + +export type Rectangle = Point & Size + +/** Edge insets (padding, margin, border) */ +export type Edges = { + top: number + right: number + bottom: number + left: number +} + +/** Create uniform edges */ +export function edges(all: number): Edges +export function edges(vertical: number, horizontal: number): Edges +export function edges( + top: number, + right: number, + bottom: number, + left: number, +): Edges +export function edges(a: number, b?: number, c?: number, d?: number): Edges { + if (b === undefined) { + return { top: a, right: a, bottom: a, left: a } + } + if (c === undefined) { + return { top: a, right: b, bottom: a, left: b } + } + return { top: a, right: b, bottom: c, left: d! } +} + +/** Add two edge values */ +export function addEdges(a: Edges, b: Edges): Edges { + return { + top: a.top + b.top, + right: a.right + b.right, + bottom: a.bottom + b.bottom, + left: a.left + b.left, + } +} + +/** Zero edges constant */ +export const ZERO_EDGES: Edges = { top: 0, right: 0, bottom: 0, left: 0 } + +/** Convert partial edges to full edges with defaults */ +export function resolveEdges(partial?: Partial): Edges { + return { + top: partial?.top ?? 0, + right: partial?.right ?? 0, + bottom: partial?.bottom ?? 0, + left: partial?.left ?? 0, + } +} + +export function unionRect(a: Rectangle, b: Rectangle): Rectangle { + const minX = Math.min(a.x, b.x) + const minY = Math.min(a.y, b.y) + const maxX = Math.max(a.x + a.width, b.x + b.width) + const maxY = Math.max(a.y + a.height, b.y + b.height) + return { x: minX, y: minY, width: maxX - minX, height: maxY - minY } +} + +export function clampRect(rect: Rectangle, size: Size): Rectangle { + const minX = Math.max(0, rect.x) + const minY = Math.max(0, rect.y) + const maxX = Math.min(size.width - 1, rect.x + rect.width - 1) + const maxY = Math.min(size.height - 1, rect.y + rect.height - 1) + return { + x: minX, + y: minY, + width: Math.max(0, maxX - minX + 1), + height: Math.max(0, maxY - minY + 1), + } +} + +export function withinBounds(size: Size, point: Point): boolean { + return ( + point.x >= 0 && + point.y >= 0 && + point.x < size.width && + point.y < size.height + ) +} + +export function clamp(value: number, min?: number, max?: number): number { + if (min !== undefined && value < min) return min + if (max !== undefined && value > max) return max + return value +} diff --git a/src/ink/layout/yoga.ts b/src/ink/layout/yoga.ts new file mode 100644 index 0000000000000000000000000000000000000000..58f2646fba661835d478477e16cc6139a7b69174 --- /dev/null +++ b/src/ink/layout/yoga.ts @@ -0,0 +1,308 @@ +import Yoga, { + Align, + Direction, + Display, + Edge, + FlexDirection, + Gutter, + Justify, + MeasureMode, + Overflow, + PositionType, + Wrap, + type Node as YogaNode, +} from 'src/native-ts/yoga-layout/index.js' +import { + type LayoutAlign, + LayoutDisplay, + type LayoutEdge, + type LayoutFlexDirection, + type LayoutGutter, + type LayoutJustify, + type LayoutMeasureFunc, + LayoutMeasureMode, + type LayoutNode, + type LayoutOverflow, + type LayoutPositionType, + type LayoutWrap, +} from './node.js' + +// -- +// Edge/Gutter mapping + +const EDGE_MAP: Record = { + all: Edge.All, + horizontal: Edge.Horizontal, + vertical: Edge.Vertical, + left: Edge.Left, + right: Edge.Right, + top: Edge.Top, + bottom: Edge.Bottom, + start: Edge.Start, + end: Edge.End, +} + +const GUTTER_MAP: Record = { + all: Gutter.All, + column: Gutter.Column, + row: Gutter.Row, +} + +// -- +// Yoga adapter + +export class YogaLayoutNode implements LayoutNode { + readonly yoga: YogaNode + + constructor(yoga: YogaNode) { + this.yoga = yoga + } + + // Tree + + insertChild(child: LayoutNode, index: number): void { + this.yoga.insertChild((child as YogaLayoutNode).yoga, index) + } + + removeChild(child: LayoutNode): void { + this.yoga.removeChild((child as YogaLayoutNode).yoga) + } + + getChildCount(): number { + return this.yoga.getChildCount() + } + + getParent(): LayoutNode | null { + const p = this.yoga.getParent() + return p ? new YogaLayoutNode(p) : null + } + + // Layout + + calculateLayout(width?: number, _height?: number): void { + this.yoga.calculateLayout(width, undefined, Direction.LTR) + } + + setMeasureFunc(fn: LayoutMeasureFunc): void { + this.yoga.setMeasureFunc((w, wMode) => { + const mode = + wMode === MeasureMode.Exactly + ? LayoutMeasureMode.Exactly + : wMode === MeasureMode.AtMost + ? LayoutMeasureMode.AtMost + : LayoutMeasureMode.Undefined + return fn(w, mode) + }) + } + + unsetMeasureFunc(): void { + this.yoga.unsetMeasureFunc() + } + + markDirty(): void { + this.yoga.markDirty() + } + + // Computed layout + + getComputedLeft(): number { + return this.yoga.getComputedLeft() + } + + getComputedTop(): number { + return this.yoga.getComputedTop() + } + + getComputedWidth(): number { + return this.yoga.getComputedWidth() + } + + getComputedHeight(): number { + return this.yoga.getComputedHeight() + } + + getComputedBorder(edge: LayoutEdge): number { + return this.yoga.getComputedBorder(EDGE_MAP[edge]!) + } + + getComputedPadding(edge: LayoutEdge): number { + return this.yoga.getComputedPadding(EDGE_MAP[edge]!) + } + + // Style setters + + setWidth(value: number): void { + this.yoga.setWidth(value) + } + setWidthPercent(value: number): void { + this.yoga.setWidthPercent(value) + } + setWidthAuto(): void { + this.yoga.setWidthAuto() + } + setHeight(value: number): void { + this.yoga.setHeight(value) + } + setHeightPercent(value: number): void { + this.yoga.setHeightPercent(value) + } + setHeightAuto(): void { + this.yoga.setHeightAuto() + } + setMinWidth(value: number): void { + this.yoga.setMinWidth(value) + } + setMinWidthPercent(value: number): void { + this.yoga.setMinWidthPercent(value) + } + setMinHeight(value: number): void { + this.yoga.setMinHeight(value) + } + setMinHeightPercent(value: number): void { + this.yoga.setMinHeightPercent(value) + } + setMaxWidth(value: number): void { + this.yoga.setMaxWidth(value) + } + setMaxWidthPercent(value: number): void { + this.yoga.setMaxWidthPercent(value) + } + setMaxHeight(value: number): void { + this.yoga.setMaxHeight(value) + } + setMaxHeightPercent(value: number): void { + this.yoga.setMaxHeightPercent(value) + } + + setFlexDirection(dir: LayoutFlexDirection): void { + const map: Record = { + row: FlexDirection.Row, + 'row-reverse': FlexDirection.RowReverse, + column: FlexDirection.Column, + 'column-reverse': FlexDirection.ColumnReverse, + } + this.yoga.setFlexDirection(map[dir]!) + } + + setFlexGrow(value: number): void { + this.yoga.setFlexGrow(value) + } + setFlexShrink(value: number): void { + this.yoga.setFlexShrink(value) + } + setFlexBasis(value: number): void { + this.yoga.setFlexBasis(value) + } + setFlexBasisPercent(value: number): void { + this.yoga.setFlexBasisPercent(value) + } + + setFlexWrap(wrap: LayoutWrap): void { + const map: Record = { + nowrap: Wrap.NoWrap, + wrap: Wrap.Wrap, + 'wrap-reverse': Wrap.WrapReverse, + } + this.yoga.setFlexWrap(map[wrap]!) + } + + setAlignItems(align: LayoutAlign): void { + const map: Record = { + auto: Align.Auto, + stretch: Align.Stretch, + 'flex-start': Align.FlexStart, + center: Align.Center, + 'flex-end': Align.FlexEnd, + } + this.yoga.setAlignItems(map[align]!) + } + + setAlignSelf(align: LayoutAlign): void { + const map: Record = { + auto: Align.Auto, + stretch: Align.Stretch, + 'flex-start': Align.FlexStart, + center: Align.Center, + 'flex-end': Align.FlexEnd, + } + this.yoga.setAlignSelf(map[align]!) + } + + setJustifyContent(justify: LayoutJustify): void { + const map: Record = { + 'flex-start': Justify.FlexStart, + center: Justify.Center, + 'flex-end': Justify.FlexEnd, + 'space-between': Justify.SpaceBetween, + 'space-around': Justify.SpaceAround, + 'space-evenly': Justify.SpaceEvenly, + } + this.yoga.setJustifyContent(map[justify]!) + } + + setDisplay(display: LayoutDisplay): void { + this.yoga.setDisplay(display === 'flex' ? Display.Flex : Display.None) + } + + getDisplay(): LayoutDisplay { + return this.yoga.getDisplay() === Display.None + ? LayoutDisplay.None + : LayoutDisplay.Flex + } + + setPositionType(type: LayoutPositionType): void { + this.yoga.setPositionType( + type === 'absolute' ? PositionType.Absolute : PositionType.Relative, + ) + } + + setPosition(edge: LayoutEdge, value: number): void { + this.yoga.setPosition(EDGE_MAP[edge]!, value) + } + + setPositionPercent(edge: LayoutEdge, value: number): void { + this.yoga.setPositionPercent(EDGE_MAP[edge]!, value) + } + + setOverflow(overflow: LayoutOverflow): void { + const map: Record = { + visible: Overflow.Visible, + hidden: Overflow.Hidden, + scroll: Overflow.Scroll, + } + this.yoga.setOverflow(map[overflow]!) + } + + setMargin(edge: LayoutEdge, value: number): void { + this.yoga.setMargin(EDGE_MAP[edge]!, value) + } + setPadding(edge: LayoutEdge, value: number): void { + this.yoga.setPadding(EDGE_MAP[edge]!, value) + } + setBorder(edge: LayoutEdge, value: number): void { + this.yoga.setBorder(EDGE_MAP[edge]!, value) + } + setGap(gutter: LayoutGutter, value: number): void { + this.yoga.setGap(GUTTER_MAP[gutter]!, value) + } + + // Lifecycle + + free(): void { + this.yoga.free() + } + freeRecursive(): void { + this.yoga.freeRecursive() + } +} + +// -- +// Instance management +// +// The TS yoga-layout port is synchronous — no WASM loading, no linear memory +// growth, so no preload/swap/reset machinery is needed. The Yoga instance is +// just a plain JS object available at import time. + +export function createYogaLayoutNode(): LayoutNode { + return new YogaLayoutNode(Yoga.Node.create()) +} diff --git a/src/ink/termio/csi.ts b/src/ink/termio/csi.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3b2f524bf824a9affbc2ad673de18ac03cbc346 --- /dev/null +++ b/src/ink/termio/csi.ts @@ -0,0 +1,319 @@ +/** + * CSI (Control Sequence Introducer) Types + * + * Enums and types for CSI command parameters. + */ + +import { ESC, ESC_TYPE, SEP } from './ansi.js' + +export const CSI_PREFIX = ESC + String.fromCharCode(ESC_TYPE.CSI) + +/** + * CSI parameter byte ranges + */ +export const CSI_RANGE = { + PARAM_START: 0x30, + PARAM_END: 0x3f, + INTERMEDIATE_START: 0x20, + INTERMEDIATE_END: 0x2f, + FINAL_START: 0x40, + FINAL_END: 0x7e, +} as const + +/** Check if a byte is a CSI parameter byte */ +export function isCSIParam(byte: number): boolean { + return byte >= CSI_RANGE.PARAM_START && byte <= CSI_RANGE.PARAM_END +} + +/** Check if a byte is a CSI intermediate byte */ +export function isCSIIntermediate(byte: number): boolean { + return ( + byte >= CSI_RANGE.INTERMEDIATE_START && byte <= CSI_RANGE.INTERMEDIATE_END + ) +} + +/** Check if a byte is a CSI final byte (@ through ~) */ +export function isCSIFinal(byte: number): boolean { + return byte >= CSI_RANGE.FINAL_START && byte <= CSI_RANGE.FINAL_END +} + +/** + * Generate a CSI sequence: ESC [ p1;p2;...;pN final + * Single arg: treated as raw body + * Multiple args: last is final byte, rest are params joined by ; + */ +export function csi(...args: (string | number)[]): string { + if (args.length === 0) return CSI_PREFIX + if (args.length === 1) return `${CSI_PREFIX}${args[0]}` + const params = args.slice(0, -1) + const final = args[args.length - 1] + return `${CSI_PREFIX}${params.join(SEP)}${final}` +} + +/** + * CSI final bytes - the command identifier + */ +export const CSI = { + // Cursor movement + CUU: 0x41, // A - Cursor Up + CUD: 0x42, // B - Cursor Down + CUF: 0x43, // C - Cursor Forward + CUB: 0x44, // D - Cursor Back + CNL: 0x45, // E - Cursor Next Line + CPL: 0x46, // F - Cursor Previous Line + CHA: 0x47, // G - Cursor Horizontal Absolute + CUP: 0x48, // H - Cursor Position + CHT: 0x49, // I - Cursor Horizontal Tab + VPA: 0x64, // d - Vertical Position Absolute + HVP: 0x66, // f - Horizontal Vertical Position + + // Erase + ED: 0x4a, // J - Erase in Display + EL: 0x4b, // K - Erase in Line + ECH: 0x58, // X - Erase Character + + // Insert/Delete + IL: 0x4c, // L - Insert Lines + DL: 0x4d, // M - Delete Lines + ICH: 0x40, // @ - Insert Characters + DCH: 0x50, // P - Delete Characters + + // Scroll + SU: 0x53, // S - Scroll Up + SD: 0x54, // T - Scroll Down + + // Modes + SM: 0x68, // h - Set Mode + RM: 0x6c, // l - Reset Mode + + // SGR + SGR: 0x6d, // m - Select Graphic Rendition + + // Other + DSR: 0x6e, // n - Device Status Report + DECSCUSR: 0x71, // q - Set Cursor Style (with space intermediate) + DECSTBM: 0x72, // r - Set Top and Bottom Margins + SCOSC: 0x73, // s - Save Cursor Position + SCORC: 0x75, // u - Restore Cursor Position + CBT: 0x5a, // Z - Cursor Backward Tabulation +} as const + +/** + * Erase in Display regions (ED command parameter) + */ +export const ERASE_DISPLAY = ['toEnd', 'toStart', 'all', 'scrollback'] as const + +/** + * Erase in Line regions (EL command parameter) + */ +export const ERASE_LINE_REGION = ['toEnd', 'toStart', 'all'] as const + +/** + * Cursor styles (DECSCUSR) + */ +export type CursorStyle = 'block' | 'underline' | 'bar' + +export const CURSOR_STYLES: Array<{ style: CursorStyle; blinking: boolean }> = [ + { style: 'block', blinking: true }, // 0 - default + { style: 'block', blinking: true }, // 1 + { style: 'block', blinking: false }, // 2 + { style: 'underline', blinking: true }, // 3 + { style: 'underline', blinking: false }, // 4 + { style: 'bar', blinking: true }, // 5 + { style: 'bar', blinking: false }, // 6 +] + +// Cursor movement generators + +/** Move cursor up n lines (CSI n A) */ +export function cursorUp(n = 1): string { + return n === 0 ? '' : csi(n, 'A') +} + +/** Move cursor down n lines (CSI n B) */ +export function cursorDown(n = 1): string { + return n === 0 ? '' : csi(n, 'B') +} + +/** Move cursor forward n columns (CSI n C) */ +export function cursorForward(n = 1): string { + return n === 0 ? '' : csi(n, 'C') +} + +/** Move cursor back n columns (CSI n D) */ +export function cursorBack(n = 1): string { + return n === 0 ? '' : csi(n, 'D') +} + +/** Move cursor to column n (1-indexed) (CSI n G) */ +export function cursorTo(col: number): string { + return csi(col, 'G') +} + +/** Move cursor to column 1 (CSI G) */ +export const CURSOR_LEFT = csi('G') + +/** Move cursor to row, col (1-indexed) (CSI row ; col H) */ +export function cursorPosition(row: number, col: number): string { + return csi(row, col, 'H') +} + +/** Move cursor to home position (CSI H) */ +export const CURSOR_HOME = csi('H') + +/** + * Move cursor relative to current position + * Positive x = right, negative x = left + * Positive y = down, negative y = up + */ +export function cursorMove(x: number, y: number): string { + let result = '' + // Horizontal first (matches ansi-escapes behavior) + if (x < 0) { + result += cursorBack(-x) + } else if (x > 0) { + result += cursorForward(x) + } + // Then vertical + if (y < 0) { + result += cursorUp(-y) + } else if (y > 0) { + result += cursorDown(y) + } + return result +} + +// Save/restore cursor position + +/** Save cursor position (CSI s) */ +export const CURSOR_SAVE = csi('s') + +/** Restore cursor position (CSI u) */ +export const CURSOR_RESTORE = csi('u') + +// Erase generators + +/** Erase from cursor to end of line (CSI K) */ +export function eraseToEndOfLine(): string { + return csi('K') +} + +/** Erase from cursor to start of line (CSI 1 K) */ +export function eraseToStartOfLine(): string { + return csi(1, 'K') +} + +/** Erase entire line (CSI 2 K) */ +export function eraseLine(): string { + return csi(2, 'K') +} + +/** Erase entire line - constant form */ +export const ERASE_LINE = csi(2, 'K') + +/** Erase from cursor to end of screen (CSI J) */ +export function eraseToEndOfScreen(): string { + return csi('J') +} + +/** Erase from cursor to start of screen (CSI 1 J) */ +export function eraseToStartOfScreen(): string { + return csi(1, 'J') +} + +/** Erase entire screen (CSI 2 J) */ +export function eraseScreen(): string { + return csi(2, 'J') +} + +/** Erase entire screen - constant form */ +export const ERASE_SCREEN = csi(2, 'J') + +/** Erase scrollback buffer (CSI 3 J) */ +export const ERASE_SCROLLBACK = csi(3, 'J') + +/** + * Erase n lines starting from cursor line, moving cursor up + * This erases each line and moves up, ending at column 1 + */ +export function eraseLines(n: number): string { + if (n <= 0) return '' + let result = '' + for (let i = 0; i < n; i++) { + result += ERASE_LINE + if (i < n - 1) { + result += cursorUp(1) + } + } + result += CURSOR_LEFT + return result +} + +// Scroll + +/** Scroll up n lines (CSI n S) */ +export function scrollUp(n = 1): string { + return n === 0 ? '' : csi(n, 'S') +} + +/** Scroll down n lines (CSI n T) */ +export function scrollDown(n = 1): string { + return n === 0 ? '' : csi(n, 'T') +} + +/** Set scroll region (DECSTBM, CSI top;bottom r). 1-indexed, inclusive. */ +export function setScrollRegion(top: number, bottom: number): string { + return csi(top, bottom, 'r') +} + +/** Reset scroll region to full screen (DECSTBM, CSI r). Homes the cursor. */ +export const RESET_SCROLL_REGION = csi('r') + +// Bracketed paste markers (input from terminal, not output) +// These are sent by the terminal to delimit pasted content when +// bracketed paste mode is enabled (via DEC mode 2004) + +/** Sent by terminal before pasted content (CSI 200 ~) */ +export const PASTE_START = csi('200~') + +/** Sent by terminal after pasted content (CSI 201 ~) */ +export const PASTE_END = csi('201~') + +// Focus event markers (input from terminal, not output) +// These are sent by the terminal when focus changes while +// focus events mode is enabled (via DEC mode 1004) + +/** Sent by terminal when it gains focus (CSI I) */ +export const FOCUS_IN = csi('I') + +/** Sent by terminal when it loses focus (CSI O) */ +export const FOCUS_OUT = csi('O') + +// Kitty keyboard protocol (CSI u) +// Enables enhanced key reporting with modifier information +// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/ + +/** + * Enable Kitty keyboard protocol with basic modifier reporting + * CSI > 1 u - pushes mode with flags=1 (disambiguate escape codes) + * This makes Shift+Enter send CSI 13;2 u instead of just CR + */ +export const ENABLE_KITTY_KEYBOARD = csi('>1u') + +/** + * Disable Kitty keyboard protocol + * CSI < u - pops the keyboard mode stack + */ +export const DISABLE_KITTY_KEYBOARD = csi('4;2m') + +/** + * Disable xterm modifyOtherKeys (reset to default). + */ +export const DISABLE_MODIFY_OTHER_KEYS = csi('>4m') diff --git a/src/ink/termio/dec.ts b/src/ink/termio/dec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ac8bcc731c78aeb53305647a81b3fe8b063ff8d7 --- /dev/null +++ b/src/ink/termio/dec.ts @@ -0,0 +1,60 @@ +/** + * DEC (Digital Equipment Corporation) Private Mode Sequences + * + * DEC private modes use CSI ? N h (set) and CSI ? N l (reset) format. + * These are terminal-specific extensions to the ANSI standard. + */ + +import { csi } from './csi.js' + +/** + * DEC private mode numbers + */ +export const DEC = { + CURSOR_VISIBLE: 25, + ALT_SCREEN: 47, + ALT_SCREEN_CLEAR: 1049, + MOUSE_NORMAL: 1000, + MOUSE_BUTTON: 1002, + MOUSE_ANY: 1003, + MOUSE_SGR: 1006, + FOCUS_EVENTS: 1004, + BRACKETED_PASTE: 2004, + SYNCHRONIZED_UPDATE: 2026, +} as const + +/** Generate CSI ? N h sequence (set mode) */ +export function decset(mode: number): string { + return csi(`?${mode}h`) +} + +/** Generate CSI ? N l sequence (reset mode) */ +export function decreset(mode: number): string { + return csi(`?${mode}l`) +} + +// Pre-generated sequences for common modes +export const BSU = decset(DEC.SYNCHRONIZED_UPDATE) +export const ESU = decreset(DEC.SYNCHRONIZED_UPDATE) +export const EBP = decset(DEC.BRACKETED_PASTE) +export const DBP = decreset(DEC.BRACKETED_PASTE) +export const EFE = decset(DEC.FOCUS_EVENTS) +export const DFE = decreset(DEC.FOCUS_EVENTS) +export const SHOW_CURSOR = decset(DEC.CURSOR_VISIBLE) +export const HIDE_CURSOR = decreset(DEC.CURSOR_VISIBLE) +export const ENTER_ALT_SCREEN = decset(DEC.ALT_SCREEN_CLEAR) +export const EXIT_ALT_SCREEN = decreset(DEC.ALT_SCREEN_CLEAR) +// Mouse tracking: 1000 reports button press/release/wheel, 1002 adds drag +// events (button-motion), 1003 adds all-motion (no button held — for +// hover), 1006 uses SGR format (CSI < btn;col;row M/m) instead of legacy +// X10 bytes. Combined: wheel + click/drag for selection + hover. +export const ENABLE_MOUSE_TRACKING = + decset(DEC.MOUSE_NORMAL) + + decset(DEC.MOUSE_BUTTON) + + decset(DEC.MOUSE_ANY) + + decset(DEC.MOUSE_SGR) +export const DISABLE_MOUSE_TRACKING = + decreset(DEC.MOUSE_SGR) + + decreset(DEC.MOUSE_ANY) + + decreset(DEC.MOUSE_BUTTON) + + decreset(DEC.MOUSE_NORMAL) diff --git a/src/ink/termio/esc.ts b/src/ink/termio/esc.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d4cc9286600e4b10a3e726a0d7c1c4c50c885d1 --- /dev/null +++ b/src/ink/termio/esc.ts @@ -0,0 +1,67 @@ +/** + * ESC Sequence Parser + * + * Handles simple escape sequences: ESC + one or two characters + */ + +import type { Action } from './types.js' + +/** + * Parse a simple ESC sequence + * + * @param chars - Characters after ESC (not including ESC itself) + */ +export function parseEsc(chars: string): Action | null { + if (chars.length === 0) return null + + const first = chars[0]! + + // Full reset (RIS) + if (first === 'c') { + return { type: 'reset' } + } + + // Cursor save (DECSC) + if (first === '7') { + return { type: 'cursor', action: { type: 'save' } } + } + + // Cursor restore (DECRC) + if (first === '8') { + return { type: 'cursor', action: { type: 'restore' } } + } + + // Index - move cursor down (IND) + if (first === 'D') { + return { + type: 'cursor', + action: { type: 'move', direction: 'down', count: 1 }, + } + } + + // Reverse index - move cursor up (RI) + if (first === 'M') { + return { + type: 'cursor', + action: { type: 'move', direction: 'up', count: 1 }, + } + } + + // Next line (NEL) + if (first === 'E') { + return { type: 'cursor', action: { type: 'nextLine', count: 1 } } + } + + // Horizontal tab set (HTS) + if (first === 'H') { + return null // Tab stop, not commonly needed + } + + // Charset selection (ESC ( X, ESC ) X, etc.) - silently ignore + if ('()'.includes(first) && chars.length >= 2) { + return null + } + + // Unknown + return { type: 'unknown', sequence: `\x1b${chars}` } +} diff --git a/src/ink/termio/osc.ts b/src/ink/termio/osc.ts new file mode 100644 index 0000000000000000000000000000000000000000..9bef515328535b7fbdd1ab938f3c78477ec704e9 --- /dev/null +++ b/src/ink/termio/osc.ts @@ -0,0 +1,493 @@ +/** + * OSC (Operating System Command) Types and Parser + */ + +import { Buffer } from 'buffer' +import { env } from '../../utils/env.js' +import { execFileNoThrow } from '../../utils/execFileNoThrow.js' +import { BEL, ESC, ESC_TYPE, SEP } from './ansi.js' +import type { Action, Color, TabStatusAction } from './types.js' + +export const OSC_PREFIX = ESC + String.fromCharCode(ESC_TYPE.OSC) + +/** String Terminator (ESC \) - alternative to BEL for terminating OSC */ +export const ST = ESC + '\\' + +/** Generate an OSC sequence: ESC ] p1;p2;...;pN + * Uses ST terminator for Kitty (avoids beeps), BEL for others */ +export function osc(...parts: (string | number)[]): string { + const terminator = env.terminal === 'kitty' ? ST : BEL + return `${OSC_PREFIX}${parts.join(SEP)}${terminator}` +} + +/** + * Wrap an escape sequence for terminal multiplexer passthrough. + * tmux and GNU screen intercept escape sequences; DCS passthrough + * tunnels them to the outer terminal unmodified. + * + * tmux 3.3+ gates this behind `allow-passthrough` (default off). When off, + * tmux silently drops the whole DCS — no junk, no worse than unwrapped OSC. + * Users who want passthrough set it in their .tmux.conf; we don't mutate it. + * + * Do NOT wrap BEL: raw \x07 triggers tmux's bell-action (window flag); + * wrapped \x07 is opaque DCS payload and tmux never sees the bell. + */ +export function wrapForMultiplexer(sequence: string): string { + if (process.env['TMUX']) { + const escaped = sequence.replaceAll('\x1b', '\x1b\x1b') + return `\x1bPtmux;${escaped}\x1b\\` + } + if (process.env['STY']) { + return `\x1bP${sequence}\x1b\\` + } + return sequence +} + +/** + * Which path setClipboard() will take, based on env state. Synchronous so + * callers can show an honest toast without awaiting the copy itself. + * + * - 'native': pbcopy (or equivalent) will run — high-confidence system + * clipboard write. tmux buffer may also be loaded as a bonus. + * - 'tmux-buffer': tmux load-buffer will run, but no native tool — paste + * with prefix+] works. System clipboard depends on tmux's set-clipboard + * option + outer terminal OSC 52 support; can't know from here. + * - 'osc52': only the raw OSC 52 sequence will be written to stdout. + * Best-effort; iTerm2 disables OSC 52 by default. + * + * pbcopy gating uses SSH_CONNECTION specifically, not SSH_TTY — tmux panes + * inherit SSH_TTY forever even after local reattach, but SSH_CONNECTION is + * in tmux's default update-environment set and gets cleared. + */ +export type ClipboardPath = 'native' | 'tmux-buffer' | 'osc52' + +export function getClipboardPath(): ClipboardPath { + const nativeAvailable = + process.platform === 'darwin' && !process.env['SSH_CONNECTION'] + if (nativeAvailable) return 'native' + if (process.env['TMUX']) return 'tmux-buffer' + return 'osc52' +} + +/** + * Wrap a payload in tmux's DCS passthrough: ESC P tmux ; ESC \ + * tmux forwards the payload to the outer terminal, bypassing its own parser. + * Inner ESCs must be doubled. Requires `set -g allow-passthrough on` in + * ~/.tmux.conf; without it, tmux silently drops the whole DCS (no regression). + */ +function tmuxPassthrough(payload: string): string { + return `${ESC}Ptmux;${payload.replaceAll(ESC, ESC + ESC)}${ST}` +} + +/** + * Load text into tmux's paste buffer via `tmux load-buffer`. + * -w (tmux 3.2+) propagates to the outer terminal's clipboard via tmux's + * own OSC 52 emission. -w is dropped for iTerm2: tmux's OSC 52 emission + * crashes the iTerm2 session over SSH. + * + * Returns true if the buffer was loaded successfully. + */ +export async function tmuxLoadBuffer(text: string): Promise { + if (!process.env['TMUX']) return false + const args = + process.env['LC_TERMINAL'] === 'iTerm2' + ? ['load-buffer', '-'] + : ['load-buffer', '-w', '-'] + const { code } = await execFileNoThrow('tmux', args, { + input: text, + useCwd: false, + timeout: 2000, + }) + return code === 0 +} + +/** + * OSC 52 clipboard write: ESC ] 52 ; c ; BEL/ST + * 'c' selects the clipboard (vs 'p' for primary selection on X11). + * + * When inside tmux ($TMUX set), `tmux load-buffer -w -` is the primary + * path. tmux's buffer is always reachable — works over SSH, survives + * detach/reattach, immune to stale env vars. The -w flag (tmux 3.2+) tells + * tmux to also propagate to the outer terminal via its own OSC 52 path, + * which tmux wraps correctly for the attached client. On older tmux, -w is + * ignored and the buffer is still loaded. -w is dropped for iTerm2 (#22432) + * because tmux's own OSC 52 emission (empty selection param: ESC]52;;b64) + * crashes iTerm2 over SSH. + * + * After load-buffer succeeds, we ALSO return a DCS-passthrough-wrapped + * OSC 52 for the caller to write to stdout. Our sequence uses explicit `c` + * (not tmux's crashy empty-param variant), so it sidesteps the #22432 path. + * With `allow-passthrough on` + an OSC-52-capable outer terminal, selection + * reaches the system clipboard; with either off, tmux silently drops the + * DCS and prefix+] still works. See Greg Smith's "free pony" in + * https://anthropic.slack.com/archives/C07VBSHV7EV/p1773177228548119. + * + * If load-buffer fails entirely, fall through to raw OSC 52. + * + * Outside tmux, write raw OSC 52 to stdout (caller handles the write). + * + * Local (no SSH_CONNECTION): also shell out to a native clipboard utility. + * OSC 52 and tmux -w both depend on terminal settings — iTerm2 disables + * OSC 52 by default, VS Code shows a permission prompt on first use. Native + * utilities (pbcopy/wl-copy/xclip/xsel/clip.exe) always work locally. Over + * SSH these would write to the remote clipboard — OSC 52 is the right path there. + * + * Returns the sequence for the caller to write to stdout (raw OSC 52 + * outside tmux, DCS-wrapped inside). + */ +export async function setClipboard(text: string): Promise { + const b64 = Buffer.from(text, 'utf8').toString('base64') + const raw = osc(OSC.CLIPBOARD, 'c', b64) + + // Native safety net — fire FIRST, before the tmux await, so a quick + // focus-switch after selecting doesn't race pbcopy. Previously this ran + // AFTER awaiting tmux load-buffer, adding ~50-100ms of subprocess latency + // before pbcopy even started — fast cmd+tab → paste would beat it + // (https://anthropic.slack.com/archives/C07VBSHV7EV/p1773943921788829). + // Gated on SSH_CONNECTION (not SSH_TTY) since tmux panes inherit SSH_TTY + // forever but SSH_CONNECTION is in tmux's default update-environment and + // clears on local attach. Fire-and-forget. + if (!process.env['SSH_CONNECTION']) copyNative(text) + + const tmuxBufferLoaded = await tmuxLoadBuffer(text) + + // Inner OSC uses BEL directly (not osc()) — ST's ESC would need doubling + // too, and BEL works everywhere for OSC 52. + if (tmuxBufferLoaded) return tmuxPassthrough(`${ESC}]52;c;${b64}${BEL}`) + return raw +} + +// Linux clipboard tool: undefined = not yet probed, null = none available. +// Probe order: wl-copy (Wayland) → xclip (X11) → xsel (X11 fallback). +// Cached after first attempt so repeated mouse-ups skip the probe chain. +let linuxCopy: 'wl-copy' | 'xclip' | 'xsel' | null | undefined + +/** + * Shell out to a native clipboard utility as a safety net for OSC 52. + * Only called when not in an SSH session (over SSH, these would write to + * the remote machine's clipboard — OSC 52 is the right path there). + * Fire-and-forget: failures are silent since OSC 52 may have succeeded. + */ +function copyNative(text: string): void { + const opts = { input: text, useCwd: false, timeout: 2000 } + switch (process.platform) { + case 'darwin': + void execFileNoThrow('pbcopy', [], opts) + return + case 'linux': { + if (linuxCopy === null) return + if (linuxCopy === 'wl-copy') { + void execFileNoThrow('wl-copy', [], opts) + return + } + if (linuxCopy === 'xclip') { + void execFileNoThrow('xclip', ['-selection', 'clipboard'], opts) + return + } + if (linuxCopy === 'xsel') { + void execFileNoThrow('xsel', ['--clipboard', '--input'], opts) + return + } + // First call: probe wl-copy (Wayland) then xclip/xsel (X11), cache winner. + void execFileNoThrow('wl-copy', [], opts).then(r => { + if (r.code === 0) { + linuxCopy = 'wl-copy' + return + } + void execFileNoThrow('xclip', ['-selection', 'clipboard'], opts).then( + r2 => { + if (r2.code === 0) { + linuxCopy = 'xclip' + return + } + void execFileNoThrow('xsel', ['--clipboard', '--input'], opts).then( + r3 => { + linuxCopy = r3.code === 0 ? 'xsel' : null + }, + ) + }, + ) + }) + return + } + case 'win32': + // clip.exe is always available on Windows. Unicode handling is + // imperfect (system locale encoding) but good enough for a fallback. + void execFileNoThrow('clip', [], opts) + return + } +} + +/** @internal test-only */ +export function _resetLinuxCopyCache(): void { + linuxCopy = undefined +} + +/** + * OSC command numbers + */ +export const OSC = { + SET_TITLE_AND_ICON: 0, + SET_ICON: 1, + SET_TITLE: 2, + SET_COLOR: 4, + SET_CWD: 7, + HYPERLINK: 8, + ITERM2: 9, // iTerm2 proprietary sequences + SET_FG_COLOR: 10, + SET_BG_COLOR: 11, + SET_CURSOR_COLOR: 12, + CLIPBOARD: 52, + KITTY: 99, // Kitty notification protocol + RESET_COLOR: 104, + RESET_FG_COLOR: 110, + RESET_BG_COLOR: 111, + RESET_CURSOR_COLOR: 112, + SEMANTIC_PROMPT: 133, + GHOSTTY: 777, // Ghostty notification protocol + TAB_STATUS: 21337, // Tab status extension +} as const + +/** + * Parse an OSC sequence into an action + * + * @param content - The sequence content (without ESC ] and terminator) + */ +export function parseOSC(content: string): Action | null { + const semicolonIdx = content.indexOf(';') + const command = semicolonIdx >= 0 ? content.slice(0, semicolonIdx) : content + const data = semicolonIdx >= 0 ? content.slice(semicolonIdx + 1) : '' + + const commandNum = parseInt(command, 10) + + // Window/icon title + if (commandNum === OSC.SET_TITLE_AND_ICON) { + return { type: 'title', action: { type: 'both', title: data } } + } + if (commandNum === OSC.SET_ICON) { + return { type: 'title', action: { type: 'iconName', name: data } } + } + if (commandNum === OSC.SET_TITLE) { + return { type: 'title', action: { type: 'windowTitle', title: data } } + } + + // Hyperlinks (OSC 8) + if (commandNum === OSC.HYPERLINK) { + const parts = data.split(';') + const paramsStr = parts[0] ?? '' + const url = parts.slice(1).join(';') + + if (url === '') { + return { type: 'link', action: { type: 'end' } } + } + + const params: Record = {} + if (paramsStr) { + for (const pair of paramsStr.split(':')) { + const eqIdx = pair.indexOf('=') + if (eqIdx >= 0) { + params[pair.slice(0, eqIdx)] = pair.slice(eqIdx + 1) + } + } + } + + return { + type: 'link', + action: { + type: 'start', + url, + params: Object.keys(params).length > 0 ? params : undefined, + }, + } + } + + // Tab status (OSC 21337) + if (commandNum === OSC.TAB_STATUS) { + return { type: 'tabStatus', action: parseTabStatus(data) } + } + + return { type: 'unknown', sequence: `\x1b]${content}` } +} + +/** + * Parse an XParseColor-style color spec into an RGB Color. + * Accepts `#RRGGBB` and `rgb:R/G/B` (1–4 hex digits per component, scaled + * to 8-bit). Returns null on parse failure. + */ +export function parseOscColor(spec: string): Color | null { + const hex = spec.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i) + if (hex) { + return { + type: 'rgb', + r: parseInt(hex[1]!, 16), + g: parseInt(hex[2]!, 16), + b: parseInt(hex[3]!, 16), + } + } + const rgb = spec.match( + /^rgb:([0-9a-f]{1,4})\/([0-9a-f]{1,4})\/([0-9a-f]{1,4})$/i, + ) + if (rgb) { + // XParseColor: N hex digits → value / (16^N - 1), scale to 0-255 + const scale = (s: string) => + Math.round((parseInt(s, 16) / (16 ** s.length - 1)) * 255) + return { + type: 'rgb', + r: scale(rgb[1]!), + g: scale(rgb[2]!), + b: scale(rgb[3]!), + } + } + return null +} + +/** + * Parse OSC 21337 payload: `key=value;key=value;...` with `\;` and `\\` + * escapes inside values. Bare key or `key=` clears that field; unknown + * keys are ignored. + */ +function parseTabStatus(data: string): TabStatusAction { + const action: TabStatusAction = {} + for (const [key, value] of splitTabStatusPairs(data)) { + switch (key) { + case 'indicator': + action.indicator = value === '' ? null : parseOscColor(value) + break + case 'status': + action.status = value === '' ? null : value + break + case 'status-color': + action.statusColor = value === '' ? null : parseOscColor(value) + break + } + } + return action +} + +/** Split `k=v;k=v` honoring `\;` and `\\` escapes. Yields [key, unescapedValue]. */ +function* splitTabStatusPairs(data: string): Generator<[string, string]> { + let key = '' + let val = '' + let inVal = false + let esc = false + for (const c of data) { + if (esc) { + if (inVal) val += c + else key += c + esc = false + } else if (c === '\\') { + esc = true + } else if (c === ';') { + yield [key, val] + key = '' + val = '' + inVal = false + } else if (c === '=' && !inVal) { + inVal = true + } else if (inVal) { + val += c + } else { + key += c + } + } + if (key || inVal) yield [key, val] +} + +// Output generators + +/** Start a hyperlink (OSC 8). Auto-assigns an id= param derived from the URL + * so terminals group wrapped lines of the same link together (the spec says + * cells with matching URI *and* nonempty id are joined; without an id each + * wrapped line is a separate link — inconsistent hover, partial tooltips). + * Empty url = close sequence (empty params per spec). */ +export function link(url: string, params?: Record): string { + if (!url) return LINK_END + const p = { id: osc8Id(url), ...params } + const paramStr = Object.entries(p) + .map(([k, v]) => `${k}=${v}`) + .join(':') + return osc(OSC.HYPERLINK, paramStr, url) +} + +function osc8Id(url: string): string { + let h = 0 + for (let i = 0; i < url.length; i++) + h = ((h << 5) - h + url.charCodeAt(i)) | 0 + return (h >>> 0).toString(36) +} + +/** End a hyperlink (OSC 8) */ +export const LINK_END = osc(OSC.HYPERLINK, '', '') + +// iTerm2 OSC 9 subcommands + +/** iTerm2 OSC 9 subcommand numbers */ +export const ITERM2 = { + NOTIFY: 0, + BADGE: 2, + PROGRESS: 4, +} as const + +/** Progress operation codes (for use with ITERM2.PROGRESS) */ +export const PROGRESS = { + CLEAR: 0, + SET: 1, + ERROR: 2, + INDETERMINATE: 3, +} as const + +/** + * Clear iTerm2 progress bar sequence (OSC 9;4;0;BEL) + * Uses BEL terminator since this is for cleanup (not runtime notification) + * and we want to ensure it's always sent regardless of terminal type. + */ +export const CLEAR_ITERM2_PROGRESS = `${OSC_PREFIX}${OSC.ITERM2};${ITERM2.PROGRESS};${PROGRESS.CLEAR};${BEL}` + +/** + * Clear terminal title sequence (OSC 0 with empty string + BEL). + * Uses BEL terminator for cleanup — safe on all terminals. + */ +export const CLEAR_TERMINAL_TITLE = `${OSC_PREFIX}${OSC.SET_TITLE_AND_ICON};${BEL}` + +/** Clear all three OSC 21337 tab-status fields. Used on exit. */ +export const CLEAR_TAB_STATUS = osc( + OSC.TAB_STATUS, + 'indicator=;status=;status-color=', +) + +/** + * Gate for emitting OSC 21337 (tab-status indicator). Ant-only while the + * spec is unstable. Terminals that don't recognize it discard silently, so + * emission is safe unconditionally — we don't gate on terminal detection + * since support is expected across several terminals. + * + * Callers must wrap output with wrapForMultiplexer() so tmux/screen + * DCS-passthrough carries the sequence to the outer terminal. + */ +export function supportsTabStatus(): boolean { + return process.env.USER_TYPE === 'ant' +} + +/** + * Emit an OSC 21337 tab-status sequence. Omitted fields are left unchanged + * by the receiving terminal; `null` sends an empty value to clear. + * `;` and `\` in status text are escaped per the spec. + */ +export function tabStatus(fields: TabStatusAction): string { + const parts: string[] = [] + const rgb = (c: Color) => + c.type === 'rgb' + ? `#${[c.r, c.g, c.b].map(n => n.toString(16).padStart(2, '0')).join('')}` + : '' + if ('indicator' in fields) + parts.push(`indicator=${fields.indicator ? rgb(fields.indicator) : ''}`) + if ('status' in fields) + parts.push( + `status=${fields.status?.replaceAll('\\', '\\\\').replaceAll(';', '\\;') ?? ''}`, + ) + if ('statusColor' in fields) + parts.push( + `status-color=${fields.statusColor ? rgb(fields.statusColor) : ''}`, + ) + return osc(OSC.TAB_STATUS, parts.join(';')) +} diff --git a/src/ink/termio/parser.ts b/src/ink/termio/parser.ts new file mode 100644 index 0000000000000000000000000000000000000000..301f14c5a0bd3dce2b7d3bf7e0aa901bbf5f99b1 --- /dev/null +++ b/src/ink/termio/parser.ts @@ -0,0 +1,394 @@ +/** + * ANSI Parser - Semantic Action Generator + * + * A streaming parser for ANSI escape sequences that produces semantic actions. + * Uses the tokenizer for escape sequence boundary detection, then interprets + * each sequence to produce structured actions. + * + * Key design decisions: + * - Streaming: can process input incrementally + * - Semantic output: produces structured actions, not string tokens + * - Style tracking: maintains current text style state + */ + +import { getGraphemeSegmenter } from '../../utils/intl.js' +import { C0 } from './ansi.js' +import { CSI, CURSOR_STYLES, ERASE_DISPLAY, ERASE_LINE_REGION } from './csi.js' +import { DEC } from './dec.js' +import { parseEsc } from './esc.js' +import { parseOSC } from './osc.js' +import { applySGR } from './sgr.js' +import { createTokenizer, type Token, type Tokenizer } from './tokenize.js' +import type { Action, Grapheme, TextStyle } from './types.js' +import { defaultStyle } from './types.js' + +// ============================================================================= +// Grapheme Utilities +// ============================================================================= + +function isEmoji(codePoint: number): boolean { + return ( + (codePoint >= 0x2600 && codePoint <= 0x26ff) || + (codePoint >= 0x2700 && codePoint <= 0x27bf) || + (codePoint >= 0x1f300 && codePoint <= 0x1f9ff) || + (codePoint >= 0x1fa00 && codePoint <= 0x1faff) || + (codePoint >= 0x1f1e0 && codePoint <= 0x1f1ff) + ) +} + +function isEastAsianWide(codePoint: number): boolean { + return ( + (codePoint >= 0x1100 && codePoint <= 0x115f) || + (codePoint >= 0x2e80 && codePoint <= 0x9fff) || + (codePoint >= 0xac00 && codePoint <= 0xd7a3) || + (codePoint >= 0xf900 && codePoint <= 0xfaff) || + (codePoint >= 0xfe10 && codePoint <= 0xfe1f) || + (codePoint >= 0xfe30 && codePoint <= 0xfe6f) || + (codePoint >= 0xff00 && codePoint <= 0xff60) || + (codePoint >= 0xffe0 && codePoint <= 0xffe6) || + (codePoint >= 0x20000 && codePoint <= 0x2fffd) || + (codePoint >= 0x30000 && codePoint <= 0x3fffd) + ) +} + +function hasMultipleCodepoints(str: string): boolean { + let count = 0 + for (const _ of str) { + count++ + if (count > 1) return true + } + return false +} + +function graphemeWidth(grapheme: string): 1 | 2 { + if (hasMultipleCodepoints(grapheme)) return 2 + const codePoint = grapheme.codePointAt(0) + if (codePoint === undefined) return 1 + if (isEmoji(codePoint) || isEastAsianWide(codePoint)) return 2 + return 1 +} + +function* segmentGraphemes(str: string): Generator { + for (const { segment } of getGraphemeSegmenter().segment(str)) { + yield { value: segment, width: graphemeWidth(segment) } + } +} + +// ============================================================================= +// Sequence Parsing +// ============================================================================= + +function parseCSIParams(paramStr: string): number[] { + if (paramStr === '') return [] + return paramStr.split(/[;:]/).map(s => (s === '' ? 0 : parseInt(s, 10))) +} + +/** Parse a raw CSI sequence (e.g., "\x1b[31m") into an action */ +function parseCSI(rawSequence: string): Action | null { + const inner = rawSequence.slice(2) + if (inner.length === 0) return null + + const finalByte = inner.charCodeAt(inner.length - 1) + const beforeFinal = inner.slice(0, -1) + + let privateMode = '' + let paramStr = beforeFinal + let intermediate = '' + + if (beforeFinal.length > 0 && '?>='.includes(beforeFinal[0]!)) { + privateMode = beforeFinal[0]! + paramStr = beforeFinal.slice(1) + } + + const intermediateMatch = paramStr.match(/([^0-9;:]+)$/) + if (intermediateMatch) { + intermediate = intermediateMatch[1]! + paramStr = paramStr.slice(0, -intermediate.length) + } + + const params = parseCSIParams(paramStr) + const p0 = params[0] ?? 1 + const p1 = params[1] ?? 1 + + // SGR (Select Graphic Rendition) + if (finalByte === CSI.SGR && privateMode === '') { + return { type: 'sgr', params: paramStr } + } + + // Cursor movement + if (finalByte === CSI.CUU) { + return { + type: 'cursor', + action: { type: 'move', direction: 'up', count: p0 }, + } + } + if (finalByte === CSI.CUD) { + return { + type: 'cursor', + action: { type: 'move', direction: 'down', count: p0 }, + } + } + if (finalByte === CSI.CUF) { + return { + type: 'cursor', + action: { type: 'move', direction: 'forward', count: p0 }, + } + } + if (finalByte === CSI.CUB) { + return { + type: 'cursor', + action: { type: 'move', direction: 'back', count: p0 }, + } + } + if (finalByte === CSI.CNL) { + return { type: 'cursor', action: { type: 'nextLine', count: p0 } } + } + if (finalByte === CSI.CPL) { + return { type: 'cursor', action: { type: 'prevLine', count: p0 } } + } + if (finalByte === CSI.CHA) { + return { type: 'cursor', action: { type: 'column', col: p0 } } + } + if (finalByte === CSI.CUP || finalByte === CSI.HVP) { + return { type: 'cursor', action: { type: 'position', row: p0, col: p1 } } + } + if (finalByte === CSI.VPA) { + return { type: 'cursor', action: { type: 'row', row: p0 } } + } + + // Erase + if (finalByte === CSI.ED) { + const region = ERASE_DISPLAY[params[0] ?? 0] ?? 'toEnd' + return { type: 'erase', action: { type: 'display', region } } + } + if (finalByte === CSI.EL) { + const region = ERASE_LINE_REGION[params[0] ?? 0] ?? 'toEnd' + return { type: 'erase', action: { type: 'line', region } } + } + if (finalByte === CSI.ECH) { + return { type: 'erase', action: { type: 'chars', count: p0 } } + } + + // Scroll + if (finalByte === CSI.SU) { + return { type: 'scroll', action: { type: 'up', count: p0 } } + } + if (finalByte === CSI.SD) { + return { type: 'scroll', action: { type: 'down', count: p0 } } + } + if (finalByte === CSI.DECSTBM) { + return { + type: 'scroll', + action: { type: 'setRegion', top: p0, bottom: p1 }, + } + } + + // Cursor save/restore + if (finalByte === CSI.SCOSC) { + return { type: 'cursor', action: { type: 'save' } } + } + if (finalByte === CSI.SCORC) { + return { type: 'cursor', action: { type: 'restore' } } + } + + // Cursor style + if (finalByte === CSI.DECSCUSR && intermediate === ' ') { + const styleInfo = CURSOR_STYLES[p0] ?? CURSOR_STYLES[0]! + return { type: 'cursor', action: { type: 'style', ...styleInfo } } + } + + // Private modes + if (privateMode === '?' && (finalByte === CSI.SM || finalByte === CSI.RM)) { + const enabled = finalByte === CSI.SM + + if (p0 === DEC.CURSOR_VISIBLE) { + return { + type: 'cursor', + action: enabled ? { type: 'show' } : { type: 'hide' }, + } + } + if (p0 === DEC.ALT_SCREEN_CLEAR || p0 === DEC.ALT_SCREEN) { + return { type: 'mode', action: { type: 'alternateScreen', enabled } } + } + if (p0 === DEC.BRACKETED_PASTE) { + return { type: 'mode', action: { type: 'bracketedPaste', enabled } } + } + if (p0 === DEC.MOUSE_NORMAL) { + return { + type: 'mode', + action: { type: 'mouseTracking', mode: enabled ? 'normal' : 'off' }, + } + } + if (p0 === DEC.MOUSE_BUTTON) { + return { + type: 'mode', + action: { type: 'mouseTracking', mode: enabled ? 'button' : 'off' }, + } + } + if (p0 === DEC.MOUSE_ANY) { + return { + type: 'mode', + action: { type: 'mouseTracking', mode: enabled ? 'any' : 'off' }, + } + } + if (p0 === DEC.FOCUS_EVENTS) { + return { type: 'mode', action: { type: 'focusEvents', enabled } } + } + } + + return { type: 'unknown', sequence: rawSequence } +} + +/** + * Identify the type of escape sequence from its raw form. + */ +function identifySequence( + seq: string, +): 'csi' | 'osc' | 'esc' | 'ss3' | 'unknown' { + if (seq.length < 2) return 'unknown' + if (seq.charCodeAt(0) !== C0.ESC) return 'unknown' + + const second = seq.charCodeAt(1) + if (second === 0x5b) return 'csi' // [ + if (second === 0x5d) return 'osc' // ] + if (second === 0x4f) return 'ss3' // O + return 'esc' +} + +// ============================================================================= +// Main Parser +// ============================================================================= + +/** + * Parser class - maintains state for streaming/incremental parsing + * + * Usage: + * ```typescript + * const parser = new Parser() + * const actions1 = parser.feed('partial\x1b[') + * const actions2 = parser.feed('31mred') // state maintained internally + * ``` + */ +export class Parser { + private tokenizer: Tokenizer = createTokenizer() + + style: TextStyle = defaultStyle() + inLink = false + linkUrl: string | undefined + + reset(): void { + this.tokenizer.reset() + this.style = defaultStyle() + this.inLink = false + this.linkUrl = undefined + } + + /** Feed input and get resulting actions */ + feed(input: string): Action[] { + const tokens = this.tokenizer.feed(input) + const actions: Action[] = [] + + for (const token of tokens) { + const tokenActions = this.processToken(token) + actions.push(...tokenActions) + } + + return actions + } + + private processToken(token: Token): Action[] { + switch (token.type) { + case 'text': + return this.processText(token.value) + + case 'sequence': + return this.processSequence(token.value) + } + } + + private processText(text: string): Action[] { + // Handle BEL characters embedded in text + const actions: Action[] = [] + let current = '' + + for (const char of text) { + if (char.charCodeAt(0) === C0.BEL) { + if (current) { + const graphemes = [...segmentGraphemes(current)] + if (graphemes.length > 0) { + actions.push({ type: 'text', graphemes, style: { ...this.style } }) + } + current = '' + } + actions.push({ type: 'bell' }) + } else { + current += char + } + } + + if (current) { + const graphemes = [...segmentGraphemes(current)] + if (graphemes.length > 0) { + actions.push({ type: 'text', graphemes, style: { ...this.style } }) + } + } + + return actions + } + + private processSequence(seq: string): Action[] { + const seqType = identifySequence(seq) + + switch (seqType) { + case 'csi': { + const action = parseCSI(seq) + if (!action) return [] + if (action.type === 'sgr') { + this.style = applySGR(action.params, this.style) + return [] + } + return [action] + } + + case 'osc': { + // Extract OSC content (between ESC ] and terminator) + let content = seq.slice(2) + // Remove terminator (BEL or ESC \) + if (content.endsWith('\x07')) { + content = content.slice(0, -1) + } else if (content.endsWith('\x1b\\')) { + content = content.slice(0, -2) + } + + const action = parseOSC(content) + if (action) { + if (action.type === 'link') { + if (action.action.type === 'start') { + this.inLink = true + this.linkUrl = action.action.url + } else { + this.inLink = false + this.linkUrl = undefined + } + } + return [action] + } + return [] + } + + case 'esc': { + const escContent = seq.slice(1) + const action = parseEsc(escContent) + return action ? [action] : [] + } + + case 'ss3': + // SS3 sequences are typically cursor keys in application mode + // For output parsing, treat as unknown + return [{ type: 'unknown', sequence: seq }] + + default: + return [{ type: 'unknown', sequence: seq }] + } + } +} diff --git a/src/ink/termio/tokenize.ts b/src/ink/termio/tokenize.ts new file mode 100644 index 0000000000000000000000000000000000000000..68a0d113c596772512a2149a5b58edb0b235d9cf --- /dev/null +++ b/src/ink/termio/tokenize.ts @@ -0,0 +1,319 @@ +/** + * Input Tokenizer - Escape sequence boundary detection + * + * Splits terminal input into tokens: text chunks and raw escape sequences. + * Unlike the Parser which interprets sequences semantically, this just + * identifies boundaries for use by keyboard input parsing. + */ + +import { C0, ESC_TYPE, isEscFinal } from './ansi.js' +import { isCSIFinal, isCSIIntermediate, isCSIParam } from './csi.js' + +export type Token = + | { type: 'text'; value: string } + | { type: 'sequence'; value: string } + +type State = + | 'ground' + | 'escape' + | 'escapeIntermediate' + | 'csi' + | 'ss3' + | 'osc' + | 'dcs' + | 'apc' + +export type Tokenizer = { + /** Feed input and get resulting tokens */ + feed(input: string): Token[] + /** Flush any buffered incomplete sequences */ + flush(): Token[] + /** Reset tokenizer state */ + reset(): void + /** Get any buffered incomplete sequence */ + buffer(): string +} + +type TokenizerOptions = { + /** + * Treat `CSI M` as an X10 mouse event prefix and consume 3 payload bytes. + * Only enable for stdin input — `\x1b[M` is also CSI DL (Delete Lines) in + * output streams, and enabling this there swallows display text. Default false. + */ + x10Mouse?: boolean +} + +/** + * Create a streaming tokenizer for terminal input. + * + * Usage: + * ```typescript + * const tokenizer = createTokenizer() + * const tokens1 = tokenizer.feed('hello\x1b[') + * const tokens2 = tokenizer.feed('A') // completes the escape sequence + * const remaining = tokenizer.flush() // force output incomplete sequences + * ``` + */ +export function createTokenizer(options?: TokenizerOptions): Tokenizer { + let currentState: State = 'ground' + let currentBuffer = '' + const x10Mouse = options?.x10Mouse ?? false + + return { + feed(input: string): Token[] { + const result = tokenize( + input, + currentState, + currentBuffer, + false, + x10Mouse, + ) + currentState = result.state.state + currentBuffer = result.state.buffer + return result.tokens + }, + + flush(): Token[] { + const result = tokenize('', currentState, currentBuffer, true, x10Mouse) + currentState = result.state.state + currentBuffer = result.state.buffer + return result.tokens + }, + + reset(): void { + currentState = 'ground' + currentBuffer = '' + }, + + buffer(): string { + return currentBuffer + }, + } +} + +type InternalState = { + state: State + buffer: string +} + +function tokenize( + input: string, + initialState: State, + initialBuffer: string, + flush: boolean, + x10Mouse: boolean, +): { tokens: Token[]; state: InternalState } { + const tokens: Token[] = [] + const result: InternalState = { + state: initialState, + buffer: '', + } + + const data = initialBuffer + input + let i = 0 + let textStart = 0 + let seqStart = 0 + + const flushText = (): void => { + if (i > textStart) { + const text = data.slice(textStart, i) + if (text) { + tokens.push({ type: 'text', value: text }) + } + } + textStart = i + } + + const emitSequence = (seq: string): void => { + if (seq) { + tokens.push({ type: 'sequence', value: seq }) + } + result.state = 'ground' + textStart = i + } + + while (i < data.length) { + const code = data.charCodeAt(i) + + switch (result.state) { + case 'ground': + if (code === C0.ESC) { + flushText() + seqStart = i + result.state = 'escape' + i++ + } else { + i++ + } + break + + case 'escape': + if (code === ESC_TYPE.CSI) { + result.state = 'csi' + i++ + } else if (code === ESC_TYPE.OSC) { + result.state = 'osc' + i++ + } else if (code === ESC_TYPE.DCS) { + result.state = 'dcs' + i++ + } else if (code === ESC_TYPE.APC) { + result.state = 'apc' + i++ + } else if (code === 0x4f) { + // 'O' - SS3 + result.state = 'ss3' + i++ + } else if (isCSIIntermediate(code)) { + // Intermediate byte (e.g., ESC ( for charset) - continue buffering + result.state = 'escapeIntermediate' + i++ + } else if (isEscFinal(code)) { + // Two-character escape sequence + i++ + emitSequence(data.slice(seqStart, i)) + } else if (code === C0.ESC) { + // Double escape - emit first, start new + emitSequence(data.slice(seqStart, i)) + seqStart = i + result.state = 'escape' + i++ + } else { + // Invalid - treat ESC as text + result.state = 'ground' + textStart = seqStart + } + break + + case 'escapeIntermediate': + // After intermediate byte(s), wait for final byte + if (isCSIIntermediate(code)) { + // More intermediate bytes + i++ + } else if (isEscFinal(code)) { + // Final byte - complete the sequence + i++ + emitSequence(data.slice(seqStart, i)) + } else { + // Invalid - treat as text + result.state = 'ground' + textStart = seqStart + } + break + + case 'csi': + // X10 mouse: CSI M + 3 raw payload bytes (Cb+32, Cx+32, Cy+32). + // M immediately after [ (offset 2) means no params — SGR mouse + // (CSI < … M) has a `<` param byte first and reaches M at offset > 2. + // Terminals that ignore DECSET 1006 but honor 1000/1002 emit this + // legacy encoding; without this branch the 3 payload bytes leak + // through as text (`` `rK `` / `arK` garbage in the prompt). + // + // Gated on x10Mouse — `\x1b[M` is also CSI DL (Delete Lines) and + // blindly consuming 3 chars corrupts output rendering (Parser/Ansi) + // and fragments bracketed-paste PASTE_END. Only stdin enables this. + // The ≥0x20 check on each payload slot is belt-and-suspenders: X10 + // guarantees Cb≥32, Cx≥33, Cy≥33, so a control byte (ESC=0x1B) in + // any slot means this is CSI DL adjacent to another sequence, not a + // mouse event. Checking all three slots prevents PASTE_END's ESC + // from being consumed when paste content ends in `\x1b[M`+0-2 chars. + // + // Known limitation: this counts JS string chars, but X10 is byte- + // oriented and stdin uses utf8 encoding (App.tsx). At col 162-191 × + // row 96-159 the two coord bytes (0xC2-0xDF, 0x80-0xBF) form a valid + // UTF-8 2-byte sequence and collapse to one char — the length check + // fails and the event buffers until the next keypress absorbs it. + // Fixing this requires latin1 stdin; X10's 223-coord cap is exactly + // why SGR was invented, and no-SGR terminals at 162+ cols are rare. + if ( + x10Mouse && + code === 0x4d /* M */ && + i - seqStart === 2 && + (i + 1 >= data.length || data.charCodeAt(i + 1) >= 0x20) && + (i + 2 >= data.length || data.charCodeAt(i + 2) >= 0x20) && + (i + 3 >= data.length || data.charCodeAt(i + 3) >= 0x20) + ) { + if (i + 4 <= data.length) { + i += 4 + emitSequence(data.slice(seqStart, i)) + } else { + // Incomplete — exit loop; end-of-input buffers from seqStart. + // Re-entry re-tokenizes from ground via the invalid-CSI fallthrough. + i = data.length + } + break + } + if (isCSIFinal(code)) { + i++ + emitSequence(data.slice(seqStart, i)) + } else if (isCSIParam(code) || isCSIIntermediate(code)) { + i++ + } else { + // Invalid CSI - abort, treat as text + result.state = 'ground' + textStart = seqStart + } + break + + case 'ss3': + // SS3 sequences: ESC O followed by a single final byte + if (code >= 0x40 && code <= 0x7e) { + i++ + emitSequence(data.slice(seqStart, i)) + } else { + // Invalid - treat as text + result.state = 'ground' + textStart = seqStart + } + break + + case 'osc': + if (code === C0.BEL) { + i++ + emitSequence(data.slice(seqStart, i)) + } else if ( + code === C0.ESC && + i + 1 < data.length && + data.charCodeAt(i + 1) === ESC_TYPE.ST + ) { + i += 2 + emitSequence(data.slice(seqStart, i)) + } else { + i++ + } + break + + case 'dcs': + case 'apc': + if (code === C0.BEL) { + i++ + emitSequence(data.slice(seqStart, i)) + } else if ( + code === C0.ESC && + i + 1 < data.length && + data.charCodeAt(i + 1) === ESC_TYPE.ST + ) { + i += 2 + emitSequence(data.slice(seqStart, i)) + } else { + i++ + } + break + } + } + + // Handle end of input + if (result.state === 'ground') { + flushText() + } else if (flush) { + // Force output incomplete sequence + const remaining = data.slice(seqStart) + if (remaining) tokens.push({ type: 'sequence', value: remaining }) + result.state = 'ground' + } else { + // Buffer incomplete sequence for next call + result.buffer = data.slice(seqStart) + } + + return { tokens, state: result } +} diff --git a/src/ink/termio/types.ts b/src/ink/termio/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c9bf737833153216962006c7e96b1ae64e0d133 --- /dev/null +++ b/src/ink/termio/types.ts @@ -0,0 +1,236 @@ +/** + * ANSI Parser - Semantic Types + * + * These types represent the semantic meaning of ANSI escape sequences, + * not their string representation. Inspired by ghostty's action-based design. + */ + +// ============================================================================= +// Colors +// ============================================================================= + +/** Named colors from the 16-color palette */ +export type NamedColor = + | 'black' + | 'red' + | 'green' + | 'yellow' + | 'blue' + | 'magenta' + | 'cyan' + | 'white' + | 'brightBlack' + | 'brightRed' + | 'brightGreen' + | 'brightYellow' + | 'brightBlue' + | 'brightMagenta' + | 'brightCyan' + | 'brightWhite' + +/** Color specification - can be named, indexed (256), or RGB */ +export type Color = + | { type: 'named'; name: NamedColor } + | { type: 'indexed'; index: number } // 0-255 + | { type: 'rgb'; r: number; g: number; b: number } + | { type: 'default' } + +// ============================================================================= +// Text Styles +// ============================================================================= + +/** Underline style variants */ +export type UnderlineStyle = + | 'none' + | 'single' + | 'double' + | 'curly' + | 'dotted' + | 'dashed' + +/** Text style attributes - represents current styling state */ +export type TextStyle = { + bold: boolean + dim: boolean + italic: boolean + underline: UnderlineStyle + blink: boolean + inverse: boolean + hidden: boolean + strikethrough: boolean + overline: boolean + fg: Color + bg: Color + underlineColor: Color +} + +/** Create a default (reset) text style */ +export function defaultStyle(): TextStyle { + return { + bold: false, + dim: false, + italic: false, + underline: 'none', + blink: false, + inverse: false, + hidden: false, + strikethrough: false, + overline: false, + fg: { type: 'default' }, + bg: { type: 'default' }, + underlineColor: { type: 'default' }, + } +} + +/** Check if two styles are equal */ +export function stylesEqual(a: TextStyle, b: TextStyle): boolean { + return ( + a.bold === b.bold && + a.dim === b.dim && + a.italic === b.italic && + a.underline === b.underline && + a.blink === b.blink && + a.inverse === b.inverse && + a.hidden === b.hidden && + a.strikethrough === b.strikethrough && + a.overline === b.overline && + colorsEqual(a.fg, b.fg) && + colorsEqual(a.bg, b.bg) && + colorsEqual(a.underlineColor, b.underlineColor) + ) +} + +/** Check if two colors are equal */ +export function colorsEqual(a: Color, b: Color): boolean { + if (a.type !== b.type) return false + switch (a.type) { + case 'named': + return a.name === (b as typeof a).name + case 'indexed': + return a.index === (b as typeof a).index + case 'rgb': + return ( + a.r === (b as typeof a).r && + a.g === (b as typeof a).g && + a.b === (b as typeof a).b + ) + case 'default': + return true + } +} + +// ============================================================================= +// Cursor Actions +// ============================================================================= + +export type CursorDirection = 'up' | 'down' | 'forward' | 'back' + +export type CursorAction = + | { type: 'move'; direction: CursorDirection; count: number } + | { type: 'position'; row: number; col: number } + | { type: 'column'; col: number } + | { type: 'row'; row: number } + | { type: 'save' } + | { type: 'restore' } + | { type: 'show' } + | { type: 'hide' } + | { + type: 'style' + style: 'block' | 'underline' | 'bar' + blinking: boolean + } + | { type: 'nextLine'; count: number } + | { type: 'prevLine'; count: number } + +// ============================================================================= +// Erase Actions +// ============================================================================= + +export type EraseAction = + | { type: 'display'; region: 'toEnd' | 'toStart' | 'all' | 'scrollback' } + | { type: 'line'; region: 'toEnd' | 'toStart' | 'all' } + | { type: 'chars'; count: number } + +// ============================================================================= +// Scroll Actions +// ============================================================================= + +export type ScrollAction = + | { type: 'up'; count: number } + | { type: 'down'; count: number } + | { type: 'setRegion'; top: number; bottom: number } + +// ============================================================================= +// Mode Actions +// ============================================================================= + +export type ModeAction = + | { type: 'alternateScreen'; enabled: boolean } + | { type: 'bracketedPaste'; enabled: boolean } + | { type: 'mouseTracking'; mode: 'off' | 'normal' | 'button' | 'any' } + | { type: 'focusEvents'; enabled: boolean } + +// ============================================================================= +// Link Actions (OSC 8) +// ============================================================================= + +export type LinkAction = + | { type: 'start'; url: string; params?: Record } + | { type: 'end' } + +// ============================================================================= +// Title Actions (OSC 0/1/2) +// ============================================================================= + +export type TitleAction = + | { type: 'windowTitle'; title: string } + | { type: 'iconName'; name: string } + | { type: 'both'; title: string } + +// ============================================================================= +// Tab Status Action (OSC 21337) +// ============================================================================= + +/** + * Per-tab chrome metadata. Tristate for each field: + * - property absent → not mentioned in sequence, no change + * - null → explicitly cleared (bare key or key= with empty value) + * - value → set to this + */ +export type TabStatusAction = { + indicator?: Color | null + status?: string | null + statusColor?: Color | null +} + +// ============================================================================= +// Parsed Segments - The output of the parser +// ============================================================================= + +/** A segment of styled text */ +export type TextSegment = { + type: 'text' + text: string + style: TextStyle +} + +/** A grapheme (visual character unit) with width info */ +export type Grapheme = { + value: string + width: 1 | 2 // Display width in columns +} + +/** All possible parsed actions */ +export type Action = + | { type: 'text'; graphemes: Grapheme[]; style: TextStyle } + | { type: 'cursor'; action: CursorAction } + | { type: 'erase'; action: EraseAction } + | { type: 'scroll'; action: ScrollAction } + | { type: 'mode'; action: ModeAction } + | { type: 'link'; action: LinkAction } + | { type: 'title'; action: TitleAction } + | { type: 'tabStatus'; action: TabStatusAction } + | { type: 'sgr'; params: string } // Select Graphic Rendition (style change) + | { type: 'bell' } + | { type: 'reset' } // Full terminal reset (ESC c) + | { type: 'unknown'; sequence: string } // Unrecognized sequence diff --git a/src/tools/AgentTool/built-in/verificationAgent.ts b/src/tools/AgentTool/built-in/verificationAgent.ts new file mode 100644 index 0000000000000000000000000000000000000000..3609f97e533c5f38c5eeb37a16e3375e55694877 --- /dev/null +++ b/src/tools/AgentTool/built-in/verificationAgent.ts @@ -0,0 +1,152 @@ +import { BASH_TOOL_NAME } from 'src/tools/BashTool/toolName.js' +import { EXIT_PLAN_MODE_TOOL_NAME } from 'src/tools/ExitPlanModeTool/constants.js' +import { FILE_EDIT_TOOL_NAME } from 'src/tools/FileEditTool/constants.js' +import { FILE_WRITE_TOOL_NAME } from 'src/tools/FileWriteTool/prompt.js' +import { NOTEBOOK_EDIT_TOOL_NAME } from 'src/tools/NotebookEditTool/constants.js' +import { WEB_FETCH_TOOL_NAME } from 'src/tools/WebFetchTool/prompt.js' +import { AGENT_TOOL_NAME } from '../constants.js' +import type { BuiltInAgentDefinition } from '../loadAgentsDir.js' + +const VERIFICATION_SYSTEM_PROMPT = `You are a verification specialist. Your job is not to confirm the implementation works — it's to try to break it. + +You have two documented failure patterns. First, verification avoidance: when faced with a check, you find reasons not to run it — you read code, narrate what you would test, write "PASS," and move on. Second, being seduced by the first 80%: you see a polished UI or a passing test suite and feel inclined to pass it, not noticing half the buttons do nothing, the state vanishes on refresh, or the backend crashes on bad input. The first 80% is the easy part. Your entire value is in finding the last 20%. The caller may spot-check your commands by re-running them — if a PASS step has no command output, or output that doesn't match re-execution, your report gets rejected. + +=== CRITICAL: DO NOT MODIFY THE PROJECT === +You are STRICTLY PROHIBITED from: +- Creating, modifying, or deleting any files IN THE PROJECT DIRECTORY +- Installing dependencies or packages +- Running git write operations (add, commit, push) + +You MAY write ephemeral test scripts to a temp directory (/tmp or $TMPDIR) via ${BASH_TOOL_NAME} redirection when inline commands aren't sufficient — e.g., a multi-step race harness or a Playwright test. Clean up after yourself. + +Check your ACTUAL available tools rather than assuming from this prompt. You may have browser automation (mcp__claude-in-chrome__*, mcp__playwright__*), ${WEB_FETCH_TOOL_NAME}, or other MCP tools depending on the session — do not skip capabilities you didn't think to check for. + +=== WHAT YOU RECEIVE === +You will receive: the original task description, files changed, approach taken, and optionally a plan file path. + +=== VERIFICATION STRATEGY === +Adapt your strategy based on what was changed: + +**Frontend changes**: Start dev server → check your tools for browser automation (mcp__claude-in-chrome__*, mcp__playwright__*) and USE them to navigate, screenshot, click, and read console — do NOT say "needs a real browser" without attempting → curl a sample of page subresources (image-optimizer URLs like /_next/image, same-origin API routes, static assets) since HTML can serve 200 while everything it references fails → run frontend tests +**Backend/API changes**: Start server → curl/fetch endpoints → verify response shapes against expected values (not just status codes) → test error handling → check edge cases +**CLI/script changes**: Run with representative inputs → verify stdout/stderr/exit codes → test edge inputs (empty, malformed, boundary) → verify --help / usage output is accurate +**Infrastructure/config changes**: Validate syntax → dry-run where possible (terraform plan, kubectl apply --dry-run=server, docker build, nginx -t) → check env vars / secrets are actually referenced, not just defined +**Library/package changes**: Build → full test suite → import the library from a fresh context and exercise the public API as a consumer would → verify exported types match README/docs examples +**Bug fixes**: Reproduce the original bug → verify fix → run regression tests → check related functionality for side effects +**Mobile (iOS/Android)**: Clean build → install on simulator/emulator → dump accessibility/UI tree (idb ui describe-all / uiautomator dump), find elements by label, tap by tree coords, re-dump to verify; screenshots secondary → kill and relaunch to test persistence → check crash logs (logcat / device console) +**Data/ML pipeline**: Run with sample input → verify output shape/schema/types → test empty input, single row, NaN/null handling → check for silent data loss (row counts in vs out) +**Database migrations**: Run migration up → verify schema matches intent → run migration down (reversibility) → test against existing data, not just empty DB +**Refactoring (no behavior change)**: Existing test suite MUST pass unchanged → diff the public API surface (no new/removed exports) → spot-check observable behavior is identical (same inputs → same outputs) +**Other change types**: The pattern is always the same — (a) figure out how to exercise this change directly (run/call/invoke/deploy it), (b) check outputs against expectations, (c) try to break it with inputs/conditions the implementer didn't test. The strategies above are worked examples for common cases. + +=== REQUIRED STEPS (universal baseline) === +1. Read the project's CLAUDE.md / README for build/test commands and conventions. Check package.json / Makefile / pyproject.toml for script names. If the implementer pointed you to a plan or spec file, read it — that's the success criteria. +2. Run the build (if applicable). A broken build is an automatic FAIL. +3. Run the project's test suite (if it has one). Failing tests are an automatic FAIL. +4. Run linters/type-checkers if configured (eslint, tsc, mypy, etc.). +5. Check for regressions in related code. + +Then apply the type-specific strategy above. Match rigor to stakes: a one-off script doesn't need race-condition probes; production payments code needs everything. + +Test suite results are context, not evidence. Run the suite, note pass/fail, then move on to your real verification. The implementer is an LLM too — its tests may be heavy on mocks, circular assertions, or happy-path coverage that proves nothing about whether the system actually works end-to-end. + +=== RECOGNIZE YOUR OWN RATIONALIZATIONS === +You will feel the urge to skip checks. These are the exact excuses you reach for — recognize them and do the opposite: +- "The code looks correct based on my reading" — reading is not verification. Run it. +- "The implementer's tests already pass" — the implementer is an LLM. Verify independently. +- "This is probably fine" — probably is not verified. Run it. +- "Let me start the server and check the code" — no. Start the server and hit the endpoint. +- "I don't have a browser" — did you actually check for mcp__claude-in-chrome__* / mcp__playwright__*? If present, use them. If an MCP tool fails, troubleshoot (server running? selector right?). The fallback exists so you don't invent your own "can't do this" story. +- "This would take too long" — not your call. +If you catch yourself writing an explanation instead of a command, stop. Run the command. + +=== ADVERSARIAL PROBES (adapt to the change type) === +Functional tests confirm the happy path. Also try to break it: +- **Concurrency** (servers/APIs): parallel requests to create-if-not-exists paths — duplicate sessions? lost writes? +- **Boundary values**: 0, -1, empty string, very long strings, unicode, MAX_INT +- **Idempotency**: same mutating request twice — duplicate created? error? correct no-op? +- **Orphan operations**: delete/reference IDs that don't exist +These are seeds, not a checklist — pick the ones that fit what you're verifying. + +=== BEFORE ISSUING PASS === +Your report must include at least one adversarial probe you ran (concurrency, boundary, idempotency, orphan op, or similar) and its result — even if the result was "handled correctly." If all your checks are "returns 200" or "test suite passes," you have confirmed the happy path, not verified correctness. Go back and try to break something. + +=== BEFORE ISSUING FAIL === +You found something that looks broken. Before reporting FAIL, check you haven't missed why it's actually fine: +- **Already handled**: is there defensive code elsewhere (validation upstream, error recovery downstream) that prevents this? +- **Intentional**: does CLAUDE.md / comments / commit message explain this as deliberate? +- **Not actionable**: is this a real limitation but unfixable without breaking an external contract (stable API, protocol spec, backwards compat)? If so, note it as an observation, not a FAIL — a "bug" that can't be fixed isn't actionable. +Don't use these as excuses to wave away real issues — but don't FAIL on intentional behavior either. + +=== OUTPUT FORMAT (REQUIRED) === +Every check MUST follow this structure. A check without a Command run block is not a PASS — it's a skip. + +\`\`\` +### Check: [what you're verifying] +**Command run:** + [exact command you executed] +**Output observed:** + [actual terminal output — copy-paste, not paraphrased. Truncate if very long but keep the relevant part.] +**Result: PASS** (or FAIL — with Expected vs Actual) +\`\`\` + +Bad (rejected): +\`\`\` +### Check: POST /api/register validation +**Result: PASS** +Evidence: Reviewed the route handler in routes/auth.py. The logic correctly validates +email format and password length before DB insert. +\`\`\` +(No command run. Reading code is not verification.) + +Good: +\`\`\` +### Check: POST /api/register rejects short password +**Command run:** + curl -s -X POST localhost:8000/api/register -H 'Content-Type: application/json' \\ + -d '{"email":"t@t.co","password":"short"}' | python3 -m json.tool +**Output observed:** + { + "error": "password must be at least 8 characters" + } + (HTTP 400) +**Expected vs Actual:** Expected 400 with password-length error. Got exactly that. +**Result: PASS** +\`\`\` + +End with exactly this line (parsed by caller): + +VERDICT: PASS +or +VERDICT: FAIL +or +VERDICT: PARTIAL + +PARTIAL is for environmental limitations only (no test framework, tool unavailable, server can't start) — not for "I'm unsure whether this is a bug." If you can run the check, you must decide PASS or FAIL. + +Use the literal string \`VERDICT: \` followed by exactly one of \`PASS\`, \`FAIL\`, \`PARTIAL\`. No markdown bold, no punctuation, no variation. +- **FAIL**: include what failed, exact error output, reproduction steps. +- **PARTIAL**: what was verified, what could not be and why (missing tool/env), what the implementer should know.` + +const VERIFICATION_WHEN_TO_USE = + 'Use this agent to verify that implementation work is correct before reporting completion. Invoke after non-trivial tasks (3+ file edits, backend/API changes, infrastructure changes). Pass the ORIGINAL user task description, list of files changed, and approach taken. The agent runs builds, tests, linters, and checks to produce a PASS/FAIL/PARTIAL verdict with evidence.' + +export const VERIFICATION_AGENT: BuiltInAgentDefinition = { + agentType: 'verification', + whenToUse: VERIFICATION_WHEN_TO_USE, + color: 'red', + background: true, + disallowedTools: [ + AGENT_TOOL_NAME, + EXIT_PLAN_MODE_TOOL_NAME, + FILE_EDIT_TOOL_NAME, + FILE_WRITE_TOOL_NAME, + NOTEBOOK_EDIT_TOOL_NAME, + ], + source: 'built-in', + baseDir: 'built-in', + model: 'inherit', + getSystemPrompt: () => VERIFICATION_SYSTEM_PROMPT, + criticalSystemReminder_EXPERIMENTAL: + 'CRITICAL: This is a VERIFICATION-ONLY task. You CANNOT edit, write, or create files IN THE PROJECT DIRECTORY (tmp is allowed for ephemeral test scripts). You MUST end with VERDICT: PASS, VERDICT: FAIL, or VERDICT: PARTIAL.', +} diff --git a/src/tools/AgentTool/forkSubagent.ts b/src/tools/AgentTool/forkSubagent.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f5c3542c78fb1ef486d2ea98f7bf75a6d1396ae --- /dev/null +++ b/src/tools/AgentTool/forkSubagent.ts @@ -0,0 +1,210 @@ +import { feature } from 'bun:bundle' +import type { BetaToolUseBlock } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs' +import { randomUUID } from 'crypto' +import { getIsNonInteractiveSession } from '../../bootstrap/state.js' +import { + FORK_BOILERPLATE_TAG, + FORK_DIRECTIVE_PREFIX, +} from '../../constants/xml.js' +import { isCoordinatorMode } from '../../coordinator/coordinatorMode.js' +import type { + AssistantMessage, + Message as MessageType, +} from '../../types/message.js' +import { logForDebugging } from '../../utils/debug.js' +import { createUserMessage } from '../../utils/messages.js' +import type { BuiltInAgentDefinition } from './loadAgentsDir.js' + +/** + * Fork subagent feature gate. + * + * When enabled: + * - `subagent_type` becomes optional on the Agent tool schema + * - Omitting `subagent_type` triggers an implicit fork: the child inherits + * the parent's full conversation context and system prompt + * - All agent spawns run in the background (async) for a unified + * `` interaction model + * - `/fork ` slash command is available + * + * Mutually exclusive with coordinator mode — coordinator already owns the + * orchestration role and has its own delegation model. + */ +export function isForkSubagentEnabled(): boolean { + if (feature('FORK_SUBAGENT')) { + if (isCoordinatorMode()) return false + if (getIsNonInteractiveSession()) return false + return true + } + return false +} + +/** Synthetic agent type name used for analytics when the fork path fires. */ +export const FORK_SUBAGENT_TYPE = 'fork' + +/** + * Synthetic agent definition for the fork path. + * + * Not registered in builtInAgents — used only when `!subagent_type` and the + * experiment is active. `tools: ['*']` with `useExactTools` means the fork + * child receives the parent's exact tool pool (for cache-identical API + * prefixes). `permissionMode: 'bubble'` surfaces permission prompts to the + * parent terminal. `model: 'inherit'` keeps the parent's model for context + * length parity. + * + * The getSystemPrompt here is unused: the fork path passes + * `override.systemPrompt` with the parent's already-rendered system prompt + * bytes, threaded via `toolUseContext.renderedSystemPrompt`. Reconstructing + * by re-calling getSystemPrompt() can diverge (GrowthBook cold→warm) and + * bust the prompt cache; threading the rendered bytes is byte-exact. + */ +export const FORK_AGENT = { + agentType: FORK_SUBAGENT_TYPE, + whenToUse: + 'Implicit fork — inherits full conversation context. Not selectable via subagent_type; triggered by omitting subagent_type when the fork experiment is active.', + tools: ['*'], + maxTurns: 200, + model: 'inherit', + permissionMode: 'bubble', + source: 'built-in', + baseDir: 'built-in', + getSystemPrompt: () => '', +} satisfies BuiltInAgentDefinition + +/** + * Guard against recursive forking. Fork children keep the Agent tool in their + * tool pool for cache-identical tool definitions, so we reject fork attempts + * at call time by detecting the fork boilerplate tag in conversation history. + */ +export function isInForkChild(messages: MessageType[]): boolean { + return messages.some(m => { + if (m.type !== 'user') return false + const content = m.message.content + if (!Array.isArray(content)) return false + return content.some( + block => + block.type === 'text' && + block.text.includes(`<${FORK_BOILERPLATE_TAG}>`), + ) + }) +} + +/** Placeholder text used for all tool_result blocks in the fork prefix. + * Must be identical across all fork children for prompt cache sharing. */ +const FORK_PLACEHOLDER_RESULT = 'Fork started — processing in background' + +/** + * Build the forked conversation messages for the child agent. + * + * For prompt cache sharing, all fork children must produce byte-identical + * API request prefixes. This function: + * 1. Keeps the full parent assistant message (all tool_use blocks, thinking, text) + * 2. Builds a single user message with tool_results for every tool_use block + * using an identical placeholder, then appends a per-child directive text block + * + * Result: [...history, assistant(all_tool_uses), user(placeholder_results..., directive)] + * Only the final text block differs per child, maximizing cache hits. + */ +export function buildForkedMessages( + directive: string, + assistantMessage: AssistantMessage, +): MessageType[] { + // Clone the assistant message to avoid mutating the original, keeping all + // content blocks (thinking, text, and every tool_use) + const fullAssistantMessage: AssistantMessage = { + ...assistantMessage, + uuid: randomUUID(), + message: { + ...assistantMessage.message, + content: [...assistantMessage.message.content], + }, + } + + // Collect all tool_use blocks from the assistant message + const toolUseBlocks = assistantMessage.message.content.filter( + (block): block is BetaToolUseBlock => block.type === 'tool_use', + ) + + if (toolUseBlocks.length === 0) { + logForDebugging( + `No tool_use blocks found in assistant message for fork directive: ${directive.slice(0, 50)}...`, + { level: 'error' }, + ) + return [ + createUserMessage({ + content: [ + { type: 'text' as const, text: buildChildMessage(directive) }, + ], + }), + ] + } + + // Build tool_result blocks for every tool_use, all with identical placeholder text + const toolResultBlocks = toolUseBlocks.map(block => ({ + type: 'tool_result' as const, + tool_use_id: block.id, + content: [ + { + type: 'text' as const, + text: FORK_PLACEHOLDER_RESULT, + }, + ], + })) + + // Build a single user message: all placeholder tool_results + the per-child directive + // TODO(smoosh): this text sibling creates a [tool_result, text] pattern on the wire + // (renders as \n\nHuman:). One-off per-child construction, + // not a repeated teacher, so low-priority. If we ever care, use smooshIntoToolResult + // from src/utils/messages.ts to fold the directive into the last tool_result.content. + const toolResultMessage = createUserMessage({ + content: [ + ...toolResultBlocks, + { + type: 'text' as const, + text: buildChildMessage(directive), + }, + ], + }) + + return [fullAssistantMessage, toolResultMessage] +} + +export function buildChildMessage(directive: string): string { + return `<${FORK_BOILERPLATE_TAG}> +STOP. READ THIS FIRST. + +You are a forked worker process. You are NOT the main agent. + +RULES (non-negotiable): +1. Your system prompt says "default to forking." IGNORE IT \u2014 that's for the parent. You ARE the fork. Do NOT spawn sub-agents; execute directly. +2. Do NOT converse, ask questions, or suggest next steps +3. Do NOT editorialize or add meta-commentary +4. USE your tools directly: Bash, Read, Write, etc. +5. If you modify files, commit your changes before reporting. Include the commit hash in your report. +6. Do NOT emit text between tool calls. Use tools silently, then report once at the end. +7. Stay strictly within your directive's scope. If you discover related systems outside your scope, mention them in one sentence at most — other workers cover those areas. +8. Keep your report under 500 words unless the directive specifies otherwise. Be factual and concise. +9. Your response MUST begin with "Scope:". No preamble, no thinking-out-loud. +10. REPORT structured facts, then stop + +Output format (plain text labels, not markdown headers): + Scope: + Result: + Key files: + Files changed: + Issues: + + +${FORK_DIRECTIVE_PREFIX}${directive}` +} + +/** + * Notice injected into fork children running in an isolated worktree. + * Tells the child to translate paths from the inherited context, re-read + * potentially stale files, and that its changes are isolated. + */ +export function buildWorktreeNotice( + parentCwd: string, + worktreeCwd: string, +): string { + return `You've inherited the conversation context above from a parent agent working in ${parentCwd}. You are operating in an isolated git worktree at ${worktreeCwd} — same repository, same relative file structure, separate working copy. Paths in the inherited context refer to the parent's working directory; translate them to your worktree root. Re-read files before editing if the parent may have modified them since they appear in the context. Your changes stay in this worktree and will not affect the parent's files.` +}