pywebexec 1.2.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pywebexec/__init__.py +5 -0
- pywebexec/pywebexec.py +595 -0
- pywebexec/static/css/style.css +178 -0
- pywebexec/static/css/xterm.css +209 -0
- pywebexec/static/images/aborted.svg +1 -0
- pywebexec/static/images/copy.svg +1 -0
- pywebexec/static/images/copy_ok.svg +1 -0
- pywebexec/static/images/failed.svg +1 -0
- pywebexec/static/images/favicon.svg +1 -0
- pywebexec/static/images/running.gif +0 -0
- pywebexec/static/images/success.svg +1 -0
- pywebexec/static/js/script.js +280 -0
- pywebexec/static/js/xterm/LICENSE +21 -0
- pywebexec/static/js/xterm/ansi_up.min.js +7 -0
- pywebexec/static/js/xterm/xterm-addon-fit.js +1 -0
- pywebexec/static/js/xterm/xterm.js +1 -0
- pywebexec/templates/__init__.py +0 -0
- pywebexec/templates/index.html +48 -0
- pywebexec/version.py +16 -0
- pywebexec-1.2.4.dist-info/LICENSE +21 -0
- pywebexec-1.2.4.dist-info/METADATA +171 -0
- pywebexec-1.2.4.dist-info/RECORD +25 -0
- pywebexec-1.2.4.dist-info/WHEEL +5 -0
- pywebexec-1.2.4.dist-info/entry_points.txt +2 -0
- pywebexec-1.2.4.dist-info/top_level.txt +1 -0
| @@ -0,0 +1,178 @@ | |
| 1 | 
            +
            body {
         | 
| 2 | 
            +
                font-family: Arial, sans-serif;
         | 
| 3 | 
            +
                overflow: hidden;
         | 
| 4 | 
            +
            }
         | 
| 5 | 
            +
            .table-container {
         | 
| 6 | 
            +
                height: 270px;
         | 
| 7 | 
            +
                overflow-y: auto;
         | 
| 8 | 
            +
                position: relative;
         | 
| 9 | 
            +
                border-radius: 10px;
         | 
| 10 | 
            +
                border: 1px solid #aaa;
         | 
| 11 | 
            +
                box-shadow: 0 0 10px rgba(0, 0, 0, 0.30);
         | 
| 12 | 
            +
            }
         | 
| 13 | 
            +
            table {
         | 
| 14 | 
            +
                width: 100%;
         | 
| 15 | 
            +
                border-collapse: collapse;
         | 
| 16 | 
            +
            }
         | 
| 17 | 
            +
            th, td {
         | 
| 18 | 
            +
                padding: 8px;
         | 
| 19 | 
            +
                text-align: left;
         | 
| 20 | 
            +
                border-bottom: 1px solid #ddd;
         | 
| 21 | 
            +
                white-space: nowrap;
         | 
| 22 | 
            +
            }
         | 
| 23 | 
            +
            th { 
         | 
| 24 | 
            +
                background-color: #444;
         | 
| 25 | 
            +
                color: #eee;
         | 
| 26 | 
            +
                position: sticky;
         | 
| 27 | 
            +
                top: 0;
         | 
| 28 | 
            +
                z-index: 1;
         | 
| 29 | 
            +
            }
         | 
| 30 | 
            +
            .outcol {
         | 
| 31 | 
            +
                width: 100%;
         | 
| 32 | 
            +
            }
         | 
| 33 | 
            +
            select { /* Safari bug */
         | 
| 34 | 
            +
                font-size: 15px;
         | 
| 35 | 
            +
                border: #aaa solid 1px;
         | 
| 36 | 
            +
                border-radius: 5px;
         | 
| 37 | 
            +
            }
         | 
| 38 | 
            +
            .output {
         | 
| 39 | 
            +
                white-space: pre-wrap;
         | 
| 40 | 
            +
                background: #111;
         | 
| 41 | 
            +
                padding: 10px;
         | 
| 42 | 
            +
                border: 1px solid #ccc;
         | 
| 43 | 
            +
                font-family: monospace;
         | 
| 44 | 
            +
                border-radius: 10px;
         | 
| 45 | 
            +
                overflow-y: hidden;
         | 
| 46 | 
            +
            }
         | 
| 47 | 
            +
            .copy-icon { cursor: pointer; }
         | 
| 48 | 
            +
            .monospace { font-family: monospace; }
         | 
| 49 | 
            +
            .copied { color: green; margin-left: 5px; }
         | 
| 50 | 
            +
            button {
         | 
| 51 | 
            +
                -webkit-appearance: none;
         | 
| 52 | 
            +
                -webkit-border-radius: none;
         | 
| 53 | 
            +
                appearance: none;
         | 
| 54 | 
            +
                border-radius: 15px;
         | 
| 55 | 
            +
                padding-right: 13px;
         | 
| 56 | 
            +
                border: 1px #555 solid;
         | 
| 57 | 
            +
                height: 22px;
         | 
| 58 | 
            +
                font-size: 13px;
         | 
| 59 | 
            +
                outline: none;
         | 
| 60 | 
            +
                text-indent: 10px;
         | 
| 61 | 
            +
                background-color: #eee;
         | 
| 62 | 
            +
            }
         | 
| 63 | 
            +
            form {
         | 
| 64 | 
            +
                padding-bottom: 15px;
         | 
| 65 | 
            +
            }
         | 
| 66 | 
            +
            .status-icon {
         | 
| 67 | 
            +
                display: inline-block;
         | 
| 68 | 
            +
                width: 16px;
         | 
| 69 | 
            +
                height: 16px;
         | 
| 70 | 
            +
                margin-right: 5px;
         | 
| 71 | 
            +
                background-size: contain;
         | 
| 72 | 
            +
                background-repeat: no-repeat;
         | 
| 73 | 
            +
                vertical-align: middle;
         | 
| 74 | 
            +
            }
         | 
