mirror of
https://github.com/its-a-feature/Mythic
synced 2026-06-08 14:55:38 +00:00
adding xterm for interactive tasking responses
This commit is contained in:
Generated
+55
-2
@@ -23,6 +23,8 @@
|
||||
"@mui/x-charts": "^8.1.0",
|
||||
"@mui/x-data-grid": "^8.1.0",
|
||||
"@mui/x-date-pickers": "^8.1.0",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"@xyflow/react": "^12.6.0",
|
||||
"ace-builds": "^1.40.1",
|
||||
"anser": "^2.3.2",
|
||||
@@ -168,6 +170,7 @@
|
||||
"integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
@@ -855,6 +858,7 @@
|
||||
"integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
},
|
||||
@@ -1776,6 +1780,7 @@
|
||||
"integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.25.9",
|
||||
"@babel/helper-module-imports": "^7.25.9",
|
||||
@@ -2646,6 +2651,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@@ -2689,6 +2695,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz",
|
||||
"integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@@ -2885,6 +2892,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
|
||||
"integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.7.2"
|
||||
},
|
||||
@@ -3582,6 +3590,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-7.0.2.tgz",
|
||||
"integrity": "sha512-rjJlJ13+3LdLfobRplkXbjIFEIkn6LgpetgU/Cs3Xd8qINCCQK9qXQIjjQ6P0FXFTPFzEVMj0VgBR1mN+FhOcA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.0",
|
||||
"@mui/core-downloads-tracker": "^7.0.2",
|
||||
@@ -3692,6 +3701,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-7.0.2.tgz",
|
||||
"integrity": "sha512-yFUraAWYWuKIISPPEVPSQ1NLeqmTT4qiQ+ktmyS8LO/KwHxB+NNVOacEZaIofh5x1NxY8rzphvU5X2heRZ/RDA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.27.0",
|
||||
"@mui/private-theming": "^7.0.2",
|
||||
@@ -5032,6 +5042,7 @@
|
||||
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
@@ -5087,6 +5098,7 @@
|
||||
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
@@ -5467,6 +5479,21 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@xterm/addon-fit": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz",
|
||||
"integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@xterm/xterm": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz",
|
||||
"integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==",
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
"addons/*"
|
||||
]
|
||||
},
|
||||
"node_modules/@xtuc/ieee754": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||
@@ -5552,6 +5579,7 @@
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -5658,6 +5686,7 @@
|
||||
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -6814,6 +6843,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -8238,6 +8268,7 @@
|
||||
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
|
||||
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -8402,7 +8433,8 @@
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
@@ -9266,6 +9298,7 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.6.1",
|
||||
@@ -10974,6 +11007,7 @@
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz",
|
||||
"integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||
}
|
||||
@@ -10998,6 +11032,7 @@
|
||||
"resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-6.0.4.tgz",
|
||||
"integrity": "sha512-8b4OZtNOvv8+NZva8HXamrc0y1jluYC0+13gdh7198FKjVzXyTvVc95DCwGzaKEfn3YuWZxUqjJlHe3qKM/F2g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
@@ -11590,6 +11625,7 @@
|
||||
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/immer"
|
||||
@@ -12422,6 +12458,7 @@
|
||||
"integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/core": "^27.5.1",
|
||||
"import-local": "^3.0.2",
|
||||
@@ -14138,6 +14175,7 @@
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@@ -15008,6 +15046,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.8",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -16269,6 +16308,7 @@
|
||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
@@ -16484,6 +16524,7 @@
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -16714,6 +16755,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
@@ -16915,6 +16957,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
@@ -17005,6 +17048,7 @@
|
||||
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -17053,6 +17097,7 @@
|
||||
"integrity": "sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.16.0",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
|
||||
@@ -17304,7 +17349,8 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/reflect.getprototypeof": {
|
||||
"version": "1.0.10",
|
||||
@@ -17780,6 +17826,7 @@
|
||||
"integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
@@ -18043,6 +18090,7 @@
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -19885,6 +19933,7 @@
|
||||
"integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
|
||||
"dev": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -20374,6 +20423,7 @@
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz",
|
||||
"integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
@@ -20447,6 +20497,7 @@
|
||||
"integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/bonjour": "^3.5.9",
|
||||
"@types/connect-history-api-fallback": "^1.3.5",
|
||||
@@ -20858,6 +20909,7 @@
|
||||
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -21177,6 +21229,7 @@
|
||||
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
"@mui/x-charts": "^8.1.0",
|
||||
"@mui/x-data-grid": "^8.1.0",
|
||||
"@mui/x-date-pickers": "^8.1.0",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"@xyflow/react": "^12.6.0",
|
||||
"ace-builds": "^1.40.1",
|
||||
"anser": "^2.3.2",
|
||||
|
||||
@@ -14,6 +14,9 @@ import Anser from "anser";
|
||||
import PaletteIcon from '@mui/icons-material/Palette';
|
||||
import {MythicStyledTooltip} from "../../MythicComponents/MythicStyledTooltip";
|
||||
import './ResponseDisplayInteractiveANSI.css';
|
||||
import {Terminal} from '@xterm/xterm';
|
||||
import {FitAddon} from '@xterm/addon-fit';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
import KeyboardReturnIcon from '@mui/icons-material/KeyboardReturn';
|
||||
import InputLabel from "@mui/material/InputLabel";
|
||||
import FormControl from "@mui/material/FormControl";
|
||||
@@ -224,6 +227,225 @@ export const GetOutputFormatAll = ({data, useASNIColor, messagesEndRef, showTask
|
||||
|
||||
}
|
||||
|
||||
const INTERACTIVE_TERMINAL_SCROLLBACK = 5000;
|
||||
const INTERACTIVE_TERMINAL_MAX_UNWRAPPED_COLS = 300;
|
||||
const ANSI_STYLE_CODE_REGEX = /\x1b\[[0-?]*[ -/]*m/g;
|
||||
const TERMINAL_CONTROL_CODE_REGEX = /\x1b\][^\x07]*(?:\x07|\x1b\\)|\x1b\[[0-?]*[ -/]*[@-~]|\x1b[ -/]*[@-~]/g;
|
||||
const stripAnsiStyleCodes = (value) => {
|
||||
return value.replace(ANSI_STYLE_CODE_REGEX, "");
|
||||
}
|
||||
const getTerminalText = (value) => {
|
||||
return value === undefined || value === null ? "" : String(value);
|
||||
}
|
||||
const getInteractiveTerminalTheme = (theme) => {
|
||||
return {
|
||||
background: theme.outputBackgroundColor,
|
||||
foreground: theme.outputTextColor,
|
||||
cursor: theme.palette.info.main,
|
||||
selectionBackground: theme.palette.info.main + "55",
|
||||
}
|
||||
}
|
||||
const getInteractiveTerminalEntrySignature = (entry) => {
|
||||
if(entry.response !== undefined){
|
||||
return `response:${entry.id}:${entry.timestamp}:${entry.is_error}:${entry.response.length}`;
|
||||
}
|
||||
return `task:${entry.id}:${entry.status}:${entry.original_params}:${entry.display_params}`;
|
||||
}
|
||||
const getInteractiveTerminalMeasurementText = (value) => {
|
||||
return getTerminalText(value).replace(TERMINAL_CONTROL_CODE_REGEX, "");
|
||||
}
|
||||
const getInteractiveTerminalEntryMeasurementText = (entry) => {
|
||||
if(entry.response !== undefined){
|
||||
return getInteractiveTerminalMeasurementText(entry.response);
|
||||
}
|
||||
return getInteractiveTerminalMeasurementText(entry.original_params || entry.display_params);
|
||||
}
|
||||
const getLongestInteractiveTerminalLineLength = (entries) => {
|
||||
return entries.reduce( (longestLineLength, entry) => {
|
||||
return getInteractiveTerminalEntryMeasurementText(entry).split(/\r\n|\n|\r/).reduce( (longestEntryLineLength, line) => {
|
||||
return Math.max(longestEntryLineLength, Array.from(line.replaceAll("\t", " ")).length);
|
||||
}, longestLineLength);
|
||||
}, 0);
|
||||
}
|
||||
const getInteractiveTerminalTaskStatus = ({task, useASNIColor}) => {
|
||||
if(task.status === "completed" || task.status === "success"){
|
||||
return useASNIColor ? "\x1b[32m[completed]\x1b[0m " : "[completed] ";
|
||||
}
|
||||
if(task.status === "submitted"){
|
||||
return useASNIColor ? "\x1b[33m[submitted]\x1b[0m " : "[submitted] ";
|
||||
}
|
||||
if(task.status === "error"){
|
||||
return useASNIColor ? "\x1b[31m[error]\x1b[0m " : "[error] ";
|
||||
}
|
||||
return task.status ? `[${task.status}] ` : "";
|
||||
}
|
||||
const formatInteractiveTerminalEntry = ({entry, useASNIColor, showTaskStatus}) => {
|
||||
if(entry.response !== undefined){
|
||||
const response = useASNIColor ? getTerminalText(entry.response) : stripAnsiStyleCodes(getTerminalText(entry.response));
|
||||
if(entry.is_error && useASNIColor){
|
||||
return `\x1b[37;41m${response}\x1b[0m`;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
const status = showTaskStatus ? getInteractiveTerminalTaskStatus({task: entry, useASNIColor}) : "";
|
||||
const prompt = useASNIColor ? "\x1b[36m> \x1b[0m" : "> ";
|
||||
return `\r\n${status}${prompt}${getTerminalText(entry.original_params || entry.display_params)}\r\n`;
|
||||
}
|
||||
const InteractiveTerminalDisplay = ({data, useASNIColor, showTaskStatus, wrapText, autoScroll, theme}) => {
|
||||
const terminalScrollContainerRef = React.useRef(null);
|
||||
const terminalElementRef = React.useRef(null);
|
||||
const terminalRef = React.useRef(null);
|
||||
const fitAddonRef = React.useRef(null);
|
||||
const resizeObserverRef = React.useRef(null);
|
||||
const replayStateRef = React.useRef({settingsKey: "", signatures: []});
|
||||
const autoScrollRef = React.useRef(autoScroll);
|
||||
const wrapTextRef = React.useRef(wrapText);
|
||||
const unwrappedColumnCountRef = React.useRef(0);
|
||||
const [terminalReady, setTerminalReady] = React.useState(false);
|
||||
const longestLineLength = React.useMemo( () => {
|
||||
return getLongestInteractiveTerminalLineLength(data);
|
||||
}, [data]);
|
||||
const fitTerminal = React.useCallback( () => {
|
||||
const terminal = terminalRef.current;
|
||||
const fitAddon = fitAddonRef.current;
|
||||
const terminalElement = terminalElementRef.current;
|
||||
const scrollContainer = terminalScrollContainerRef.current;
|
||||
if(!terminal || !fitAddon || !terminalElement || !scrollContainer){
|
||||
return;
|
||||
}
|
||||
try{
|
||||
terminalElement.style.width = "100%";
|
||||
const proposedDimensions = fitAddon.proposeDimensions();
|
||||
if(!proposedDimensions){
|
||||
return;
|
||||
}
|
||||
const rows = Math.max(1, proposedDimensions.rows);
|
||||
if(wrapTextRef.current){
|
||||
fitAddon.fit();
|
||||
scrollContainer.scrollLeft = 0;
|
||||
} else {
|
||||
const visibleCols = Math.max(1, proposedDimensions.cols);
|
||||
const cols = Math.max(visibleCols, unwrappedColumnCountRef.current);
|
||||
terminalElement.style.width = `${Math.ceil((cols / visibleCols) * scrollContainer.clientWidth)}px`;
|
||||
terminal.resize(cols, rows);
|
||||
}
|
||||
}catch(error){
|
||||
console.error(error);
|
||||
}
|
||||
}, []);
|
||||
React.useEffect( () => {
|
||||
autoScrollRef.current = autoScroll;
|
||||
if(autoScroll){
|
||||
terminalRef.current?.scrollToBottom();
|
||||
}
|
||||
}, [autoScroll]);
|
||||
React.useEffect( () => {
|
||||
wrapTextRef.current = wrapText;
|
||||
fitTerminal();
|
||||
}, [wrapText, fitTerminal]);
|
||||
React.useEffect( () => {
|
||||
if(wrapText){
|
||||
unwrappedColumnCountRef.current = 0;
|
||||
} else {
|
||||
unwrappedColumnCountRef.current = Math.min(Math.max(longestLineLength + 2, 1), INTERACTIVE_TERMINAL_MAX_UNWRAPPED_COLS);
|
||||
}
|
||||
fitTerminal();
|
||||
}, [fitTerminal, longestLineLength, wrapText]);
|
||||
React.useEffect( () => {
|
||||
if(!terminalElementRef.current){
|
||||
return;
|
||||
}
|
||||
const terminal = new Terminal({
|
||||
allowTransparency: true,
|
||||
convertEol: true,
|
||||
cursorBlink: true,
|
||||
disableStdin: true,
|
||||
fontFamily: 'Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
||||
fontSize: 13,
|
||||
scrollback: INTERACTIVE_TERMINAL_SCROLLBACK,
|
||||
theme: getInteractiveTerminalTheme(theme),
|
||||
});
|
||||
const fitAddon = new FitAddon();
|
||||
terminal.loadAddon(fitAddon);
|
||||
terminal.open(terminalElementRef.current);
|
||||
terminalRef.current = terminal;
|
||||
fitAddonRef.current = fitAddon;
|
||||
resizeObserverRef.current = new ResizeObserver(() => fitTerminal());
|
||||
if(terminalScrollContainerRef.current){
|
||||
resizeObserverRef.current.observe(terminalScrollContainerRef.current);
|
||||
}
|
||||
window.requestAnimationFrame(() => {
|
||||
fitTerminal();
|
||||
setTerminalReady(true);
|
||||
});
|
||||
return () => {
|
||||
resizeObserverRef.current?.disconnect();
|
||||
resizeObserverRef.current = null;
|
||||
fitAddonRef.current = null;
|
||||
terminalRef.current = null;
|
||||
terminal.dispose();
|
||||
}
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
React.useEffect( () => {
|
||||
if(terminalRef.current){
|
||||
terminalRef.current.options.theme = getInteractiveTerminalTheme(theme);
|
||||
}
|
||||
}, [theme]);
|
||||
React.useEffect( () => {
|
||||
if(!terminalReady || !terminalRef.current){
|
||||
return;
|
||||
}
|
||||
const terminal = terminalRef.current;
|
||||
fitTerminal();
|
||||
const nextSignatures = data.map(getInteractiveTerminalEntrySignature);
|
||||
const settingsKey = `${useASNIColor}:${showTaskStatus}:${wrapText}`;
|
||||
const previousReplayState = replayStateRef.current;
|
||||
const canAppend = previousReplayState.settingsKey === settingsKey &&
|
||||
nextSignatures.length >= previousReplayState.signatures.length &&
|
||||
previousReplayState.signatures.every((signature, index) => signature === nextSignatures[index]);
|
||||
const previousViewportY = terminal.buffer.active.viewportY;
|
||||
const entriesToWrite = canAppend ? data.slice(previousReplayState.signatures.length) : data;
|
||||
if(!canAppend){
|
||||
terminal.reset();
|
||||
}
|
||||
const terminalOutput = entriesToWrite.map( (entry) => formatInteractiveTerminalEntry({
|
||||
entry,
|
||||
useASNIColor,
|
||||
showTaskStatus,
|
||||
})).join("");
|
||||
replayStateRef.current = {
|
||||
settingsKey,
|
||||
signatures: nextSignatures,
|
||||
};
|
||||
const afterWrite = () => {
|
||||
if(autoScrollRef.current){
|
||||
terminal.scrollToBottom();
|
||||
} else {
|
||||
terminal.scrollToLine(previousViewportY);
|
||||
}
|
||||
};
|
||||
if(terminalOutput.length > 0){
|
||||
terminal.write(terminalOutput, afterWrite);
|
||||
} else {
|
||||
afterWrite();
|
||||
}
|
||||
}, [data, showTaskStatus, terminalReady, useASNIColor, wrapText]);
|
||||
return (
|
||||
<div
|
||||
ref={terminalScrollContainerRef}
|
||||
className={"MythicInteractiveTerminal"}
|
||||
style={{
|
||||
height: "100%",
|
||||
minHeight: "50px",
|
||||
overflowX: wrapText ? "hidden" : "auto",
|
||||
overflowY: "hidden",
|
||||
width: "100%",
|
||||
}}>
|
||||
<div ref={terminalElementRef} style={{height: "100%", width: "100%"}} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const InteractiveMessageTypes = [
|
||||
{"name": "None", "value": -1, "text": "None"},
|
||||
{"name": "Tab", "value": 13, "text": "^I"},
|
||||
@@ -267,7 +489,6 @@ export const ResponseDisplayInteractive = (props) =>{
|
||||
const [search, setSearch] = React.useState("");
|
||||
const [totalCount, setTotalCount] = React.useState(0);
|
||||
const totalCountRef = React.useRef(0);
|
||||
const messagesEndRef = React.useRef();
|
||||
const [currentPage, setCurrentPage] = React.useState(1);
|
||||
const currentPageRef = React.useRef(1);
|
||||
const taskResponseCountRef = React.useRef(props.task.response_count || 0);
|
||||
@@ -503,16 +724,15 @@ export const ResponseDisplayInteractive = (props) =>{
|
||||
{props.searchOutput &&
|
||||
<SearchBar onSubmitSearch={onSubmitSearch}/>
|
||||
}
|
||||
<div style={{overflowY: "auto", width: "100%", marginBottom: "5px", height: props.expand ? "100%": undefined,
|
||||
flexGrow: 1, paddingLeft: "10px", minHeight: "50px"}} ref={props.responseRef}
|
||||
<div style={{overflow: "hidden", width: "100%", marginBottom: "5px", height: props.expand ? "100%": undefined,
|
||||
flexGrow: 1, minHeight: "50px"}} ref={props.responseRef}
|
||||
id={`ptytask${props.task.id}`}>
|
||||
<GetOutputFormatAll data={visibleOutput}
|
||||
useASNIColor={useASNIColor}
|
||||
messagesEndRef={messagesEndRef}
|
||||
showTaskStatus={showTaskStatus}
|
||||
wrapText={wrapText}
|
||||
autoScroll={autoScroll}/>
|
||||
<div ref={messagesEndRef}/>
|
||||
<InteractiveTerminalDisplay data={visibleOutput}
|
||||
useASNIColor={useASNIColor}
|
||||
showTaskStatus={showTaskStatus}
|
||||
wrapText={wrapText}
|
||||
autoScroll={autoScroll}
|
||||
theme={theme}/>
|
||||
</div>
|
||||
{!props.task?.is_interactive_task &&
|
||||
<div style={{width: "100%", display: "inline-flex", alignItems: "flex-end"}}>
|
||||
|
||||
@@ -269,6 +269,16 @@ tspan {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.MythicInteractiveTerminal {
|
||||
background-color: ${(props) => props.theme.outputBackgroundColor};
|
||||
}
|
||||
.MythicInteractiveTerminal .xterm {
|
||||
height: 100%;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
.MythicInteractiveTerminal .xterm-viewport {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.Toastify__toast {
|
||||
word-break: break-all;
|
||||
white-space: pre-wrap !important;
|
||||
|
||||
Reference in New Issue
Block a user