From 4730e60a388de2e7a99c63b8b185728a8503ca1f Mon Sep 17 00:00:00 2001 From: phase3dev <77866949+phase3dev@users.noreply.github.com> Date: Wed, 10 Jun 2026 02:15:38 -1000 Subject: [PATCH] fix(md-copy): leading semicolon on the injected payload (ASI safety) When appended after a bundle whose final statement is an expression with no trailing semicolon, the IIFE would parse as a call on that expression and throw before the webview app initializes. Prefix the payload with ';' so the patch is safe regardless of the bundle's final token. Regenerated the three embeds; adds a node-exec test on a no-semicolon bundle. Not triggered by the current 2.1.170 bundle (it ends with '}'); this hardens against future builds. Codex PR review, P2. Co-Authored-By: Claude Opus 4.8 (1M context) --- fixes/markdown-copy-export/add-md-copy.py | 2 +- fixes/markdown-copy-export/webview-inject.js | 5 ++++- launcher/claudemax | 5 ++++- launcher/claudemax.win.js | 2 +- tests/test_md_patcher.py | 11 +++++++++++ 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/fixes/markdown-copy-export/add-md-copy.py b/fixes/markdown-copy-export/add-md-copy.py index 2958e3e..619b3fc 100644 --- a/fixes/markdown-copy-export/add-md-copy.py +++ b/fixes/markdown-copy-export/add-md-copy.py @@ -31,7 +31,7 @@ BACKUP_SUFFIX = ".bak-md-copy" # >>>CCWA-MD-COPY-EMBED>>> (generated by tools/gen-embeds; do not edit) -INJECT_JS = base64.b64decode("LyogY2MtbWQtY29weTogcGVyLW1lc3NhZ2UgYW5kIHdob2xlLWNvbnZlcnNhdGlvbiBjb3B5IChtYXJrZG93bi9wbGFpbikgZm9yIHRoZQogKiBDbGF1ZGUgQ29kZSBWUyBDb2RlIHdlYnZpZXcuIFNlbGYtY29udGFpbmVkIElJRkUgYXBwZW5kZWQgdG8gd2Vidmlldy9pbmRleC5qcy4KICogQWRkaXRpdmUgYW5kIHJlYWQtb25seSB3LnIudC4gYXBwIHN0YXRlOyBrZXllZCBvbiBzdGFibGUgQ1NTLW1vZHVsZSBjbGFzcwogKiBwcmVmaXhlcywgc28gaXQgZmFpbHMgc2FmZSAoY29udHJvbHMgc2ltcGx5IGRvIG5vdCBhcHBlYXIpIGlmIGEgcHJlZml4IG1vdmVzLgogKiBFeHBvc2VzIGl0cyBwdXJlIGZ1bmN0aW9ucyBmb3Igbm9kZSB1bml0IHRlc3RzOyBib290KClzIG9ubHkgaW4gYSByZWFsIHdlYnZpZXcuICovCihmdW5jdGlvbiAoKSB7CiAgInVzZSBzdHJpY3QiOwoKICB2YXIgQ09OVFJPTF9QUkVGSVggPSAiY2MtbWQtY29weSI7IC8vIGV2ZXJ5IGluamVjdGVkIG5vZGUncyBjbGFzcyBzdGFydHMgd2l0aCB0aGlzCiAgdmFyIFVTRVJfQlVCQkxFID0gJ1tjbGFzcyo9InVzZXJNZXNzYWdlQ29udGFpbmVyXyJdJzsKICAvLyBBc3Npc3RhbnQgbWVzc2FnZSB3cmFwcGVyLiBWZXJpZmllZCBvbiAyLjEuMTcwOiB0aGUgcmVuZGVyIGVtaXRzIGV4YWN0bHkgb25lCiAgLy8gYGRhdGEtdGVzdGlkPSJhc3Npc3RhbnQtbWVzc2FnZSJgIGRpdiBwZXIgYXNzaXN0YW50IHR1cm4sIHdpdGggdGhlIHJhdGluZwogIC8vIHdpZGdldCBhbmQgY29udGVudCBibG9ja3MgYXMgaXRzIGNoaWxkcmVuLiAoVGhlIGVhcmxpZXIgYFtkYXRhLW1lc3NhZ2UtcmF0aW5nXWAKICAvLyB3YXMgV1JPTkc6IHRoYXQgYXR0cmlidXRlIHNpdHMgb24gdGhlIG5lc3RlZCByYXRpbmcgY29udHJvbCwgd2hpY2ggaXMgYWxzbyBvbmx5CiAgLy8gcmVuZGVyZWQgYmVoaW5kIGFuIGV4cGVyaW1lbnQrYW5hbHl0aWNzIGdhdGUuKSBSZS1waW5uZWQgaW4gVGFzayA2LgogIHZhciBBU1NJU1RBTlRfQlVCQkxFID0gJ1tkYXRhLXRlc3RpZD0iYXNzaXN0YW50LW1lc3NhZ2UiXSc7CiAgdmFyIE1FU1NBR0VTX0NPTlRBSU5FUiA9ICdbY2xhc3MqPSJtZXNzYWdlc0NvbnRhaW5lcl8iXSc7IC8vIGUuZy4gJ1tjbGFzcyo9InRpbWVsaW5lXyJdJzsgIiIgLT4gb2JzZXJ2ZSBkb2N1bWVudC5ib2R5CiAgLy8gT3B0aW9uYWwgbmFycm93aW5nIG9ubHkuIE1VU1QgYmUgYSBzaW5nbGUgd3JhcHBlciBhcm91bmQgQUxMIGNvbnRlbnQgYmxvY2tzLAogIC8vIG5vdCBhIHBlci1ibG9jayBjbGFzcyAoYSB0dXJuIGhhcyBtdWx0aXBsZSBibG9ja3MpLiAiIiAtPiB1c2UgdGhlIGJ1YmJsZSBpdHNlbGYKICAvLyAoYWxyZWFkeSBhZ2dyZWdhdGVzIGFsbCBibG9ja3M7IHNhbml0aXplQ2xvbmUgaXMgdGhlIGNvcnJlY3RuZXNzIGdhdGUpLgogIHZhciBBU1NJU1RBTlRfQ09OVEVOVCA9ICIiOwogIHZhciBGRUVEQkFDS19NUyA9IDE4MDA7CgogIC8vIC0tLS0gSFRNTCAtPiBNYXJrZG93biAoRE9NIHdhbGspIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICAvLyBVc2VzIG9ubHk6IG5vZGVUeXBlLCB0YWdOYW1lLCBjaGlsZE5vZGVzLCB0ZXh0Q29udGVudCwgZ2V0QXR0cmlidXRlLCBjbGFzc05hbWUuCiAgZnVuY3Rpb24gaHRtbFRvTWFya2Rvd24ocm9vdCkgewogICAgLy8gTG9uZ2VzdCBydW4gb2YgY29uc2VjdXRpdmUgYmFja3RpY2tzIGluIHMsIHNvIGEgY29kZSBkZWxpbWl0ZXIvZmVuY2UgY2FuIGJlCiAgICAvLyBjaG9zZW4gbG9uZ2VyIHRoYW4gYW55dGhpbmcgaW5zaWRlIGl0IChlbHNlIGBgYCBpbiB0aGUgY29udGVudCBjbG9zZXMgZWFybHkpLgogICAgZnVuY3Rpb24gYmFja3RpY2tSdW4ocykgewogICAgICB2YXIgbWF4ID0gMCwgY3VyID0gMDsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgaWYgKHMuY2hhckF0KGkpID09PSAiYCIpIHsgY3VyKys7IGlmIChjdXIgPiBtYXgpIG1heCA9IGN1cjsgfSBlbHNlIGN1ciA9IDA7CiAgICAgIH0KICAgICAgcmV0dXJuIG1heDsKICAgIH0KICAgIGZ1bmN0aW9uIGZlbmNlKHMsIG1pbikgeyB2YXIgbiA9IGJhY2t0aWNrUnVuKHMpICsgMTsgaWYgKG4gPCBtaW4pIG4gPSBtaW47IHJldHVybiBuZXcgQXJyYXkobiArIDEpLmpvaW4oImAiKTsgfQogICAgZnVuY3Rpb24gaW5saW5lKG5vZGUpIHsKICAgICAgdmFyIG91dCA9ICIiOwogICAgICB2YXIga2lkcyA9IG5vZGUuY2hpbGROb2RlcyB8fCBbXTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBraWRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdmFyIGMgPSBraWRzW2ldOwogICAgICAgIGlmIChjLm5vZGVUeXBlID09PSAzKSB7IG91dCArPSBjLnRleHRDb250ZW50IHx8ICIiOyBjb250aW51ZTsgfQogICAgICAgIGlmIChjLm5vZGVUeXBlICE9PSAxKSBjb250aW51ZTsKICAgICAgICB2YXIgdGFnID0gKGMudGFnTmFtZSB8fCAiIikudG9VcHBlckNhc2UoKTsKICAgICAgICBpZiAodGFnID09PSAiQlIiKSBvdXQgKz0gIlxuIjsKICAgICAgICBlbHNlIGlmICh0YWcgPT09ICJTVFJPTkciIHx8IHRhZyA9PT0gIkIiKSBvdXQgKz0gIioqIiArIGlubGluZShjKSArICIqKiI7CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiRU0iIHx8IHRhZyA9PT0gIkkiKSBvdXQgKz0gIioiICsgaW5saW5lKGMpICsgIioiOwogICAgICAgIGVsc2UgaWYgKHRhZyA9PT0gIkRFTCIgfHwgdGFnID09PSAiUyIpIG91dCArPSAifn4iICsgaW5saW5lKGMpICsgIn5+IjsKICAgICAgICBlbHNlIGlmICh0YWcgPT09ICJDT0RFIikgewogICAgICAgICAgdmFyIGN0ID0gYy50ZXh0Q29udGVudCB8fCAiIjsKICAgICAgICAgIHZhciBkID0gZmVuY2UoY3QsIDEpOwogICAgICAgICAgLy8gQ29tbW9uTWFyayBzdHJpcHMgb25lIGxlYWRpbmcrdHJhaWxpbmcgc3BhY2UsIHNvIHBhZCB3aGVuIGFuIGVkZ2UgaXMgYQogICAgICAgICAgLy8gYmFja3RpY2sgdG8ga2VlcCBpdCBmcm9tIG1lcmdpbmcgd2l0aCB0aGUgZGVsaW1pdGVyLgogICAgICAgICAgdmFyIHAgPSAoY3QuY2hhckF0KDApID09PSAiYCIgfHwgY3QuY2hhckF0KGN0Lmxlbmd0aCAtIDEpID09PSAiYCIpID8gIiAiIDogIiI7CiAgICAgICAgICBvdXQgKz0gZCArIHAgKyBjdCArIHAgKyBkOwogICAgICAgIH0KICAgICAgICBlbHNlIGlmICh0YWcgPT09ICJBIikgewogICAgICAgICAgdmFyIGhyZWYgPSBjLmdldEF0dHJpYnV0ZSA/IGMuZ2V0QXR0cmlidXRlKCJocmVmIikgOiBudWxsOwogICAgICAgICAgdmFyIHQgPSBpbmxpbmUoYyk7CiAgICAgICAgICBvdXQgKz0gaHJlZiA/ICJbIiArIHQgKyAiXSgiICsgaHJlZiArICIpIiA6IHQ7CiAgICAgICAgfSBlbHNlIG91dCArPSBpbmxpbmUoYyk7IC8vIHVua25vd24gaW5saW5lIHdyYXBwZXI6IGtlZXAgdGV4dCwgZHJvcCB0YWcKICAgICAgfQogICAgICByZXR1cm4gb3V0OwogICAgfQogICAgZnVuY3Rpb24gbGFuZ09mKGNvZGVFbCkgewogICAgICB2YXIgY2xzID0gIiI7CiAgICAgIGlmIChjb2RlRWwpIGNscyA9IChjb2RlRWwuZ2V0QXR0cmlidXRlICYmIGNvZGVFbC5nZXRBdHRyaWJ1dGUoImNsYXNzIikpIHx8IGNvZGVFbC5jbGFzc05hbWUgfHwgIiI7CiAgICAgIHZhciBtID0gL2xhbmd1YWdlLShbQS1aYS16MC05KyMuXC1dKykvLmV4ZWMoY2xzIHx8ICIiKTsKICAgICAgcmV0dXJuIG0gPyBtWzFdIDogIiI7CiAgICB9CiAgICBmdW5jdGlvbiBmaW5kQ2hpbGRUYWcobm9kZSwgdGFnKSB7CiAgICAgIHZhciBraWRzID0gbm9kZS5jaGlsZE5vZGVzIHx8IFtdOwogICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGtpZHMubGVuZ3RoOyBpKyspIHsKICAgICAgICBpZiAoa2lkc1tpXS5ub2RlVHlwZSA9PT0gMSAmJiAoa2lkc1tpXS50YWdOYW1lIHx8ICIiKS50b1VwcGVyQ2FzZSgpID09PSB0YWcpIHJldHVybiBraWRzW2ldOwogICAgICB9CiAgICAgIHJldHVybiBudWxsOwogICAgfQogICAgZnVuY3Rpb24gbGlzdChub2RlLCBvcmRlcmVkLCBkZXB0aCkgewogICAgICB2YXIgb3V0ID0gIiIsIG4gPSAxOwogICAgICB2YXIga2lkcyA9IG5vZGUuY2hpbGROb2RlcyB8fCBbXTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBraWRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdmFyIGxpID0ga2lkc1tpXTsKICAgICAgICBpZiAobGkubm9kZVR5cGUgIT09IDEgfHwgKGxpLnRhZ05hbWUgfHwgIiIpLnRvVXBwZXJDYXNlKCkgIT09ICJMSSIpIGNvbnRpbnVlOwogICAgICAgIHZhciBtYXJrZXIgPSBvcmRlcmVkID8gbisrICsgIi4gIiA6ICItICI7CiAgICAgICAgdmFyIGluZGVudCA9IG5ldyBBcnJheShkZXB0aCArIDEpLmpvaW4oIiAgIik7CiAgICAgICAgdmFyIGxlYWQgPSAiIiwgbmVzdGVkID0gIiI7CiAgICAgICAgdmFyIGxrID0gbGkuY2hpbGROb2RlcyB8fCBbXTsKICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGxrLmxlbmd0aDsgaisrKSB7CiAgICAgICAgICB2YXIgY2ggPSBsa1tqXTsKICAgICAgICAgIHZhciBjdCA9IGNoLm5vZGVUeXBlID09PSAxID8gKGNoLnRhZ05hbWUgfHwgIiIpLnRvVXBwZXJDYXNlKCkgOiAiIjsKICAgICAgICAgIGlmIChjdCA9PT0gIlVMIikgbmVzdGVkICs9IGxpc3QoY2gsIGZhbHNlLCBkZXB0aCArIDEpOwogICAgICAgICAgZWxzZSBpZiAoY3QgPT09ICJPTCIpIG5lc3RlZCArPSBsaXN0KGNoLCB0cnVlLCBkZXB0aCArIDEpOwogICAgICAgICAgZWxzZSBpZiAoY2gubm9kZVR5cGUgPT09IDMpIGxlYWQgKz0gY2gudGV4dENvbnRlbnQgfHwgIiI7CiAgICAgICAgICBlbHNlIGxlYWQgKz0gaW5saW5lKGNoKTsKICAgICAgICB9CiAgICAgICAgb3V0ICs9IGluZGVudCArIG1hcmtlciArIGxlYWQudHJpbSgpICsgIlxuIiArIG5lc3RlZDsKICAgICAgfQogICAgICByZXR1cm4gb3V0OwogICAgfQogICAgZnVuY3Rpb24gdGFibGUobm9kZSkgewogICAgICB2YXIgcm93cyA9IFtdOwogICAgICAoZnVuY3Rpb24gY29sbGVjdChjb250YWluZXIpIHsKICAgICAgICB2YXIga2lkcyA9IGNvbnRhaW5lci5jaGlsZE5vZGVzIHx8IFtdOwogICAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwga2lkcy5sZW5ndGg7IGkrKykgewogICAgICAgICAgdmFyIGMgPSBraWRzW2ldOwogICAgICAgICAgaWYgKGMubm9kZVR5cGUgIT09IDEpIGNvbnRpbnVlOwogICAgICAgICAgdmFyIHQgPSAoYy50YWdOYW1lIHx8ICIiKS50b1VwcGVyQ2FzZSgpOwogICAgICAgICAgaWYgKHQgPT09ICJUSEVBRCIgfHwgdCA9PT0gIlRCT0RZIiB8fCB0ID09PSAiVEZPT1QiKSBjb2xsZWN0KGMpOwogICAgICAgICAgZWxzZSBpZiAodCA9PT0gIlRSIikgewogICAgICAgICAgICB2YXIgY2VsbHMgPSBbXSwgY2MgPSBjLmNoaWxkTm9kZXMgfHwgW107CiAgICAgICAgICAgIGZvciAodmFyIGogPSAwOyBqIDwgY2MubGVuZ3RoOyBqKyspIHsKICAgICAgICAgICAgICB2YXIgZCA9IGNjW2pdOwogICAgICAgICAgICAgIGlmIChkLm5vZGVUeXBlICE9PSAxKSBjb250aW51ZTsKICAgICAgICAgICAgICB2YXIgZHQgPSAoZC50YWdOYW1lIHx8ICIiKS50b1VwcGVyQ2FzZSgpOwogICAgICAgICAgICAgIGlmIChkdCA9PT0gIlRIIiB8fCBkdCA9PT0gIlREIikgY2VsbHMucHVzaChpbmxpbmUoZCkudHJpbSgpKTsKICAgICAgICAgICAgfQogICAgICAgICAgICByb3dzLnB1c2goY2VsbHMpOwogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfSkobm9kZSk7CiAgICAgIGlmICghcm93cy5sZW5ndGgpIHJldHVybiAiIjsKICAgICAgdmFyIGhlYWQgPSByb3dzWzBdLCBib2R5ID0gcm93cy5zbGljZSgxKTsKICAgICAgdmFyIHNlcCA9IGhlYWQubWFwKGZ1bmN0aW9uICgpIHsgcmV0dXJuICItLS0iOyB9KTsKICAgICAgdmFyIG91dCA9ICJ8ICIgKyBoZWFkLmpvaW4oIiB8ICIpICsgIiB8XG58ICIgKyBzZXAuam9pbigiIHwgIikgKyAiIHxcbiI7CiAgICAgIGZvciAodmFyIGsgPSAwOyBrIDwgYm9keS5sZW5ndGg7IGsrKykgb3V0ICs9ICJ8ICIgKyBib2R5W2tdLmpvaW4oIiB8ICIpICsgIiB8XG4iOwogICAgICByZXR1cm4gb3V0OwogICAgfQogICAgZnVuY3Rpb24gYmxvY2sobm9kZSkgewogICAgICB2YXIgb3V0ID0gIiI7CiAgICAgIHZhciBraWRzID0gbm9kZS5jaGlsZE5vZGVzIHx8IFtdOwogICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGtpZHMubGVuZ3RoOyBpKyspIHsKICAgICAgICB2YXIgYyA9IGtpZHNbaV07CiAgICAgICAgaWYgKGMubm9kZVR5cGUgPT09IDMpIHsgaWYgKChjLnRleHRDb250ZW50IHx8ICIiKS50cmltKCkpIG91dCArPSBjLnRleHRDb250ZW50OyBjb250aW51ZTsgfQogICAgICAgIGlmIChjLm5vZGVUeXBlICE9PSAxKSBjb250aW51ZTsKICAgICAgICB2YXIgdGFnID0gKGMudGFnTmFtZSB8fCAiIikudG9VcHBlckNhc2UoKTsKICAgICAgICBpZiAoL15IWzEtNl0kLy50ZXN0KHRhZykpIG91dCArPSBuZXcgQXJyYXkoK3RhZ1sxXSArIDEpLmpvaW4oIiMiKSArICIgIiArIGlubGluZShjKS50cmltKCkgKyAiXG5cbiI7CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiUCIpIG91dCArPSBpbmxpbmUoYykudHJpbSgpICsgIlxuXG4iOwogICAgICAgIGVsc2UgaWYgKHRhZyA9PT0gIlVMIikgb3V0ICs9IGxpc3QoYywgZmFsc2UsIDApICsgIlxuIjsKICAgICAgICBlbHNlIGlmICh0YWcgPT09ICJPTCIpIG91dCArPSBsaXN0KGMsIHRydWUsIDApICsgIlxuIjsKICAgICAgICBlbHNlIGlmICh0YWcgPT09ICJQUkUiKSB7CiAgICAgICAgICB2YXIgY29kZSA9IGZpbmRDaGlsZFRhZyhjLCAiQ09ERSIpOwogICAgICAgICAgdmFyIGxhbmcgPSBsYW5nT2YoY29kZSB8fCBjKTsKICAgICAgICAgIHZhciBib2R5ID0gKGNvZGUgfHwgYykudGV4dENvbnRlbnQgfHwgIiI7CiAgICAgICAgICB2YXIgZiA9IGZlbmNlKGJvZHksIDMpOwogICAgICAgICAgb3V0ICs9IGYgKyBsYW5nICsgIlxuIiArIGJvZHkucmVwbGFjZSgvXG4kLywgIiIpICsgIlxuIiArIGYgKyAiXG5cbiI7CiAgICAgICAgfSBlbHNlIGlmICh0YWcgPT09ICJCTE9DS1FVT1RFIikgewogICAgICAgICAgdmFyIGlubmVyID0gYmxvY2soYykudHJpbSgpLnNwbGl0KCJcbiIpLm1hcChmdW5jdGlvbiAobCkgeyByZXR1cm4gIj4gIiArIGw7IH0pLmpvaW4oIlxuIik7CiAgICAgICAgICBvdXQgKz0gaW5uZXIgKyAiXG5cbiI7CiAgICAgICAgfSBlbHNlIGlmICh0YWcgPT09ICJIUiIpIG91dCArPSAiLS0tXG5cbiI7CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiVEFCTEUiKSBvdXQgKz0gdGFibGUoYykgKyAiXG4iOwogICAgICAgIGVsc2UgaWYgKHRhZyA9PT0gIkJSIikgb3V0ICs9ICJcbiI7CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiU1RST05HIiB8fCB0YWcgPT09ICJCIiB8fCB0YWcgPT09ICJFTSIgfHwgdGFnID09PSAiSSIgfHwKICAgICAgICAgICAgICAgICB0YWcgPT09ICJBIiB8fCB0YWcgPT09ICJDT0RFIiB8fCB0YWcgPT09ICJERUwiIHx8IHRhZyA9PT0gIlMiKQogICAgICAgICAgb3V0ICs9IGlubGluZShjKSArICJcblxuIjsKICAgICAgICBlbHNlIG91dCArPSBibG9jayhjKTsgLy8gdW5rbm93biB3cmFwcGVyOiByZWN1cnNlIChkcm9wIHRhZywga2VlcCBjb250ZW50KQogICAgICB9CiAgICAgIHJldHVybiBvdXQ7CiAgICB9CiAgICAvLyBibG9jaygpIGRpc3BhdGNoZXMgb24gZWFjaCBDSElMRCdzIHRhZywgdHJlYXRpbmcgdGhlIHBhc3NlZCBub2RlIGFzIGEgcGxhaW4KICAgIC8vIGNvbnRhaW5lci4gV3JhcCByb290IGluIGEgb25lLW9mZiBjb250YWluZXIgc28gcm9vdCdzIE9XTiB0YWcgaXMgZGlzcGF0Y2hlZAogICAgLy8gdG9vOiBjYWxsZXJzIHBhc3MgZWl0aGVyIHRoZSBidWJibGUgY29udGFpbmVyIChpdHMgYmxvY2sgY2hpbGRyZW4gcmVuZGVyKSBvcgogICAgLy8gYSBzaW5nbGUgYmxvY2sgZWxlbWVudCBsaWtlIDxwcmU+Lzx1bD4vPHRhYmxlPiAobm93IGhhbmRsZWQsIG5vdCBmbGF0dGVuZWQpLgogICAgcmV0dXJuIGJsb2NrKHsgY2hpbGROb2RlczogW3Jvb3RdIH0pLnJlcGxhY2UoL1xuezMsfS9nLCAiXG5cbiIpLnRyaW0oKTsKICB9CgogIC8vIC0tLS0gcHVyZSBoZWxwZXJzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KICBmdW5jdGlvbiBoYXNQcmVmaXgobm9kZSwgcHJlZml4KSB7CiAgICBpZiAobm9kZS5ub2RlVHlwZSAhPT0gMSB8fCB0eXBlb2Ygbm9kZS5jbGFzc05hbWUgIT09ICJzdHJpbmciKSByZXR1cm4gZmFsc2U7CiAgICB2YXIgcGFydHMgPSBub2RlLmNsYXNzTmFtZS5zcGxpdCgvXHMrLyk7CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IHBhcnRzLmxlbmd0aDsgaSsrKSBpZiAocGFydHNbaV0uaW5kZXhPZihwcmVmaXgpID09PSAwKSByZXR1cm4gdHJ1ZTsKICAgIHJldHVybiBmYWxzZTsKICB9CgogIC8vIENsYXNzLXByZWZpeCBob29rcyBmb3Igbm9uLWNvbnRlbnQgY2hyb21lIHRoYXQgcmVuZGVycyAqaW5zaWRlKiBhbiBhc3Npc3RhbnQKICAvLyBidWJibGUgKHZlcmlmaWVkIG9uIDIuMS4xNzA7IFRhc2sgNiByZS1waW5zIHRoZXNlKS4gdG9vbCovdGhpbmtpbmdfIGFyZSB0aGUgdjEKICAvLyBleGNsdXNpb25zOyB1bmtub3duQ29udGVudF8gaXMgdGhlIHJlbmRlcmVyJ3MgZmFsbGJhY2sgZm9yIHVucmVjb2duaXplZCBibG9jawogIC8vIHR5cGVzLCBzbyBzdHJpcHBpbmcgaXQgbWFrZXMgYSAqZnV0dXJlKiBibG9jayB0eXBlIGZhaWwgc2FmZSB0byBleGNsdWRlZCByYXRoZXIKICAvLyB0aGFuIGxlYWtpbmcgIlVuc3VwcG9ydGVkIGNvbnRlbnQiIGludG8gdGhlIGNvcHkuIFJlLXBpbiBpZiBhIHByZWZpeCBtb3Zlcy4KICB2YXIgQ0hST01FX1BSRUZJWEVTID0gWyJ0b29sVXNlXyIsICJ0b29sUmVzdWx0XyIsICJ0b29sUmVmZXJlbmNlXyIsICJ0aGlua2luZ18iLCAidW5rbm93bkNvbnRlbnRfIl07CgogIC8vIFRydWUgZm9yIGFueSBub2RlIHRoYXQgbXVzdCBuZXZlciBhcHBlYXIgaW4gY29waWVkIG91dHB1dDogb3VyIG93biBjb250cm9scywKICAvLyB0aGUgcmF0aW5nIHdpZGdldCAoYGRhdGEtbWVzc2FnZS1yYXRpbmdgICsgaXRzICJUaGFua3MgZm9yIHlvdXIgZmVlZGJhY2siCiAgLy8gdGV4dCksIGFueSBidXR0b24gKGNvcHktY29kZSBjaHJvbWUpLCBhbmQgdGhlIGV4Y2x1ZGVkIGNvbnRlbnQgYmxvY2tzIGFib3ZlLgogIGZ1bmN0aW9uIGlzQ2hyb21lKG5vZGUpIHsKICAgIGlmIChub2RlLm5vZGVUeXBlICE9PSAxKSByZXR1cm4gZmFsc2U7CiAgICBpZiAoKG5vZGUudGFnTmFtZSB8fCAiIikudG9VcHBlckNhc2UoKSA9PT0gIkJVVFRPTiIpIHJldHVybiB0cnVlOwogICAgaWYgKG5vZGUuZ2V0QXR0cmlidXRlICYmIG5vZGUuZ2V0QXR0cmlidXRlKCJkYXRhLW1lc3NhZ2UtcmF0aW5nIikgIT09IG51bGwpIHJldHVybiB0cnVlOwogICAgaWYgKGhhc1ByZWZpeChub2RlLCBDT05UUk9MX1BSRUZJWCkpIHJldHVybiB0cnVlOwogICAgZm9yICh2YXIgaSA9IDA7IGkgPCBDSFJPTUVfUFJFRklYRVMubGVuZ3RoOyBpKyspIGlmIChoYXNQcmVmaXgobm9kZSwgQ0hST01FX1BSRUZJWEVTW2ldKSkgcmV0dXJuIHRydWU7CiAgICByZXR1cm4gZmFsc2U7CiAgfQoKICAvLyBEZWVwLWNsb25lIGBjb250ZW50Tm9kZWAsIHRoZW4gc3RyaXAgZXZlcnkgY2hyb21lIG5vZGUgc28gY29waWVkIG91dHB1dCBpcyB0aGUKICAvLyBtZXNzYWdlJ3MgdGV4dCBjb250ZW50IG9ubHkuIFRoaXMgaXMgYSBDT1JSRUNUTkVTUyBHQVRFLCBub3QgY29zbWV0aWM6IHRoZQogIC8vIGRlZmF1bHQgY29udGVudCBub2RlIGlzIHRoZSB3aG9sZSBidWJibGUgKGFsbCBjb250ZW50LWJsb2NrIHNpYmxpbmdzLCBzbyBtdWx0aS0KICAvLyBibG9jayBhc3Npc3RhbnQgdHVybnMgYXJlIGNhcHR1cmVkKSwgYW5kIHRoaXMgc3RyaXAtbGlzdCBpcyB0aGUgb25seSB0aGluZwogIC8vIGtlZXBpbmcgdGhlIHJhdGluZyB3aWRnZXQgYW5kIHYxLWV4Y2x1ZGVkIGJsb2NrcyBvdXQgb2YgdGhlIGNvcHkuCiAgZnVuY3Rpb24gc2FuaXRpemVDbG9uZShjb250ZW50Tm9kZSkgewogICAgdmFyIGNsb25lID0gY29udGVudE5vZGUuY2xvbmVOb2RlKHRydWUpOwogICAgKGZ1bmN0aW9uIHN0cmlwKG5vZGUpIHsKICAgICAgdmFyIGtpZHMgPSAobm9kZS5jaGlsZE5vZGVzIHx8IFtdKS5zbGljZSgpOwogICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGtpZHMubGVuZ3RoOyBpKyspIHsKICAgICAgICB2YXIgYyA9IGtpZHNbaV07CiAgICAgICAgaWYgKGMubm9kZVR5cGUgPT09IDEgJiYgaXNDaHJvbWUoYykpIHsgbm9kZS5yZW1vdmVDaGlsZChjKTsgY29udGludWU7IH0KICAgICAgICBpZiAoYy5ub2RlVHlwZSA9PT0gMSkgc3RyaXAoYyk7CiAgICAgIH0KICAgIH0pKGNsb25lKTsKICAgIHJldHVybiBjbG9uZTsKICB9CgogIGZ1bmN0aW9uIGNsYXNzaWZ5QnViYmxlKG5vZGUpIHsKICAgIGlmIChub2RlLm5vZGVUeXBlICE9PSAxKSByZXR1cm4gbnVsbDsKICAgIGlmIChoYXNQcmVmaXgobm9kZSwgInVzZXJNZXNzYWdlQ29udGFpbmVyXyIpKSByZXR1cm4gInVzZXIiOwogICAgaWYgKG5vZGUuZ2V0QXR0cmlidXRlICYmIG5vZGUuZ2V0QXR0cmlidXRlKCJkYXRhLXRlc3RpZCIpID09PSAiYXNzaXN0YW50LW1lc3NhZ2UiKSByZXR1cm4gImFzc2lzdGFudCI7CiAgICByZXR1cm4gbnVsbDsKICB9CgogIC8vIEJ1aWxkIHRoZSB3aG9sZS1jb252ZXJzYXRpb24gbWFya2Rvd24gZnJvbSBhbiBvcmRlcmVkIGxpc3Qgb2YgYnViYmxlcy4KICAvLyBgY29udGVudE9mKGJ1YmJsZSlgIHJlc29sdmVzIHRoZSBjb250ZW50IG5vZGUgKGRlZmF1bHQ6IHRoZSBidWJibGUgaXRzZWxmLCBzbwogIC8vIGV2ZXJ5IGNvbnRlbnQgYmxvY2sgaXMgaW5jbHVkZWQ7IHNhbml0aXplQ2xvbmUgZHJvcHMgY2hyb21lKTsgYSBkZWZhdWx0IGlzCiAgLy8gcHJvdmlkZWQgZm9yIHRlc3RzLgogIGZ1bmN0aW9uIGNvbnZlcnNhdGlvblRvTWFya2Rvd24oYnViYmxlcywgY29udGVudE9mKSB7CiAgICBjb250ZW50T2YgPSBjb250ZW50T2YgfHwgZnVuY3Rpb24gKGIpIHsgcmV0dXJuIGI7IH07CiAgICB2YXIgcGFydHMgPSBbXTsKICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYnViYmxlcy5sZW5ndGg7IGkrKykgewogICAgICB2YXIgcm9sZSA9IGNsYXNzaWZ5QnViYmxlKGJ1YmJsZXNbaV0pOwogICAgICBpZiAoIXJvbGUpIGNvbnRpbnVlOwogICAgICB2YXIgY2xlYW4gPSBzYW5pdGl6ZUNsb25lKGNvbnRlbnRPZihidWJibGVzW2ldKSk7CiAgICAgIHZhciBib2R5ID0gcm9sZSA9PT0gImFzc2lzdGFudCIgPyBodG1sVG9NYXJrZG93bihjbGVhbikgOiAoY2xlYW4udGV4dENvbnRlbnQgfHwgIiIpLnRyaW0oKTsKICAgICAgaWYgKCFib2R5KSBjb250aW51ZTsKICAgICAgcGFydHMucHVzaCgocm9sZSA9PT0gInVzZXIiID8gIiMjIFVzZXIiIDogIiMjIEFzc2lzdGFudCIpICsgIlxuXG4iICsgYm9keSk7CiAgICB9CiAgICByZXR1cm4gcGFydHMuam9pbigiXG5cbiIpICsgKHBhcnRzLmxlbmd0aCA/ICJcbiIgOiAiIik7CiAgfQoKICAvLyAtLS0tIGV4cG9ydHMgKG5vZGUgdGVzdHMpIC8gYm9vdCAocmVhbCB3ZWJ2aWV3KSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgaWYgKHR5cGVvZiBkb2N1bWVudCAhPT0gInVuZGVmaW5lZCIpIHsKICAgIGJvb3QoKTsKICB9IGVsc2UgaWYgKHR5cGVvZiBtb2R1bGUgIT09ICJ1bmRlZmluZWQiICYmIG1vZHVsZS5leHBvcnRzKSB7CiAgICBtb2R1bGUuZXhwb3J0cyA9IHsgaHRtbFRvTWFya2Rvd246IGh0bWxUb01hcmtkb3duLCBzYW5pdGl6ZUNsb25lOiBzYW5pdGl6ZUNsb25lLAogICAgICAgICAgICAgICAgICAgICAgIGNsYXNzaWZ5QnViYmxlOiBjbGFzc2lmeUJ1YmJsZSwgY29udmVyc2F0aW9uVG9NYXJrZG93bjogY29udmVyc2F0aW9uVG9NYXJrZG93biB9OwogIH0KCiAgLy8gLS0tLSBsaXZlLXdlYnZpZXcgd2lyaW5nIChydW5zIG9ubHkgd2hlbiBhIGRvY3VtZW50IGV4aXN0cykgLS0tLS0tLS0tLS0tLS0tLQogIGZ1bmN0aW9uIHFzKG5vZGUsIHNlbCkgeyB0cnkgeyByZXR1cm4gc2VsICYmIG5vZGUucXVlcnlTZWxlY3RvciA/IG5vZGUucXVlcnlTZWxlY3RvcihzZWwpIDogbnVsbDsgfSBjYXRjaCAoXykgeyByZXR1cm4gbnVsbDsgfSB9CiAgZnVuY3Rpb24gcXNhKHNlbCkgeyB0cnkgeyByZXR1cm4gQXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwoZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbChzZWwpKTsgfSBjYXRjaCAoXykgeyByZXR1cm4gW107IH0gfQoKICAvLyBUaGUgY29udGVudCBub2RlIHRvIGNvbnZlcnQvY29weTogdGhlIG9wdGlvbmFsIEFTU0lTVEFOVF9DT05URU5UIHdyYXBwZXIgaWYKICAvLyBwaW5uZWQgYW5kIHByZXNlbnQsIGVsc2UgdGhlIGJ1YmJsZSBpdHNlbGYuIFRoZSBidWJibGUgYWxyZWFkeSBjb250YWlucyBldmVyeQogIC8vIGNvbnRlbnQtYmxvY2sgc2libGluZyBvZiBhIG11bHRpLWJsb2NrIHR1cm4sIGFuZCBzYW5pdGl6ZUNsb25lIHN0cmlwcyB0aGUKICAvLyBjaHJvbWUgKHJhdGluZyB3aWRnZXQsIHRvb2wvdGhpbmtpbmcvdW5rbm93biBibG9ja3MsIGJ1dHRvbnMsIG91ciBjb250cm9scykKICAvLyBlaXRoZXIgd2F5IC0tIHNvIHRoaXMgaXMgYSBuYXJyb3dpbmcsIG5ldmVyIHRoZSB0aGluZyB0aGF0IGd1YXJhbnRlZXMKICAvLyBjb3JyZWN0bmVzcy4KICBmdW5jdGlvbiBjb250ZW50Tm9kZU9mKGJ1YmJsZSwgcm9sZSkgewogICAgaWYgKHJvbGUgPT09ICJhc3Npc3RhbnQiICYmIEFTU0lTVEFOVF9DT05URU5UKSB7CiAgICAgIHZhciBuID0gcXMoYnViYmxlLCBBU1NJU1RBTlRfQ09OVEVOVCk7CiAgICAgIGlmIChuKSByZXR1cm4gbjsKICAgIH0KICAgIHJldHVybiBidWJibGU7CiAgfQoKICBmdW5jdGlvbiBjb3B5VGV4dCh0ZXh0KSB7CiAgICB0cnkgewogICAgICBpZiAobmF2aWdhdG9yLmNsaXBib2FyZCAmJiBuYXZpZ2F0b3IuY2xpcGJvYXJkLndyaXRlVGV4dCkgcmV0dXJuIG5hdmlnYXRvci5jbGlwYm9hcmQud3JpdGVUZXh0KHRleHQpOwogICAgfSBjYXRjaCAoXykge30KICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTsgLy8gYmVzdC1lZmZvcnQ7IG5ldmVyIHRocm93IGludG8gdGhlIGFwcAogIH0KCiAgZnVuY3Rpb24gZmxhc2hGZWVkYmFjayhob3N0KSB7CiAgICB0cnkgewogICAgICB2YXIgZmIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzcGFuIik7CiAgICAgIGZiLmNsYXNzTmFtZSA9IENPTlRST0xfUFJFRklYICsgIi1mZWVkYmFjayI7CiAgICAgIGZiLnRleHRDb250ZW50ID0gIkNvcGllZCI7CiAgICAgIGhvc3QuYXBwZW5kQ2hpbGQoZmIpOwogICAgICBzZXRUaW1lb3V0KGZ1bmN0aW9uICgpIHsgaWYgKGZiICYmIGZiLnBhcmVudE5vZGUpIGZiLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoZmIpOyB9LCBGRUVEQkFDS19NUyk7CiAgICB9IGNhdGNoIChfKSB7fQogIH0KCiAgZnVuY3Rpb24gYnViYmxlTWFya2Rvd24oYnViYmxlLCByb2xlKSB7CiAgICB2YXIgY2xlYW4gPSBzYW5pdGl6ZUNsb25lKGNvbnRlbnROb2RlT2YoYnViYmxlLCByb2xlKSk7CiAgICByZXR1cm4gcm9sZSA9PT0gImFzc2lzdGFudCIgPyBodG1sVG9NYXJrZG93bihjbGVhbikgOiAoY2xlYW4udGV4dENvbnRlbnQgfHwgIiIpLnRyaW0oKTsKICB9CiAgZnVuY3Rpb24gYnViYmxlUGxhaW4oYnViYmxlLCByb2xlKSB7CiAgICByZXR1cm4gKHNhbml0aXplQ2xvbmUoY29udGVudE5vZGVPZihidWJibGUsIHJvbGUpKS50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpOwogIH0KCiAgLy8gQnVpbGQgYSBzaW5nbGUgY29udHJvbDogYSBwcmltYXJ5ICJDb3B5IiAobWFya2Rvd24pIHBsdXMgYSBzbWFsbCBjYXJldCB0aGF0CiAgLy8gdG9nZ2xlcyBhIG1lbnUgd2l0aCAiQ29weSBhcyBwbGFpbiB0ZXh0Ii4gQWxsIG5vZGVzIGNhcnJ5IHRoZSBDT05UUk9MX1BSRUZJWAogIC8vIGNsYXNzIHNvIHNhbml0aXplQ2xvbmUgcmVtb3ZlcyB0aGVtIGZyb20gYW55IGNvcGllZCBjb250ZW50LgogIGZ1bmN0aW9uIGJ1aWxkQ29udHJvbChvbk1hcmtkb3duLCBvblBsYWluKSB7CiAgICB2YXIgd3JhcCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInNwYW4iKTsKICAgIHdyYXAuY2xhc3NOYW1lID0gQ09OVFJPTF9QUkVGSVg7CiAgICB2YXIgcHJpbWFyeSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImJ1dHRvbiIpOwogICAgcHJpbWFyeS50eXBlID0gImJ1dHRvbiI7CiAgICBwcmltYXJ5LmNsYXNzTmFtZSA9IENPTlRST0xfUFJFRklYICsgIi1idG4iOwogICAgcHJpbWFyeS50aXRsZSA9ICJDb3B5IGFzIE1hcmtkb3duIjsKICAgIHByaW1hcnkudGV4dENvbnRlbnQgPSAiQ29weSI7CiAgICBwcmltYXJ5LmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgZnVuY3Rpb24gKGUpIHsgZS5zdG9wUHJvcGFnYXRpb24oKTsgb25NYXJrZG93bihwcmltYXJ5KTsgfSk7CiAgICB2YXIgY2FyZXQgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJidXR0b24iKTsKICAgIGNhcmV0LnR5cGUgPSAiYnV0dG9uIjsKICAgIGNhcmV0LmNsYXNzTmFtZSA9IENPTlRST0xfUFJFRklYICsgIi1jYXJldCI7CiAgICBjYXJldC50aXRsZSA9ICJDb3B5IG9wdGlvbnMiOwogICAgY2FyZXQudGV4dENvbnRlbnQgPSAi4pa+IjsgLy8gYmxhY2sgZG93bi1wb2ludGluZyBzbWFsbCB0cmlhbmdsZQogICAgdmFyIG1lbnUgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzcGFuIik7CiAgICBtZW51LmNsYXNzTmFtZSA9IENPTlRST0xfUFJFRklYICsgIi1tZW51IjsKICAgIG1lbnUuc3R5bGUuZGlzcGxheSA9ICJub25lIjsKICAgIHZhciBwbGFpbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImJ1dHRvbiIpOwogICAgcGxhaW4udHlwZSA9ICJidXR0b24iOwogICAgcGxhaW4uY2xhc3NOYW1lID0gQ09OVFJPTF9QUkVGSVggKyAiLWJ0biI7CiAgICBwbGFpbi50ZXh0Q29udGVudCA9ICJDb3B5IGFzIHBsYWluIHRleHQiOwogICAgcGxhaW4uYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBmdW5jdGlvbiAoZSkgeyBlLnN0b3BQcm9wYWdhdGlvbigpOyBtZW51LnN0eWxlLmRpc3BsYXkgPSAibm9uZSI7IG9uUGxhaW4ocGxhaW4pOyB9KTsKICAgIG1lbnUuYXBwZW5kQ2hpbGQocGxhaW4pOwogICAgY2FyZXQuYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBmdW5jdGlvbiAoZSkgewogICAgICBlLnN0b3BQcm9wYWdhdGlvbigpOwogICAgICBtZW51LnN0eWxlLmRpc3BsYXkgPSBtZW51LnN0eWxlLmRpc3BsYXkgPT09ICJub25lIiA/ICJpbmxpbmUtYmxvY2siIDogIm5vbmUiOwogICAgfSk7CiAgICB3cmFwLmFwcGVuZENoaWxkKHByaW1hcnkpOwogICAgd3JhcC5hcHBlbmRDaGlsZChjYXJldCk7CiAgICB3cmFwLmFwcGVuZENoaWxkKG1lbnUpOwogICAgcmV0dXJuIHdyYXA7CiAgfQoKICBmdW5jdGlvbiBkZWNvcmF0ZShidWJibGUpIHsKICAgIHRyeSB7CiAgICAgIHZhciByb2xlID0gY2xhc3NpZnlCdWJibGUoYnViYmxlKTsKICAgICAgaWYgKCFyb2xlKSByZXR1cm47CiAgICAgIGlmIChxcyhidWJibGUsICIuIiArIENPTlRST0xfUFJFRklYKSkgcmV0dXJuOyAvLyBhbHJlYWR5IGRlY29yYXRlZAogICAgICB2YXIgY29udHJvbCA9IGJ1aWxkQ29udHJvbCgKICAgICAgICBmdW5jdGlvbiAoaG9zdCkgeyBjb3B5VGV4dChidWJibGVNYXJrZG93bihidWJibGUsIHJvbGUpKS50aGVuKGZ1bmN0aW9uICgpIHsgZmxhc2hGZWVkYmFjayhjb250cm9sKTsgfSk7IH0sCiAgICAgICAgZnVuY3Rpb24gKGhvc3QpIHsgY29weVRleHQoYnViYmxlUGxhaW4oYnViYmxlLCByb2xlKSkudGhlbihmdW5jdGlvbiAoKSB7IGZsYXNoRmVlZGJhY2soY29udHJvbCk7IH0pOyB9CiAgICAgICk7CiAgICAgIGJ1YmJsZS5hcHBlbmRDaGlsZChjb250cm9sKTsKICAgIH0gY2F0Y2ggKF8pIHt9CiAgfQoKICBmdW5jdGlvbiBjb3B5Q29udmVyc2F0aW9uKGZvcm1hdCkgewogICAgdmFyIGJ1YmJsZXMgPSBxc2EoVVNFUl9CVUJCTEUgKyAiLCIgKyBBU1NJU1RBTlRfQlVCQkxFKTsKICAgIGlmIChmb3JtYXQgPT09ICJ0ZXh0IikgewogICAgICB2YXIgbGluZXMgPSBbXTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBidWJibGVzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdmFyIHJvbGUgPSBjbGFzc2lmeUJ1YmJsZShidWJibGVzW2ldKTsKICAgICAgICBpZiAoIXJvbGUpIGNvbnRpbnVlOwogICAgICAgIHZhciBib2R5ID0gYnViYmxlUGxhaW4oYnViYmxlc1tpXSwgcm9sZSk7CiAgICAgICAgaWYgKGJvZHkpIGxpbmVzLnB1c2goYm9keSk7CiAgICAgIH0KICAgICAgcmV0dXJuIGNvcHlUZXh0KGxpbmVzLmpvaW4oIlxuXG4iKSArIChsaW5lcy5sZW5ndGggPyAiXG4iIDogIiIpKTsKICAgIH0KICAgIHJldHVybiBjb3B5VGV4dChjb252ZXJzYXRpb25Ub01hcmtkb3duKGJ1YmJsZXMsIGZ1bmN0aW9uIChiKSB7CiAgICAgIHJldHVybiBjb250ZW50Tm9kZU9mKGIsIGNsYXNzaWZ5QnViYmxlKGIpKTsKICAgIH0pKTsKICB9CgogIGZ1bmN0aW9uIGluc3RhbGxDb252ZXJzYXRpb25Db250cm9sKCkgewogICAgdHJ5IHsKICAgICAgaWYgKHFzKGRvY3VtZW50LCAiLiIgKyBDT05UUk9MX1BSRUZJWCArICItY29udmVyc2F0aW9uIikpIHJldHVybjsKICAgICAgdmFyIGJhciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImRpdiIpOwogICAgICBiYXIuY2xhc3NOYW1lID0gQ09OVFJPTF9QUkVGSVggKyAiLWNvbnZlcnNhdGlvbiI7CiAgICAgIHZhciBjb250cm9sID0gYnVpbGRDb250cm9sKAogICAgICAgIGZ1bmN0aW9uICgpIHsgY29weUNvbnZlcnNhdGlvbigibWFya2Rvd24iKS50aGVuKGZ1bmN0aW9uICgpIHsgZmxhc2hGZWVkYmFjayhiYXIpOyB9KTsgfSwKICAgICAgICBmdW5jdGlvbiAoKSB7IGNvcHlDb252ZXJzYXRpb24oInRleHQiKS50aGVuKGZ1bmN0aW9uICgpIHsgZmxhc2hGZWVkYmFjayhiYXIpOyB9KTsgfQogICAgICApOwogICAgICBjb250cm9sLnRpdGxlID0gIkNvcHkgZW50aXJlIGNvbnZlcnNhdGlvbiI7CiAgICAgIGJhci5hcHBlbmRDaGlsZChjb250cm9sKTsKICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChiYXIpOyAvLyBmaXhlZC1wb3NpdGlvbiB2aWEgQ1NTOyBwbGFjZW1lbnQgcmVmaW5lZCBpbiBUYXNrIDYKICAgIH0gY2F0Y2ggKF8pIHt9CiAgfQoKICBmdW5jdGlvbiBzd2VlcCgpIHsgdmFyIGIgPSBxc2EoVVNFUl9CVUJCTEUgKyAiLCIgKyBBU1NJU1RBTlRfQlVCQkxFKTsgZm9yICh2YXIgaSA9IDA7IGkgPCBiLmxlbmd0aDsgaSsrKSBkZWNvcmF0ZShiW2ldKTsgfQoKICBmdW5jdGlvbiBib290KCkgewogICAgdHJ5IHsKICAgICAgdmFyIHRhcmdldCA9IChNRVNTQUdFU19DT05UQUlORVIgJiYgcXMoZG9jdW1lbnQsIE1FU1NBR0VTX0NPTlRBSU5FUikpIHx8IGRvY3VtZW50LmJvZHk7CiAgICAgIHN3ZWVwKCk7CiAgICAgIGluc3RhbGxDb252ZXJzYXRpb25Db250cm9sKCk7CiAgICAgIGlmICh0eXBlb2YgTXV0YXRpb25PYnNlcnZlciA9PT0gInVuZGVmaW5lZCIpIHJldHVybjsKICAgICAgdmFyIG9icyA9IG5ldyBNdXRhdGlvbk9ic2VydmVyKGZ1bmN0aW9uICgpIHsgc3dlZXAoKTsgfSk7CiAgICAgIG9icy5vYnNlcnZlKHRhcmdldCwgeyBjaGlsZExpc3Q6IHRydWUsIHN1YnRyZWU6IHRydWUgfSk7CiAgICB9IGNhdGNoIChfKSB7fQogIH0KfSkoKTsK").decode("utf-8") +INJECT_JS = base64.b64decode("LyogY2MtbWQtY29weTogcGVyLW1lc3NhZ2UgYW5kIHdob2xlLWNvbnZlcnNhdGlvbiBjb3B5IChtYXJrZG93bi9wbGFpbikgZm9yIHRoZQogKiBDbGF1ZGUgQ29kZSBWUyBDb2RlIHdlYnZpZXcuIFNlbGYtY29udGFpbmVkIElJRkUgYXBwZW5kZWQgdG8gd2Vidmlldy9pbmRleC5qcy4KICogQWRkaXRpdmUgYW5kIHJlYWQtb25seSB3LnIudC4gYXBwIHN0YXRlOyBrZXllZCBvbiBzdGFibGUgQ1NTLW1vZHVsZSBjbGFzcwogKiBwcmVmaXhlcywgc28gaXQgZmFpbHMgc2FmZSAoY29udHJvbHMgc2ltcGx5IGRvIG5vdCBhcHBlYXIpIGlmIGEgcHJlZml4IG1vdmVzLgogKiBFeHBvc2VzIGl0cyBwdXJlIGZ1bmN0aW9ucyBmb3Igbm9kZSB1bml0IHRlc3RzOyBib290KClzIG9ubHkgaW4gYSByZWFsIHdlYnZpZXcuICovCi8qIExlYWRpbmcgJzsnIHNvIHRoYXQsIGFwcGVuZGVkIGFmdGVyIHRoZSBidW5kbGUsIHRoaXMgSUlGRSBjYW4gbmV2ZXIgYmUgcGFyc2VkIGFzCiAqIGEgY2FsbCBvbiB0aGUgYnVuZGxlJ3MgZmluYWwgZXhwcmVzc2lvbiBpZiBpdCBsYWNrcyBhIHRyYWlsaW5nIHNlbWljb2xvbiAoQVNJCiAqIHNhZmV0eSBhY3Jvc3MgZXh0ZW5zaW9uIGJ1aWxkcykuICovCjsoZnVuY3Rpb24gKCkgewogICJ1c2Ugc3RyaWN0IjsKCiAgdmFyIENPTlRST0xfUFJFRklYID0gImNjLW1kLWNvcHkiOyAvLyBldmVyeSBpbmplY3RlZCBub2RlJ3MgY2xhc3Mgc3RhcnRzIHdpdGggdGhpcwogIHZhciBVU0VSX0JVQkJMRSA9ICdbY2xhc3MqPSJ1c2VyTWVzc2FnZUNvbnRhaW5lcl8iXSc7CiAgLy8gQXNzaXN0YW50IG1lc3NhZ2Ugd3JhcHBlci4gVmVyaWZpZWQgb24gMi4xLjE3MDogdGhlIHJlbmRlciBlbWl0cyBleGFjdGx5IG9uZQogIC8vIGBkYXRhLXRlc3RpZD0iYXNzaXN0YW50LW1lc3NhZ2UiYCBkaXYgcGVyIGFzc2lzdGFudCB0dXJuLCB3aXRoIHRoZSByYXRpbmcKICAvLyB3aWRnZXQgYW5kIGNvbnRlbnQgYmxvY2tzIGFzIGl0cyBjaGlsZHJlbi4gKFRoZSBlYXJsaWVyIGBbZGF0YS1tZXNzYWdlLXJhdGluZ11gCiAgLy8gd2FzIFdST05HOiB0aGF0IGF0dHJpYnV0ZSBzaXRzIG9uIHRoZSBuZXN0ZWQgcmF0aW5nIGNvbnRyb2wsIHdoaWNoIGlzIGFsc28gb25seQogIC8vIHJlbmRlcmVkIGJlaGluZCBhbiBleHBlcmltZW50K2FuYWx5dGljcyBnYXRlLikgUmUtcGlubmVkIGluIFRhc2sgNi4KICB2YXIgQVNTSVNUQU5UX0JVQkJMRSA9ICdbZGF0YS10ZXN0aWQ9ImFzc2lzdGFudC1tZXNzYWdlIl0nOwogIHZhciBNRVNTQUdFU19DT05UQUlORVIgPSAnW2NsYXNzKj0ibWVzc2FnZXNDb250YWluZXJfIl0nOyAvLyBlLmcuICdbY2xhc3MqPSJ0aW1lbGluZV8iXSc7ICIiIC0+IG9ic2VydmUgZG9jdW1lbnQuYm9keQogIC8vIE9wdGlvbmFsIG5hcnJvd2luZyBvbmx5LiBNVVNUIGJlIGEgc2luZ2xlIHdyYXBwZXIgYXJvdW5kIEFMTCBjb250ZW50IGJsb2NrcywKICAvLyBub3QgYSBwZXItYmxvY2sgY2xhc3MgKGEgdHVybiBoYXMgbXVsdGlwbGUgYmxvY2tzKS4gIiIgLT4gdXNlIHRoZSBidWJibGUgaXRzZWxmCiAgLy8gKGFscmVhZHkgYWdncmVnYXRlcyBhbGwgYmxvY2tzOyBzYW5pdGl6ZUNsb25lIGlzIHRoZSBjb3JyZWN0bmVzcyBnYXRlKS4KICB2YXIgQVNTSVNUQU5UX0NPTlRFTlQgPSAiIjsKICB2YXIgRkVFREJBQ0tfTVMgPSAxODAwOwoKICAvLyAtLS0tIEhUTUwgLT4gTWFya2Rvd24gKERPTSB3YWxrKSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgLy8gVXNlcyBvbmx5OiBub2RlVHlwZSwgdGFnTmFtZSwgY2hpbGROb2RlcywgdGV4dENvbnRlbnQsIGdldEF0dHJpYnV0ZSwgY2xhc3NOYW1lLgogIGZ1bmN0aW9uIGh0bWxUb01hcmtkb3duKHJvb3QpIHsKICAgIC8vIExvbmdlc3QgcnVuIG9mIGNvbnNlY3V0aXZlIGJhY2t0aWNrcyBpbiBzLCBzbyBhIGNvZGUgZGVsaW1pdGVyL2ZlbmNlIGNhbiBiZQogICAgLy8gY2hvc2VuIGxvbmdlciB0aGFuIGFueXRoaW5nIGluc2lkZSBpdCAoZWxzZSBgYGAgaW4gdGhlIGNvbnRlbnQgY2xvc2VzIGVhcmx5KS4KICAgIGZ1bmN0aW9uIGJhY2t0aWNrUnVuKHMpIHsKICAgICAgdmFyIG1heCA9IDAsIGN1ciA9IDA7CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgcy5sZW5ndGg7IGkrKykgewogICAgICAgIGlmIChzLmNoYXJBdChpKSA9PT0gImAiKSB7IGN1cisrOyBpZiAoY3VyID4gbWF4KSBtYXggPSBjdXI7IH0gZWxzZSBjdXIgPSAwOwogICAgICB9CiAgICAgIHJldHVybiBtYXg7CiAgICB9CiAgICBmdW5jdGlvbiBmZW5jZShzLCBtaW4pIHsgdmFyIG4gPSBiYWNrdGlja1J1bihzKSArIDE7IGlmIChuIDwgbWluKSBuID0gbWluOyByZXR1cm4gbmV3IEFycmF5KG4gKyAxKS5qb2luKCJgIik7IH0KICAgIGZ1bmN0aW9uIGlubGluZShub2RlKSB7CiAgICAgIHZhciBvdXQgPSAiIjsKICAgICAgdmFyIGtpZHMgPSBub2RlLmNoaWxkTm9kZXMgfHwgW107CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwga2lkcy5sZW5ndGg7IGkrKykgewogICAgICAgIHZhciBjID0ga2lkc1tpXTsKICAgICAgICBpZiAoYy5ub2RlVHlwZSA9PT0gMykgeyBvdXQgKz0gYy50ZXh0Q29udGVudCB8fCAiIjsgY29udGludWU7IH0KICAgICAgICBpZiAoYy5ub2RlVHlwZSAhPT0gMSkgY29udGludWU7CiAgICAgICAgdmFyIHRhZyA9IChjLnRhZ05hbWUgfHwgIiIpLnRvVXBwZXJDYXNlKCk7CiAgICAgICAgaWYgKHRhZyA9PT0gIkJSIikgb3V0ICs9ICJcbiI7CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiU1RST05HIiB8fCB0YWcgPT09ICJCIikgb3V0ICs9ICIqKiIgKyBpbmxpbmUoYykgKyAiKioiOwogICAgICAgIGVsc2UgaWYgKHRhZyA9PT0gIkVNIiB8fCB0YWcgPT09ICJJIikgb3V0ICs9ICIqIiArIGlubGluZShjKSArICIqIjsKICAgICAgICBlbHNlIGlmICh0YWcgPT09ICJERUwiIHx8IHRhZyA9PT0gIlMiKSBvdXQgKz0gIn5+IiArIGlubGluZShjKSArICJ+fiI7CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiQ09ERSIpIHsKICAgICAgICAgIHZhciBjdCA9IGMudGV4dENvbnRlbnQgfHwgIiI7CiAgICAgICAgICB2YXIgZCA9IGZlbmNlKGN0LCAxKTsKICAgICAgICAgIC8vIENvbW1vbk1hcmsgc3RyaXBzIG9uZSBsZWFkaW5nK3RyYWlsaW5nIHNwYWNlLCBzbyBwYWQgd2hlbiBhbiBlZGdlIGlzIGEKICAgICAgICAgIC8vIGJhY2t0aWNrIHRvIGtlZXAgaXQgZnJvbSBtZXJnaW5nIHdpdGggdGhlIGRlbGltaXRlci4KICAgICAgICAgIHZhciBwID0gKGN0LmNoYXJBdCgwKSA9PT0gImAiIHx8IGN0LmNoYXJBdChjdC5sZW5ndGggLSAxKSA9PT0gImAiKSA/ICIgIiA6ICIiOwogICAgICAgICAgb3V0ICs9IGQgKyBwICsgY3QgKyBwICsgZDsKICAgICAgICB9CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiQSIpIHsKICAgICAgICAgIHZhciBocmVmID0gYy5nZXRBdHRyaWJ1dGUgPyBjLmdldEF0dHJpYnV0ZSgiaHJlZiIpIDogbnVsbDsKICAgICAgICAgIHZhciB0ID0gaW5saW5lKGMpOwogICAgICAgICAgb3V0ICs9IGhyZWYgPyAiWyIgKyB0ICsgIl0oIiArIGhyZWYgKyAiKSIgOiB0OwogICAgICAgIH0gZWxzZSBvdXQgKz0gaW5saW5lKGMpOyAvLyB1bmtub3duIGlubGluZSB3cmFwcGVyOiBrZWVwIHRleHQsIGRyb3AgdGFnCiAgICAgIH0KICAgICAgcmV0dXJuIG91dDsKICAgIH0KICAgIGZ1bmN0aW9uIGxhbmdPZihjb2RlRWwpIHsKICAgICAgdmFyIGNscyA9ICIiOwogICAgICBpZiAoY29kZUVsKSBjbHMgPSAoY29kZUVsLmdldEF0dHJpYnV0ZSAmJiBjb2RlRWwuZ2V0QXR0cmlidXRlKCJjbGFzcyIpKSB8fCBjb2RlRWwuY2xhc3NOYW1lIHx8ICIiOwogICAgICB2YXIgbSA9IC9sYW5ndWFnZS0oW0EtWmEtejAtOSsjLlwtXSspLy5leGVjKGNscyB8fCAiIik7CiAgICAgIHJldHVybiBtID8gbVsxXSA6ICIiOwogICAgfQogICAgZnVuY3Rpb24gZmluZENoaWxkVGFnKG5vZGUsIHRhZykgewogICAgICB2YXIga2lkcyA9IG5vZGUuY2hpbGROb2RlcyB8fCBbXTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBraWRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgaWYgKGtpZHNbaV0ubm9kZVR5cGUgPT09IDEgJiYgKGtpZHNbaV0udGFnTmFtZSB8fCAiIikudG9VcHBlckNhc2UoKSA9PT0gdGFnKSByZXR1cm4ga2lkc1tpXTsKICAgICAgfQogICAgICByZXR1cm4gbnVsbDsKICAgIH0KICAgIGZ1bmN0aW9uIGxpc3Qobm9kZSwgb3JkZXJlZCwgZGVwdGgpIHsKICAgICAgdmFyIG91dCA9ICIiLCBuID0gMTsKICAgICAgdmFyIGtpZHMgPSBub2RlLmNoaWxkTm9kZXMgfHwgW107CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwga2lkcy5sZW5ndGg7IGkrKykgewogICAgICAgIHZhciBsaSA9IGtpZHNbaV07CiAgICAgICAgaWYgKGxpLm5vZGVUeXBlICE9PSAxIHx8IChsaS50YWdOYW1lIHx8ICIiKS50b1VwcGVyQ2FzZSgpICE9PSAiTEkiKSBjb250aW51ZTsKICAgICAgICB2YXIgbWFya2VyID0gb3JkZXJlZCA/IG4rKyArICIuICIgOiAiLSAiOwogICAgICAgIHZhciBpbmRlbnQgPSBuZXcgQXJyYXkoZGVwdGggKyAxKS5qb2luKCIgICIpOwogICAgICAgIHZhciBsZWFkID0gIiIsIG5lc3RlZCA9ICIiOwogICAgICAgIHZhciBsayA9IGxpLmNoaWxkTm9kZXMgfHwgW107CiAgICAgICAgZm9yICh2YXIgaiA9IDA7IGogPCBsay5sZW5ndGg7IGorKykgewogICAgICAgICAgdmFyIGNoID0gbGtbal07CiAgICAgICAgICB2YXIgY3QgPSBjaC5ub2RlVHlwZSA9PT0gMSA/IChjaC50YWdOYW1lIHx8ICIiKS50b1VwcGVyQ2FzZSgpIDogIiI7CiAgICAgICAgICBpZiAoY3QgPT09ICJVTCIpIG5lc3RlZCArPSBsaXN0KGNoLCBmYWxzZSwgZGVwdGggKyAxKTsKICAgICAgICAgIGVsc2UgaWYgKGN0ID09PSAiT0wiKSBuZXN0ZWQgKz0gbGlzdChjaCwgdHJ1ZSwgZGVwdGggKyAxKTsKICAgICAgICAgIGVsc2UgaWYgKGNoLm5vZGVUeXBlID09PSAzKSBsZWFkICs9IGNoLnRleHRDb250ZW50IHx8ICIiOwogICAgICAgICAgZWxzZSBsZWFkICs9IGlubGluZShjaCk7CiAgICAgICAgfQogICAgICAgIG91dCArPSBpbmRlbnQgKyBtYXJrZXIgKyBsZWFkLnRyaW0oKSArICJcbiIgKyBuZXN0ZWQ7CiAgICAgIH0KICAgICAgcmV0dXJuIG91dDsKICAgIH0KICAgIGZ1bmN0aW9uIHRhYmxlKG5vZGUpIHsKICAgICAgdmFyIHJvd3MgPSBbXTsKICAgICAgKGZ1bmN0aW9uIGNvbGxlY3QoY29udGFpbmVyKSB7CiAgICAgICAgdmFyIGtpZHMgPSBjb250YWluZXIuY2hpbGROb2RlcyB8fCBbXTsKICAgICAgICBmb3IgKHZhciBpID0gMDsgaSA8IGtpZHMubGVuZ3RoOyBpKyspIHsKICAgICAgICAgIHZhciBjID0ga2lkc1tpXTsKICAgICAgICAgIGlmIChjLm5vZGVUeXBlICE9PSAxKSBjb250aW51ZTsKICAgICAgICAgIHZhciB0ID0gKGMudGFnTmFtZSB8fCAiIikudG9VcHBlckNhc2UoKTsKICAgICAgICAgIGlmICh0ID09PSAiVEhFQUQiIHx8IHQgPT09ICJUQk9EWSIgfHwgdCA9PT0gIlRGT09UIikgY29sbGVjdChjKTsKICAgICAgICAgIGVsc2UgaWYgKHQgPT09ICJUUiIpIHsKICAgICAgICAgICAgdmFyIGNlbGxzID0gW10sIGNjID0gYy5jaGlsZE5vZGVzIHx8IFtdOwogICAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGNjLmxlbmd0aDsgaisrKSB7CiAgICAgICAgICAgICAgdmFyIGQgPSBjY1tqXTsKICAgICAgICAgICAgICBpZiAoZC5ub2RlVHlwZSAhPT0gMSkgY29udGludWU7CiAgICAgICAgICAgICAgdmFyIGR0ID0gKGQudGFnTmFtZSB8fCAiIikudG9VcHBlckNhc2UoKTsKICAgICAgICAgICAgICBpZiAoZHQgPT09ICJUSCIgfHwgZHQgPT09ICJURCIpIGNlbGxzLnB1c2goaW5saW5lKGQpLnRyaW0oKSk7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgcm93cy5wdXNoKGNlbGxzKTsKICAgICAgICAgIH0KICAgICAgICB9CiAgICAgIH0pKG5vZGUpOwogICAgICBpZiAoIXJvd3MubGVuZ3RoKSByZXR1cm4gIiI7CiAgICAgIHZhciBoZWFkID0gcm93c1swXSwgYm9keSA9IHJvd3Muc2xpY2UoMSk7CiAgICAgIHZhciBzZXAgPSBoZWFkLm1hcChmdW5jdGlvbiAoKSB7IHJldHVybiAiLS0tIjsgfSk7CiAgICAgIHZhciBvdXQgPSAifCAiICsgaGVhZC5qb2luKCIgfCAiKSArICIgfFxufCAiICsgc2VwLmpvaW4oIiB8ICIpICsgIiB8XG4iOwogICAgICBmb3IgKHZhciBrID0gMDsgayA8IGJvZHkubGVuZ3RoOyBrKyspIG91dCArPSAifCAiICsgYm9keVtrXS5qb2luKCIgfCAiKSArICIgfFxuIjsKICAgICAgcmV0dXJuIG91dDsKICAgIH0KICAgIGZ1bmN0aW9uIGJsb2NrKG5vZGUpIHsKICAgICAgdmFyIG91dCA9ICIiOwogICAgICB2YXIga2lkcyA9IG5vZGUuY2hpbGROb2RlcyB8fCBbXTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBraWRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdmFyIGMgPSBraWRzW2ldOwogICAgICAgIGlmIChjLm5vZGVUeXBlID09PSAzKSB7IGlmICgoYy50ZXh0Q29udGVudCB8fCAiIikudHJpbSgpKSBvdXQgKz0gYy50ZXh0Q29udGVudDsgY29udGludWU7IH0KICAgICAgICBpZiAoYy5ub2RlVHlwZSAhPT0gMSkgY29udGludWU7CiAgICAgICAgdmFyIHRhZyA9IChjLnRhZ05hbWUgfHwgIiIpLnRvVXBwZXJDYXNlKCk7CiAgICAgICAgaWYgKC9eSFsxLTZdJC8udGVzdCh0YWcpKSBvdXQgKz0gbmV3IEFycmF5KCt0YWdbMV0gKyAxKS5qb2luKCIjIikgKyAiICIgKyBpbmxpbmUoYykudHJpbSgpICsgIlxuXG4iOwogICAgICAgIGVsc2UgaWYgKHRhZyA9PT0gIlAiKSBvdXQgKz0gaW5saW5lKGMpLnRyaW0oKSArICJcblxuIjsKICAgICAgICBlbHNlIGlmICh0YWcgPT09ICJVTCIpIG91dCArPSBsaXN0KGMsIGZhbHNlLCAwKSArICJcbiI7CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiT0wiKSBvdXQgKz0gbGlzdChjLCB0cnVlLCAwKSArICJcbiI7CiAgICAgICAgZWxzZSBpZiAodGFnID09PSAiUFJFIikgewogICAgICAgICAgdmFyIGNvZGUgPSBmaW5kQ2hpbGRUYWcoYywgIkNPREUiKTsKICAgICAgICAgIHZhciBsYW5nID0gbGFuZ09mKGNvZGUgfHwgYyk7CiAgICAgICAgICB2YXIgYm9keSA9IChjb2RlIHx8IGMpLnRleHRDb250ZW50IHx8ICIiOwogICAgICAgICAgdmFyIGYgPSBmZW5jZShib2R5LCAzKTsKICAgICAgICAgIG91dCArPSBmICsgbGFuZyArICJcbiIgKyBib2R5LnJlcGxhY2UoL1xuJC8sICIiKSArICJcbiIgKyBmICsgIlxuXG4iOwogICAgICAgIH0gZWxzZSBpZiAodGFnID09PSAiQkxPQ0tRVU9URSIpIHsKICAgICAgICAgIHZhciBpbm5lciA9IGJsb2NrKGMpLnRyaW0oKS5zcGxpdCgiXG4iKS5tYXAoZnVuY3Rpb24gKGwpIHsgcmV0dXJuICI+ICIgKyBsOyB9KS5qb2luKCJcbiIpOwogICAgICAgICAgb3V0ICs9IGlubmVyICsgIlxuXG4iOwogICAgICAgIH0gZWxzZSBpZiAodGFnID09PSAiSFIiKSBvdXQgKz0gIi0tLVxuXG4iOwogICAgICAgIGVsc2UgaWYgKHRhZyA9PT0gIlRBQkxFIikgb3V0ICs9IHRhYmxlKGMpICsgIlxuIjsKICAgICAgICBlbHNlIGlmICh0YWcgPT09ICJCUiIpIG91dCArPSAiXG4iOwogICAgICAgIGVsc2UgaWYgKHRhZyA9PT0gIlNUUk9ORyIgfHwgdGFnID09PSAiQiIgfHwgdGFnID09PSAiRU0iIHx8IHRhZyA9PT0gIkkiIHx8CiAgICAgICAgICAgICAgICAgdGFnID09PSAiQSIgfHwgdGFnID09PSAiQ09ERSIgfHwgdGFnID09PSAiREVMIiB8fCB0YWcgPT09ICJTIikKICAgICAgICAgIG91dCArPSBpbmxpbmUoYykgKyAiXG5cbiI7CiAgICAgICAgZWxzZSBvdXQgKz0gYmxvY2soYyk7IC8vIHVua25vd24gd3JhcHBlcjogcmVjdXJzZSAoZHJvcCB0YWcsIGtlZXAgY29udGVudCkKICAgICAgfQogICAgICByZXR1cm4gb3V0OwogICAgfQogICAgLy8gYmxvY2soKSBkaXNwYXRjaGVzIG9uIGVhY2ggQ0hJTEQncyB0YWcsIHRyZWF0aW5nIHRoZSBwYXNzZWQgbm9kZSBhcyBhIHBsYWluCiAgICAvLyBjb250YWluZXIuIFdyYXAgcm9vdCBpbiBhIG9uZS1vZmYgY29udGFpbmVyIHNvIHJvb3QncyBPV04gdGFnIGlzIGRpc3BhdGNoZWQKICAgIC8vIHRvbzogY2FsbGVycyBwYXNzIGVpdGhlciB0aGUgYnViYmxlIGNvbnRhaW5lciAoaXRzIGJsb2NrIGNoaWxkcmVuIHJlbmRlcikgb3IKICAgIC8vIGEgc2luZ2xlIGJsb2NrIGVsZW1lbnQgbGlrZSA8cHJlPi88dWw+Lzx0YWJsZT4gKG5vdyBoYW5kbGVkLCBub3QgZmxhdHRlbmVkKS4KICAgIHJldHVybiBibG9jayh7IGNoaWxkTm9kZXM6IFtyb290XSB9KS5yZXBsYWNlKC9cbnszLH0vZywgIlxuXG4iKS50cmltKCk7CiAgfQoKICAvLyAtLS0tIHB1cmUgaGVscGVycyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgZnVuY3Rpb24gaGFzUHJlZml4KG5vZGUsIHByZWZpeCkgewogICAgaWYgKG5vZGUubm9kZVR5cGUgIT09IDEgfHwgdHlwZW9mIG5vZGUuY2xhc3NOYW1lICE9PSAic3RyaW5nIikgcmV0dXJuIGZhbHNlOwogICAgdmFyIHBhcnRzID0gbm9kZS5jbGFzc05hbWUuc3BsaXQoL1xzKy8pOwogICAgZm9yICh2YXIgaSA9IDA7IGkgPCBwYXJ0cy5sZW5ndGg7IGkrKykgaWYgKHBhcnRzW2ldLmluZGV4T2YocHJlZml4KSA9PT0gMCkgcmV0dXJuIHRydWU7CiAgICByZXR1cm4gZmFsc2U7CiAgfQoKICAvLyBDbGFzcy1wcmVmaXggaG9va3MgZm9yIG5vbi1jb250ZW50IGNocm9tZSB0aGF0IHJlbmRlcnMgKmluc2lkZSogYW4gYXNzaXN0YW50CiAgLy8gYnViYmxlICh2ZXJpZmllZCBvbiAyLjEuMTcwOyBUYXNrIDYgcmUtcGlucyB0aGVzZSkuIHRvb2wqL3RoaW5raW5nXyBhcmUgdGhlIHYxCiAgLy8gZXhjbHVzaW9uczsgdW5rbm93bkNvbnRlbnRfIGlzIHRoZSByZW5kZXJlcidzIGZhbGxiYWNrIGZvciB1bnJlY29nbml6ZWQgYmxvY2sKICAvLyB0eXBlcywgc28gc3RyaXBwaW5nIGl0IG1ha2VzIGEgKmZ1dHVyZSogYmxvY2sgdHlwZSBmYWlsIHNhZmUgdG8gZXhjbHVkZWQgcmF0aGVyCiAgLy8gdGhhbiBsZWFraW5nICJVbnN1cHBvcnRlZCBjb250ZW50IiBpbnRvIHRoZSBjb3B5LiBSZS1waW4gaWYgYSBwcmVmaXggbW92ZXMuCiAgdmFyIENIUk9NRV9QUkVGSVhFUyA9IFsidG9vbFVzZV8iLCAidG9vbFJlc3VsdF8iLCAidG9vbFJlZmVyZW5jZV8iLCAidGhpbmtpbmdfIiwgInVua25vd25Db250ZW50XyJdOwoKICAvLyBUcnVlIGZvciBhbnkgbm9kZSB0aGF0IG11c3QgbmV2ZXIgYXBwZWFyIGluIGNvcGllZCBvdXRwdXQ6IG91ciBvd24gY29udHJvbHMsCiAgLy8gdGhlIHJhdGluZyB3aWRnZXQgKGBkYXRhLW1lc3NhZ2UtcmF0aW5nYCArIGl0cyAiVGhhbmtzIGZvciB5b3VyIGZlZWRiYWNrIgogIC8vIHRleHQpLCBhbnkgYnV0dG9uIChjb3B5LWNvZGUgY2hyb21lKSwgYW5kIHRoZSBleGNsdWRlZCBjb250ZW50IGJsb2NrcyBhYm92ZS4KICBmdW5jdGlvbiBpc0Nocm9tZShub2RlKSB7CiAgICBpZiAobm9kZS5ub2RlVHlwZSAhPT0gMSkgcmV0dXJuIGZhbHNlOwogICAgaWYgKChub2RlLnRhZ05hbWUgfHwgIiIpLnRvVXBwZXJDYXNlKCkgPT09ICJCVVRUT04iKSByZXR1cm4gdHJ1ZTsKICAgIGlmIChub2RlLmdldEF0dHJpYnV0ZSAmJiBub2RlLmdldEF0dHJpYnV0ZSgiZGF0YS1tZXNzYWdlLXJhdGluZyIpICE9PSBudWxsKSByZXR1cm4gdHJ1ZTsKICAgIGlmIChoYXNQcmVmaXgobm9kZSwgQ09OVFJPTF9QUkVGSVgpKSByZXR1cm4gdHJ1ZTsKICAgIGZvciAodmFyIGkgPSAwOyBpIDwgQ0hST01FX1BSRUZJWEVTLmxlbmd0aDsgaSsrKSBpZiAoaGFzUHJlZml4KG5vZGUsIENIUk9NRV9QUkVGSVhFU1tpXSkpIHJldHVybiB0cnVlOwogICAgcmV0dXJuIGZhbHNlOwogIH0KCiAgLy8gRGVlcC1jbG9uZSBgY29udGVudE5vZGVgLCB0aGVuIHN0cmlwIGV2ZXJ5IGNocm9tZSBub2RlIHNvIGNvcGllZCBvdXRwdXQgaXMgdGhlCiAgLy8gbWVzc2FnZSdzIHRleHQgY29udGVudCBvbmx5LiBUaGlzIGlzIGEgQ09SUkVDVE5FU1MgR0FURSwgbm90IGNvc21ldGljOiB0aGUKICAvLyBkZWZhdWx0IGNvbnRlbnQgbm9kZSBpcyB0aGUgd2hvbGUgYnViYmxlIChhbGwgY29udGVudC1ibG9jayBzaWJsaW5ncywgc28gbXVsdGktCiAgLy8gYmxvY2sgYXNzaXN0YW50IHR1cm5zIGFyZSBjYXB0dXJlZCksIGFuZCB0aGlzIHN0cmlwLWxpc3QgaXMgdGhlIG9ubHkgdGhpbmcKICAvLyBrZWVwaW5nIHRoZSByYXRpbmcgd2lkZ2V0IGFuZCB2MS1leGNsdWRlZCBibG9ja3Mgb3V0IG9mIHRoZSBjb3B5LgogIGZ1bmN0aW9uIHNhbml0aXplQ2xvbmUoY29udGVudE5vZGUpIHsKICAgIHZhciBjbG9uZSA9IGNvbnRlbnROb2RlLmNsb25lTm9kZSh0cnVlKTsKICAgIChmdW5jdGlvbiBzdHJpcChub2RlKSB7CiAgICAgIHZhciBraWRzID0gKG5vZGUuY2hpbGROb2RlcyB8fCBbXSkuc2xpY2UoKTsKICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCBraWRzLmxlbmd0aDsgaSsrKSB7CiAgICAgICAgdmFyIGMgPSBraWRzW2ldOwogICAgICAgIGlmIChjLm5vZGVUeXBlID09PSAxICYmIGlzQ2hyb21lKGMpKSB7IG5vZGUucmVtb3ZlQ2hpbGQoYyk7IGNvbnRpbnVlOyB9CiAgICAgICAgaWYgKGMubm9kZVR5cGUgPT09IDEpIHN0cmlwKGMpOwogICAgICB9CiAgICB9KShjbG9uZSk7CiAgICByZXR1cm4gY2xvbmU7CiAgfQoKICBmdW5jdGlvbiBjbGFzc2lmeUJ1YmJsZShub2RlKSB7CiAgICBpZiAobm9kZS5ub2RlVHlwZSAhPT0gMSkgcmV0dXJuIG51bGw7CiAgICBpZiAoaGFzUHJlZml4KG5vZGUsICJ1c2VyTWVzc2FnZUNvbnRhaW5lcl8iKSkgcmV0dXJuICJ1c2VyIjsKICAgIGlmIChub2RlLmdldEF0dHJpYnV0ZSAmJiBub2RlLmdldEF0dHJpYnV0ZSgiZGF0YS10ZXN0aWQiKSA9PT0gImFzc2lzdGFudC1tZXNzYWdlIikgcmV0dXJuICJhc3Npc3RhbnQiOwogICAgcmV0dXJuIG51bGw7CiAgfQoKICAvLyBCdWlsZCB0aGUgd2hvbGUtY29udmVyc2F0aW9uIG1hcmtkb3duIGZyb20gYW4gb3JkZXJlZCBsaXN0IG9mIGJ1YmJsZXMuCiAgLy8gYGNvbnRlbnRPZihidWJibGUpYCByZXNvbHZlcyB0aGUgY29udGVudCBub2RlIChkZWZhdWx0OiB0aGUgYnViYmxlIGl0c2VsZiwgc28KICAvLyBldmVyeSBjb250ZW50IGJsb2NrIGlzIGluY2x1ZGVkOyBzYW5pdGl6ZUNsb25lIGRyb3BzIGNocm9tZSk7IGEgZGVmYXVsdCBpcwogIC8vIHByb3ZpZGVkIGZvciB0ZXN0cy4KICBmdW5jdGlvbiBjb252ZXJzYXRpb25Ub01hcmtkb3duKGJ1YmJsZXMsIGNvbnRlbnRPZikgewogICAgY29udGVudE9mID0gY29udGVudE9mIHx8IGZ1bmN0aW9uIChiKSB7IHJldHVybiBiOyB9OwogICAgdmFyIHBhcnRzID0gW107CiAgICBmb3IgKHZhciBpID0gMDsgaSA8IGJ1YmJsZXMubGVuZ3RoOyBpKyspIHsKICAgICAgdmFyIHJvbGUgPSBjbGFzc2lmeUJ1YmJsZShidWJibGVzW2ldKTsKICAgICAgaWYgKCFyb2xlKSBjb250aW51ZTsKICAgICAgdmFyIGNsZWFuID0gc2FuaXRpemVDbG9uZShjb250ZW50T2YoYnViYmxlc1tpXSkpOwogICAgICB2YXIgYm9keSA9IHJvbGUgPT09ICJhc3Npc3RhbnQiID8gaHRtbFRvTWFya2Rvd24oY2xlYW4pIDogKGNsZWFuLnRleHRDb250ZW50IHx8ICIiKS50cmltKCk7CiAgICAgIGlmICghYm9keSkgY29udGludWU7CiAgICAgIHBhcnRzLnB1c2goKHJvbGUgPT09ICJ1c2VyIiA/ICIjIyBVc2VyIiA6ICIjIyBBc3Npc3RhbnQiKSArICJcblxuIiArIGJvZHkpOwogICAgfQogICAgcmV0dXJuIHBhcnRzLmpvaW4oIlxuXG4iKSArIChwYXJ0cy5sZW5ndGggPyAiXG4iIDogIiIpOwogIH0KCiAgLy8gLS0tLSBleHBvcnRzIChub2RlIHRlc3RzKSAvIGJvb3QgKHJlYWwgd2VidmlldykgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogIGlmICh0eXBlb2YgZG9jdW1lbnQgIT09ICJ1bmRlZmluZWQiKSB7CiAgICBib290KCk7CiAgfSBlbHNlIGlmICh0eXBlb2YgbW9kdWxlICE9PSAidW5kZWZpbmVkIiAmJiBtb2R1bGUuZXhwb3J0cykgewogICAgbW9kdWxlLmV4cG9ydHMgPSB7IGh0bWxUb01hcmtkb3duOiBodG1sVG9NYXJrZG93biwgc2FuaXRpemVDbG9uZTogc2FuaXRpemVDbG9uZSwKICAgICAgICAgICAgICAgICAgICAgICBjbGFzc2lmeUJ1YmJsZTogY2xhc3NpZnlCdWJibGUsIGNvbnZlcnNhdGlvblRvTWFya2Rvd246IGNvbnZlcnNhdGlvblRvTWFya2Rvd24gfTsKICB9CgogIC8vIC0tLS0gbGl2ZS13ZWJ2aWV3IHdpcmluZyAocnVucyBvbmx5IHdoZW4gYSBkb2N1bWVudCBleGlzdHMpIC0tLS0tLS0tLS0tLS0tLS0KICBmdW5jdGlvbiBxcyhub2RlLCBzZWwpIHsgdHJ5IHsgcmV0dXJuIHNlbCAmJiBub2RlLnF1ZXJ5U2VsZWN0b3IgPyBub2RlLnF1ZXJ5U2VsZWN0b3Ioc2VsKSA6IG51bGw7IH0gY2F0Y2ggKF8pIHsgcmV0dXJuIG51bGw7IH0gfQogIGZ1bmN0aW9uIHFzYShzZWwpIHsgdHJ5IHsgcmV0dXJuIEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoc2VsKSk7IH0gY2F0Y2ggKF8pIHsgcmV0dXJuIFtdOyB9IH0KCiAgLy8gVGhlIGNvbnRlbnQgbm9kZSB0byBjb252ZXJ0L2NvcHk6IHRoZSBvcHRpb25hbCBBU1NJU1RBTlRfQ09OVEVOVCB3cmFwcGVyIGlmCiAgLy8gcGlubmVkIGFuZCBwcmVzZW50LCBlbHNlIHRoZSBidWJibGUgaXRzZWxmLiBUaGUgYnViYmxlIGFscmVhZHkgY29udGFpbnMgZXZlcnkKICAvLyBjb250ZW50LWJsb2NrIHNpYmxpbmcgb2YgYSBtdWx0aS1ibG9jayB0dXJuLCBhbmQgc2FuaXRpemVDbG9uZSBzdHJpcHMgdGhlCiAgLy8gY2hyb21lIChyYXRpbmcgd2lkZ2V0LCB0b29sL3RoaW5raW5nL3Vua25vd24gYmxvY2tzLCBidXR0b25zLCBvdXIgY29udHJvbHMpCiAgLy8gZWl0aGVyIHdheSAtLSBzbyB0aGlzIGlzIGEgbmFycm93aW5nLCBuZXZlciB0aGUgdGhpbmcgdGhhdCBndWFyYW50ZWVzCiAgLy8gY29ycmVjdG5lc3MuCiAgZnVuY3Rpb24gY29udGVudE5vZGVPZihidWJibGUsIHJvbGUpIHsKICAgIGlmIChyb2xlID09PSAiYXNzaXN0YW50IiAmJiBBU1NJU1RBTlRfQ09OVEVOVCkgewogICAgICB2YXIgbiA9IHFzKGJ1YmJsZSwgQVNTSVNUQU5UX0NPTlRFTlQpOwogICAgICBpZiAobikgcmV0dXJuIG47CiAgICB9CiAgICByZXR1cm4gYnViYmxlOwogIH0KCiAgZnVuY3Rpb24gY29weVRleHQodGV4dCkgewogICAgdHJ5IHsKICAgICAgaWYgKG5hdmlnYXRvci5jbGlwYm9hcmQgJiYgbmF2aWdhdG9yLmNsaXBib2FyZC53cml0ZVRleHQpIHJldHVybiBuYXZpZ2F0b3IuY2xpcGJvYXJkLndyaXRlVGV4dCh0ZXh0KTsKICAgIH0gY2F0Y2ggKF8pIHt9CiAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKCk7IC8vIGJlc3QtZWZmb3J0OyBuZXZlciB0aHJvdyBpbnRvIHRoZSBhcHAKICB9CgogIGZ1bmN0aW9uIGZsYXNoRmVlZGJhY2soaG9zdCkgewogICAgdHJ5IHsKICAgICAgdmFyIGZiID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3BhbiIpOwogICAgICBmYi5jbGFzc05hbWUgPSBDT05UUk9MX1BSRUZJWCArICItZmVlZGJhY2siOwogICAgICBmYi50ZXh0Q29udGVudCA9ICJDb3BpZWQiOwogICAgICBob3N0LmFwcGVuZENoaWxkKGZiKTsKICAgICAgc2V0VGltZW91dChmdW5jdGlvbiAoKSB7IGlmIChmYiAmJiBmYi5wYXJlbnROb2RlKSBmYi5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGZiKTsgfSwgRkVFREJBQ0tfTVMpOwogICAgfSBjYXRjaCAoXykge30KICB9CgogIGZ1bmN0aW9uIGJ1YmJsZU1hcmtkb3duKGJ1YmJsZSwgcm9sZSkgewogICAgdmFyIGNsZWFuID0gc2FuaXRpemVDbG9uZShjb250ZW50Tm9kZU9mKGJ1YmJsZSwgcm9sZSkpOwogICAgcmV0dXJuIHJvbGUgPT09ICJhc3Npc3RhbnQiID8gaHRtbFRvTWFya2Rvd24oY2xlYW4pIDogKGNsZWFuLnRleHRDb250ZW50IHx8ICIiKS50cmltKCk7CiAgfQogIGZ1bmN0aW9uIGJ1YmJsZVBsYWluKGJ1YmJsZSwgcm9sZSkgewogICAgcmV0dXJuIChzYW5pdGl6ZUNsb25lKGNvbnRlbnROb2RlT2YoYnViYmxlLCByb2xlKSkudGV4dENvbnRlbnQgfHwgIiIpLnRyaW0oKTsKICB9CgogIC8vIEJ1aWxkIGEgc2luZ2xlIGNvbnRyb2w6IGEgcHJpbWFyeSAiQ29weSIgKG1hcmtkb3duKSBwbHVzIGEgc21hbGwgY2FyZXQgdGhhdAogIC8vIHRvZ2dsZXMgYSBtZW51IHdpdGggIkNvcHkgYXMgcGxhaW4gdGV4dCIuIEFsbCBub2RlcyBjYXJyeSB0aGUgQ09OVFJPTF9QUkVGSVgKICAvLyBjbGFzcyBzbyBzYW5pdGl6ZUNsb25lIHJlbW92ZXMgdGhlbSBmcm9tIGFueSBjb3BpZWQgY29udGVudC4KICBmdW5jdGlvbiBidWlsZENvbnRyb2wob25NYXJrZG93biwgb25QbGFpbikgewogICAgdmFyIHdyYXAgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzcGFuIik7CiAgICB3cmFwLmNsYXNzTmFtZSA9IENPTlRST0xfUFJFRklYOwogICAgdmFyIHByaW1hcnkgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJidXR0b24iKTsKICAgIHByaW1hcnkudHlwZSA9ICJidXR0b24iOwogICAgcHJpbWFyeS5jbGFzc05hbWUgPSBDT05UUk9MX1BSRUZJWCArICItYnRuIjsKICAgIHByaW1hcnkudGl0bGUgPSAiQ29weSBhcyBNYXJrZG93biI7CiAgICBwcmltYXJ5LnRleHRDb250ZW50ID0gIkNvcHkiOwogICAgcHJpbWFyeS5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsIGZ1bmN0aW9uIChlKSB7IGUuc3RvcFByb3BhZ2F0aW9uKCk7IG9uTWFya2Rvd24ocHJpbWFyeSk7IH0pOwogICAgdmFyIGNhcmV0ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiYnV0dG9uIik7CiAgICBjYXJldC50eXBlID0gImJ1dHRvbiI7CiAgICBjYXJldC5jbGFzc05hbWUgPSBDT05UUk9MX1BSRUZJWCArICItY2FyZXQiOwogICAgY2FyZXQudGl0bGUgPSAiQ29weSBvcHRpb25zIjsKICAgIGNhcmV0LnRleHRDb250ZW50ID0gIuKWviI7IC8vIGJsYWNrIGRvd24tcG9pbnRpbmcgc21hbGwgdHJpYW5nbGUKICAgIHZhciBtZW51ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3BhbiIpOwogICAgbWVudS5jbGFzc05hbWUgPSBDT05UUk9MX1BSRUZJWCArICItbWVudSI7CiAgICBtZW51LnN0eWxlLmRpc3BsYXkgPSAibm9uZSI7CiAgICB2YXIgcGxhaW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJidXR0b24iKTsKICAgIHBsYWluLnR5cGUgPSAiYnV0dG9uIjsKICAgIHBsYWluLmNsYXNzTmFtZSA9IENPTlRST0xfUFJFRklYICsgIi1idG4iOwogICAgcGxhaW4udGV4dENvbnRlbnQgPSAiQ29weSBhcyBwbGFpbiB0ZXh0IjsKICAgIHBsYWluLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgZnVuY3Rpb24gKGUpIHsgZS5zdG9wUHJvcGFnYXRpb24oKTsgbWVudS5zdHlsZS5kaXNwbGF5ID0gIm5vbmUiOyBvblBsYWluKHBsYWluKTsgfSk7CiAgICBtZW51LmFwcGVuZENoaWxkKHBsYWluKTsKICAgIGNhcmV0LmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgZnVuY3Rpb24gKGUpIHsKICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTsKICAgICAgbWVudS5zdHlsZS5kaXNwbGF5ID0gbWVudS5zdHlsZS5kaXNwbGF5ID09PSAibm9uZSIgPyAiaW5saW5lLWJsb2NrIiA6ICJub25lIjsKICAgIH0pOwogICAgd3JhcC5hcHBlbmRDaGlsZChwcmltYXJ5KTsKICAgIHdyYXAuYXBwZW5kQ2hpbGQoY2FyZXQpOwogICAgd3JhcC5hcHBlbmRDaGlsZChtZW51KTsKICAgIHJldHVybiB3cmFwOwogIH0KCiAgZnVuY3Rpb24gZGVjb3JhdGUoYnViYmxlKSB7CiAgICB0cnkgewogICAgICB2YXIgcm9sZSA9IGNsYXNzaWZ5QnViYmxlKGJ1YmJsZSk7CiAgICAgIGlmICghcm9sZSkgcmV0dXJuOwogICAgICBpZiAocXMoYnViYmxlLCAiLiIgKyBDT05UUk9MX1BSRUZJWCkpIHJldHVybjsgLy8gYWxyZWFkeSBkZWNvcmF0ZWQKICAgICAgdmFyIGNvbnRyb2wgPSBidWlsZENvbnRyb2woCiAgICAgICAgZnVuY3Rpb24gKGhvc3QpIHsgY29weVRleHQoYnViYmxlTWFya2Rvd24oYnViYmxlLCByb2xlKSkudGhlbihmdW5jdGlvbiAoKSB7IGZsYXNoRmVlZGJhY2soY29udHJvbCk7IH0pOyB9LAogICAgICAgIGZ1bmN0aW9uIChob3N0KSB7IGNvcHlUZXh0KGJ1YmJsZVBsYWluKGJ1YmJsZSwgcm9sZSkpLnRoZW4oZnVuY3Rpb24gKCkgeyBmbGFzaEZlZWRiYWNrKGNvbnRyb2wpOyB9KTsgfQogICAgICApOwogICAgICBidWJibGUuYXBwZW5kQ2hpbGQoY29udHJvbCk7CiAgICB9IGNhdGNoIChfKSB7fQogIH0KCiAgZnVuY3Rpb24gY29weUNvbnZlcnNhdGlvbihmb3JtYXQpIHsKICAgIHZhciBidWJibGVzID0gcXNhKFVTRVJfQlVCQkxFICsgIiwiICsgQVNTSVNUQU5UX0JVQkJMRSk7CiAgICBpZiAoZm9ybWF0ID09PSAidGV4dCIpIHsKICAgICAgdmFyIGxpbmVzID0gW107CiAgICAgIGZvciAodmFyIGkgPSAwOyBpIDwgYnViYmxlcy5sZW5ndGg7IGkrKykgewogICAgICAgIHZhciByb2xlID0gY2xhc3NpZnlCdWJibGUoYnViYmxlc1tpXSk7CiAgICAgICAgaWYgKCFyb2xlKSBjb250aW51ZTsKICAgICAgICB2YXIgYm9keSA9IGJ1YmJsZVBsYWluKGJ1YmJsZXNbaV0sIHJvbGUpOwogICAgICAgIGlmIChib2R5KSBsaW5lcy5wdXNoKGJvZHkpOwogICAgICB9CiAgICAgIHJldHVybiBjb3B5VGV4dChsaW5lcy5qb2luKCJcblxuIikgKyAobGluZXMubGVuZ3RoID8gIlxuIiA6ICIiKSk7CiAgICB9CiAgICByZXR1cm4gY29weVRleHQoY29udmVyc2F0aW9uVG9NYXJrZG93bihidWJibGVzLCBmdW5jdGlvbiAoYikgewogICAgICByZXR1cm4gY29udGVudE5vZGVPZihiLCBjbGFzc2lmeUJ1YmJsZShiKSk7CiAgICB9KSk7CiAgfQoKICBmdW5jdGlvbiBpbnN0YWxsQ29udmVyc2F0aW9uQ29udHJvbCgpIHsKICAgIHRyeSB7CiAgICAgIGlmIChxcyhkb2N1bWVudCwgIi4iICsgQ09OVFJPTF9QUkVGSVggKyAiLWNvbnZlcnNhdGlvbiIpKSByZXR1cm47CiAgICAgIHZhciBiYXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKTsKICAgICAgYmFyLmNsYXNzTmFtZSA9IENPTlRST0xfUFJFRklYICsgIi1jb252ZXJzYXRpb24iOwogICAgICB2YXIgY29udHJvbCA9IGJ1aWxkQ29udHJvbCgKICAgICAgICBmdW5jdGlvbiAoKSB7IGNvcHlDb252ZXJzYXRpb24oIm1hcmtkb3duIikudGhlbihmdW5jdGlvbiAoKSB7IGZsYXNoRmVlZGJhY2soYmFyKTsgfSk7IH0sCiAgICAgICAgZnVuY3Rpb24gKCkgeyBjb3B5Q29udmVyc2F0aW9uKCJ0ZXh0IikudGhlbihmdW5jdGlvbiAoKSB7IGZsYXNoRmVlZGJhY2soYmFyKTsgfSk7IH0KICAgICAgKTsKICAgICAgY29udHJvbC50aXRsZSA9ICJDb3B5IGVudGlyZSBjb252ZXJzYXRpb24iOwogICAgICBiYXIuYXBwZW5kQ2hpbGQoY29udHJvbCk7CiAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQoYmFyKTsgLy8gZml4ZWQtcG9zaXRpb24gdmlhIENTUzsgcGxhY2VtZW50IHJlZmluZWQgaW4gVGFzayA2CiAgICB9IGNhdGNoIChfKSB7fQogIH0KCiAgZnVuY3Rpb24gc3dlZXAoKSB7IHZhciBiID0gcXNhKFVTRVJfQlVCQkxFICsgIiwiICsgQVNTSVNUQU5UX0JVQkJMRSk7IGZvciAodmFyIGkgPSAwOyBpIDwgYi5sZW5ndGg7IGkrKykgZGVjb3JhdGUoYltpXSk7IH0KCiAgZnVuY3Rpb24gYm9vdCgpIHsKICAgIHRyeSB7CiAgICAgIHZhciB0YXJnZXQgPSAoTUVTU0FHRVNfQ09OVEFJTkVSICYmIHFzKGRvY3VtZW50LCBNRVNTQUdFU19DT05UQUlORVIpKSB8fCBkb2N1bWVudC5ib2R5OwogICAgICBzd2VlcCgpOwogICAgICBpbnN0YWxsQ29udmVyc2F0aW9uQ29udHJvbCgpOwogICAgICBpZiAodHlwZW9mIE11dGF0aW9uT2JzZXJ2ZXIgPT09ICJ1bmRlZmluZWQiKSByZXR1cm47CiAgICAgIHZhciBvYnMgPSBuZXcgTXV0YXRpb25PYnNlcnZlcihmdW5jdGlvbiAoKSB7IHN3ZWVwKCk7IH0pOwogICAgICBvYnMub2JzZXJ2ZSh0YXJnZXQsIHsgY2hpbGRMaXN0OiB0cnVlLCBzdWJ0cmVlOiB0cnVlIH0pOwogICAgfSBjYXRjaCAoXykge30KICB9Cn0pKCk7Cg==").decode("utf-8") INJECT_CSS = base64.b64decode("LmNjLW1kLWNvcHkgewogIGRpc3BsYXk6IGlubGluZS1mbGV4OwogIGFsaWduLWl0ZW1zOiBjZW50ZXI7CiAgZ2FwOiAycHg7CiAgdmVydGljYWwtYWxpZ246IG1pZGRsZTsKICBtYXJnaW4tbGVmdDogNnB4Owp9Ci5jYy1tZC1jb3B5LWJ0biwKLmNjLW1kLWNvcHktY2FyZXQgewogIGZvbnQ6IGluaGVyaXQ7CiAgZm9udC1zaXplOiAxMXB4OwogIGxpbmUtaGVpZ2h0OiAxLjQ7CiAgcGFkZGluZzogMXB4IDZweDsKICBjb2xvcjogdmFyKC0tdnNjb2RlLWZvcmVncm91bmQpOwogIGJhY2tncm91bmQ6IHRyYW5zcGFyZW50OwogIGJvcmRlcjogMXB4IHNvbGlkIHZhcigtLXZzY29kZS13aWRnZXQtYm9yZGVyLCB0cmFuc3BhcmVudCk7CiAgYm9yZGVyLXJhZGl1czogNHB4OwogIGN1cnNvcjogcG9pbnRlcjsKICBvcGFjaXR5OiAwLjY1Owp9Ci5jYy1tZC1jb3B5LWJ0bjpob3ZlciwKLmNjLW1kLWNvcHktY2FyZXQ6aG92ZXIgewogIG9wYWNpdHk6IDE7CiAgYmFja2dyb3VuZDogdmFyKC0tdnNjb2RlLXRvb2xiYXItaG92ZXJCYWNrZ3JvdW5kLCByZ2JhKDEyOCwgMTI4LCAxMjgsIDAuMTUpKTsKfQouY2MtbWQtY29weS1tZW51IHsKICBwb3NpdGlvbjogcmVsYXRpdmU7CiAgbWFyZ2luLWxlZnQ6IDRweDsKICBwYWRkaW5nOiAycHg7CiAgYmFja2dyb3VuZDogdmFyKC0tdnNjb2RlLW1lbnUtYmFja2dyb3VuZCwgdmFyKC0tdnNjb2RlLWVkaXRvcldpZGdldC1iYWNrZ3JvdW5kKSk7CiAgYm9yZGVyOiAxcHggc29saWQgdmFyKC0tdnNjb2RlLW1lbnUtYm9yZGVyLCB2YXIoLS12c2NvZGUtd2lkZ2V0LWJvcmRlciwgdHJhbnNwYXJlbnQpKTsKICBib3JkZXItcmFkaXVzOiA0cHg7CiAgei1pbmRleDogNTsKfQouY2MtbWQtY29weS1mZWVkYmFjayB7CiAgbWFyZ2luLWxlZnQ6IDZweDsKICBmb250LXNpemU6IDExcHg7CiAgb3BhY2l0eTogMC44NTsKICBjb2xvcjogdmFyKC0tdnNjb2RlLWZvcmVncm91bmQpOwp9Ci5jYy1tZC1jb3B5LWNvbnZlcnNhdGlvbiB7CiAgcG9zaXRpb246IGZpeGVkOwogIHJpZ2h0OiAxNnB4OwogIGJvdHRvbTogNTZweDsKICB6LWluZGV4OiAxMDsKICBwYWRkaW5nOiAycHg7CiAgYmFja2dyb3VuZDogdmFyKC0tdnNjb2RlLWVkaXRvcldpZGdldC1iYWNrZ3JvdW5kKTsKICBib3JkZXI6IDFweCBzb2xpZCB2YXIoLS12c2NvZGUtd2lkZ2V0LWJvcmRlciwgdHJhbnNwYXJlbnQpOwogIGJvcmRlci1yYWRpdXM6IDZweDsKICBvcGFjaXR5OiAwLjg1Owp9Ci5jYy1tZC1jb3B5LWNvbnZlcnNhdGlvbjpob3ZlciB7CiAgb3BhY2l0eTogMTsKfQo=").decode("utf-8") # <<>>CCWA-MD-COPY-EMBED>>> (generated by tools/gen-embeds; do not edit) -const MD_COPY_JS = "/* cc-md-copy: per-message and whole-conversation copy (markdown/plain) for the\n * Claude Code VS Code webview. Self-contained IIFE appended to webview/index.js.\n * Additive and read-only w.r.t. app state; keyed on stable CSS-module class\n * prefixes, so it fails safe (controls simply do not appear) if a prefix moves.\n * Exposes its pure functions for node unit tests; boot()s only in a real webview. */\n(function () {\n \"use strict\";\n\n var CONTROL_PREFIX = \"cc-md-copy\"; // every injected node's class starts with this\n var USER_BUBBLE = '[class*=\"userMessageContainer_\"]';\n // Assistant message wrapper. Verified on 2.1.170: the render emits exactly one\n // `data-testid=\"assistant-message\"` div per assistant turn, with the rating\n // widget and content blocks as its children. (The earlier `[data-message-rating]`\n // was WRONG: that attribute sits on the nested rating control, which is also only\n // rendered behind an experiment+analytics gate.) Re-pinned in Task 6.\n var ASSISTANT_BUBBLE = '[data-testid=\"assistant-message\"]';\n var MESSAGES_CONTAINER = '[class*=\"messagesContainer_\"]'; // e.g. '[class*=\"timeline_\"]'; \"\" -> observe document.body\n // Optional narrowing only. MUST be a single wrapper around ALL content blocks,\n // not a per-block class (a turn has multiple blocks). \"\" -> use the bubble itself\n // (already aggregates all blocks; sanitizeClone is the correctness gate).\n var ASSISTANT_CONTENT = \"\";\n var FEEDBACK_MS = 1800;\n\n // ---- HTML -> Markdown (DOM walk) -------------------------------------------\n // Uses only: nodeType, tagName, childNodes, textContent, getAttribute, className.\n function htmlToMarkdown(root) {\n // Longest run of consecutive backticks in s, so a code delimiter/fence can be\n // chosen longer than anything inside it (else ``` in the content closes early).\n function backtickRun(s) {\n var max = 0, cur = 0;\n for (var i = 0; i < s.length; i++) {\n if (s.charAt(i) === \"`\") { cur++; if (cur > max) max = cur; } else cur = 0;\n }\n return max;\n }\n function fence(s, min) { var n = backtickRun(s) + 1; if (n < min) n = min; return new Array(n + 1).join(\"`\"); }\n function inline(node) {\n var out = \"\";\n var kids = node.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n var c = kids[i];\n if (c.nodeType === 3) { out += c.textContent || \"\"; continue; }\n if (c.nodeType !== 1) continue;\n var tag = (c.tagName || \"\").toUpperCase();\n if (tag === \"BR\") out += \"\\n\";\n else if (tag === \"STRONG\" || tag === \"B\") out += \"**\" + inline(c) + \"**\";\n else if (tag === \"EM\" || tag === \"I\") out += \"*\" + inline(c) + \"*\";\n else if (tag === \"DEL\" || tag === \"S\") out += \"~~\" + inline(c) + \"~~\";\n else if (tag === \"CODE\") {\n var ct = c.textContent || \"\";\n var d = fence(ct, 1);\n // CommonMark strips one leading+trailing space, so pad when an edge is a\n // backtick to keep it from merging with the delimiter.\n var p = (ct.charAt(0) === \"`\" || ct.charAt(ct.length - 1) === \"`\") ? \" \" : \"\";\n out += d + p + ct + p + d;\n }\n else if (tag === \"A\") {\n var href = c.getAttribute ? c.getAttribute(\"href\") : null;\n var t = inline(c);\n out += href ? \"[\" + t + \"](\" + href + \")\" : t;\n } else out += inline(c); // unknown inline wrapper: keep text, drop tag\n }\n return out;\n }\n function langOf(codeEl) {\n var cls = \"\";\n if (codeEl) cls = (codeEl.getAttribute && codeEl.getAttribute(\"class\")) || codeEl.className || \"\";\n var m = /language-([A-Za-z0-9+#.\\-]+)/.exec(cls || \"\");\n return m ? m[1] : \"\";\n }\n function findChildTag(node, tag) {\n var kids = node.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n if (kids[i].nodeType === 1 && (kids[i].tagName || \"\").toUpperCase() === tag) return kids[i];\n }\n return null;\n }\n function list(node, ordered, depth) {\n var out = \"\", n = 1;\n var kids = node.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n var li = kids[i];\n if (li.nodeType !== 1 || (li.tagName || \"\").toUpperCase() !== \"LI\") continue;\n var marker = ordered ? n++ + \". \" : \"- \";\n var indent = new Array(depth + 1).join(\" \");\n var lead = \"\", nested = \"\";\n var lk = li.childNodes || [];\n for (var j = 0; j < lk.length; j++) {\n var ch = lk[j];\n var ct = ch.nodeType === 1 ? (ch.tagName || \"\").toUpperCase() : \"\";\n if (ct === \"UL\") nested += list(ch, false, depth + 1);\n else if (ct === \"OL\") nested += list(ch, true, depth + 1);\n else if (ch.nodeType === 3) lead += ch.textContent || \"\";\n else lead += inline(ch);\n }\n out += indent + marker + lead.trim() + \"\\n\" + nested;\n }\n return out;\n }\n function table(node) {\n var rows = [];\n (function collect(container) {\n var kids = container.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n var c = kids[i];\n if (c.nodeType !== 1) continue;\n var t = (c.tagName || \"\").toUpperCase();\n if (t === \"THEAD\" || t === \"TBODY\" || t === \"TFOOT\") collect(c);\n else if (t === \"TR\") {\n var cells = [], cc = c.childNodes || [];\n for (var j = 0; j < cc.length; j++) {\n var d = cc[j];\n if (d.nodeType !== 1) continue;\n var dt = (d.tagName || \"\").toUpperCase();\n if (dt === \"TH\" || dt === \"TD\") cells.push(inline(d).trim());\n }\n rows.push(cells);\n }\n }\n })(node);\n if (!rows.length) return \"\";\n var head = rows[0], body = rows.slice(1);\n var sep = head.map(function () { return \"---\"; });\n var out = \"| \" + head.join(\" | \") + \" |\\n| \" + sep.join(\" | \") + \" |\\n\";\n for (var k = 0; k < body.length; k++) out += \"| \" + body[k].join(\" | \") + \" |\\n\";\n return out;\n }\n function block(node) {\n var out = \"\";\n var kids = node.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n var c = kids[i];\n if (c.nodeType === 3) { if ((c.textContent || \"\").trim()) out += c.textContent; continue; }\n if (c.nodeType !== 1) continue;\n var tag = (c.tagName || \"\").toUpperCase();\n if (/^H[1-6]$/.test(tag)) out += new Array(+tag[1] + 1).join(\"#\") + \" \" + inline(c).trim() + \"\\n\\n\";\n else if (tag === \"P\") out += inline(c).trim() + \"\\n\\n\";\n else if (tag === \"UL\") out += list(c, false, 0) + \"\\n\";\n else if (tag === \"OL\") out += list(c, true, 0) + \"\\n\";\n else if (tag === \"PRE\") {\n var code = findChildTag(c, \"CODE\");\n var lang = langOf(code || c);\n var body = (code || c).textContent || \"\";\n var f = fence(body, 3);\n out += f + lang + \"\\n\" + body.replace(/\\n$/, \"\") + \"\\n\" + f + \"\\n\\n\";\n } else if (tag === \"BLOCKQUOTE\") {\n var inner = block(c).trim().split(\"\\n\").map(function (l) { return \"> \" + l; }).join(\"\\n\");\n out += inner + \"\\n\\n\";\n } else if (tag === \"HR\") out += \"---\\n\\n\";\n else if (tag === \"TABLE\") out += table(c) + \"\\n\";\n else if (tag === \"BR\") out += \"\\n\";\n else if (tag === \"STRONG\" || tag === \"B\" || tag === \"EM\" || tag === \"I\" ||\n tag === \"A\" || tag === \"CODE\" || tag === \"DEL\" || tag === \"S\")\n out += inline(c) + \"\\n\\n\";\n else out += block(c); // unknown wrapper: recurse (drop tag, keep content)\n }\n return out;\n }\n // block() dispatches on each CHILD's tag, treating the passed node as a plain\n // container. Wrap root in a one-off container so root's OWN tag is dispatched\n // too: callers pass either the bubble container (its block children render) or\n // a single block element like
/
    / (now handled, not flattened).\n return block({ childNodes: [root] }).replace(/\\n{3,}/g, \"\\n\\n\").trim();\n }\n\n // ---- pure helpers ----------------------------------------------------------\n function hasPrefix(node, prefix) {\n if (node.nodeType !== 1 || typeof node.className !== \"string\") return false;\n var parts = node.className.split(/\\s+/);\n for (var i = 0; i < parts.length; i++) if (parts[i].indexOf(prefix) === 0) return true;\n return false;\n }\n\n // Class-prefix hooks for non-content chrome that renders *inside* an assistant\n // bubble (verified on 2.1.170; Task 6 re-pins these). tool*/thinking_ are the v1\n // exclusions; unknownContent_ is the renderer's fallback for unrecognized block\n // types, so stripping it makes a *future* block type fail safe to excluded rather\n // than leaking \"Unsupported content\" into the copy. Re-pin if a prefix moves.\n var CHROME_PREFIXES = [\"toolUse_\", \"toolResult_\", \"toolReference_\", \"thinking_\", \"unknownContent_\"];\n\n // True for any node that must never appear in copied output: our own controls,\n // the rating widget (`data-message-rating` + its \"Thanks for your feedback\"\n // text), any button (copy-code chrome), and the excluded content blocks above.\n function isChrome(node) {\n if (node.nodeType !== 1) return false;\n if ((node.tagName || \"\").toUpperCase() === \"BUTTON\") return true;\n if (node.getAttribute && node.getAttribute(\"data-message-rating\") !== null) return true;\n if (hasPrefix(node, CONTROL_PREFIX)) return true;\n for (var i = 0; i < CHROME_PREFIXES.length; i++) if (hasPrefix(node, CHROME_PREFIXES[i])) return true;\n return false;\n }\n\n // Deep-clone `contentNode`, then strip every chrome node so copied output is the\n // message's text content only. This is a CORRECTNESS GATE, not cosmetic: the\n // default content node is the whole bubble (all content-block siblings, so multi-\n // block assistant turns are captured), and this strip-list is the only thing\n // keeping the rating widget and v1-excluded blocks out of the copy.\n function sanitizeClone(contentNode) {\n var clone = contentNode.cloneNode(true);\n (function strip(node) {\n var kids = (node.childNodes || []).slice();\n for (var i = 0; i < kids.length; i++) {\n var c = kids[i];\n if (c.nodeType === 1 && isChrome(c)) { node.removeChild(c); continue; }\n if (c.nodeType === 1) strip(c);\n }\n })(clone);\n return clone;\n }\n\n function classifyBubble(node) {\n if (node.nodeType !== 1) return null;\n if (hasPrefix(node, \"userMessageContainer_\")) return \"user\";\n if (node.getAttribute && node.getAttribute(\"data-testid\") === \"assistant-message\") return \"assistant\";\n return null;\n }\n\n // Build the whole-conversation markdown from an ordered list of bubbles.\n // `contentOf(bubble)` resolves the content node (default: the bubble itself, so\n // every content block is included; sanitizeClone drops chrome); a default is\n // provided for tests.\n function conversationToMarkdown(bubbles, contentOf) {\n contentOf = contentOf || function (b) { return b; };\n var parts = [];\n for (var i = 0; i < bubbles.length; i++) {\n var role = classifyBubble(bubbles[i]);\n if (!role) continue;\n var clean = sanitizeClone(contentOf(bubbles[i]));\n var body = role === \"assistant\" ? htmlToMarkdown(clean) : (clean.textContent || \"\").trim();\n if (!body) continue;\n parts.push((role === \"user\" ? \"## User\" : \"## Assistant\") + \"\\n\\n\" + body);\n }\n return parts.join(\"\\n\\n\") + (parts.length ? \"\\n\" : \"\");\n }\n\n // ---- exports (node tests) / boot (real webview) ----------------------------\n if (typeof document !== \"undefined\") {\n boot();\n } else if (typeof module !== \"undefined\" && module.exports) {\n module.exports = { htmlToMarkdown: htmlToMarkdown, sanitizeClone: sanitizeClone,\n classifyBubble: classifyBubble, conversationToMarkdown: conversationToMarkdown };\n }\n\n // ---- live-webview wiring (runs only when a document exists) ----------------\n function qs(node, sel) { try { return sel && node.querySelector ? node.querySelector(sel) : null; } catch (_) { return null; } }\n function qsa(sel) { try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch (_) { return []; } }\n\n // The content node to convert/copy: the optional ASSISTANT_CONTENT wrapper if\n // pinned and present, else the bubble itself. The bubble already contains every\n // content-block sibling of a multi-block turn, and sanitizeClone strips the\n // chrome (rating widget, tool/thinking/unknown blocks, buttons, our controls)\n // either way -- so this is a narrowing, never the thing that guarantees\n // correctness.\n function contentNodeOf(bubble, role) {\n if (role === \"assistant\" && ASSISTANT_CONTENT) {\n var n = qs(bubble, ASSISTANT_CONTENT);\n if (n) return n;\n }\n return bubble;\n }\n\n function copyText(text) {\n try {\n if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(text);\n } catch (_) {}\n return Promise.resolve(); // best-effort; never throw into the app\n }\n\n function flashFeedback(host) {\n try {\n var fb = document.createElement(\"span\");\n fb.className = CONTROL_PREFIX + \"-feedback\";\n fb.textContent = \"Copied\";\n host.appendChild(fb);\n setTimeout(function () { if (fb && fb.parentNode) fb.parentNode.removeChild(fb); }, FEEDBACK_MS);\n } catch (_) {}\n }\n\n function bubbleMarkdown(bubble, role) {\n var clean = sanitizeClone(contentNodeOf(bubble, role));\n return role === \"assistant\" ? htmlToMarkdown(clean) : (clean.textContent || \"\").trim();\n }\n function bubblePlain(bubble, role) {\n return (sanitizeClone(contentNodeOf(bubble, role)).textContent || \"\").trim();\n }\n\n // Build a single control: a primary \"Copy\" (markdown) plus a small caret that\n // toggles a menu with \"Copy as plain text\". All nodes carry the CONTROL_PREFIX\n // class so sanitizeClone removes them from any copied content.\n function buildControl(onMarkdown, onPlain) {\n var wrap = document.createElement(\"span\");\n wrap.className = CONTROL_PREFIX;\n var primary = document.createElement(\"button\");\n primary.type = \"button\";\n primary.className = CONTROL_PREFIX + \"-btn\";\n primary.title = \"Copy as Markdown\";\n primary.textContent = \"Copy\";\n primary.addEventListener(\"click\", function (e) { e.stopPropagation(); onMarkdown(primary); });\n var caret = document.createElement(\"button\");\n caret.type = \"button\";\n caret.className = CONTROL_PREFIX + \"-caret\";\n caret.title = \"Copy options\";\n caret.textContent = \"\u25be\"; // black down-pointing small triangle\n var menu = document.createElement(\"span\");\n menu.className = CONTROL_PREFIX + \"-menu\";\n menu.style.display = \"none\";\n var plain = document.createElement(\"button\");\n plain.type = \"button\";\n plain.className = CONTROL_PREFIX + \"-btn\";\n plain.textContent = \"Copy as plain text\";\n plain.addEventListener(\"click\", function (e) { e.stopPropagation(); menu.style.display = \"none\"; onPlain(plain); });\n menu.appendChild(plain);\n caret.addEventListener(\"click\", function (e) {\n e.stopPropagation();\n menu.style.display = menu.style.display === \"none\" ? \"inline-block\" : \"none\";\n });\n wrap.appendChild(primary);\n wrap.appendChild(caret);\n wrap.appendChild(menu);\n return wrap;\n }\n\n function decorate(bubble) {\n try {\n var role = classifyBubble(bubble);\n if (!role) return;\n if (qs(bubble, \".\" + CONTROL_PREFIX)) return; // already decorated\n var control = buildControl(\n function (host) { copyText(bubbleMarkdown(bubble, role)).then(function () { flashFeedback(control); }); },\n function (host) { copyText(bubblePlain(bubble, role)).then(function () { flashFeedback(control); }); }\n );\n bubble.appendChild(control);\n } catch (_) {}\n }\n\n function copyConversation(format) {\n var bubbles = qsa(USER_BUBBLE + \",\" + ASSISTANT_BUBBLE);\n if (format === \"text\") {\n var lines = [];\n for (var i = 0; i < bubbles.length; i++) {\n var role = classifyBubble(bubbles[i]);\n if (!role) continue;\n var body = bubblePlain(bubbles[i], role);\n if (body) lines.push(body);\n }\n return copyText(lines.join(\"\\n\\n\") + (lines.length ? \"\\n\" : \"\"));\n }\n return copyText(conversationToMarkdown(bubbles, function (b) {\n return contentNodeOf(b, classifyBubble(b));\n }));\n }\n\n function installConversationControl() {\n try {\n if (qs(document, \".\" + CONTROL_PREFIX + \"-conversation\")) return;\n var bar = document.createElement(\"div\");\n bar.className = CONTROL_PREFIX + \"-conversation\";\n var control = buildControl(\n function () { copyConversation(\"markdown\").then(function () { flashFeedback(bar); }); },\n function () { copyConversation(\"text\").then(function () { flashFeedback(bar); }); }\n );\n control.title = \"Copy entire conversation\";\n bar.appendChild(control);\n document.body.appendChild(bar); // fixed-position via CSS; placement refined in Task 6\n } catch (_) {}\n }\n\n function sweep() { var b = qsa(USER_BUBBLE + \",\" + ASSISTANT_BUBBLE); for (var i = 0; i < b.length; i++) decorate(b[i]); }\n\n function boot() {\n try {\n var target = (MESSAGES_CONTAINER && qs(document, MESSAGES_CONTAINER)) || document.body;\n sweep();\n installConversationControl();\n if (typeof MutationObserver === \"undefined\") return;\n var obs = new MutationObserver(function () { sweep(); });\n obs.observe(target, { childList: true, subtree: true });\n } catch (_) {}\n }\n})();\n"; +const MD_COPY_JS = "/* cc-md-copy: per-message and whole-conversation copy (markdown/plain) for the\n * Claude Code VS Code webview. Self-contained IIFE appended to webview/index.js.\n * Additive and read-only w.r.t. app state; keyed on stable CSS-module class\n * prefixes, so it fails safe (controls simply do not appear) if a prefix moves.\n * Exposes its pure functions for node unit tests; boot()s only in a real webview. */\n/* Leading ';' so that, appended after the bundle, this IIFE can never be parsed as\n * a call on the bundle's final expression if it lacks a trailing semicolon (ASI\n * safety across extension builds). */\n;(function () {\n \"use strict\";\n\n var CONTROL_PREFIX = \"cc-md-copy\"; // every injected node's class starts with this\n var USER_BUBBLE = '[class*=\"userMessageContainer_\"]';\n // Assistant message wrapper. Verified on 2.1.170: the render emits exactly one\n // `data-testid=\"assistant-message\"` div per assistant turn, with the rating\n // widget and content blocks as its children. (The earlier `[data-message-rating]`\n // was WRONG: that attribute sits on the nested rating control, which is also only\n // rendered behind an experiment+analytics gate.) Re-pinned in Task 6.\n var ASSISTANT_BUBBLE = '[data-testid=\"assistant-message\"]';\n var MESSAGES_CONTAINER = '[class*=\"messagesContainer_\"]'; // e.g. '[class*=\"timeline_\"]'; \"\" -> observe document.body\n // Optional narrowing only. MUST be a single wrapper around ALL content blocks,\n // not a per-block class (a turn has multiple blocks). \"\" -> use the bubble itself\n // (already aggregates all blocks; sanitizeClone is the correctness gate).\n var ASSISTANT_CONTENT = \"\";\n var FEEDBACK_MS = 1800;\n\n // ---- HTML -> Markdown (DOM walk) -------------------------------------------\n // Uses only: nodeType, tagName, childNodes, textContent, getAttribute, className.\n function htmlToMarkdown(root) {\n // Longest run of consecutive backticks in s, so a code delimiter/fence can be\n // chosen longer than anything inside it (else ``` in the content closes early).\n function backtickRun(s) {\n var max = 0, cur = 0;\n for (var i = 0; i < s.length; i++) {\n if (s.charAt(i) === \"`\") { cur++; if (cur > max) max = cur; } else cur = 0;\n }\n return max;\n }\n function fence(s, min) { var n = backtickRun(s) + 1; if (n < min) n = min; return new Array(n + 1).join(\"`\"); }\n function inline(node) {\n var out = \"\";\n var kids = node.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n var c = kids[i];\n if (c.nodeType === 3) { out += c.textContent || \"\"; continue; }\n if (c.nodeType !== 1) continue;\n var tag = (c.tagName || \"\").toUpperCase();\n if (tag === \"BR\") out += \"\\n\";\n else if (tag === \"STRONG\" || tag === \"B\") out += \"**\" + inline(c) + \"**\";\n else if (tag === \"EM\" || tag === \"I\") out += \"*\" + inline(c) + \"*\";\n else if (tag === \"DEL\" || tag === \"S\") out += \"~~\" + inline(c) + \"~~\";\n else if (tag === \"CODE\") {\n var ct = c.textContent || \"\";\n var d = fence(ct, 1);\n // CommonMark strips one leading+trailing space, so pad when an edge is a\n // backtick to keep it from merging with the delimiter.\n var p = (ct.charAt(0) === \"`\" || ct.charAt(ct.length - 1) === \"`\") ? \" \" : \"\";\n out += d + p + ct + p + d;\n }\n else if (tag === \"A\") {\n var href = c.getAttribute ? c.getAttribute(\"href\") : null;\n var t = inline(c);\n out += href ? \"[\" + t + \"](\" + href + \")\" : t;\n } else out += inline(c); // unknown inline wrapper: keep text, drop tag\n }\n return out;\n }\n function langOf(codeEl) {\n var cls = \"\";\n if (codeEl) cls = (codeEl.getAttribute && codeEl.getAttribute(\"class\")) || codeEl.className || \"\";\n var m = /language-([A-Za-z0-9+#.\\-]+)/.exec(cls || \"\");\n return m ? m[1] : \"\";\n }\n function findChildTag(node, tag) {\n var kids = node.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n if (kids[i].nodeType === 1 && (kids[i].tagName || \"\").toUpperCase() === tag) return kids[i];\n }\n return null;\n }\n function list(node, ordered, depth) {\n var out = \"\", n = 1;\n var kids = node.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n var li = kids[i];\n if (li.nodeType !== 1 || (li.tagName || \"\").toUpperCase() !== \"LI\") continue;\n var marker = ordered ? n++ + \". \" : \"- \";\n var indent = new Array(depth + 1).join(\" \");\n var lead = \"\", nested = \"\";\n var lk = li.childNodes || [];\n for (var j = 0; j < lk.length; j++) {\n var ch = lk[j];\n var ct = ch.nodeType === 1 ? (ch.tagName || \"\").toUpperCase() : \"\";\n if (ct === \"UL\") nested += list(ch, false, depth + 1);\n else if (ct === \"OL\") nested += list(ch, true, depth + 1);\n else if (ch.nodeType === 3) lead += ch.textContent || \"\";\n else lead += inline(ch);\n }\n out += indent + marker + lead.trim() + \"\\n\" + nested;\n }\n return out;\n }\n function table(node) {\n var rows = [];\n (function collect(container) {\n var kids = container.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n var c = kids[i];\n if (c.nodeType !== 1) continue;\n var t = (c.tagName || \"\").toUpperCase();\n if (t === \"THEAD\" || t === \"TBODY\" || t === \"TFOOT\") collect(c);\n else if (t === \"TR\") {\n var cells = [], cc = c.childNodes || [];\n for (var j = 0; j < cc.length; j++) {\n var d = cc[j];\n if (d.nodeType !== 1) continue;\n var dt = (d.tagName || \"\").toUpperCase();\n if (dt === \"TH\" || dt === \"TD\") cells.push(inline(d).trim());\n }\n rows.push(cells);\n }\n }\n })(node);\n if (!rows.length) return \"\";\n var head = rows[0], body = rows.slice(1);\n var sep = head.map(function () { return \"---\"; });\n var out = \"| \" + head.join(\" | \") + \" |\\n| \" + sep.join(\" | \") + \" |\\n\";\n for (var k = 0; k < body.length; k++) out += \"| \" + body[k].join(\" | \") + \" |\\n\";\n return out;\n }\n function block(node) {\n var out = \"\";\n var kids = node.childNodes || [];\n for (var i = 0; i < kids.length; i++) {\n var c = kids[i];\n if (c.nodeType === 3) { if ((c.textContent || \"\").trim()) out += c.textContent; continue; }\n if (c.nodeType !== 1) continue;\n var tag = (c.tagName || \"\").toUpperCase();\n if (/^H[1-6]$/.test(tag)) out += new Array(+tag[1] + 1).join(\"#\") + \" \" + inline(c).trim() + \"\\n\\n\";\n else if (tag === \"P\") out += inline(c).trim() + \"\\n\\n\";\n else if (tag === \"UL\") out += list(c, false, 0) + \"\\n\";\n else if (tag === \"OL\") out += list(c, true, 0) + \"\\n\";\n else if (tag === \"PRE\") {\n var code = findChildTag(c, \"CODE\");\n var lang = langOf(code || c);\n var body = (code || c).textContent || \"\";\n var f = fence(body, 3);\n out += f + lang + \"\\n\" + body.replace(/\\n$/, \"\") + \"\\n\" + f + \"\\n\\n\";\n } else if (tag === \"BLOCKQUOTE\") {\n var inner = block(c).trim().split(\"\\n\").map(function (l) { return \"> \" + l; }).join(\"\\n\");\n out += inner + \"\\n\\n\";\n } else if (tag === \"HR\") out += \"---\\n\\n\";\n else if (tag === \"TABLE\") out += table(c) + \"\\n\";\n else if (tag === \"BR\") out += \"\\n\";\n else if (tag === \"STRONG\" || tag === \"B\" || tag === \"EM\" || tag === \"I\" ||\n tag === \"A\" || tag === \"CODE\" || tag === \"DEL\" || tag === \"S\")\n out += inline(c) + \"\\n\\n\";\n else out += block(c); // unknown wrapper: recurse (drop tag, keep content)\n }\n return out;\n }\n // block() dispatches on each CHILD's tag, treating the passed node as a plain\n // container. Wrap root in a one-off container so root's OWN tag is dispatched\n // too: callers pass either the bubble container (its block children render) or\n // a single block element like
    /
      /
    (now handled, not flattened).\n return block({ childNodes: [root] }).replace(/\\n{3,}/g, \"\\n\\n\").trim();\n }\n\n // ---- pure helpers ----------------------------------------------------------\n function hasPrefix(node, prefix) {\n if (node.nodeType !== 1 || typeof node.className !== \"string\") return false;\n var parts = node.className.split(/\\s+/);\n for (var i = 0; i < parts.length; i++) if (parts[i].indexOf(prefix) === 0) return true;\n return false;\n }\n\n // Class-prefix hooks for non-content chrome that renders *inside* an assistant\n // bubble (verified on 2.1.170; Task 6 re-pins these). tool*/thinking_ are the v1\n // exclusions; unknownContent_ is the renderer's fallback for unrecognized block\n // types, so stripping it makes a *future* block type fail safe to excluded rather\n // than leaking \"Unsupported content\" into the copy. Re-pin if a prefix moves.\n var CHROME_PREFIXES = [\"toolUse_\", \"toolResult_\", \"toolReference_\", \"thinking_\", \"unknownContent_\"];\n\n // True for any node that must never appear in copied output: our own controls,\n // the rating widget (`data-message-rating` + its \"Thanks for your feedback\"\n // text), any button (copy-code chrome), and the excluded content blocks above.\n function isChrome(node) {\n if (node.nodeType !== 1) return false;\n if ((node.tagName || \"\").toUpperCase() === \"BUTTON\") return true;\n if (node.getAttribute && node.getAttribute(\"data-message-rating\") !== null) return true;\n if (hasPrefix(node, CONTROL_PREFIX)) return true;\n for (var i = 0; i < CHROME_PREFIXES.length; i++) if (hasPrefix(node, CHROME_PREFIXES[i])) return true;\n return false;\n }\n\n // Deep-clone `contentNode`, then strip every chrome node so copied output is the\n // message's text content only. This is a CORRECTNESS GATE, not cosmetic: the\n // default content node is the whole bubble (all content-block siblings, so multi-\n // block assistant turns are captured), and this strip-list is the only thing\n // keeping the rating widget and v1-excluded blocks out of the copy.\n function sanitizeClone(contentNode) {\n var clone = contentNode.cloneNode(true);\n (function strip(node) {\n var kids = (node.childNodes || []).slice();\n for (var i = 0; i < kids.length; i++) {\n var c = kids[i];\n if (c.nodeType === 1 && isChrome(c)) { node.removeChild(c); continue; }\n if (c.nodeType === 1) strip(c);\n }\n })(clone);\n return clone;\n }\n\n function classifyBubble(node) {\n if (node.nodeType !== 1) return null;\n if (hasPrefix(node, \"userMessageContainer_\")) return \"user\";\n if (node.getAttribute && node.getAttribute(\"data-testid\") === \"assistant-message\") return \"assistant\";\n return null;\n }\n\n // Build the whole-conversation markdown from an ordered list of bubbles.\n // `contentOf(bubble)` resolves the content node (default: the bubble itself, so\n // every content block is included; sanitizeClone drops chrome); a default is\n // provided for tests.\n function conversationToMarkdown(bubbles, contentOf) {\n contentOf = contentOf || function (b) { return b; };\n var parts = [];\n for (var i = 0; i < bubbles.length; i++) {\n var role = classifyBubble(bubbles[i]);\n if (!role) continue;\n var clean = sanitizeClone(contentOf(bubbles[i]));\n var body = role === \"assistant\" ? htmlToMarkdown(clean) : (clean.textContent || \"\").trim();\n if (!body) continue;\n parts.push((role === \"user\" ? \"## User\" : \"## Assistant\") + \"\\n\\n\" + body);\n }\n return parts.join(\"\\n\\n\") + (parts.length ? \"\\n\" : \"\");\n }\n\n // ---- exports (node tests) / boot (real webview) ----------------------------\n if (typeof document !== \"undefined\") {\n boot();\n } else if (typeof module !== \"undefined\" && module.exports) {\n module.exports = { htmlToMarkdown: htmlToMarkdown, sanitizeClone: sanitizeClone,\n classifyBubble: classifyBubble, conversationToMarkdown: conversationToMarkdown };\n }\n\n // ---- live-webview wiring (runs only when a document exists) ----------------\n function qs(node, sel) { try { return sel && node.querySelector ? node.querySelector(sel) : null; } catch (_) { return null; } }\n function qsa(sel) { try { return Array.prototype.slice.call(document.querySelectorAll(sel)); } catch (_) { return []; } }\n\n // The content node to convert/copy: the optional ASSISTANT_CONTENT wrapper if\n // pinned and present, else the bubble itself. The bubble already contains every\n // content-block sibling of a multi-block turn, and sanitizeClone strips the\n // chrome (rating widget, tool/thinking/unknown blocks, buttons, our controls)\n // either way -- so this is a narrowing, never the thing that guarantees\n // correctness.\n function contentNodeOf(bubble, role) {\n if (role === \"assistant\" && ASSISTANT_CONTENT) {\n var n = qs(bubble, ASSISTANT_CONTENT);\n if (n) return n;\n }\n return bubble;\n }\n\n function copyText(text) {\n try {\n if (navigator.clipboard && navigator.clipboard.writeText) return navigator.clipboard.writeText(text);\n } catch (_) {}\n return Promise.resolve(); // best-effort; never throw into the app\n }\n\n function flashFeedback(host) {\n try {\n var fb = document.createElement(\"span\");\n fb.className = CONTROL_PREFIX + \"-feedback\";\n fb.textContent = \"Copied\";\n host.appendChild(fb);\n setTimeout(function () { if (fb && fb.parentNode) fb.parentNode.removeChild(fb); }, FEEDBACK_MS);\n } catch (_) {}\n }\n\n function bubbleMarkdown(bubble, role) {\n var clean = sanitizeClone(contentNodeOf(bubble, role));\n return role === \"assistant\" ? htmlToMarkdown(clean) : (clean.textContent || \"\").trim();\n }\n function bubblePlain(bubble, role) {\n return (sanitizeClone(contentNodeOf(bubble, role)).textContent || \"\").trim();\n }\n\n // Build a single control: a primary \"Copy\" (markdown) plus a small caret that\n // toggles a menu with \"Copy as plain text\". All nodes carry the CONTROL_PREFIX\n // class so sanitizeClone removes them from any copied content.\n function buildControl(onMarkdown, onPlain) {\n var wrap = document.createElement(\"span\");\n wrap.className = CONTROL_PREFIX;\n var primary = document.createElement(\"button\");\n primary.type = \"button\";\n primary.className = CONTROL_PREFIX + \"-btn\";\n primary.title = \"Copy as Markdown\";\n primary.textContent = \"Copy\";\n primary.addEventListener(\"click\", function (e) { e.stopPropagation(); onMarkdown(primary); });\n var caret = document.createElement(\"button\");\n caret.type = \"button\";\n caret.className = CONTROL_PREFIX + \"-caret\";\n caret.title = \"Copy options\";\n caret.textContent = \"\u25be\"; // black down-pointing small triangle\n var menu = document.createElement(\"span\");\n menu.className = CONTROL_PREFIX + \"-menu\";\n menu.style.display = \"none\";\n var plain = document.createElement(\"button\");\n plain.type = \"button\";\n plain.className = CONTROL_PREFIX + \"-btn\";\n plain.textContent = \"Copy as plain text\";\n plain.addEventListener(\"click\", function (e) { e.stopPropagation(); menu.style.display = \"none\"; onPlain(plain); });\n menu.appendChild(plain);\n caret.addEventListener(\"click\", function (e) {\n e.stopPropagation();\n menu.style.display = menu.style.display === \"none\" ? \"inline-block\" : \"none\";\n });\n wrap.appendChild(primary);\n wrap.appendChild(caret);\n wrap.appendChild(menu);\n return wrap;\n }\n\n function decorate(bubble) {\n try {\n var role = classifyBubble(bubble);\n if (!role) return;\n if (qs(bubble, \".\" + CONTROL_PREFIX)) return; // already decorated\n var control = buildControl(\n function (host) { copyText(bubbleMarkdown(bubble, role)).then(function () { flashFeedback(control); }); },\n function (host) { copyText(bubblePlain(bubble, role)).then(function () { flashFeedback(control); }); }\n );\n bubble.appendChild(control);\n } catch (_) {}\n }\n\n function copyConversation(format) {\n var bubbles = qsa(USER_BUBBLE + \",\" + ASSISTANT_BUBBLE);\n if (format === \"text\") {\n var lines = [];\n for (var i = 0; i < bubbles.length; i++) {\n var role = classifyBubble(bubbles[i]);\n if (!role) continue;\n var body = bubblePlain(bubbles[i], role);\n if (body) lines.push(body);\n }\n return copyText(lines.join(\"\\n\\n\") + (lines.length ? \"\\n\" : \"\"));\n }\n return copyText(conversationToMarkdown(bubbles, function (b) {\n return contentNodeOf(b, classifyBubble(b));\n }));\n }\n\n function installConversationControl() {\n try {\n if (qs(document, \".\" + CONTROL_PREFIX + \"-conversation\")) return;\n var bar = document.createElement(\"div\");\n bar.className = CONTROL_PREFIX + \"-conversation\";\n var control = buildControl(\n function () { copyConversation(\"markdown\").then(function () { flashFeedback(bar); }); },\n function () { copyConversation(\"text\").then(function () { flashFeedback(bar); }); }\n );\n control.title = \"Copy entire conversation\";\n bar.appendChild(control);\n document.body.appendChild(bar); // fixed-position via CSS; placement refined in Task 6\n } catch (_) {}\n }\n\n function sweep() { var b = qsa(USER_BUBBLE + \",\" + ASSISTANT_BUBBLE); for (var i = 0; i < b.length; i++) decorate(b[i]); }\n\n function boot() {\n try {\n var target = (MESSAGES_CONTAINER && qs(document, MESSAGES_CONTAINER)) || document.body;\n sweep();\n installConversationControl();\n if (typeof MutationObserver === \"undefined\") return;\n var obs = new MutationObserver(function () { sweep(); });\n obs.observe(target, { childList: true, subtree: true });\n } catch (_) {}\n }\n})();\n"; const MD_COPY_CSS = ".cc-md-copy {\n display: inline-flex;\n align-items: center;\n gap: 2px;\n vertical-align: middle;\n margin-left: 6px;\n}\n.cc-md-copy-btn,\n.cc-md-copy-caret {\n font: inherit;\n font-size: 11px;\n line-height: 1.4;\n padding: 1px 6px;\n color: var(--vscode-foreground);\n background: transparent;\n border: 1px solid var(--vscode-widget-border, transparent);\n border-radius: 4px;\n cursor: pointer;\n opacity: 0.65;\n}\n.cc-md-copy-btn:hover,\n.cc-md-copy-caret:hover {\n opacity: 1;\n background: var(--vscode-toolbar-hoverBackground, rgba(128, 128, 128, 0.15));\n}\n.cc-md-copy-menu {\n position: relative;\n margin-left: 4px;\n padding: 2px;\n background: var(--vscode-menu-background, var(--vscode-editorWidget-background));\n border: 1px solid var(--vscode-menu-border, var(--vscode-widget-border, transparent));\n border-radius: 4px;\n z-index: 5;\n}\n.cc-md-copy-feedback {\n margin-left: 6px;\n font-size: 11px;\n opacity: 0.85;\n color: var(--vscode-foreground);\n}\n.cc-md-copy-conversation {\n position: fixed;\n right: 16px;\n bottom: 56px;\n z-index: 10;\n padding: 2px;\n background: var(--vscode-editorWidget-background);\n border: 1px solid var(--vscode-widget-border, transparent);\n border-radius: 6px;\n opacity: 0.85;\n}\n.cc-md-copy-conversation:hover {\n opacity: 1;\n}\n"; // <<