| 75 | 
            +
            .title-icon {
         | 
| 76 | 
            +
                width: 30px;
         | 
| 77 | 
            +
                height: 30px;
         | 
| 78 | 
            +
                background-image: url("/static/images/favicon.svg");
         | 
| 79 | 
            +
                vertical-align: bottom;
         | 
| 80 | 
            +
            }
         | 
| 81 | 
            +
            .status-running { 
         | 
| 82 | 
            +
                background-image: url("/static/images/running.gif")
         | 
| 83 | 
            +
            }
         | 
| 84 | 
            +
            .status-success { 
         | 
| 85 | 
            +
                background-image: url("/static/images/success.svg")
         | 
| 86 | 
            +
            }
         | 
| 87 | 
            +
            .status-failed { 
         | 
| 88 | 
            +
                background-image: url("/static/images/failed.svg")
         | 
| 89 | 
            +
            }
         | 
| 90 | 
            +
            .status-aborted { 
         | 
| 91 | 
            +
                background-image: url("/static/images/aborted.svg")
         | 
| 92 | 
            +
            }
         | 
| 93 | 
            +
            .copy_clip {
         | 
| 94 | 
            +
                padding-right: 20px;
         | 
| 95 | 
            +
                background-repeat: no-repeat;
         | 
| 96 | 
            +
                background-position: right top;
         | 
| 97 | 
            +
                background-size: 20px 12px;
         | 
| 98 | 
            +
                white-space: nowrap;
         | 
| 99 | 
            +
            }
         | 
| 100 | 
            +
            .copy_clip:hover {
         | 
| 101 | 
            +
                cursor: pointer;
         | 
| 102 | 
            +
                background-image: url("/static/images/copy.svg");
         | 
| 103 | 
            +
            }
         | 
| 104 | 
            +
            .copy_clip_ok, .copy_clip_ok:hover {
         | 
| 105 | 
            +
                background-image: url("/static/images/copy_ok.svg");
         | 
| 106 | 
            +
            }
         | 
| 107 | 
            +
            input {
         | 
| 108 | 
            +
                width: 50%;
         | 
| 109 | 
            +
                -webkit-appearance: none;
         | 
| 110 | 
            +
                -webkit-border-radius: none;
         | 
| 111 | 
            +
                appearance: none;
         | 
| 112 | 
            +
                border-radius: 15px;
         | 
| 113 | 
            +
                padding: 3px;
         | 
| 114 | 
            +
                padding-right: 13px;
         | 
| 115 | 
            +
                border: 1px #aaa solid;
         | 
| 116 | 
            +
                height: 15px;
         | 
| 117 | 
            +
                font-size: 15px;
         | 
| 118 | 
            +
                outline: none;
         | 
| 119 | 
            +
                text-indent: 5px;
         | 
| 120 | 
            +
                background-color: white;
         | 
| 121 | 
            +
            }
         | 
| 122 | 
            +
            .currentcommand {
         | 
| 123 | 
            +
                background-color: #eef;
         | 
| 124 | 
            +
            }
         | 
| 125 | 
            +
            .resizer {
         | 
| 126 | 
            +
                width: 100%;
         | 
| 127 | 
            +
                height: 5px;
         | 
| 128 | 
            +
                border-radius: 5px;
         | 
| 129 | 
            +
                background: #aaa;
         | 
| 130 | 
            +
                cursor: ns-resize;
         | 
| 131 | 
            +
                position: absolute;
         | 
| 132 | 
            +
                bottom: 0;
         | 
| 133 | 
            +
                left: 0;
         | 
| 134 | 
            +
            }
         | 
| 135 | 
            +
            .resizer-container {
         | 
| 136 | 
            +
                position: relative;
         | 
| 137 | 
            +
                height: 5px;
         | 
| 138 | 
            +
                margin: 5px;
         | 
| 139 | 
            +
                /*margin-bottom: 10px;*/
         | 
| 140 | 
            +
            }
         | 
| 141 | 
            +
            tr.clickable-row {
         | 
| 142 | 
            +
                cursor: pointer;
         | 
| 143 | 
            +
            }
         | 
| 144 | 
            +
            body.dimmed {
         | 
| 145 | 
            +
                background-color: rgba(0, 0, 0, 0.5);
         | 
| 146 | 
            +
                pointer-events: none;
         | 
| 147 | 
            +
            }
         | 
| 148 | 
            +
            body.dimmed * {
         | 
| 149 | 
            +
                pointer-events: none;
         | 
| 150 | 
            +
            }
         | 
| 151 | 
            +
            .dimmer {
         | 
| 152 | 
            +
                display: none;
         | 
| 153 | 
            +
                position: fixed;
         | 
| 154 | 
            +
                top: 0;
         | 
| 155 | 
            +
                left: 0;
         | 
| 156 | 
            +
                width: 100%;
         | 
| 157 | 
            +
                height: 100%;
         | 
| 158 | 
            +
                background-color: rgba(0, 0, 0, 0.5);
         | 
| 159 | 
            +
                z-index: 1000;
         | 
| 160 | 
            +
                overflow-y: hidden;
         | 
| 161 | 
            +
            }
         | 
| 162 | 
            +
            .dimmer-text {
         | 
| 163 | 
            +
                color: white;
         | 
| 164 | 
            +
                font-size: 24px;
         | 
| 165 | 
            +
                text-align: center;
         | 
| 166 | 
            +
                position: absolute;
         | 
| 167 | 
            +
                top: 50%;
         | 
| 168 | 
            +
                left: 50%;
         | 
| 169 | 
            +
                transform: translate(-50%, -50%);
         | 
| 170 | 
            +
            }
         | 
