Filemedium importancesource

TextInput.tsx

components/TextInput.tsx

124
Lines
20998
Bytes
2
Exports
13
Imports
10
Keywords

What this is

This page documents one file from the repository and includes its full source so you can read it without leaving the docs site.

Beginner explanation

This file is one piece of the larger system. Its name, directory, imports, and exports show where it fits. Start by reading the exports and related files first.

How it is used

Start from the exports list and related files. Those are the easiest clues for where this file fits into the system.

Expert explanation

Architecturally, this file intersects with ui-flow. It contains 124 lines, 13 detected imports, and 2 detected exports.

Important relationships

Detected exports

  • Props
  • TextInput

Keywords

invertfeaturechalkbarswaveformsmoothisterminalfocusedisvoicerecordingaudiolevelstext

Detected imports

  • bun:bundle
  • chalk
  • react
  • ../context/voice.js
  • ../hooks/useClipboardImageHint.js
  • ../hooks/useSettings.js
  • ../hooks/useTextInput.js
  • ../ink.js
  • ../types/textInputTypes.js
  • ../utils/envUtils.js
  • ../utils/textHighlighting.js
  • ./BaseTextInput.js
  • ./Spinner/utils.js

Source notes

This page embeds the full file contents. Small or leaf files are still indexed honestly instead of being over-explained.

Open parent directory

Full source

import { feature } from 'bun:bundle';
import chalk from 'chalk';
import React, { useMemo, useRef } from 'react';
import { useVoiceState } from '../context/voice.js';
import { useClipboardImageHint } from '../hooks/useClipboardImageHint.js';
import { useSettings } from '../hooks/useSettings.js';
import { useTextInput } from '../hooks/useTextInput.js';
import { Box, color, useAnimationFrame, useTerminalFocus, useTheme } from '../ink.js';
import type { BaseTextInputProps } from '../types/textInputTypes.js';
import { isEnvTruthy } from '../utils/envUtils.js';
import type { TextHighlight } from '../utils/textHighlighting.js';
import { BaseTextInput } from './BaseTextInput.js';
import { hueToRgb } from './Spinner/utils.js';

// Block characters for waveform bars: space (silent) + 8 rising block elements.
const BARS = ' \u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588';

// Mini waveform cursor width
const CURSOR_WAVEFORM_WIDTH = 1;

// Smoothing factor (0 = instant, 1 = frozen). Applied as EMA to
// smooth both rises and falls for a steady, non-jittery bar.
const SMOOTH = 0.7;

// Boost factor for audio levels — computeLevel normalizes with a
// conservative divisor (rms/2000), so normal speech sits around
// 0.3-0.5. This multiplier lets the bar use the full range.
const LEVEL_BOOST = 1.8;

