/ components / permissions / PermissionExplanation.tsx
PermissionExplanation.tsx
  1  import { c as _c } from "react/compiler-runtime";
  2  import React, { Suspense, use, useState } from 'react';
  3  import { Box, Text } from '../../ink.js';
  4  import { useKeybinding } from '../../keybindings/useKeybinding.js';
  5  import { logEvent } from '../../services/analytics/index.js';
  6  import type { Message } from '../../types/message.js';
  7  import { generatePermissionExplanation, isPermissionExplainerEnabled, type PermissionExplanation as PermissionExplanationType, type RiskLevel } from '../../utils/permissions/permissionExplainer.js';
  8  import { ShimmerChar } from '../Spinner/ShimmerChar.js';
  9  import { useShimmerAnimation } from '../Spinner/useShimmerAnimation.js';
 10  const LOADING_MESSAGE = 'Loading explanation…';
 11  function ShimmerLoadingText() {
 12    const $ = _c(7);
 13    const [ref, glimmerIndex] = useShimmerAnimation("responding", LOADING_MESSAGE, false);
 14    let t0;
 15    if ($[0] !== glimmerIndex) {
 16      t0 = LOADING_MESSAGE.split("").map((char, index) => <ShimmerChar key={index} char={char} index={index} glimmerIndex={glimmerIndex} messageColor="inactive" shimmerColor="text" />);
 17      $[0] = glimmerIndex;
 18      $[1] = t0;
 19    } else {
 20      t0 = $[1];
 21    }
 22    let t1;
 23    if ($[2] !== t0) {
 24      t1 = <Text>{t0}</Text>;
 25      $[2] = t0;
 26      $[3] = t1;
 27    } else {
 28      t1 = $[3];
 29    }
 30    let t2;
 31    if ($[4] !== ref || $[5] !== t1) {
 32      t2 = <Box ref={ref}>{t1}</Box>;
 33      $[4] = ref;
 34      $[5] = t1;
 35      $[6] = t2;
 36    } else {
 37      t2 = $[6];
 38    }
 39    return t2;
 40  }
 41  function getRiskColor(riskLevel: RiskLevel): 'success' | 'warning' | 'error' {
 42    switch (riskLevel) {
 43      case 'LOW':
 44        return 'success';
 45      case 'MEDIUM':
 46        return 'warning';
 47      case 'HIGH':
 48        return 'error';
 49    }
 50  }
 51  function getRiskLabel(riskLevel: RiskLevel): string {
 52    switch (riskLevel) {
 53      case 'LOW':
 54        return 'Low risk';
 55      case 'MEDIUM':
 56        return 'Med risk';
 57      case 'HIGH':
 58        return 'High risk';
 59    }
 60  }
 61  type PermissionExplanationProps = {
 62    toolName: string;
 63    toolInput: unknown;
 64    toolDescription?: string;
 65    messages?: Message[];
 66  };
 67  type ExplainerState = {
 68    visible: boolean;
 69    enabled: boolean;
 70    promise: Promise<PermissionExplanationType | null> | null;
 71  };
 72  
 73  /**
 74   * Creates an explanation promise that never rejects.
 75   * Errors are caught and returned as null.
 76   */
 77  function createExplanationPromise(props: PermissionExplanationProps): Promise<PermissionExplanationType | null> {
 78    return generatePermissionExplanation({
 79      toolName: props.toolName,
 80      toolInput: props.toolInput,
 81      toolDescription: props.toolDescription,
 82      messages: props.messages,
 83      signal: new AbortController().signal // Won't abort - request is fast enough
 84    }).catch(() => null);
 85  }
 86  
 87  /**
 88   * Hook that manages the permission explainer state.
 89   * Creates the fetch promise lazily (only when user hits Ctrl+E)
 90   * to avoid consuming tokens for explanations users never view.
 91   */
 92  export function usePermissionExplainerUI(props) {
 93    const $ = _c(9);
 94    let t0;
 95    if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
 96      t0 = isPermissionExplainerEnabled();
 97      $[0] = t0;
 98    } else {
 99      t0 = $[0];
100    }
101    const enabled = t0;
102    const [visible, setVisible] = useState(false);
103    const [promise, setPromise] = useState(null);
104    let t1;
105    if ($[1] !== promise || $[2] !== props || $[3] !== visible) {
106      t1 = () => {
107        if (!visible) {
108          logEvent("tengu_permission_explainer_shortcut_used", {});
109          if (!promise) {
110            setPromise(createExplanationPromise(props));
111          }
112        }
113        setVisible(_temp);
114      };
115      $[1] = promise;
116      $[2] = props;
117      $[3] = visible;
118      $[4] = t1;
119    } else {
120      t1 = $[4];
121    }
122    let t2;
123    if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
124      t2 = {
125        context: "Confirmation",
126        isActive: enabled
127      };
128      $[5] = t2;
129    } else {
130      t2 = $[5];
131    }
132    useKeybinding("confirm:toggleExplanation", t1, t2);
133    let t3;
134    if ($[6] !== promise || $[7] !== visible) {
135      t3 = {
136        visible,
137        enabled,
138        promise
139      };
140      $[6] = promise;
141      $[7] = visible;
142      $[8] = t3;
143    } else {
144      t3 = $[8];
145    }
146    return t3;
147  }
148  
149  /**
150   * Inner component that uses React 19's use() to read the promise.
151   * Suspends while loading, returns null on error.
152   */
153  function _temp(v) {
154    return !v;
155  }
156  function ExplanationResult(t0) {
157    const $ = _c(21);
158    const {
159      promise
160    } = t0;
161    const explanation = use(promise);
162    if (!explanation) {
163      let t1;
164      if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
165        t1 = <Box marginTop={1}><Text dimColor={true}>Explanation unavailable</Text></Box>;
166        $[0] = t1;
167      } else {
168        t1 = $[0];
169      }
170      return t1;
171    }
172    let t1;
173    if ($[1] !== explanation.explanation) {
174      t1 = <Text>{explanation.explanation}</Text>;
175      $[1] = explanation.explanation;
176      $[2] = t1;
177    } else {
178      t1 = $[2];
179    }
180    let t2;
181    if ($[3] !== explanation.reasoning) {
182      t2 = <Box marginTop={1}><Text>{explanation.reasoning}</Text></Box>;
183      $[3] = explanation.reasoning;
184      $[4] = t2;
185    } else {
186      t2 = $[4];
187    }
188    let t3;
189    if ($[5] !== explanation.riskLevel) {
190      t3 = getRiskColor(explanation.riskLevel);
191      $[5] = explanation.riskLevel;
192      $[6] = t3;
193    } else {
194      t3 = $[6];
195    }
196    let t4;
197    if ($[7] !== explanation.riskLevel) {
198      t4 = getRiskLabel(explanation.riskLevel);
199      $[7] = explanation.riskLevel;
200      $[8] = t4;
201    } else {
202      t4 = $[8];
203    }
204    let t5;
205    if ($[9] !== t3 || $[10] !== t4) {
206      t5 = <Text color={t3}>{t4}:</Text>;
207      $[9] = t3;
208      $[10] = t4;
209      $[11] = t5;
210    } else {
211      t5 = $[11];
212    }
213    let t6;
214    if ($[12] !== explanation.risk) {
215      t6 = <Text> {explanation.risk}</Text>;
216      $[12] = explanation.risk;
217      $[13] = t6;
218    } else {
219      t6 = $[13];
220    }
221    let t7;
222    if ($[14] !== t5 || $[15] !== t6) {
223      t7 = <Box marginTop={1}><Text>{t5}{t6}</Text></Box>;
224      $[14] = t5;
225      $[15] = t6;
226      $[16] = t7;
227    } else {
228      t7 = $[16];
229    }
230    let t8;
231    if ($[17] !== t1 || $[18] !== t2 || $[19] !== t7) {
232      t8 = <Box flexDirection="column" marginTop={1}>{t1}{t2}{t7}</Box>;
233      $[17] = t1;
234      $[18] = t2;
235      $[19] = t7;
236      $[20] = t8;
237    } else {
238      t8 = $[20];
239    }
240    return t8;
241  }
242  
243  /**
244   * Content component - shows loading (via Suspense) or explanation when visible
245   */
246  export function PermissionExplainerContent(t0) {
247    const $ = _c(3);
248    const {
249      visible,
250      promise
251    } = t0;
252    if (!visible || !promise) {
253      return null;
254    }
255    let t1;
256    if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
257      t1 = <Box marginTop={1}><ShimmerLoadingText /></Box>;
258      $[0] = t1;
259    } else {
260      t1 = $[0];
261    }
262    let t2;
263    if ($[1] !== promise) {
264      t2 = <Suspense fallback={t1}><ExplanationResult promise={promise} /></Suspense>;
265      $[1] = promise;
266      $[2] = t2;
267    } else {
268      t2 = $[2];
269    }
270    return t2;
271  }
272  //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsIlN1c3BlbnNlIiwidXNlIiwidXNlU3RhdGUiLCJCb3giLCJUZXh0IiwidXNlS2V5YmluZGluZyIsImxvZ0V2ZW50IiwiTWVzc2FnZSIsImdlbmVyYXRlUGVybWlzc2lvbkV4cGxhbmF0aW9uIiwiaXNQZXJtaXNzaW9uRXhwbGFpbmVyRW5hYmxlZCIsIlBlcm1pc3Npb25FeHBsYW5hdGlvbiIsIlBlcm1pc3Npb25FeHBsYW5hdGlvblR5cGUiLCJSaXNrTGV2ZWwiLCJTaGltbWVyQ2hhciIsInVzZVNoaW1tZXJBbmltYXRpb24iLCJMT0FESU5HX01FU1NBR0UiLCJTaGltbWVyTG9hZGluZ1RleHQiLCIkIiwiX2MiLCJyZWYiLCJnbGltbWVySW5kZXgiLCJ0MCIsInNwbGl0IiwibWFwIiwiY2hhciIsImluZGV4IiwidDEiLCJ0MiIsImdldFJpc2tDb2xvciIsInJpc2tMZXZlbCIsImdldFJpc2tMYWJlbCIsIlBlcm1pc3Npb25FeHBsYW5hdGlvblByb3BzIiwidG9vbE5hbWUiLCJ0b29sSW5wdXQiLCJ0b29sRGVzY3JpcHRpb24iLCJtZXNzYWdlcyIsIkV4cGxhaW5lclN0YXRlIiwidmlzaWJsZSIsImVuYWJsZWQiLCJwcm9taXNlIiwiUHJvbWlzZSIsImNyZWF0ZUV4cGxhbmF0aW9uUHJvbWlzZSIsInByb3BzIiwic2lnbmFsIiwiQWJvcnRDb250cm9sbGVyIiwiY2F0Y2giLCJ1c2VQZXJtaXNzaW9uRXhwbGFpbmVyVUkiLCJTeW1ib2wiLCJmb3IiLCJzZXRWaXNpYmxlIiwic2V0UHJvbWlzZSIsIl90ZW1wIiwiY29udGV4dCIsImlzQWN0aXZlIiwidDMiLCJ2IiwiRXhwbGFuYXRpb25SZXN1bHQiLCJleHBsYW5hdGlvbiIsInJlYXNvbmluZyIsInQ0IiwidDUiLCJ0NiIsInJpc2siLCJ0NyIsInQ4IiwiUGVybWlzc2lvbkV4cGxhaW5lckNvbnRlbnQiXSwic291cmNlcyI6WyJQZXJtaXNzaW9uRXhwbGFuYXRpb24udHN4Il0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBSZWFjdCwgeyBTdXNwZW5zZSwgdXNlLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgdXNlS2V5YmluZGluZyB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyBsb2dFdmVudCB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2FuYWx5dGljcy9pbmRleC5qcydcbmltcG9ydCB0eXBlIHsgTWVzc2FnZSB9IGZyb20gJy4uLy4uL3R5cGVzL21lc3NhZ2UuanMnXG5pbXBvcnQge1xuICBnZW5lcmF0ZVBlcm1pc3Npb25FeHBsYW5hdGlvbixcbiAgaXNQZXJtaXNzaW9uRXhwbGFpbmVyRW5hYmxlZCxcbiAgdHlwZSBQZXJtaXNzaW9uRXhwbGFuYXRpb24gYXMgUGVybWlzc2lvbkV4cGxhbmF0aW9uVHlwZSxcbiAgdHlwZSBSaXNrTGV2ZWwsXG59IGZyb20gJy4uLy4uL3V0aWxzL3Blcm1pc3Npb25zL3Blcm1pc3Npb25FeHBsYWluZXIuanMnXG5pbXBvcnQgeyBTaGltbWVyQ2hhciB9IGZyb20gJy4uL1NwaW5uZXIvU2hpbW1lckNoYXIuanMnXG5pbXBvcnQgeyB1c2VTaGltbWVyQW5pbWF0aW9uIH0gZnJvbSAnLi4vU3Bpbm5lci91c2VTaGltbWVyQW5pbWF0aW9uLmpzJ1xuXG5jb25zdCBMT0FESU5HX01FU1NBR0UgPSAnTG9hZGluZyBleHBsYW5hdGlvbuKApidcblxuZnVuY3Rpb24gU2hpbW1lckxvYWRpbmdUZXh0KCk6IFJlYWN0LlJlYWN0Tm9kZSB7XG4gIGNvbnN0IFtyZWYsIGdsaW1tZXJJbmRleF0gPSB1c2VTaGltbWVyQW5pbWF0aW9uKFxuICAgICdyZXNwb25kaW5nJyxcbiAgICBMT0FESU5HX01FU1NBR0UsXG4gICAgZmFsc2UsXG4gIClcblxuICByZXR1cm4gKFxuICAgIDxCb3ggcmVmPXtyZWZ9PlxuICAgICAgPFRleHQ+XG4gICAgICAgIHtMT0FESU5HX01FU1NBR0Uuc3BsaXQoJycpLm1hcCgoY2hhciwgaW5kZXgpID0+IChcbiAgICAgICAgICA8U2hpbW1lckNoYXJcbiAgICAgICAgICAgIGtleT17aW5kZXh9XG4gICAgICAgICAgICBjaGFyPXtjaGFyfVxuICAgICAgICAgICAgaW5kZXg9e2luZGV4fVxuICAgICAgICAgICAgZ2xpbW1lckluZGV4PXtnbGltbWVySW5kZXh9XG4gICAgICAgICAgICBtZXNzYWdlQ29sb3I9XCJpbmFjdGl2ZVwiXG4gICAgICAgICAgICBzaGltbWVyQ29sb3I9XCJ0ZXh0XCJcbiAgICAgICAgICAvPlxuICAgICAgICApKX1cbiAgICAgIDwvVGV4dD5cbiAgICA8L0JveD5cbiAgKVxufVxuXG5mdW5jdGlvbiBnZXRSaXNrQ29sb3Iocmlza0xldmVsOiBSaXNrTGV2ZWwpOiAnc3VjY2VzcycgfCAnd2FybmluZycgfCAnZXJyb3InIHtcbiAgc3dpdGNoIChyaXNrTGV2ZWwpIHtcbiAgICBjYXNlICdMT1cnOlxuICAgICAgcmV0dXJuICdzdWNjZXNzJ1xuICAgIGNhc2UgJ01FRElVTSc6XG4gICAgICByZXR1cm4gJ3dhcm5pbmcnXG4gICAgY2FzZSAnSElHSCc6XG4gICAgICByZXR1cm4gJ2Vycm9yJ1xuICB9XG59XG5cbmZ1bmN0aW9uIGdldFJpc2tMYWJlbChyaXNrTGV2ZWw6IFJpc2tMZXZlbCk6IHN0cmluZyB7XG4gIHN3aXRjaCAocmlza0xldmVsKSB7XG4gICAgY2FzZSAnTE9XJzpcbiAgICAgIHJldHVybiAnTG93IHJpc2snXG4gICAgY2FzZSAnTUVESVVNJzpcbiAgICAgIHJldHVybiAnTWVkIHJpc2snXG4gICAgY2FzZSAnSElHSCc6XG4gICAgICByZXR1cm4gJ0hpZ2ggcmlzaydcbiAgfVxufVxuXG50eXBlIFBlcm1pc3Npb25FeHBsYW5hdGlvblByb3BzID0ge1xuICB0b29sTmFtZTogc3RyaW5nXG4gIHRvb2xJbnB1dDogdW5rbm93blxuICB0b29sRGVzY3JpcHRpb24/OiBzdHJpbmdcbiAgbWVzc2FnZXM/OiBNZXNzYWdlW11cbn1cblxudHlwZSBFeHBsYWluZXJTdGF0ZSA9IHtcbiAgdmlzaWJsZTogYm9vbGVhblxuICBlbmFibGVkOiBib29sZWFuXG4gIHByb21pc2U6IFByb21pc2U8UGVybWlzc2lvbkV4cGxhbmF0aW9uVHlwZSB8IG51bGw+IHwgbnVsbFxufVxuXG4vKipcbiAqIENyZWF0ZXMgYW4gZXhwbGFuYXRpb24gcHJvbWlzZSB0aGF0IG5ldmVyIHJlamVjdHMuXG4gKiBFcnJvcnMgYXJlIGNhdWdodCBhbmQgcmV0dXJuZWQgYXMgbnVsbC5cbiAqL1xuZnVuY3Rpb24gY3JlYXRlRXhwbGFuYXRpb25Qcm9taXNlKFxuICBwcm9wczogUGVybWlzc2lvbkV4cGxhbmF0aW9uUHJvcHMsXG4pOiBQcm9taXNlPFBlcm1pc3Npb25FeHBsYW5hdGlvblR5cGUgfCBudWxsPiB7XG4gIHJldHVybiBnZW5lcmF0ZVBlcm1pc3Npb25FeHBsYW5hdGlvbih7XG4gICAgdG9vbE5hbWU6IHByb3BzLnRvb2xOYW1lLFxuICAgIHRvb2xJbnB1dDogcHJvcHMudG9vbElucHV0LFxuICAgIHRvb2xEZXNjcmlwdGlvbjogcHJvcHMudG9vbERlc2NyaXB0aW9uLFxuICAgIG1lc3NhZ2VzOiBwcm9wcy5tZXNzYWdlcyxcbiAgICBzaWduYWw6IG5ldyBBYm9ydENvbnRyb2xsZXIoKS5zaWduYWwsIC8vIFdvbid0IGFib3J0IC0gcmVxdWVzdCBpcyBmYXN0IGVub3VnaFxuICB9KS5jYXRjaCgoKSA9PiBudWxsKVxufVxuXG4vKipcbiAqIEhvb2sgdGhhdCBtYW5hZ2VzIHRoZSBwZXJtaXNzaW9uIGV4cGxhaW5lciBzdGF0ZS5cbiAqIENyZWF0ZXMgdGhlIGZldGNoIHByb21pc2UgbGF6aWx5IChvbmx5IHdoZW4gdXNlciBoaXRzIEN0cmwrRSlcbiAqIHRvIGF2b2lkIGNvbnN1bWluZyB0b2tlbnMgZm9yIGV4cGxhbmF0aW9ucyB1c2VycyBuZXZlciB2aWV3LlxuICovXG5leHBvcnQgZnVuY3Rpb24gdXNlUGVybWlzc2lvbkV4cGxhaW5lclVJKFxuICBwcm9wczogUGVybWlzc2lvbkV4cGxhbmF0aW9uUHJvcHMsXG4pOiBFeHBsYWluZXJTdGF0ZSB7XG4gIGNvbnN0IGVuYWJsZWQgPSBpc1Blcm1pc3Npb25FeHBsYWluZXJFbmFibGVkKClcbiAgY29uc3QgW3Zpc2libGUsIHNldFZpc2libGVdID0gdXNlU3RhdGUoZmFsc2UpXG4gIGNvbnN0IFtwcm9taXNlLCBzZXRQcm9taXNlXSA9XG4gICAgdXNlU3RhdGU8UHJvbWlzZTxQZXJtaXNzaW9uRXhwbGFuYXRpb25UeXBlIHwgbnVsbD4gfCBudWxsPihudWxsKVxuXG4gIC8vIFVzZSBrZXliaW5kaW5nIGZvciBjdHJsK2UgdG9nZ2xlIChjb25maWd1cmFibGUgdmlhIGtleWJpbmRpbmdzLmpzb24pXG4gIHVzZUtleWJpbmRpbmcoXG4gICAgJ2NvbmZpcm06dG9nZ2xlRXhwbGFuYXRpb24nLFxuICAgICgpID0+IHtcbiAgICAgIGlmICghdmlzaWJsZSkge1xuICAgICAgICBsb2dFdmVudCgndGVuZ3VfcGVybWlzc2lvbl9leHBsYWluZXJfc2hvcnRjdXRfdXNlZCcsIHt9KVxuICAgICAgICAvLyBPbmx5IGNyZWF0ZSB0aGUgcHJvbWlzZSBvbiBmaXJzdCB0b2dnbGUgKGxhenkgbG9hZGluZylcbiAgICAgICAgaWYgKCFwcm9taXNlKSB7XG4gICAgICAgICAgc2V0UHJvbWlzZShjcmVhdGVFeHBsYW5hdGlvblByb21pc2UocHJvcHMpKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBzZXRWaXNpYmxlKHYgPT4gIXYpXG4gICAgfSxcbiAgICB7IGNvbnRleHQ6ICdDb25maXJtYXRpb24nLCBpc0FjdGl2ZTogZW5hYmxlZCB9LFxuICApXG5cbiAgcmV0dXJuIHsgdmlzaWJsZSwgZW5hYmxlZCwgcHJvbWlzZSB9XG59XG5cbi8qKlxuICogSW5uZXIgY29tcG9uZW50IHRoYXQgdXNlcyBSZWFjdCAxOSdzIHVzZSgpIHRvIHJlYWQgdGhlIHByb21pc2UuXG4gKiBTdXNwZW5kcyB3aGlsZSBsb2FkaW5nLCByZXR1cm5zIG51bGwgb24gZXJyb3IuXG4gKi9cbmZ1bmN0aW9uIEV4cGxhbmF0aW9uUmVzdWx0KHtcbiAgcHJvbWlzZSxcbn06IHtcbiAgcHJvbWlzZTogUHJvbWlzZTxQZXJtaXNzaW9uRXhwbGFuYXRpb25UeXBlIHwgbnVsbD5cbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBjb25zdCBleHBsYW5hdGlvbiA9IHVzZShwcm9taXNlKVxuXG4gIGlmICghZXhwbGFuYXRpb24pIHtcbiAgICByZXR1cm4gKFxuICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICA8VGV4dCBkaW1Db2xvcj5FeHBsYW5hdGlvbiB1bmF2YWlsYWJsZTwvVGV4dD5cbiAgICAgIDwvQm94PlxuICAgIClcbiAgfVxuXG4gIHJldHVybiAoXG4gICAgPEJveCBmbGV4RGlyZWN0aW9uPVwiY29sdW1uXCIgbWFyZ2luVG9wPXsxfT5cbiAgICAgIDxUZXh0PntleHBsYW5hdGlvbi5leHBsYW5hdGlvbn08L1RleHQ+XG4gICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgIDxUZXh0PntleHBsYW5hdGlvbi5yZWFzb25pbmd9PC9UZXh0PlxuICAgICAgPC9Cb3g+XG4gICAgICA8Qm94IG1hcmdpblRvcD17MX0+XG4gICAgICAgIDxUZXh0PlxuICAgICAgICAgIDxUZXh0IGNvbG9yPXtnZXRSaXNrQ29sb3IoZXhwbGFuYXRpb24ucmlza0xldmVsKX0+XG4gICAgICAgICAgICB7Z2V0Umlza0xhYmVsKGV4cGxhbmF0aW9uLnJpc2tMZXZlbCl9OlxuICAgICAgICAgIDwvVGV4dD5cbiAgICAgICAgICA8VGV4dD4ge2V4cGxhbmF0aW9uLnJpc2t9PC9UZXh0PlxuICAgICAgICA8L1RleHQ+XG4gICAgICA8L0JveD5cbiAgICA8L0JveD5cbiAgKVxufVxuXG4vKipcbiAqIENvbnRlbnQgY29tcG9uZW50IC0gc2hvd3MgbG9hZGluZyAodmlhIFN1c3BlbnNlKSBvciBleHBsYW5hdGlvbiB3aGVuIHZpc2libGVcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIFBlcm1pc3Npb25FeHBsYWluZXJDb250ZW50KHtcbiAgdmlzaWJsZSxcbiAgcHJvbWlzZSxcbn06IHtcbiAgdmlzaWJsZTogYm9vbGVhblxuICBwcm9taXNlOiBQcm9taXNlPFBlcm1pc3Npb25FeHBsYW5hdGlvblR5cGUgfCBudWxsPiB8IG51bGxcbn0pOiBSZWFjdC5SZWFjdE5vZGUge1xuICBpZiAoIXZpc2libGUgfHwgIXByb21pc2UpIHtcbiAgICByZXR1cm4gbnVsbFxuICB9XG5cbiAgcmV0dXJuIChcbiAgICA8U3VzcGVuc2VcbiAgICAgIGZhbGxiYWNrPXtcbiAgICAgICAgPEJveCBtYXJnaW5Ub3A9ezF9PlxuICAgICAgICAgIDxTaGltbWVyTG9hZGluZ1RleHQgLz5cbiAgICAgICAgPC9Cb3g+XG4gICAgICB9XG4gICAgPlxuICAgICAgPEV4cGxhbmF0aW9uUmVzdWx0IHByb21pc2U9e3Byb21pc2V9IC8+XG4gICAgPC9TdXNwZW5zZT5cbiAgKVxufVxuIl0sIm1hcHBpbmdzIjoiO0FBQUEsT0FBT0EsS0FBSyxJQUFJQyxRQUFRLEVBQUVDLEdBQUcsRUFBRUMsUUFBUSxRQUFRLE9BQU87QUFDdEQsU0FBU0MsR0FBRyxFQUFFQyxJQUFJLFFBQVEsY0FBYztBQUN4QyxTQUFTQyxhQUFhLFFBQVEsb0NBQW9DO0FBQ2xFLFNBQVNDLFFBQVEsUUFBUSxtQ0FBbUM7QUFDNUQsY0FBY0MsT0FBTyxRQUFRLHdCQUF3QjtBQUNyRCxTQUNFQyw2QkFBNkIsRUFDN0JDLDRCQUE0QixFQUM1QixLQUFLQyxxQkFBcUIsSUFBSUMseUJBQXlCLEVBQ3ZELEtBQUtDLFNBQVMsUUFDVCxnREFBZ0Q7QUFDdkQsU0FBU0MsV0FBVyxRQUFRLDJCQUEyQjtBQUN2RCxTQUFTQyxtQkFBbUIsUUFBUSxtQ0FBbUM7QUFFdkUsTUFBTUMsZUFBZSxHQUFHLHNCQUFzQjtBQUU5QyxTQUFBQyxtQkFBQTtFQUFBLE1BQUFDLENBQUEsR0FBQUMsRUFBQTtFQUNF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