| 171 | 
            +
            .xterm-cursor {
         | 
| 172 | 
            +
                display: none;
         | 
| 173 | 
            +
                visibility: hidden;
         | 
| 174 | 
            +
                height: 0px;
         | 
| 175 | 
            +
            }
         | 
| 176 | 
            +
            span { /* allow wide chars in terminal */
         | 
| 177 | 
            +
                letter-spacing: unset !important;
         | 
| 178 | 
            +
            }
         | 
| @@ -0,0 +1,209 @@ | |
| 1 | 
            +
            /**
         | 
| 2 | 
            +
             * Copyright (c) 2014 The xterm.js authors. All rights reserved.
         | 
| 3 | 
            +
             * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
         | 
| 4 | 
            +
             * https://github.com/chjj/term.js
         | 
| 5 | 
            +
             * @license MIT
         | 
| 6 | 
            +
             *
         | 
| 7 | 
            +
             * Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 8 | 
            +
             * of this software and associated documentation files (the "Software"), to deal
         | 
| 9 | 
            +
             * in the Software without restriction, including without limitation the rights
         | 
| 10 | 
            +
             * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         | 
| 11 | 
            +
             * copies of the Software, and to permit persons to whom the Software is
         | 
| 12 | 
            +
             * furnished to do so, subject to the following conditions:
         | 
| 13 | 
            +
             *
         | 
| 14 | 
            +
             * The above copyright notice and this permission notice shall be included in
         | 
| 15 | 
            +
             * all copies or substantial portions of the Software.
         | 
| 16 | 
            +
             *
         | 
| 17 | 
            +
             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         | 
| 18 | 
            +
             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         | 
| 19 | 
            +
             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         | 
| 20 | 
            +
             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         | 
| 21 | 
            +
             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         | 
| 22 | 
            +
             * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         | 
| 23 | 
            +
             * THE SOFTWARE.
         | 
| 24 | 
            +
             *
         | 
| 25 | 
            +
             * Originally forked from (with the author's permission):
         | 
| 26 | 
            +
             *   Fabrice Bellard's javascript vt100 for jslinux:
         | 
| 27 | 
            +
             *   http://bellard.org/jslinux/
         | 
| 28 | 
            +
             *   Copyright (c) 2011 Fabrice Bellard
         | 
| 29 | 
            +
             *   The original design remains. The terminal itself
         | 
| 30 | 
            +
             *   has been extended to include xterm CSI codes, among
         | 
| 31 | 
            +
             *   other features.
         | 
| 32 | 
            +
             */
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            /**
         | 
| 35 | 
            +
             *  Default styles for xterm.js
         | 
| 36 | 
            +
             */
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            .xterm {
         | 
| 39 | 
            +
                cursor: text;
         | 
| 40 | 
            +
                position: relative;
         | 
| 41 | 
            +
                user-select: none;
         | 
| 42 | 
            +
                -ms-user-select: none;
         | 
| 43 | 
            +
                -webkit-user-select: none;
         | 
| 44 | 
            +
            }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            .xterm.focus,
         | 
| 47 | 
            +
            .xterm:focus {
         | 
| 48 | 
            +
                outline: none;
         | 
| 49 | 
            +
            }
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            .xterm .xterm-helpers {
         | 
| 52 | 
            +
                position: absolute;
         | 
| 53 | 
            +
                top: 0;
         | 
| 54 | 
            +
                /**
         | 
| 55 | 
            +
                 * The z-index of the helpers must be higher than the canvases in order for
         | 
| 56 | 
            +
                 * IMEs to appear on top.
         | 
| 57 | 
            +
                 */
         | 
| 58 | 
            +
                z-index: 5;
         | 
| 59 | 
            +
            }
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            .xterm .xterm-helper-textarea {
         | 
| 62 | 
            +
                padding: 0;
         | 
| 63 | 
            +
                border: 0;
         | 
| 64 | 
            +
                margin: 0;
         | 
| 65 | 
            +
                /* Move textarea out of the screen to the far left, so that the cursor is not visible */
         | 
| 66 | 
            +
                position: absolute;
         | 
| 67 | 
            +
                opacity: 0;
         | 
| 68 | 
            +
                left: -9999em;
         | 
| 69 | 
            +
                top: 0;
         | 
| 70 | 
            +
                width: 0;
         | 
| 71 | 
            +
                height: 0;
         | 
| 72 | 
            +
                z-index: -5;
         | 
| 73 | 
            +
                /** Prevent wrapping so the IME appears against the textarea at the correct position */
         | 
| 74 | 
            +
                white-space: nowrap;
         | 
| 75 | 
            +
                overflow: hidden;
         | 
| 76 | 
            +
                resize: none;
         | 
| 77 | 
            +
            }
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            .xterm .composition-view {
         | 
| 80 | 
            +
                /* TODO: Composition position got messed up somewhere */
         | 
| 81 | 
            +
                background: #000;
         | 
| 82 | 
            +
                color: #FFF;
         | 
| 83 | 
            +
                display: none;
         | 
| 84 | 
            +
                position: absolute;
         | 
| 85 | 
            +
                white-space: nowrap;
         | 
| 86 | 
            +
                z-index: 1;
         | 
| 87 | 
            +
            }
         | 
| 88 | 
            +
             | 