// Raw audio level threshold (pre-boost) below which the cursor is
// grey. computeLevel returns sqrt(rms/2000), so ambient mic noise
// typically sits at 0.05-0.15. Speech starts around 0.2+.
const SILENCE_THRESHOLD = 0.15;
export type Props = BaseTextInputProps & {
  highlights?: TextHighlight[];
};
export default function TextInput(props: Props): React.ReactNode {
  const [theme] = useTheme();
  const isTerminalFocused = useTerminalFocus();
  // Hoisted to mount-time — this component re-renders on every keystroke.
  const accessibilityEnabled = useMemo(() => isEnvTruthy(process.env.CLAUDE_CODE_ACCESSIBILITY), []);
  const settings = useSettings();
  const reducedMotion = settings.prefersReducedMotion ?? false;
  const voiceState = feature('VOICE_MODE') ?
  // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
  useVoiceState(s => s.voiceState) : 'idle' as const;
  const isVoiceRecording = voiceState === 'recording';
  const audioLevels = feature('VOICE_MODE') ?
  // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
  useVoiceState(s_0 => s_0.voiceAudioLevels) : [];
  const smoothedRef = useRef<number[]>(new Array(CURSOR_WAVEFORM_WIDTH).fill(0));
  const needsAnimation = isVoiceRecording && !reducedMotion;
  const [animRef, animTime] = feature('VOICE_MODE') ?
  // biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
  useAnimationFrame(needsAnimation ? 50 : null) : [() => {}, 0];

  // Show hint when terminal regains focus and clipboard has an image
  useClipboardImageHint(isTerminalFocused, !!props.onImagePaste);

  // Cursor invert function: mini waveform during voice recording,
  // standard chalk.inverse otherwise. No warmup pulse — the ~120ms
  // warmup window is too short for a 1s-period pulse to register, and
  // driving TextInput re-renders at 50ms during warmup (while spaces
  // are simultaneously arriving every 30-80ms) causes visible stutter.
  const canShowCursor = isTerminalFocused && !accessibilityEnabled;
  let invert: (text: string) => string;
  if (!canShowCursor) {
    invert = (text: string) => text;
  } else if (isVoiceRecording && !reducedMotion) {
    // Single-bar waveform from the latest audio level
    const smoothed = smoothedRef.current;
    const raw = audioLevels.length > 0 ? audioLevels[audioLevels.length - 1] ?? 0 : 0;
    const target = Math.min(raw * LEVEL_BOOST, 1);
    smoothed[0] = (smoothed[0] ?? 0) * SMOOTH + target * (1 - SMOOTH);
    const displayLevel = smoothed[0] ?? 0;
    const barIndex = Math.max(1, Math.min(Math.round(displayLevel * (BARS.length - 1)), BARS.length - 1));
    const isSilent = raw < SILENCE_THRESHOLD;
    const hue = animTime / 1000 * 90 % 360;
    const {
      r,
      g,
      b
    } = isSilent ? {
      r: 128,
      g: 128,
      b: 128
    } : hueToRgb(hue);
    invert = () => chalk.rgb(r, g, b)(BARS[barIndex]!);
  } else {
    invert = chalk.inverse;
  }
  const textInputState = useTextInput({
    value: props.value,
    onChange: props.onChange,
    onSubmit: props.onSubmit,
    onExit: props.onExit,
    onExitMessage: props.onExitMessage,
    onHistoryReset: props.onHistoryReset,
    onHistoryUp: props.onHistoryUp,
    onHistoryDown: props.onHistoryDown,
    onClearInput: props.onClearInput,
    focus: props.focus,
    mask: props.mask,
    multiline: props.multiline,
    cursorChar: props.showCursor ? ' ' : '',
    highlightPastedText: props.highlightPastedText,
    invert,
    themeText: color('text', theme),
    columns: props.columns,
    maxVisibleLines: props.maxVisibleLines,
    onImagePaste: props.onImagePaste,
    disableCursorMovementForUpDownKeys: props.disableCursorMovementForUpDownKeys,
    disableEscapeDoublePress: props.disableEscapeDoublePress,
    externalOffset: props.cursorOffset,
    onOffsetChange: props.onChangeCursorOffset,
    inputFilter: props.inputFilter,
    inlineGhostText: props.inlineGhostText,
    dim: chalk.dim
  });
  return <Box ref={animRef}>
      <BaseTextInput inputState={textInputState} terminalFocus={isTerminalFocused} highlights={props.highlights} invert={invert} hidePlaceholderText={isVoiceRecording} {...props} />
    </Box>;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJmZWF0dXJlIiwiY2hhbGsiLCJSZWFjdCIsInVzZU1lbW8iLCJ1c2VSZWYiLCJ1c2VWb2ljZVN0YXRlIiwidXNlQ2xpcGJvYXJkSW1hZ2VIaW50IiwidXNlU2V0dGluZ3MiLCJ1c2VUZXh0SW5wdXQiLCJCb3giLCJjb2xvciIsInVzZUFuaW1hdGlvbkZyYW1lIiwidXNlVGVybWluYWxGb2N1cyIsInVzZVRoZW1lIiwiQmFzZVRleHRJbnB1dFByb3BzIiwiaXNFbnZUcnV0aHkiLCJUZXh0SGlnaGxpZ2h0IiwiQmFzZVRleHRJbnB1dCIsImh1ZVRvUmdiIiwiQkFSUyIsIkNVUlNPUl9XQVZFRk9STV9XSURUSCIsIlNNT09USCIsIkxFVkVMX0JPT1NUIiwiU0lMRU5DRV9USFJFU0hPTEQiLCJQcm9wcyIsImhpZ2hsaWdodHMiLCJUZXh0SW5wdXQiLCJwcm9wcyIsIlJlYWN0Tm9kZSIsInRoZW1lIiwiaXNUZXJtaW5hbEZvY3VzZWQiLCJhY2Nlc3NpYmlsaXR5RW5hYmxlZCIsInByb2Nlc3MiLCJlbnYiLCJDTEFVREVfQ09ERV9BQ0NFU1NJQklMSVRZIiwic2V0dGluZ3MiLCJyZWR1Y2VkTW90aW9uIiwicHJlZmVyc1JlZHVjZWRNb3Rpb24iLCJ2b2ljZVN0YXRlIiwicyIsImNvbnN0IiwiaXNWb2ljZVJlY29yZGluZyIsImF1ZGlvTGV2ZWxzIiwidm9pY2VBdWRpb0xldmVscyIsInNtb290aGVkUmVmIiwiQXJyYXkiLCJmaWxsIiwibmVlZHNBbmltYXRpb24iLCJhbmltUmVmIiwiYW5pbVRpbWUiLCJvbkltYWdlUGFzdGUiLCJjYW5TaG93Q3Vyc29yIiwiaW52ZXJ0IiwidGV4dCIsInNtb290aGVkIiwiY3VycmVudCIsInJhdyIsImxlbmd0aCIsInRhcmdldCIsIk1hdGgiLCJtaW4iLCJkaXNwbGF5TGV2ZWwiLCJiYXJJbmRleCIsIm1heCIsInJvdW5kIiwiaXNTaWxlbnQiLCJodWUiLCJyIiwiZyIsImIiLCJyZ2IiLCJpbnZlcnNlIiwidGV4dElucHV0U3RhdGUiLCJ2YWx1ZSIsIm9uQ2hhbmdlIiwib25TdWJtaXQiLCJvbkV4aXQiLCJvbkV4aXRNZXNzYWdlIiwib25IaXN0b3J5UmVzZXQiLCJvbkhpc3RvcnlVcCIsIm9uSGlzdG9yeURvd24iLCJvbkNsZWFySW5wdXQiLCJmb2N1cyIsIm1hc2siLCJtdWx0aWxpbmUiLCJjdXJzb3JDaGFyIiwic2hvd0N1cnNvciIsImhpZ2hsaWdodFBhc3RlZFRleHQiLCJ0aGVtZVRleHQiLCJjb2x1bW5zIiwibWF4VmlzaWJsZUxpbmVzIiwiZGlzYWJsZUN1cnNvck1vdmVtZW50Rm9yVXBEb3duS2V5cyIsImRpc2FibGVFc2NhcGVEb3VibGVQcmVzcyIsImV4dGVybmFsT2Zmc2V0IiwiY3Vyc29yT2Zmc2V0Iiwib25PZmZzZXRDaGFuZ2UiLCJvbkNoYW5nZUN1cnNvck9mZnNldCIsImlucHV0RmlsdGVyIiwiaW5saW5lR2hvc3RUZXh0IiwiZGltIl0sInNvdXJjZXMiOlsiVGV4dElucHV0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBmZWF0dXJlIH0gZnJvbSAnYnVuOmJ1bmRsZSdcbmltcG9ydCBjaGFsayBmcm9tICdjaGFsaydcbmltcG9ydCBSZWFjdCwgeyB1c2VNZW1vLCB1c2VSZWYgfSBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZVZvaWNlU3RhdGUgfSBmcm9tICcuLi9jb250ZXh0L3ZvaWNlLmpzJ1xuaW1wb3J0IHsgdXNlQ2xpcGJvYXJkSW1hZ2VIaW50IH0gZnJvbSAnLi4vaG9va3MvdXNlQ2xpcGJvYXJkSW1hZ2VIaW50LmpzJ1xuaW1wb3J0IHsgdXNlU2V0dGluZ3MgfSBmcm9tICcuLi9ob29rcy91c2VTZXR0aW5ncy5qcydcbmltcG9ydCB7IHVzZVRleHRJbnB1dCB9IGZyb20gJy4uL2hvb2tzL3VzZVRleHRJbnB1dC5qcydcbmltcG9ydCB7XG4gIEJveCxcbiAgY29sb3IsXG4gIHVzZUFuaW1hdGlvbkZyYW1lLFxuICB1c2VUZXJtaW5hbEZvY3VzLFxuICB1c2VUaGVtZSxcbn0gZnJvbSAnLi4vaW5rLmpzJ1xuaW1wb3J0IHR5cGUgeyBCYXNlVGV4dElucHV0UHJvcHMgfSBmcm9tICcuLi90eXBlcy90ZXh0SW5wdXRUeXBlcy5qcydcbmltcG9ydCB7IGlzRW52VHJ1dGh5IH0gZnJvbSAnLi4vdXRpbHMvZW52VXRpbHMuanMnXG5pbXBvcnQgdHlwZSB7IFRleHRIaWdobGlnaHQgfSBmcm9tICcuLi91dGlscy90ZXh0SGlnaGxpZ2h0aW5nLmpzJ1xuaW1wb3J0IHsgQmFzZVRleHRJbnB1dCB9IGZyb20gJy4vQmFzZVRleHRJbnB1dC5qcydcbmltcG9ydCB7IGh1ZVRvUmdiIH0gZnJvbSAnLi9TcGlubmVyL3V0aWxzLmpzJ1xuXG4vLyBCbG9jayBjaGFyYWN0ZXJzIGZvciB3YXZlZm9ybSBiYXJzOiBzcGFjZSAoc2lsZW50KSArIDggcmlzaW5nIGJsb2NrIGVsZW1lbnRzLlxuY29uc3QgQkFSUyA9ICcgXFx1MjU4MVxcdTI1ODJcXHUyNTgzXFx1MjU4NFxcdTI1ODVcXHUyNTg2XFx1MjU4N1xcdTI1ODgnXG5cbi8vIE1pbmkgd2F2ZWZvcm0gY3Vyc29yIHdpZHRoXG5jb25zdCBDVVJTT1JfV0FWRUZPUk1fV0lEVEggPSAxXG5cbi8vIFNtb290aGluZyBmYWN0b3IgKDAgPSBpbnN0YW50LCAxID0gZnJvemVuKS4gQXBwbGllZCBhcyBFTUEgdG9cbi8vIHNtb290aCBib3RoIHJpc2VzIGFuZCBmYWxscyBmb3IgYSBzdGVhZHksIG5vbi1qaXR0ZXJ5IGJhci5cbmNvbnN0IFNNT09USCA9IDAuN1xuXG4vLyBCb29zdCBmYWN0b3IgZm9yIGF1ZGlvIGxldmVscyDigJQgY29tcHV0ZUxldmVsIG5vcm1hbGl6ZXMgd2l0aCBhXG4vLyBjb25zZXJ2YXRpdmUgZGl2aXNvciAocm1zLzIwMDApLCBzbyBub3JtYWwgc3BlZWNoIHNpdHMgYXJvdW5kXG4vLyAwLjMtMC41LiBUaGlzIG11bHRpcGxpZXIgbGV0cyB0aGUgYmFyIHVzZSB0aGUgZnVsbCByYW5nZS5cbmNvbnN0IExFVkVMX0JPT1NUID0gMS44XG5cbi8vIFJhdyBhdWRpbyBsZXZlbCB0aHJlc2hvbGQgKHByZS1ib29zdCkgYmVsb3cgd2hpY2ggdGhlIGN1cnNvciBpc1xuLy8gZ3JleS4gY29tcHV0ZUxldmVsIHJldHVybnMgc3FydChybXMvMjAwMCksIHNvIGFtYmllbnQgbWljIG5vaXNlXG4vLyB0eXBpY2FsbHkgc2l0cyBhdCAwLjA1LTAuMTUuIFNwZWVjaCBzdGFydHMgYXJvdW5kIDAuMisuXG5jb25zdCBTSUxFTkNFX1RIUkVTSE9MRCA9IDAuMTVcblxuZXhwb3J0IHR5cGUgUHJvcHMgPSBCYXNlVGV4dElucHV0UHJvcHMgJiB7XG4gIGhpZ2hsaWdodHM/OiBUZXh0SGlnaGxpZ2h0W11cbn1cblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gVGV4dElucHV0KHByb3BzOiBQcm9wcyk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFt0aGVtZV0gPSB1c2VUaGVtZSgpXG4gIGNvbnN0IGlzVGVybWluYWxGb2N1c2VkID0gdXNlVGVybWluYWxGb2N1cygpXG4gIC8vIEhvaXN0ZWQgdG8gbW91bnQtdGltZSDigJQgdGhpcyBjb21wb25lbnQgcmUtcmVuZGVycyBvbiBldmVyeSBrZXlzdHJva2UuXG4gIGNvbnN0IGFjY2Vzc2liaWxpdHlFbmFibGVkID0gdXNlTWVtbyhcbiAgICAoKSA9PiBpc0VudlRydXRoeShwcm9jZXNzLmVudi5DTEFVREVfQ09ERV9BQ0NFU1NJQklMSVRZKSxcbiAgICBbXSxcbiAgKVxuICBjb25zdCBzZXR0aW5ncyA9IHVzZVNldHRpbmdzKClcbiAgY29uc3QgcmVkdWNlZE1vdGlvbiA9IHNldHRpbmdzLnByZWZlcnNSZWR1Y2VkTW90aW9uID8/IGZhbHNlXG5cbiAgY29uc3Qgdm9pY2VTdGF0ZSA9IGZlYXR1cmUoJ1ZPSUNFX01PREUnKVxuICAgID8gLy8gYmlvbWUtaWdub3JlIGxpbnQvY29ycmVjdG5lc3MvdXNlSG9va0F0VG9wTGV2ZWw6IGZlYXR1cmUoKSBpcyBhIGNvbXBpbGUtdGltZSBjb25zdGFudFxuICAgICAgdXNlVm9pY2VTdGF0ZShzID0+IHMudm9pY2VTdGF0ZSlcbiAgICA6ICgnaWRsZScgYXMgY29uc3QpXG4gIGNvbnN0IGlzVm9pY2VSZWNvcmRpbmcgPSB2b2ljZVN0YXRlID09PSAncmVjb3JkaW5nJ1xuXG4gIGNvbnN0IGF1ZGlvTGV2ZWxzID0gZmVhdHVyZSgnVk9JQ0VfTU9ERScpXG4gICAgPyAvLyBiaW9tZS1pZ25vcmUgbGludC9jb3JyZWN0bmVzcy91c2VIb29rQXRUb3BMZXZlbDogZmVhdHVyZSgpIGlzIGEgY29tcGlsZS10aW1lIGNvbnN0YW50XG4gICAgICB1c2VWb2ljZVN0YXRlKHMgPT4gcy52b2ljZUF1ZGlvTGV2ZWxzKVxuICAgIDogW11cbiAgY29uc3Qgc21vb3RoZWRSZWYgPSB1c2VSZWY8bnVtYmVyW10+KG5ldyBBcnJheShDVVJTT1JfV0FWRUZPUk1fV0lEVEgpLmZpbGwoMCkpXG5cbiAgY29uc3QgbmVlZHNBbmltYXRpb24gPSBpc1ZvaWNlUmVjb3JkaW5nICYmICFyZWR1Y2VkTW90aW9uXG4gIGNvbnN0IFthbmltUmVmLCBhbmltVGltZV0gPSBmZWF0dXJlKCdWT0lDRV9NT0RFJylcbiAgICA/IC8vIGJpb21lLWlnbm9yZSBsaW50L2NvcnJlY3RuZXNzL3VzZUhvb2tBdFRvcExldmVsOiBmZWF0dXJlKCkgaXMgYSBjb21waWxlLXRpbWUgY29uc3RhbnRcbiAgICAgIHVzZUFuaW1hdGlvbkZyYW1lKG5lZWRzQW5pbWF0aW9uID8gNTAgOiBudWxsKVxuICAgIDogWygpID0+IHt9LCAwXVxuXG4gIC8vIFNob3cgaGludCB3aGVuIHRlcm1pbmFsIHJlZ2FpbnMgZm9jdXMgYW5kIGNsaXBib2FyZCBoYXMgYW4gaW1hZ2VcbiAgdXNlQ2xpcGJvYXJkSW1hZ2VIaW50KGlzVGVybWluYWxGb2N1c2VkLCAhIXByb3BzLm9uSW1hZ2VQYXN0ZSlcblxuICAvLyBDdXJzb3IgaW52ZXJ0IGZ1bmN0aW9uOiBtaW5pIHdhdmVmb3JtIGR1cmluZyB2b2ljZSByZWNvcmRpbmcsXG4gIC8vIHN0YW5kYXJkIGNoYWxrLmludmVyc2Ugb3RoZXJ3aXNlLiBObyB3YXJtdXAgcHVsc2Ug4oCUIHRoZSB+MTIwbXNcbiAgLy8gd2FybXVwIHdpbmRvdyBpcyB0b28gc2hvcnQgZm9yIGEgMXMtcGVyaW9kIHB1bHNlIHRvIHJlZ2lzdGVyLCBhbmRcbiAgLy8gZHJpdmluZyBUZXh0SW5wdXQgcmUtcmVuZGVycyBhdCA1MG1zIGR1cmluZyB3YXJtdXAgKHdoaWxlIHNwYWNlc1xuICAvLyBhcmUgc2ltdWx0YW5lb3VzbHkgYXJyaXZpbmcgZXZlcnkgMzAtODBtcykgY2F1c2VzIHZpc2libGUgc3R1dHRlci5cbiAgY29uc3QgY2FuU2hvd0N1cnNvciA9IGlzVGVybWluYWxGb2N1c2VkICYmICFhY2Nlc3NpYmlsaXR5RW5hYmxlZFxuICBsZXQgaW52ZXJ0OiAodGV4dDogc3RyaW5nKSA9PiBzdHJpbmdcbiAgaWYgKCFjYW5TaG93Q3Vyc29yKSB7XG4gICAgaW52ZXJ0ID0gKHRleHQ6IHN0cmluZykgPT4gdGV4dFxuICB9IGVsc2UgaWYgKGlzVm9pY2VSZWNvcmRpbmcgJiYgIXJlZHVjZWRNb3Rpb24pIHtcbiAgICAvLyBTaW5nbGUtYmFyIHdhdmVmb3JtIGZyb20gdGhlIGxhdGVzdCBhdWRpbyBsZXZlbFxuICAgIGNvbnN0IHNtb290aGVkID0gc21vb3RoZWRSZWYuY3VycmVudFxuICAgIGNvbnN0IHJhdyA9XG4gICAgICBhdWRpb0xldmVscy5sZW5ndGggPiAwID8gKGF1ZGlvTGV2ZWxzW2F1ZGlvTGV2ZWxzLmxlbmd0aCAtIDFdID8/IDApIDogMFxuICAgIGNvbnN0IHRhcmdldCA9IE1hdGgubWluKHJhdyAqIExFVkVMX0JPT1NULCAxKVxuICAgIHNtb290aGVkWzBdID0gKHNtb290aGVkWzBdID8/IDApICogU01PT1RIICsgdGFyZ2V0ICogKDEgLSBTTU9PVEgpXG4gICAgY29uc3QgZGlzcGxheUxldmVsID0gc21vb3RoZWRbMF0gPz8gMFxuICAgIGNvbnN0IGJhckluZGV4ID0gTWF0aC5tYXgoXG4gICAgICAxLFxuICAgICAgTWF0aC5taW4oTWF0aC5yb3VuZChkaXNwbGF5TGV2ZWwgKiAoQkFSUy5sZW5ndGggLSAxKSksIEJBUlMubGVuZ3RoIC0gMSksXG4gICAgKVxuICAgIGNvbnN0IGlzU2lsZW50ID0gcmF3IDwgU0lMRU5DRV9USFJFU0hPTERcbiAgICBjb25zdCBodWUgPSAoKGFuaW1UaW1lIC8gMTAwMCkgKiA5MCkgJSAzNjBcbiAgICBjb25zdCB7IHIsIGcsIGIgfSA9IGlzU2lsZW50ID8geyByOiAxMjgsIGc6IDEyOCwgYjogMTI4IH0gOiBodWVUb1JnYihodWUpXG4gICAgaW52ZXJ0ID0gKCkgPT4gY2hhbGsucmdiKHIsIGcsIGIpKEJBUlNbYmFySW5kZXhdISlcbiAgfSBlbHNlIHtcbiAgICBpbnZlcnQgPSBjaGFsay5pbnZlcnNlXG4gIH1cblxuICBjb25zdCB0ZXh0SW5wdXRTdGF0ZSA9IHVzZVRleHRJbnB1dCh7XG4gICAgdmFsdWU6IHByb3BzLnZhbHVlLFxuICAgIG9uQ2hhbmdlOiBwcm9wcy5vbkNoYW5nZSxcbiAgICBvblN1Ym1pdDogcHJvcHMub25TdWJtaXQsXG4gICAgb25FeGl0OiBwcm9wcy5vbkV4aXQsXG4gICAgb25FeGl0TWVzc2FnZTogcHJvcHMub25FeGl0TWVzc2FnZSxcbiAgICBvbkhpc3RvcnlSZXNldDogcHJvcHMub25IaXN0b3J5UmVzZXQsXG4gICAgb25IaXN0b3J5VXA6IHByb3BzLm9uSGlzdG9yeVVwLFxuICAgIG9uSGlzdG9yeURvd246IHByb3BzLm9uSGlzdG9yeURvd24sXG4gICAgb25DbGVhcklucHV0OiBwcm9wcy5vbkNsZWFySW5wdXQsXG4gICAgZm9jdXM6IHByb3BzLmZvY3VzLFxuICAgIG1hc2s6IHByb3BzLm1hc2ssXG4gICAgbXVsdGlsaW5lOiBwcm9wcy5tdWx0aWxpbmUsXG4gICAgY3Vyc29yQ2hhcjogcHJvcHMuc2hvd0N1cnNvciA/ICcgJyA6ICcnLFxuICAgIGhpZ2hsaWdodFBhc3RlZFRleHQ6IHByb3BzLmhpZ2hsaWdodFBhc3RlZFRleHQsXG4gICAgaW52ZXJ0LFxuICAgIHRoZW1lVGV4dDogY29sb3IoJ3RleHQnLCB0aGVtZSksXG4gICAgY29sdW1uczogcHJvcHMuY29sdW1ucyxcbiAgICBtYXhWaXNpYmxlTGluZXM6IHByb3BzLm1heFZpc2libGVMaW5lcyxcbiAgICBvbkltYWdlUGFzdGU6IHByb3BzLm9uSW1hZ2VQYXN0ZSxcbiAgICBkaXNhYmxlQ3Vyc29yTW92ZW1lbnRGb3JVcERvd25LZXlzOlxuICAgICAgcHJvcHMuZGlzYWJsZUN1cnNvck1vdmVtZW50Rm9yVXBEb3duS2V5cyxcbiAgICBkaXNhYmxlRXNjYXBlRG91YmxlUHJlc3M6IHByb3BzLmRpc2FibGVFc2NhcGVEb3VibGVQcmVzcyxcbiAgICBleHRlcm5hbE9mZnNldDogcHJvcHMuY3Vyc29yT2Zmc2V0LFxuICAgIG9uT2Zmc2V0Q2hhbmdlOiBwcm9wcy5vbkNoYW5nZUN1cnNvck9mZnNldCxcbiAgICBpbnB1dEZpbHRlcjogcHJvcHMuaW5wdXRGaWx0ZXIsXG4gICAgaW5saW5lR2hvc3RUZXh0OiBwcm9wcy5pbmxpbmVHaG9zdFRleHQsXG4gICAgZGltOiBjaGFsay5kaW0sXG4gIH0pXG5cbiAgcmV0dXJuIChcbiAgICA8Qm94IHJlZj17YW5pbVJlZn0+XG4gICAgICA8QmFzZVRleHRJbnB1dFxuICAgICAgICBpbnB1dFN0YXRlPXt0ZXh0SW5wdXRTdGF0ZX1cbiAgICAgICAgdGVybWluYWxGb2N1cz17aXNUZXJtaW5hbEZvY3VzZWR9XG4gICAgICAgIGhpZ2hsaWdodHM9e3Byb3BzLmhpZ2hsaWdodHN9XG4gICAgICAgIGludmVydD17aW52ZXJ0fVxuICAgICAgICBoaWRlUGxhY2Vob2xkZXJUZXh0PXtpc1ZvaWNlUmVjb3JkaW5nfVxuICAgICAgICB7Li4ucHJvcHN9XG4gICAgICAvPlxuICAgIDwvQm94PlxuICApXG59XG4iXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLE9BQU8sUUFBUSxZQUFZO0FBQ3BDLE9BQU9DLEtBQUssTUFBTSxPQUFPO0FBQ3pCLE9BQU9DLEtBQUssSUFBSUMsT0FBTyxFQUFFQyxNQUFNLFFBQVEsT0FBTztBQUM5QyxTQUFTQyxhQUFhLFFBQVEscUJBQXFCO0FBQ25ELFNBQVNDLHFCQUFxQixRQUFRLG1DQUFtQztBQUN6RSxTQUFTQyxXQUFXLFFBQVEseUJBQXlCO0FBQ3JELFNBQVNDLFlBQVksUUFBUSwwQkFBMEI7QUFDdkQsU0FDRUMsR0FBRyxFQUNIQyxLQUFLLEVBQ0xDLGlCQUFpQixFQUNqQkMsZ0JBQWdCLEVBQ2hCQyxRQUFRLFFBQ0gsV0FBVztBQUNsQixjQUFjQyxrQkFBa0IsUUFBUSw0QkFBNEI7QUFDcEUsU0FBU0MsV0FBVyxRQUFRLHNCQUFzQjtBQUNsRCxjQUFjQyxhQUFhLFFBQVEsOEJBQThCO0FBQ2pFLFNBQVNDLGFBQWEsUUFBUSxvQkFBb0I7QUFDbEQsU0FBU0MsUUFBUSxRQUFRLG9CQUFvQjs7QUFFN0M7QUFDQSxNQUFNQyxJQUFJLEdBQUcsbURBQW1EOztBQUVoRTtBQUNBLE1BQU1DLHFCQUFxQixHQUFHLENBQUM7O0FBRS9CO0FBQ0E7QUFDQSxNQUFNQyxNQUFNLEdBQUcsR0FBRzs7QUFFbEI7QUFDQTtBQUNBO0FBQ0EsTUFBTUMsV0FBVyxHQUFHLEdBQUc7O0FBRXZCO0FBQ0E7QUFDQTtBQUNBLE1BQU1DLGlCQUFpQixHQUFHLElBQUk7QUFFOUIsT0FBTyxLQUFLQyxLQUFLLEdBQUdWLGtCQUFrQixHQUFHO0VBQ3ZDVyxVQUFVLENBQUMsRUFBRVQsYUFBYSxFQUFFO0FBQzlCLENBQUM7QUFFRCxlQUFlLFNBQVNVLFNBQVNBLENBQUNDLEtBQUssRUFBRUgsS0FBSyxDQUFDLEVBQUV0QixLQUFLLENBQUMwQixTQUFTLENBQUM7RUFDL0QsTUFBTSxDQUFDQyxLQUFLLENBQUMsR0FBR2hCLFFBQVEsQ0FBQyxDQUFDO0VBQzFCLE1BQU1pQixpQkFBaUIsR0FBR2xCLGdCQUFnQixDQUFDLENBQUM7RUFDNUM7RUFDQSxNQUFNbUIsb0JBQW9CLEdBQUc1QixPQUFPLENBQ2xDLE1BQU1ZLFdBQVcsQ0FBQ2lCLE9BQU8sQ0FBQ0MsR0FBRyxDQUFDQyx5QkFBeUIsQ0FBQyxFQUN4RCxFQUNGLENBQUM7RUFDRCxNQUFNQyxRQUFRLEdBQUc1QixXQUFXLENBQUMsQ0FBQztFQUM5QixNQUFNNkIsYUFBYSxHQUFHRCxRQUFRLENBQUNFLG9CQUFvQixJQUFJLEtBQUs7RUFFNUQsTUFBTUMsVUFBVSxHQUFHdEMsT0FBTyxDQUFDLFlBQVksQ0FBQztFQUNwQztFQUNBSyxhQUFhLENBQUNrQyxDQUFDLElBQUlBLENBQUMsQ0FBQ0QsVUFBVSxDQUFDLEdBQy9CLE1BQU0sSUFBSUUsS0FBTTtFQUNyQixNQUFNQyxnQkFBZ0IsR0FBR0gsVUFBVSxLQUFLLFdBQVc7RUFFbkQsTUFBTUksV0FBVyxHQUFHMUMsT0FBTyxDQUFDLFlBQVksQ0FBQztFQUNyQztFQUNBSyxhQUFhLENBQUNrQyxHQUFDLElBQUlBLEdBQUMsQ0FBQ0ksZ0JBQWdCLENBQUMsR0FDdEMsRUFBRTtFQUNOLE1BQU1DLFdBQVcsR0FBR3hDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLElBQUl5QyxLQUFLLENBQUN6QixxQkFBcUIsQ0FBQyxDQUFDMEIsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0VBRTlFLE1BQU1DLGNBQWMsR0FBR04sZ0JBQWdCLElBQUksQ0FBQ0wsYUFBYTtFQUN6RCxNQUFNLENBQUNZLE9BQU8sRUFBRUMsUUFBUSxDQUFDLEdBQUdqRCxPQUFPLENBQUMsWUFBWSxDQUFDO0VBQzdDO0VBQ0FXLGlCQUFpQixDQUFDb0MsY0FBYyxHQUFHLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FDN0MsQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQzs7RUFFakI7RUFDQXpDLHFCQUFxQixDQUFDd0IsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDSCxLQUFLLENBQUN1QixZQUFZLENBQUM7O0VBRTlEO0VBQ0E7RUFDQTtFQUNBO0VBQ0E7RUFDQSxNQUFNQyxhQUFhLEdBQUdyQixpQkFBaUIsSUFBSSxDQUFDQyxvQkFBb0I7RUFDaEUsSUFBSXFCLE1BQU0sRUFBRSxDQUFDQyxJQUFJLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTTtFQUNwQyxJQUFJLENBQUNGLGFBQWEsRUFBRTtJQUNsQkMsTUFBTSxHQUFHQSxDQUFDQyxJQUFJLEVBQUUsTUFBTSxLQUFLQSxJQUFJO0VBQ2pDLENBQUMsTUFBTSxJQUFJWixnQkFBZ0IsSUFBSSxDQUFDTCxhQUFhLEVBQUU7SUFDN0M7SUFDQSxNQUFNa0IsUUFBUSxHQUFHVixXQUFXLENBQUNXLE9BQU87SUFDcEMsTUFBTUMsR0FBRyxHQUNQZCxXQUFXLENBQUNlLE1BQU0sR0FBRyxDQUFDLEdBQUlmLFdBQVcsQ0FBQ0EsV0FBVyxDQUFDZSxNQUFNLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFJLENBQUM7SUFDekUsTUFBTUMsTUFBTSxHQUFHQyxJQUFJLENBQUNDLEdBQUcsQ0FBQ0osR0FBRyxHQUFHbEMsV0FBVyxFQUFFLENBQUMsQ0FBQztJQUM3Q2dDLFFBQVEsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDQSxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJakMsTUFBTSxHQUFHcUMsTUFBTSxJQUFJLENBQUMsR0FBR3JDLE1BQU0sQ0FBQztJQUNqRSxNQUFNd0MsWUFBWSxHQUFHUCxRQUFRLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUNyQyxNQUFNUSxRQUFRLEdBQUdILElBQUksQ0FBQ0ksR0FBRyxDQUN2QixDQUFDLEVBQ0RKLElBQUksQ0FBQ0MsR0FBRyxDQUFDRCxJQUFJLENBQUNLLEtBQUssQ0FBQ0gsWUFBWSxJQUFJMUMsSUFBSSxDQUFDc0MsTUFBTSxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUV0QyxJQUFJLENBQUNzQyxNQUFNLEdBQUcsQ0FBQyxDQUN4RSxDQUFDO0lBQ0QsTUFBTVEsUUFBUSxHQUFHVCxHQUFHLEdBQUdqQyxpQkFBaUI7SUFDeEMsTUFBTTJDLEdBQUcsR0FBS2pCLFFBQVEsR0FBRyxJQUFJLEdBQUksRUFBRSxHQUFJLEdBQUc7SUFDMUMsTUFBTTtNQUFFa0IsQ0FBQztNQUFFQyxDQUFDO01BQUVDO0lBQUUsQ0FBQyxHQUFHSixRQUFRLEdBQUc7TUFBRUUsQ0FBQyxFQUFFLEdBQUc7TUFBRUMsQ0FBQyxFQUFFLEdBQUc7TUFBRUMsQ0FBQyxFQUFFO0lBQUksQ0FBQyxHQUFHbkQsUUFBUSxDQUFDZ0QsR0FBRyxDQUFDO0lBQ3pFZCxNQUFNLEdBQUdBLENBQUEsS0FBTW5ELEtBQUssQ0FBQ3FFLEdBQUcsQ0FBQ0gsQ0FBQyxFQUFFQyxDQUFDLEVBQUVDLENBQUMsQ0FBQyxDQUFDbEQsSUFBSSxDQUFDMkMsUUFBUSxDQUFDLENBQUMsQ0FBQztFQUNwRCxDQUFDLE1BQU07SUFDTFYsTUFBTSxHQUFHbkQsS0FBSyxDQUFDc0UsT0FBTztFQUN4QjtFQUVBLE1BQU1DLGNBQWMsR0FBR2hFLFlBQVksQ0FBQztJQUNsQ2lFLEtBQUssRUFBRTlDLEtBQUssQ0FBQzhDLEtBQUs7SUFDbEJDLFFBQVEsRUFBRS9DLEtBQUssQ0FBQytDLFFBQVE7SUFDeEJDLFFBQVEsRUFBRWhELEtBQUssQ0FBQ2dELFFBQVE7SUFDeEJDLE1BQU0sRUFBRWpELEtBQUssQ0FBQ2lELE1BQU07SUFDcEJDLGFBQWEsRUFBRWxELEtBQUssQ0FBQ2tELGFBQWE7SUFDbENDLGNBQWMsRUFBRW5ELEtBQUssQ0FBQ21ELGNBQWM7SUFDcENDLFdBQVcsRUFBRXBELEtBQUssQ0FBQ29ELFdBQVc7SUFDOUJDLGFBQWEsRUFBRXJELEtBQUssQ0FBQ3FELGFBQWE7SUFDbENDLFlBQVksRUFBRXRELEtBQUssQ0FBQ3NELFlBQVk7SUFDaENDLEtBQUssRUFBRXZELEtBQUssQ0FBQ3VELEtBQUs7SUFDbEJDLElBQUksRUFBRXhELEtBQUssQ0FBQ3dELElBQUk7SUFDaEJDLFNBQVMsRUFBRXpELEtBQUssQ0FBQ3lELFNBQVM7SUFDMUJDLFVBQVUsRUFBRTFELEtBQUssQ0FBQzJELFVBQVUsR0FBRyxHQUFHLEdBQUcsRUFBRTtJQUN2Q0MsbUJBQW1CLEVBQUU1RCxLQUFLLENBQUM0RCxtQkFBbUI7SUFDOUNuQyxNQUFNO0lBQ05vQyxTQUFTLEVBQUU5RSxLQUFLLENBQUMsTUFBTSxFQUFFbUIsS0FBSyxDQUFDO0lBQy9CNEQsT0FBTyxFQUFFOUQsS0FBSyxDQUFDOEQsT0FBTztJQUN0QkMsZUFBZSxFQUFFL0QsS0FBSyxDQUFDK0QsZUFBZTtJQUN0Q3hDLFlBQVksRUFBRXZCLEtBQUssQ0FBQ3VCLFlBQVk7SUFDaEN5QyxrQ0FBa0MsRUFDaENoRSxLQUFLLENBQUNnRSxrQ0FBa0M7SUFDMUNDLHdCQUF3QixFQUFFakUsS0FBSyxDQUFDaUUsd0JBQXdCO0lBQ3hEQyxjQUFjLEVBQUVsRSxLQUFLLENBQUNtRSxZQUFZO0lBQ2xDQyxjQUFjLEVBQUVwRSxLQUFLLENBQUNxRSxvQkFBb0I7SUFDMUNDLFdBQVcsRUFBRXRFLEtBQUssQ0FBQ3NFLFdBQVc7SUFDOUJDLGVBQWUsRUFBRXZFLEtBQUssQ0FBQ3VFLGVBQWU7SUFDdENDLEdBQUcsRUFBRWxHLEtBQUssQ0FBQ2tHO0VBQ2IsQ0FBQyxDQUFDO0VBRUYsT0FDRSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQ25ELE9BQU8sQ0FBQztBQUN0QixNQUFNLENBQUMsYUFBYSxDQUNaLFVBQVUsQ0FBQyxDQUFDd0IsY0FBYyxDQUFDLENBQzNCLGFBQWEsQ0FBQyxDQUFDMUMsaUJBQWlCLENBQUMsQ0FDakMsVUFBVSxDQUFDLENBQUNILEtBQUssQ0FBQ0YsVUFBVSxDQUFDLENBQzdCLE1BQU0sQ0FBQyxDQUFDMkIsTUFBTSxDQUFDLENBQ2YsbUJBQW1CLENBQUMsQ0FBQ1gsZ0JBQWdCLENBQUMsQ0FDdEMsSUFBSWQsS0FBSyxDQUFDO0FBRWxCLElBQUksRUFBRSxHQUFHLENBQUM7QUFFViIsImlnbm9yZUxpc3QiOltdfQ==