mirror of
http://10.0.2.1:3031/sauer/claude-code.git
synced 2026-06-30 12:26:58 +10:00
383 lines
42 KiB
TypeScript
383 lines
42 KiB
TypeScript
|
|
import { c as _c } from "react/compiler-runtime";
|
||
|
|
import type { StructuredPatchHunk } from 'diff';
|
||
|
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||
|
|
import type { CommandResultDisplay } from '../../commands.js';
|
||
|
|
import { useRegisterOverlay } from '../../context/overlayContext.js';
|
||
|
|
import { type DiffData, useDiffData } from '../../hooks/useDiffData.js';
|
||
|
|
import { type TurnDiff, useTurnDiffs } from '../../hooks/useTurnDiffs.js';
|
||
|
|
import { Box, Text } from '../../ink.js';
|
||
|
|
import { useKeybindings } from '../../keybindings/useKeybinding.js';
|
||
|
|
import { useShortcutDisplay } from '../../keybindings/useShortcutDisplay.js';
|
||
|
|
import type { Message } from '../../types/message.js';
|
||
|
|
import { plural } from '../../utils/stringUtils.js';
|
||
|
|
import { Byline } from '../design-system/Byline.js';
|
||
|
|
import { Dialog } from '../design-system/Dialog.js';
|
||
|
|
import { DiffDetailView } from './DiffDetailView.js';
|
||
|
|
import { DiffFileList } from './DiffFileList.js';
|
||
|
|
type Props = {
|
||
|
|
messages: Message[];
|
||
|
|
onDone: (result?: string, options?: {
|
||
|
|
display?: CommandResultDisplay;
|
||
|
|
}) => void;
|
||
|
|
};
|
||
|
|
type ViewMode = 'list' | 'detail';
|
||
|
|
type DiffSource = {
|
||
|
|
type: 'current';
|
||
|
|
} | {
|
||
|
|
type: 'turn';
|
||
|
|
turn: TurnDiff;
|
||
|
|
};
|
||
|
|
function turnDiffToDiffData(turn: TurnDiff): DiffData {
|
||
|
|
const files = Array.from(turn.files.values()).map(f => ({
|
||
|
|
path: f.filePath,
|
||
|
|
linesAdded: f.linesAdded,
|
||
|
|
linesRemoved: f.linesRemoved,
|
||
|
|
isBinary: false,
|
||
|
|
isLargeFile: false,
|
||
|
|
isTruncated: false,
|
||
|
|
isNewFile: f.isNewFile
|
||
|
|
})).sort((a, b) => a.path.localeCompare(b.path));
|
||
|
|
const hunks = new Map<string, StructuredPatchHunk[]>();
|
||
|
|
for (const f of turn.files.values()) {
|
||
|
|
hunks.set(f.filePath, f.hunks);
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
stats: {
|
||
|
|
filesCount: turn.stats.filesChanged,
|
||
|
|
linesAdded: turn.stats.linesAdded,
|
||
|
|
linesRemoved: turn.stats.linesRemoved
|
||
|
|
},
|
||
|
|
files,
|
||
|
|
hunks,
|
||
|
|
loading: false
|
||
|
|
};
|
||
|
|
}
|
||
|
|
export function DiffDialog(t0) {
|
||
|
|
const $ = _c(73);
|
||
|
|
const {
|
||
|
|
messages,
|
||
|
|
onDone
|
||
|
|
} = t0;
|
||
|
|
const gitDiffData = useDiffData();
|
||
|
|
const turnDiffs = useTurnDiffs(messages);
|
||
|
|
const [viewMode, setViewMode] = useState("list");
|
||
|
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||
|
|
const [sourceIndex, setSourceIndex] = useState(0);
|
||
|
|
let t1;
|
||
|
|
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t1 = {
|
||
|
|
type: "current"
|
||
|
|
};
|
||
|
|
$[0] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[0];
|
||
|
|
}
|
||
|
|
let t2;
|
||
|
|
if ($[1] !== turnDiffs) {
|
||
|
|
t2 = [t1, ...turnDiffs.map(_temp)];
|
||
|
|
$[1] = turnDiffs;
|
||
|
|
$[2] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[2];
|
||
|
|
}
|
||
|
|
const sources = t2;
|
||
|
|
const currentSource = sources[sourceIndex];
|
||
|
|
const currentTurn = currentSource?.type === "turn" ? currentSource.turn : null;
|
||
|
|
let t3;
|
||
|
|
if ($[3] !== currentTurn || $[4] !== gitDiffData) {
|
||
|
|
t3 = currentTurn ? turnDiffToDiffData(currentTurn) : gitDiffData;
|
||
|
|
$[3] = currentTurn;
|
||
|
|
$[4] = gitDiffData;
|
||
|
|
$[5] = t3;
|
||
|
|
} else {
|
||
|
|
t3 = $[5];
|
||
|
|
}
|
||
|
|
const diffData = t3;
|
||
|
|
const selectedFile = diffData.files[selectedIndex];
|
||
|
|
let t4;
|
||
|
|
if ($[6] !== diffData.hunks || $[7] !== selectedFile) {
|
||
|
|
t4 = selectedFile ? diffData.hunks.get(selectedFile.path) || [] : [];
|
||
|
|
$[6] = diffData.hunks;
|
||
|
|
$[7] = selectedFile;
|
||
|
|
$[8] = t4;
|
||
|
|
} else {
|
||
|
|
t4 = $[8];
|
||
|
|
}
|
||
|
|
const selectedHunks = t4;
|
||
|
|
let t5;
|
||
|
|
let t6;
|
||
|
|
if ($[9] !== sourceIndex || $[10] !== sources.length) {
|
||
|
|
t5 = () => {
|
||
|
|
if (sourceIndex >= sources.length) {
|
||
|
|
setSourceIndex(Math.max(0, sources.length - 1));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
t6 = [sources.length, sourceIndex];
|
||
|
|
$[9] = sourceIndex;
|
||
|
|
$[10] = sources.length;
|
||
|
|
$[11] = t5;
|
||
|
|
$[12] = t6;
|
||
|
|
} else {
|
||
|
|
t5 = $[11];
|
||
|
|
t6 = $[12];
|
||
|
|
}
|
||
|
|
useEffect(t5, t6);
|
||
|
|
const prevSourceIndex = useRef(sourceIndex);
|
||
|
|
let t7;
|
||
|
|
let t8;
|
||
|
|
if ($[13] !== sourceIndex) {
|
||
|
|
t7 = () => {
|
||
|
|
if (prevSourceIndex.current !== sourceIndex) {
|
||
|
|
setSelectedIndex(0);
|
||
|
|
prevSourceIndex.current = sourceIndex;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
t8 = [sourceIndex];
|
||
|
|
$[13] = sourceIndex;
|
||
|
|
$[14] = t7;
|
||
|
|
$[15] = t8;
|
||
|
|
} else {
|
||
|
|
t7 = $[14];
|
||
|
|
t8 = $[15];
|
||
|
|
}
|
||
|
|
useEffect(t7, t8);
|
||
|
|
useRegisterOverlay("diff-dialog");
|
||
|
|
let t10;
|
||
|
|
let t9;
|
||
|
|
if ($[16] !== sources.length || $[17] !== viewMode) {
|
||
|
|
t9 = () => {
|
||
|
|
if (viewMode === "detail") {
|
||
|
|
setViewMode("list");
|
||
|
|
} else {
|
||
|
|
if (viewMode === "list" && sources.length > 1) {
|
||
|
|
setSourceIndex(_temp2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
t10 = () => {
|
||
|
|
if (viewMode === "list" && sources.length > 1) {
|
||
|
|
setSourceIndex(prev_0 => Math.min(sources.length - 1, prev_0 + 1));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
$[16] = sources.length;
|
||
|
|
$[17] = viewMode;
|
||
|
|
$[18] = t10;
|
||
|
|
$[19] = t9;
|
||
|
|
} else {
|
||
|
|
t10 = $[18];
|
||
|
|
t9 = $[19];
|
||
|
|
}
|
||
|
|
let t11;
|
||
|
|
if ($[20] !== viewMode) {
|
||
|
|
t11 = () => {
|
||
|
|
if (viewMode === "detail") {
|
||
|
|
setViewMode("list");
|
||
|
|
}
|
||
|
|
};
|
||
|
|
$[20] = viewMode;
|
||
|
|
$[21] = t11;
|
||
|
|
} else {
|
||
|
|
t11 = $[21];
|
||
|
|
}
|
||
|
|
let t12;
|
||
|
|
if ($[22] !== selectedFile || $[23] !== viewMode) {
|
||
|
|
t12 = () => {
|
||
|
|
if (viewMode === "list" && selectedFile) {
|
||
|
|
setViewMode("detail");
|
||
|
|
}
|
||
|
|
};
|
||
|
|
$[22] = selectedFile;
|
||
|
|
$[23] = viewMode;
|
||
|
|
$[24] = t12;
|
||
|
|
} else {
|
||
|
|
t12 = $[24];
|
||
|
|
}
|
||
|
|
let t13;
|
||
|
|
if ($[25] !== viewMode) {
|
||
|
|
t13 = () => {
|
||
|
|
if (viewMode === "list") {
|
||
|
|
setSelectedIndex(_temp3);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
$[25] = viewMode;
|
||
|
|
$[26] = t13;
|
||
|
|
} else {
|
||
|
|
t13 = $[26];
|
||
|
|
}
|
||
|
|
let t14;
|
||
|
|
if ($[27] !== diffData.files.length || $[28] !== viewMode) {
|
||
|
|
t14 = () => {
|
||
|
|
if (viewMode === "list") {
|
||
|
|
setSelectedIndex(prev_2 => Math.min(diffData.files.length - 1, prev_2 + 1));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
$[27] = diffData.files.length;
|
||
|
|
$[28] = viewMode;
|
||
|
|
$[29] = t14;
|
||
|
|
} else {
|
||
|
|
t14 = $[29];
|
||
|
|
}
|
||
|
|
let t15;
|
||
|
|
if ($[30] !== t10 || $[31] !== t11 || $[32] !== t12 || $[33] !== t13 || $[34] !== t14 || $[35] !== t9) {
|
||
|
|
t15 = {
|
||
|
|
"diff:previousSource": t9,
|
||
|
|
"diff:nextSource": t10,
|
||
|
|
"diff:back": t11,
|
||
|
|
"diff:viewDetails": t12,
|
||
|
|
"diff:previousFile": t13,
|
||
|
|
"diff:nextFile": t14
|
||
|
|
};
|
||
|
|
$[30] = t10;
|
||
|
|
$[31] = t11;
|
||
|
|
$[32] = t12;
|
||
|
|
$[33] = t13;
|
||
|
|
$[34] = t14;
|
||
|
|
$[35] = t9;
|
||
|
|
$[36] = t15;
|
||
|
|
} else {
|
||
|
|
t15 = $[36];
|
||
|
|
}
|
||
|
|
let t16;
|
||
|
|
if ($[37] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t16 = {
|
||
|
|
context: "DiffDialog"
|
||
|
|
};
|
||
|
|
$[37] = t16;
|
||
|
|
} else {
|
||
|
|
t16 = $[37];
|
||
|
|
}
|
||
|
|
useKeybindings(t15, t16);
|
||
|
|
let t17;
|
||
|
|
if ($[38] !== diffData.stats) {
|
||
|
|
t17 = diffData.stats ? <Text dimColor={true}>{diffData.stats.filesCount} {plural(diffData.stats.filesCount, "file")}{" "}changed{diffData.stats.linesAdded > 0 && <Text color="diffAddedWord"> +{diffData.stats.linesAdded}</Text>}{diffData.stats.linesRemoved > 0 && <Text color="diffRemovedWord"> -{diffData.stats.linesRemoved}</Text>}</Text> : null;
|
||
|
|
$[38] = diffData.stats;
|
||
|
|
$[39] = t17;
|
||
|
|
} else {
|
||
|
|
t17 = $[39];
|
||
|
|
}
|
||
|
|
const subtitle = t17;
|
||
|
|
const headerTitle = currentTurn ? `Turn ${currentTurn.turnIndex}` : "Uncommitted changes";
|
||
|
|
const headerSubtitle = currentTurn ? currentTurn.userPromptPreview ? `"${currentTurn.userPromptPreview}"` : "" : "(git diff HEAD)";
|
||
|
|
let t18;
|
||
|
|
if ($[40] !== sourceIndex || $[41] !== sources) {
|
||
|
|
t18 = sources.length > 1 ? <Box>{sourceIndex > 0 && <Text dimColor={true}>◀ </Text>}{sources.map((source, i) => {
|
||
|
|
const isSelected = i === sourceIndex;
|
||
|
|
const label = source.type === "current" ? "Current" : `T${source.turn.turnIndex}`;
|
||
|
|
return <Text key={i} dimColor={!isSelected} bold={isSelected}>{i > 0 ? " \xB7 " : ""}{label}</Text>;
|
||
|
|
})}{sourceIndex < sources.length - 1 && <Text dimColor={true}> ▶</Text>}</Box> : null;
|
||
|
|
$[40] = sourceIndex;
|
||
|
|
$[41] = sources;
|
||
|
|
$[42] = t18;
|
||
|
|
} else {
|
||
|
|
t18 = $[42];
|
||
|
|
}
|
||
|
|
const sourceSelector = t18;
|
||
|
|
const dismissShortcut = useShortcutDisplay("diff:dismiss", "DiffDialog", "esc");
|
||
|
|
let t19;
|
||
|
|
bb0: {
|
||
|
|
if (diffData.loading) {
|
||
|
|
t19 = "Loading diff\u2026";
|
||
|
|
break bb0;
|
||
|
|
}
|
||
|
|
if (currentTurn) {
|
||
|
|
t19 = "No file changes in this turn";
|
||
|
|
break bb0;
|
||
|
|
}
|
||
|
|
if (diffData.stats && diffData.stats.filesCount > 0 && diffData.files.length === 0) {
|
||
|
|
t19 = "Too many files to display details";
|
||
|
|
break bb0;
|
||
|
|
}
|
||
|
|
t19 = "Working tree is clean";
|
||
|
|
}
|
||
|
|
const emptyMessage = t19;
|
||
|
|
let t20;
|
||
|
|
if ($[43] !== headerSubtitle) {
|
||
|
|
t20 = headerSubtitle && <Text dimColor={true}> {headerSubtitle}</Text>;
|
||
|
|
$[43] = headerSubtitle;
|
||
|
|
$[44] = t20;
|
||
|
|
} else {
|
||
|
|
t20 = $[44];
|
||
|
|
}
|
||
|
|
let t21;
|
||
|
|
if ($[45] !== headerTitle || $[46] !== t20) {
|
||
|
|
t21 = <Text>{headerTitle}{t20}</Text>;
|
||
|
|
$[45] = headerTitle;
|
||
|
|
$[46] = t20;
|
||
|
|
$[47] = t21;
|
||
|
|
} else {
|
||
|
|
t21 = $[47];
|
||
|
|
}
|
||
|
|
const title = t21;
|
||
|
|
let t22;
|
||
|
|
if ($[48] !== onDone || $[49] !== viewMode) {
|
||
|
|
t22 = function handleCancel() {
|
||
|
|
if (viewMode === "detail") {
|
||
|
|
setViewMode("list");
|
||
|
|
} else {
|
||
|
|
onDone("Diff dialog dismissed", {
|
||
|
|
display: "system"
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
$[48] = onDone;
|
||
|
|
$[49] = viewMode;
|
||
|
|
$[50] = t22;
|
||
|
|
} else {
|
||
|
|
t22 = $[50];
|
||
|
|
}
|
||
|
|
const handleCancel = t22;
|
||
|
|
let t23;
|
||
|
|
if ($[51] !== dismissShortcut || $[52] !== sources.length || $[53] !== viewMode) {
|
||
|
|
t23 = exitState => exitState.pending ? <Text>Press {exitState.keyName} again to exit</Text> : viewMode === "list" ? <Byline>{sources.length > 1 && <Text>←/→ source</Text>}<Text>↑/↓ select</Text><Text>Enter view</Text><Text>{dismissShortcut} close</Text></Byline> : <Byline><Text>← back</Text><Text>{dismissShortcut} close</Text></Byline>;
|
||
|
|
$[51] = dismissShortcut;
|
||
|
|
$[52] = sources.length;
|
||
|
|
$[53] = viewMode;
|
||
|
|
$[54] = t23;
|
||
|
|
} else {
|
||
|
|
t23 = $[54];
|
||
|
|
}
|
||
|
|
let t24;
|
||
|
|
if ($[55] !== diffData.files || $[56] !== emptyMessage || $[57] !== selectedFile?.isBinary || $[58] !== selectedFile?.isLargeFile || $[59] !== selectedFile?.isTruncated || $[60] !== selectedFile?.isUntracked || $[61] !== selectedFile?.path || $[62] !== selectedHunks || $[63] !== selectedIndex || $[64] !== viewMode) {
|
||
|
|
t24 = diffData.files.length === 0 ? <Box marginTop={1}><Text dimColor={true}>{emptyMessage}</Text></Box> : viewMode === "list" ? <Box flexDirection="column" marginTop={1}><DiffFileList files={diffData.files} selectedIndex={selectedIndex} /></Box> : <Box flexDirection="column" marginTop={1}><DiffDetailView filePath={selectedFile?.path || ""} hunks={selectedHunks} isLargeFile={selectedFile?.isLargeFile} isBinary={selectedFile?.isBinary} isTruncated={selectedFile?.isTruncated} isUntracked={selectedFile?.isUntracked} /></Box>;
|
||
|
|
$[55] = diffData.files;
|
||
|
|
$[56] = emptyMessage;
|
||
|
|
$[57] = selectedFile?.isBinary;
|
||
|
|
$[58] = selectedFile?.isLargeFile;
|
||
|
|
$[59] = selectedFile?.isTruncated;
|
||
|
|
$[60] = selectedFile?.isUntracked;
|
||
|
|
$[61] = selectedFile?.path;
|
||
|
|
$[62] = selectedHunks;
|
||
|
|
$[63] = selectedIndex;
|
||
|
|
$[64] = viewMode;
|
||
|
|
$[65] = t24;
|
||
|
|
} else {
|
||
|
|
t24 = $[65];
|
||
|
|
}
|
||
|
|
let t25;
|
||
|
|
if ($[66] !== handleCancel || $[67] !== sourceSelector || $[68] !== subtitle || $[69] !== t23 || $[70] !== t24 || $[71] !== title) {
|
||
|
|
t25 = <Dialog title={title} onCancel={handleCancel} color="background" inputGuide={t23}>{sourceSelector}{subtitle}{t24}</Dialog>;
|
||
|
|
$[66] = handleCancel;
|
||
|
|
$[67] = sourceSelector;
|
||
|
|
$[68] = subtitle;
|
||
|
|
$[69] = t23;
|
||
|
|
$[70] = t24;
|
||
|
|
$[71] = title;
|
||
|
|
$[72] = t25;
|
||
|
|
} else {
|
||
|
|
t25 = $[72];
|
||
|
|
}
|
||
|
|
return t25;
|
||
|
|
}
|
||
|
|
function _temp3(prev_1) {
|
||
|
|
return Math.max(0, prev_1 - 1);
|
||
|
|
}
|
||
|
|
function _temp2(prev) {
|
||
|
|
return Math.max(0, prev - 1);
|
||
|
|
}
|
||
|
|
function _temp(turn) {
|
||
|
|
return {
|
||
|
|
type: "turn",
|
||
|
|
turn
|
||
|
|
};
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJTdHJ1Y3R1cmVkUGF0Y2hIdW5rIiwiUmVhY3QiLCJ1c2VFZmZlY3QiLCJ1c2VNZW1vIiwidXNlUmVmIiwidXNlU3RhdGUiLCJDb21tYW5kUmVzdWx0RGlzcGxheSIsInVzZVJlZ2lzdGVyT3ZlcmxheSIsIkRpZmZEYXRhIiwidXNlRGlmZkRhdGEiLCJUdXJuRGlmZiIsInVzZVR1cm5EaWZmcyIsIkJveCIsIlRleHQiLCJ1c2VLZXliaW5kaW5ncyIsInVzZVNob3J0Y3V0RGlzcGxheSIsIk1lc3NhZ2UiLCJwbHVyYWwiLCJCeWxpbmUiLCJEaWFsb2ciLCJEaWZmRGV0YWlsVmlldyIsIkRpZmZGaWxlTGlzdCIsIlByb3BzIiwibWVzc2FnZXMiLCJvbkRvbmUiLCJyZXN1bHQiLCJvcHRpb25zIiwiZGlzcGxheSIsIlZpZXdNb2RlIiwiRGlmZlNvdXJjZSIsInR5cGUiLCJ0dXJuIiwidHVybkRpZmZUb0RpZmZEYXRhIiwiZmlsZXMiLCJBcnJheSIsImZyb20iLCJ2YWx1ZXMiLCJtYXAiLCJmIiwicGF0aCIsImZpbGVQYXRoIiwibGluZXNBZGRlZCIsImxpbmVzUmVtb3ZlZCIsImlzQmluYXJ5IiwiaXNMYXJnZUZpbGUiLCJpc1RydW5jYXRlZCIsImlzTmV3RmlsZSIsInNvcnQiLCJhIiwiYiIsImxvY2FsZUNvbXBhcmUiLCJodW5rcyIsIk1hcCIsInNldCIsInN0YXRzIiwiZmlsZXNDb3VudCIsImZpbGVzQ2hhbmdlZCIsImxvYWRpbmciLCJEaWZmRGlhbG9nIiwidDAiLCIkIiwiX2MiLCJnaXREaWZmRGF0YSIsInR1cm5EaWZmcyIsInZpZXdNb2RlIiwic2V0Vmlld01vZGUiLCJzZWxlY3RlZEluZGV4Iiwic2V0U2VsZWN0ZWRJbmRleCIsInNvdXJjZUluZGV4Iiwic2V0U291cmNlSW5kZXgiLCJ0MSIsIlN5bWJvbCIsImZvciIsInQyIiwiX3RlbXAiLCJzb3VyY2VzIiwiY3VycmVudFNvdXJjZSIsImN1cnJlbnRUdXJuIiwidDMiLCJkaWZmRGF0YSIsInNlbGVjdGVkRmlsZSIsInQ0IiwiZ2V0Iiwic2VsZWN0ZWRIdW5rcyIsInQ1IiwidDYiLCJsZW5ndGgiLCJNYXRoIiwibWF4IiwicHJldlNvdXJjZUluZGV4IiwidDciLCJ0OCIsImN1cnJlbnQiLCJ0MTAiLCJ0OSIsIl90ZW1wMiIsInByZXZfMCIsIm1pbiIsInByZXYiLCJ0MTEiLCJ0MTIiLCJ0MTMiLCJfdGVtcDMiLCJ0MTQiLCJwcmV2XzIiLCJ0MTUiLCJ0MTYiLCJjb250ZXh0IiwidDE3Iiwic3VidGl0bGUiLCJoZWFkZXJUaXRsZSIsInR1cm5JbmRleCIsImhlYWRlclN1YnRpdGxlIiwidXNlclByb21wdFByZXZpZXciLCJ0MTgiLCJzb3VyY2UiLCJpIiwiaXNTZWxlY3RlZCIsImxhYmVsIiwic291cmNlU2VsZWN0b3IiLCJkaXNtaXNzU2hvcnRjdXQiLCJ0MTkiLCJiYjAiLCJlbXB0eU1lc3NhZ2UiLCJ0MjAiLCJ0MjEiLCJ0aXRsZSIsInQyMiIsImhhbmRsZUNhbmNlbCIsInQyMyIsImV4aXRTdGF0ZSIsInBlbmRpbmciLCJrZXlOYW1lIiwidDI0IiwiaXNVbnRyYWNrZWQiLCJ0MjUiLCJwcmV2XzEiXSwic291cmNlcyI6WyJEaWZmRGlhbG9nLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdHlwZSB7IFN0cnVjdHVyZWRQYXRjaEh1bmsgfSBmcm9tICdkaWZmJ1xuaW1wb3J0IFJlYWN0LCB7IHVzZUVmZmVjdCwgdXNlTWVtbywgdXNlUmVmLCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0J1xuaW1wb3J0IHR5cGUgeyBDb21tYW5kUmVzdWx0RGlzcGxheSB9IGZyb20gJy4uLy4uL2NvbW1hbmRzLmpzJ1xuaW1wb3J0IHsgdXNlUmVnaXN0ZXJPdmVybGF5IH0gZnJvbSAnLi4vLi4vY29udGV4dC9vdmVybGF5Q29udGV4dC5qcydcbmltcG9ydCB7IHR5cGUgRGlmZkRhdGEsIHVzZURpZmZEYXRhIH0gZnJvbSAnLi4vLi4vaG9va3MvdXNlRGlmZkRhdGEuanMnXG5pbXBvcnQgeyB0eXBlIFR1cm5EaWZmLCB1c2VUdXJuRGlmZnMgfSBmcm9tICcuLi8uLi9ob29rcy91c2VUdXJuRGlmZnMuanMnXG5pbXBvcnQgeyBCb3gsIFRleHQgfSBmcm9tICcuLi8uLi9pbmsuanMnXG5pbXBvcnQgeyB1c2VLZXliaW5kaW5ncyB9IGZyb20gJy4uLy4uL2tleWJpbmRpbmdzL3VzZUtleWJpbmRpbmcuanMnXG5pbXBvcnQgeyB1c2VTaG9ydGN1dERpc3BsYXkgfSBmcm9tICcuLi8uLi9rZXliaW5kaW5ncy91c2VTaG9ydGN1dERpc3BsYXkuanMnXG5pbXBvcnQgdHlwZSB7IE1lc3NhZ2UgfSBmcm9tICcuLi8uLi90eXBlcy9tZXNzYWdlLmpzJ1xuaW1wb3J0IHsgcGx1cmFsIH0gZnJvbSAnLi4vLi4vdXRpbHMvc3RyaW5nVXRpbHMuanMnXG5pbXBvcnQgeyBCeWxpbmUgfSBmcm9tICcuLi9kZXNpZ24tc3lzdGVtL0J5bGluZS5qcydcbmltcG9ydCB7IERpYWxvZyB9IGZyb20gJy4uL2Rlc2lnbi1zeXN0ZW0vRGlhbG9nLmpzJ1xuaW1wb3J0IHsgRGlmZkRldGFpbFZpZXcgfSBmcm9tICcuL0RpZmZEZXRhaWxWaWV3LmpzJ1xuaW1wb3J0IHsgRGlmZkZpbGVMaXN0IH0gZnJvbSAnLi9EaWZmRmlsZUxpc3QuanMnXG5cbnR5cGUgUHJvcHMgPSB7XG4gIG1lc3NhZ2VzOiBNZXNzYWdlW11cbiAgb25Eb25lOiAoXG4gICAgcmVzdWx0Pzogc3RyaW5nLFxuICAgIG9wdGlvbnM/OiB7IGRpc3BsYXk/OiBDb21tYW5kUmVzdWx0RGlzcGxheSB9LFxuICApID0+IHZvaWRcbn1cblxudHlwZSBWaWV3TW9kZSA9ICdsaXN0JyB8ICdkZXRhaWwnXG5cbnR5cGUgRGlmZlNvdXJjZSA9IHsgdHlwZTogJ2N1cnJlbnQnIH0gfCB7IHR5cGU6ICd0dXJuJzsgdHVybjogVHVybkRpZmYgfVxuXG5mdW5jdGlvbiB0dXJuRGlmZlRvRGlmZkRhdGEodHVybjogVHVybkRpZmYpOiBEaWZmRGF0YSB7XG4gIGNvbnN0IGZpbGVzID0gQXJyYXkuZnJvbSh0dXJuLmZpbGVzLnZhbHVlcygpKVxuICAgIC5tYXAoZiA9PiAoe1xuICAgICAgcGF0aDogZi5maWxlUGF0aCxcbiAgICAgIGxpbmVzQWRkZWQ6IGYubGluZXNBZGRlZCxcbiAgICAgIGxpbmVzUmVtb3ZlZDogZi5saW5lc1JlbW92ZWQsXG4gICAgICBpc0JpbmFyeTogZmFsc2UsXG4gICAgICBpc0xhcmdlRmlsZTogZmFsc2UsXG4gICAgICBpc1RydW5jYXRlZDogZmFsc2UsXG4gICAgICBpc05ld0ZpbGU6IGYuaXNOZXdGaWxlLFxuICAgIH0pKVxuICAgIC5zb3J0KChhLCBiKSA9PiBhLnBhdGgubG9jYWxlQ29tcGFyZShiLnBhdGgpKVxuXG4gIGNvbnN0IGh1bmt
|