| 89 | 
            +
            .xterm .composition-view.active {
         | 
| 90 | 
            +
                display: block;
         | 
| 91 | 
            +
            }
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            .xterm .xterm-viewport {
         | 
| 94 | 
            +
                /* On OS X this is required in order for the scroll bar to appear fully opaque */
         | 
| 95 | 
            +
                background-color: #000;
         | 
| 96 | 
            +
                overflow-y: scroll;
         | 
| 97 | 
            +
                cursor: default;
         | 
| 98 | 
            +
                position: absolute;
         | 
| 99 | 
            +
                right: 0;
         | 
| 100 | 
            +
                left: 0;
         | 
| 101 | 
            +
                top: 0;
         | 
| 102 | 
            +
                bottom: 0;
         | 
| 103 | 
            +
            }
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            .xterm .xterm-screen {
         | 
| 106 | 
            +
                position: relative;
         | 
| 107 | 
            +
            }
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            .xterm .xterm-screen canvas {
         | 
| 110 | 
            +
                position: absolute;
         | 
| 111 | 
            +
                left: 0;
         | 
| 112 | 
            +
                top: 0;
         | 
| 113 | 
            +
            }
         | 
| 114 | 
            +
             | 
| 115 | 
            +
            .xterm .xterm-scroll-area {
         | 
| 116 | 
            +
                visibility: hidden;
         | 
| 117 | 
            +
            }
         | 
| 118 | 
            +
             | 
| 119 | 
            +
            .xterm-char-measure-element {
         | 
| 120 | 
            +
                display: inline-block;
         | 
| 121 | 
            +
                visibility: hidden;
         | 
| 122 | 
            +
                position: absolute;
         | 
| 123 | 
            +
                top: 0;
         | 
| 124 | 
            +
                left: -9999em;
         | 
| 125 | 
            +
                line-height: normal;
         | 
| 126 | 
            +
            }
         | 
| 127 | 
            +
             | 
| 128 | 
            +
            .xterm.enable-mouse-events {
         | 
| 129 | 
            +
                /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
         | 
| 130 | 
            +
                cursor: default;
         | 
| 131 | 
            +
            }
         | 
| 132 | 
            +
             | 
| 133 | 
            +
            .xterm.xterm-cursor-pointer,
         | 
| 134 | 
            +
            .xterm .xterm-cursor-pointer {
         | 
| 135 | 
            +
                cursor: pointer;
         | 
| 136 | 
            +
            }
         | 
| 137 | 
            +
             | 
| 138 | 
            +
            .xterm.column-select.focus {
         | 
| 139 | 
            +
                /* Column selection mode */
         | 
| 140 | 
            +
                cursor: crosshair;
         | 
| 141 | 
            +
            }
         | 
| 142 | 
            +
             | 
| 143 | 
            +
            .xterm .xterm-accessibility,
         | 
| 144 | 
            +
            .xterm .xterm-message {
         | 
| 145 | 
            +
                position: absolute;
         | 
| 146 | 
            +
                left: 0;
         | 
| 147 | 
            +
                top: 0;
         | 
| 148 | 
            +
                bottom: 0;
         | 
| 149 | 
            +
                right: 0;
         | 
| 150 | 
            +
                z-index: 10;
         | 
| 151 | 
            +
                color: transparent;
         | 
| 152 | 
            +
                pointer-events: none;
         | 
| 153 | 
            +
            }
         | 
| 154 | 
            +
             | 
| 155 | 
            +
            .xterm .live-region {
         | 
| 156 | 
            +
                position: absolute;
         | 
| 157 | 
            +
                left: -9999px;
         | 
| 158 | 
            +
                width: 1px;
         | 
| 159 | 
            +
                height: 1px;
         | 
| 160 | 
            +
                overflow: hidden;
         | 
| 161 | 
            +
            }
         | 
| 162 | 
            +
             | 
| 163 | 
            +
            .xterm-dim {
         | 
| 164 | 
            +
                /* Dim should not apply to background, so the opacity of the foreground color is applied
         | 
| 165 | 
            +
                 * explicitly in the generated class and reset to 1 here */
         | 
| 166 | 
            +
                opacity: 1 !important;
         | 
| 167 | 
            +
            }
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            .xterm-underline-1 { text-decoration: underline; }
         | 
| 170 | 
            +
            .xterm-underline-2 { text-decoration: double underline; }
         | 
| 171 | 
            +
            .xterm-underline-3 { text-decoration: wavy underline; }
         | 
| 172 | 
            +
            .xterm-underline-4 { text-decoration: dotted underline; }
         | 
| 173 | 
            +
            .xterm-underline-5 { text-decoration: dashed underline; }
         | 
| 174 | 
            +
             | 
| 175 | 
            +
            .xterm-overline {
         | 
| 176 | 
            +
                text-decoration: overline;
         | 
| 177 | 
            +
            }
         | 
| 178 | 
            +
             | 
| 179 | 
            +
            .xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
         | 
| 180 | 
            +
            .xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
         | 
| 181 | 
            +
            .xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
         | 
| 182 | 
            +
            .xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
         | 
| 183 | 
            +
            .xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }
         | 
| 184 | 
            +
             | 
| 185 | 
            +
            .xterm-strikethrough {
         | 
| 186 | 
            +
                text-decoration: line-through;
         | 
| 187 | 
            +
            }
         | 
| 188 | 
            +
             | 
| 189 | 
            +
            .xterm-screen .xterm-decoration-container .xterm-decoration {
         | 
| 190 | 
            +
            	z-index: 6;
         | 
| 191 | 
            +
            	position: absolute;
         | 
| 192 | 
            +
            }
         | 
| 193 | 
            +
             | 
| 194 | 
            +
            .xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
         | 
| 195 | 
            +
            	z-index: 7;
         | 
| 196 | 
            +
            }
         | 
| 197 | 
            +
             | 
| 198 | 
            +
            .xterm-decoration-overview-ruler {
         | 
| 199 | 
            +
                z-index: 8;
         | 
| 200 | 
            +
                position: absolute;
         | 
| 201 | 
            +
                top: 0;
         | 
| 202 | 
            +
                right: 0;
         | 
| 203 | 
            +
                pointer-events: none;
         | 
| 204 | 
            +
            }
         | 
| 205 | 
            +
             | 
| 206 | 
            +
            .xterm-decoration-top {
         | 
| 207 | 
            +
                z-index: 2;
         | 
| 208 | 
            +
                position: relative;
         | 
| 209 | 
            +
            }
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg viewBox="-1 -1 13 13" xmlns="http://www.w3.org/2000/svg" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill-rule="evenodd" clip-rule="evenodd" d="M6 12A6 6 0 106 0a6 6 0 000 12zM3 5a1 1 0 000 2h6a1 1 0 100-2H3z" fill="#ff641a"></path></g></svg>
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" role="img" viewBox="0 0 16 16" width="25" height="16" fill="currentColor"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg>
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" role="img" viewBox="0 0 16 16" width="25" height="16" fill="currentColor" style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"><path fill="#118811" d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path></svg>
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg viewBox="-2 -2 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><title>cross-circle</title><desc>Created with Sketch Beta.</desc><defs></defs><g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage"><g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-570.000000, -1089.000000)" fill="#ca0000"><path d="M591.657,1109.24 C592.048,1109.63 592.048,1110.27 591.657,1110.66 C591.267,1111.05 590.633,1111.05 590.242,1110.66 L586.006,1106.42 L581.74,1110.69 C581.346,1111.08 580.708,1111.08 580.314,1110.69 C579.921,1110.29 579.921,1109.65 580.314,1109.26 L584.58,1104.99 L580.344,1100.76 C579.953,1100.37 579.953,1099.73 580.344,1099.34 C580.733,1098.95 581.367,1098.95 581.758,1099.34 L585.994,1103.58 L590.292,1099.28 C590.686,1098.89 591.323,1098.89 591.717,1099.28 C592.11,1099.68 592.11,1100.31 591.717,1100.71 L587.42,1105.01 L591.657,1109.24 L591.657,1109.24 Z M586,1089 C577.163,1089 570,1096.16 570,1105 C570,1113.84 577.163,1121 586,1121 C594.837,1121 602,1113.84 602,1105 C602,1096.16 594.837,1089 586,1089 L586,1089 Z" id="cross-circle" sketch:type="MSShapeGroup"></path></g></g></g></svg>
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg fill="#000000" viewBox="-1 0 19 19" xmlns="http://www.w3.org/2000/svg" class="cf-icon-svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path d="M16.5 9.5a8 8 0 1 1-8-8 8 8 0 0 1 8 8zm-2.97.006a5.03 5.03 0 1 0-5.03 5.03 5.03 5.03 0 0 0 5.03-5.03zm-7.383-.4H4.289a4.237 4.237 0 0 1 2.565-3.498q.1-.042.2-.079a7.702 7.702 0 0 0-.907 3.577zm0 .8a7.7 7.7 0 0 0 .908 3.577q-.102-.037-.201-.079a4.225 4.225 0 0 1-2.565-3.498zm.8-.8a9.04 9.04 0 0 1 .163-1.402 6.164 6.164 0 0 1 .445-1.415c.289-.615.66-1.013.945-1.013.285 0 .656.398.945 1.013a6.18 6.18 0 0 1 .445 1.415 9.078 9.078 0 0 1 .163 1.402zm3.106.8a9.073 9.073 0 0 1-.163 1.402 6.187 6.187 0 0 1-.445 1.415c-.289.616-.66 1.013-.945 1.013-.285 0-.656-.397-.945-1.013a6.172 6.172 0 0 1-.445-1.415 9.036 9.036 0 0 1-.163-1.402zm1.438-3.391a4.211 4.211 0 0 1 1.22 2.591h-1.858a7.698 7.698 0 0 0-.908-3.577q.102.037.201.08a4.208 4.208 0 0 1 1.345.906zm-.638 3.391h1.858a4.238 4.238 0 0 1-2.565 3.498q-.1.043-.2.08a7.697 7.697 0 0 0 .907-3.578z"></path></g></svg>
         | 
| Binary file | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            <svg viewBox="0 75 949 949" xmlns="http://www.w3.org/2000/svg" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"><path fill="#00a600" d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm-55.808 536.384-99.52-99.584a38.4 38.4 0 1 0-54.336 54.336l126.72 126.72a38.272 38.272 0 0 0 54.336 0l262.4-262.464a38.4 38.4 0 1 0-54.272-54.336L456.192 600.384z"></path></g></svg>
         | 
| @@ -0,0 +1,280 @@ | |
| 1 | 
            +
            let currentCommandId = null;
         | 
| 2 | 
            +
            let outputInterval = null;
         | 
| 3 | 
            +
            let nextOutputLink = null;
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            const terminal = new Terminal({
         | 
| 6 | 
            +
                cursorBlink: false,
         | 
| 7 | 
            +
                cursorHidden: true,
         | 
| 8 | 
            +
                disableStdin: true,
         | 
| 9 | 
            +
                convertEol: true,
         | 
| 10 | 
            +
                fontFamily: 'Consolas NF, monospace, courier-new, courier'
         | 
| 11 | 
            +
            });
         | 
| 12 | 
            +
            const fitAddon = new FitAddon.FitAddon();
         | 
| 13 | 
            +
            terminal.loadAddon(fitAddon);
         | 
| 14 | 
            +
            terminal.open(document.getElementById('output'));
         | 
| 15 | 
            +
            fitAddon.fit();
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            terminal.onSelectionChange(() => {
         | 
| 18 | 
            +
                const selectionText = terminal.getSelection();
         | 
| 19 | 
            +
                if (selectionText) {
         | 
| 20 | 
            +
                    navigator.clipboard.writeText(selectionText).catch(err => {
         | 
| 21 | 
            +
                        console.error('Failed to copy text to clipboard:', err);
         | 
| 22 | 
            +
                    });
         | 
| 23 | 
            +
                }
         | 
| 24 | 
            +
            });
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            document.getElementById('launchForm').addEventListener('submit', async (event) => {
         | 
| 27 | 
            +
                event.preventDefault();
         | 
| 28 | 
            +
                const commandName = document.getElementById('commandName').value;
         | 
| 29 | 
            +
                const params = document.getElementById('params').value.split(' ');
         | 
| 30 | 
            +
                try {
         | 
| 31 | 
            +
                    const response = await fetch('/run_command', {
         | 
| 32 | 
            +
                        method: 'POST',
         | 
| 33 | 
            +
                        headers: {
         | 
| 34 | 
            +
                            'Content-Type': 'application/json'
         | 
| 35 | 
            +
                        },
         | 
| 36 | 
            +
                        body: JSON.stringify({ command: commandName, params: params })
         | 
| 37 | 
            +
                    });
         | 
| 38 | 
            +
                    if (!response.ok) {
         | 
| 39 | 
            +
                        throw new Error('Failed to launch command');
         | 
| 40 | 
            +
                    }
         | 
| 41 | 
            +
                    const data = await response.json();
         | 
| 42 | 
            +
                    await new Promise(r => setTimeout(r, 200));
         | 
| 43 | 
            +
                    fetchCommands();
         | 
| 44 | 
            +
                    viewOutput(data.command_id);
         | 
| 45 | 
            +
                } catch (error) {
         | 
| 46 | 
            +
                    console.log('Error running command:', error);
         | 
| 47 | 
            +
                }
         | 
| 48 | 
            +
            });
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            async function fetchCommands() {
         | 
| 51 | 
            +
                try {
         | 
| 52 | 
            +
                    const response = await fetch('/commands');
         | 
| 53 | 
            +
                    if (!response.ok) {
         | 
| 54 | 
            +
                        document.getElementById('dimmer').style.display = 'block';
         | 
| 55 | 
            +
                        return;
         | 
| 56 | 
            +
                    }
         | 
| 57 | 
            +
                    const commands = await response.json();
         | 
| 58 | 
            +
                    commands.sort((a, b) => new Date(b.start_time) - new Date(a.start_time));
         | 
| 59 | 
            +
                    const commandsTbody = document.getElementById('commands');
         | 
| 60 | 
            +
                    commandsTbody.innerHTML = '';
         | 
| 61 | 
            +
                    if (!currentCommandId && commands.length) {
         | 
| 62 | 
            +
                        currentCommandId = commands[0].command_id;
         | 
| 63 | 
            +
                        viewOutput(currentCommandId);
         | 
| 64 | 
            +
                    }
         | 
| 65 | 
            +
                    commands.forEach(command => {
         | 
| 66 | 
            +
                        const commandRow = document.createElement('tr');
         | 
| 67 | 
            +
                        commandRow.className = `clickable-row ${command.command_id === currentCommandId ? 'currentcommand' : ''}`;
         | 
| 68 | 
            +
                        commandRow.onclick = () => viewOutput(command.command_id);
         | 
| 69 | 
            +
                        commandRow.innerHTML = `
         | 
| 70 | 
            +
                            <td class="monospace">
         | 
| 71 | 
            +
                                ${navigator.clipboard == undefined ? `${command.command_id.slice(0, 8)}` : `<span class="copy_clip" onclick="copyToClipboard('${command.command_id}', this, event)">${command.command_id.slice(0, 8)}</span>`}
         | 
| 72 | 
            +
                            </td>
         | 
| 73 | 
            +
                            <td>${formatTime(command.start_time)}</td>
         | 
| 74 | 
            +
                            <td>${command.status === 'running' ? formatDuration(command.start_time, new Date().toISOString()) : formatDuration(command.start_time, command.end_time)}</td>
         | 
| 75 | 
            +
                            <td>${command.command.replace(/^\.\//, '')}</td>
         | 
| 76 | 
            +
                            <td>${command.exit_code}</td>
         | 
| 77 | 
            +
                            <td><span class="status-icon status-${command.status}"></span>${command.status}</td>
         | 
| 78 | 
            +
                            <td>
         | 
| 79 | 
            +
                                ${command.status === 'running' ? `<button onclick="stopCommand('${command.command_id}')">Stop</button>` : `<button onclick="relaunchCommand('${command.command_id}')">Run</button>`}
         | 
| 80 | 
            +
                            </td>
         | 
| 81 | 
            +
                            <td class="monospace outcol">${command.last_output_line || ''}</td>
         | 
| 82 | 
            +
                        `;
         | 
| 83 | 
            +
                        commandsTbody.appendChild(commandRow);
         | 
| 84 | 
            +
                    });
         | 
| 85 | 
            +
                    document.getElementById('dimmer').style.display = 'none';
         | 
| 86 | 
            +
                } catch (error) {
         | 
| 87 | 
            +
                    console.log('Error fetching commands:', error);
         | 
| 88 | 
            +
                    document.getElementById('dimmer').style.display = 'block';
         | 
| 89 | 
            +
                }
         | 
| 90 | 
            +
            }
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            async function fetchExecutables() {
         | 
| 93 | 
            +
                try {
         | 
| 94 | 
            +
                    const response = await fetch('/executables');
         | 
| 95 | 
            +
                    if (!response.ok) {
         | 
| 96 | 
            +
                        throw new Error('Failed to fetch command status');
         | 
| 97 | 
            +
                    }
         | 
| 98 | 
            +
                    const executables = await response.json();
         | 
| 99 | 
            +
                    const commandNameSelect = document.getElementById('commandName');
         | 
| 100 | 
            +
                    commandNameSelect.innerHTML = '';
         | 
| 101 | 
            +
                    executables.forEach(executable => {
         | 
| 102 | 
            +
                        const option = document.createElement('option');
         | 
| 103 | 
            +
                        option.value = executable;
         | 
| 104 | 
            +
                        option.textContent = executable;
         | 
| 105 | 
            +
                        commandNameSelect.appendChild(option);
         | 
| 106 | 
            +
                    });
         | 
| 107 | 
            +
                } catch (error) {
         | 
| 108 | 
            +
                    console.log('Error fetching executables:', error);
         | 
| 109 | 
            +
                    alert("Failed to fetch executables");
         | 
| 110 | 
            +
                }
         | 
| 111 | 
            +
            }
         | 
| 112 | 
            +
             | 
| 113 | 
            +
            async function fetchOutput(url) {
         | 
| 114 | 
            +
                try {
         | 
| 115 | 
            +
                    const response = await fetch(url);
         | 
| 116 | 
            +
                    if (!response.ok) {
         | 
| 117 | 
            +
                        return;
         | 
| 118 | 
            +
                    }
         | 
| 119 | 
            +
                    const data = await response.json();
         | 
| 120 | 
            +
                    if (data.error) {
         | 
| 121 | 
            +
                        terminal.write(data.error);
         | 
| 122 | 
            +
                        clearInterval(outputInterval);
         | 
| 123 | 
            +
                    } else {
         | 
| 124 | 
            +
                        terminal.write(data.output);
         | 
| 125 | 
            +
                        nextOutputLink = data.links.next;
         | 
| 126 | 
            +
                        if (data.status != 'running') {
         | 
| 127 | 
            +
                            clearInterval(outputInterval);
         | 
| 128 | 
            +
                        }
         | 
| 129 | 
            +
                    }
         | 
| 130 | 
            +
                } catch (error) {
         | 
| 131 | 
            +
                    console.log('Error fetching output:', error);
         | 
| 132 | 
            +
                }
         | 
| 133 | 
            +
            }
         | 
| 134 | 
            +
             | 
| 135 | 
            +
            async function viewOutput(command_id) {
         | 
| 136 | 
            +
                adjustOutputHeight();
         | 
| 137 | 
            +
                currentCommandId = command_id;
         | 
| 138 | 
            +
                nextOutputLink = `/command_output/${command_id}`;
         | 
| 139 | 
            +
                clearInterval(outputInterval);
         | 
| 140 | 
            +
                terminal.clear();
         | 
| 141 | 
            +
                try {
         | 
| 142 | 
            +
                    const response = await fetch(`/command_status/${command_id}`);
         | 
| 143 | 
            +
                    if (!response.ok) {
         | 
| 144 | 
            +
                        return;
         | 
| 145 | 
            +
                    }
         | 
| 146 | 
            +
                    const data = await response.json();
         | 
| 147 | 
            +
                    if (data.status === 'running') {
         | 
| 148 | 
            +
                        fetchOutput(nextOutputLink);
         | 
| 149 | 
            +
                        outputInterval = setInterval(() => fetchOutput(nextOutputLink), 1000);
         | 
| 150 | 
            +
                    } else {
         | 
| 151 | 
            +
                        fetchOutput(nextOutputLink);
         | 
| 152 | 
            +
                    }
         | 
| 153 | 
            +
                    fetchCommands(); // Refresh the command list to highlight the current command
         | 
| 154 | 
            +
                } catch (error) {
         | 
| 155 | 
            +
                    console.log('Error viewing output:', error);
         | 
| 156 | 
            +
                }
         | 
| 157 | 
            +
            }
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            async function relaunchCommand(command_id) {
         | 
| 160 | 
            +
                try {
         | 
| 161 | 
            +
                    const response = await fetch(`/command_status/${command_id}`);
         | 
| 162 | 
            +
                    if (!response.ok) {
         | 
| 163 | 
            +
                        throw new Error('Failed to fetch command status');
         | 
| 164 | 
            +
                    }
         | 
| 165 | 
            +
                    const data = await response.json();
         | 
| 166 | 
            +
                    if (data.error) {
         | 
| 167 | 
            +
                        alert(data.error);
         | 
| 168 | 
            +
                        return;
         | 
| 169 | 
            +
                    }
         | 
| 170 | 
            +
                    const relaunchResponse = await fetch('/run_command', {
         | 
| 171 | 
            +
                        method: 'POST',
         | 
| 172 | 
            +
                        headers: {
         | 
| 173 | 
            +
                            'Content-Type': 'application/json'
         | 
| 174 | 
            +
                        },
         | 
| 175 | 
            +
                        body: JSON.stringify({
         | 
| 176 | 
            +
                            command: data.command,
         | 
| 177 | 
            +
                            params: data.params
         | 
| 178 | 
            +
                        })
         | 
| 179 | 
            +
                    });
         | 
| 180 | 
            +
                    if (!relaunchResponse.ok) {
         | 
| 181 | 
            +
                        throw new Error('Failed to relaunch command');
         | 
| 182 | 
            +
                    }
         | 
| 183 | 
            +
                    const relaunchData = await relaunchResponse.json();
         | 
| 184 | 
            +
                    fetchCommands();
         | 
| 185 | 
            +
                    viewOutput(relaunchData.command_id);
         | 
| 186 | 
            +
                } catch (error) {
         | 
| 187 | 
            +
                    console.log('Error relaunching command:', error);
         | 
| 188 | 
            +
                    alert('Failed to relaunch command. Please try again.');
         | 
| 189 | 
            +
                }
         | 
| 190 | 
            +
            }
         | 
| 191 | 
            +
             | 
| 192 | 
            +
            async function stopCommand(command_id) {
         | 
| 193 | 
            +
                try {
         | 
| 194 | 
            +
                    const response = await fetch(`/stop_command/${command_id}`, {
         | 
| 195 | 
            +
                        method: 'POST'
         | 
| 196 | 
            +
                    });
         | 
| 197 | 
            +
                    if (!response.ok) {
         | 
| 198 | 
            +
                        throw new Error('Failed to stop command');
         | 
| 199 | 
            +
                    }
         | 
| 200 | 
            +
                    const data = await response.json();
         | 
| 201 | 
            +
                    if (data.error) {
         | 
| 202 | 
            +
                        alert(data.error);
         | 
| 203 | 
            +
                    } else {
         | 
| 204 | 
            +
                        fetchCommands();
         | 
| 205 | 
            +
                    }
         | 
| 206 | 
            +
                } catch (error) {
         | 
| 207 | 
            +
                    console.log('Error stopping command:', error);
         | 
| 208 | 
            +
                    alert('Failed to stop command. Please try again.');
         | 
| 209 | 
            +
                }
         | 
| 210 | 
            +
            }
         | 
| 211 | 
            +
             | 
| 212 | 
            +
            function formatTime(time) {
         | 
| 213 | 
            +
                if (!time || time === 'N/A') return 'N/A';
         | 
| 214 | 
            +
                const date = new Date(time);
         | 
| 215 | 
            +
                return date.toISOString().slice(0, 16).replace('T', ' ');
         | 
| 216 | 
            +
            }
         | 
| 217 | 
            +
             | 
| 218 | 
            +
            function formatDuration(startTime, endTime) {
         | 
| 219 | 
            +
                if (!startTime || !endTime) return 'N/A';
         | 
| 220 | 
            +
                const start = new Date(startTime);
         | 
| 221 | 
            +
                const end = new Date(endTime);
         | 
| 222 | 
            +
                const duration = (end - start) / 1000;
         | 
| 223 | 
            +
                const hours = Math.floor(duration / 3600);
         | 
| 224 | 
            +
                const minutes = Math.floor((duration % 3600) / 60);
         | 
| 225 | 
            +
                const seconds = Math.floor(duration % 60);
         | 
| 226 | 
            +
                return `${hours}h ${minutes}m ${seconds}s`;
         | 
| 227 | 
            +
            }
         | 
| 228 | 
            +
             | 
| 229 | 
            +
            function copyToClipboard(text, element, event) {
         | 
| 230 | 
            +
                event.stopPropagation();
         | 
| 231 | 
            +
                event.stopImmediatePropagation();
         | 
| 232 | 
            +
                navigator.clipboard.writeText(text).then(() => {
         | 
| 233 | 
            +
                    element.classList.add('copy_clip_ok');
         | 
| 234 | 
            +
                    setTimeout(() => {
         | 
| 235 | 
            +
                        element.classList.remove('copy_clip_ok');
         | 
| 236 | 
            +
                    }, 1000);
         | 
| 237 | 
            +
                });
         | 
| 238 | 
            +
            }
         | 
| 239 | 
            +
             | 
| 240 | 
            +
            function adjustOutputHeight() {
         | 
| 241 | 
            +
                const outputDiv = document.getElementById('output');
         | 
| 242 | 
            +
                const windowHeight = window.innerHeight;
         | 
| 243 | 
            +
                const outputTop = outputDiv.getBoundingClientRect().top;
         | 
| 244 | 
            +
                const maxHeight = windowHeight - outputTop - 30; // 20px for padding/margin
         | 
| 245 | 
            +
                outputDiv.style.height = `${maxHeight}px`;
         | 
| 246 | 
            +
                fitAddon.fit();
         | 
| 247 | 
            +
            }
         | 
| 248 | 
            +
             | 
| 249 | 
            +
            function initResizer() {
         | 
| 250 | 
            +
                const resizer = document.getElementById('resizer');
         | 
| 251 | 
            +
                const tableContainer = document.getElementById('tableContainer');
         | 
| 252 | 
            +
                let startY, startHeight;
         | 
| 253 | 
            +
             | 
| 254 | 
            +
                resizer.addEventListener('mousedown', (e) => {
         | 
| 255 | 
            +
                    startY = e.clientY;
         | 
| 256 | 
            +
                    startHeight = parseInt(document.defaultView.getComputedStyle(tableContainer).height, 10);
         | 
| 257 | 
            +
                    document.documentElement.addEventListener('mousemove', doDrag, false);
         | 
| 258 | 
            +
                    document.documentElement.addEventListener('mouseup', stopDrag, false);
         | 
| 259 | 
            +
                });
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                function doDrag(e) {
         | 
| 262 | 
            +
                    tableContainer.style.height = `${startHeight + e.clientY - startY}px`;
         | 
| 263 | 
            +
                    adjustOutputHeight();
         | 
| 264 | 
            +
                }
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                function stopDrag() {
         | 
| 267 | 
            +
                    document.documentElement.removeEventListener('mousemove', doDrag, false);
         | 
| 268 | 
            +
                    document.documentElement.removeEventListener('mouseup', stopDrag, false);
         | 
| 269 | 
            +
                }
         | 
| 270 | 
            +
            }
         | 
| 271 | 
            +
             | 
| 272 | 
            +
            window.addEventListener('resize', adjustOutputHeight);
         | 
| 273 | 
            +
            window.addEventListener('load', () => {
         | 
| 274 | 
            +
                adjustOutputHeight();
         | 
| 275 | 
            +
                initResizer();
         | 
| 276 | 
            +
            });
         | 
| 277 | 
            +
             | 
| 278 | 
            +
            fetchExecutables();
         | 
| 279 | 
            +
            fetchCommands();
         | 
| 280 | 
            +
            setInterval(fetchCommands, 5000);
         |