-
- Finished: unknown
-
-
- Exit Code: unknown
-
+
-
+
Kill
diff --git a/webui.dev/js/ActionStatusDisplay.js b/webui.dev/js/ActionStatusDisplay.js
new file mode 100644
index 0000000..757a77a
--- /dev/null
+++ b/webui.dev/js/ActionStatusDisplay.js
@@ -0,0 +1,41 @@
+export class ActionStatusDisplay {
+ constructor (parentElement) {
+ this.exitCodeElement = document.createElement('span')
+ this.statusElement = document.createElement('span')
+ this.statusElement.innerText = 'unknown'
+
+ parentElement.innerText = ''
+ parentElement.appendChild(this.statusElement)
+ parentElement.appendChild(this.exitCodeElement)
+ }
+
+ getText () {
+ return this.statusElement.innerText
+ }
+
+ update (logEntry) {
+ this.statusElement.classList.remove(...this.statusElement.classList)
+
+ if (logEntry.executionFinished) {
+ this.statusElement.innerText = 'Completed'
+ this.exitCodeElement.innerText = ', Exit code: ' + logEntry.exitCode
+
+ if (logEntry.exitCode === 0) {
+ this.statusElement.classList.add('action-success')
+ } else if (logEntry.blocked) {
+ this.statusElement.innerText = 'Blocked'
+ this.statusElement.classList.add('action-blocked')
+ this.exitCodeElement.innerText = ''
+ } else if (logEntry.timedOut) {
+ this.statusElement.innerText = 'Timed out'
+ this.statusElement.classList.add('action-timeout')
+ this.exitCodeElement.innerText = ''
+ } else {
+ this.statusElement.classList.add('action-nonzero-exit')
+ }
+ } else {
+ this.statusElement.innerText = 'Still running...'
+ this.exitCodeElement.innerText = ''
+ }
+ }
+}
diff --git a/webui.dev/js/ExecutionDialog.js b/webui.dev/js/ExecutionDialog.js
index a4bac44..340e8fc 100644
--- a/webui.dev/js/ExecutionDialog.js
+++ b/webui.dev/js/ExecutionDialog.js
@@ -1,3 +1,5 @@
+import { ActionStatusDisplay } from './ActionStatusDisplay.js'
+
// This ExecutionDialog is NOT a custom HTML element, but rather just picks up
// the element out of index.html and just re-uses that - as only
// one dialog can be shown at a time.
@@ -8,7 +10,6 @@ export class ExecutionDialog {
this.domIcon = document.getElementById('execution-dialog-icon')
this.domTitle = document.getElementById('execution-dialog-title')
this.domOutput = document.getElementById('execution-dialog-xterm')
- this.domOutputDetails = document.getElementById('execution-dialog-output-details')
this.domOutputToggleBig = document.getElementById('execution-dialog-toggle-size')
this.domOutputToggleBig.onclick = () => {
this.toggleSize()
@@ -16,32 +17,28 @@ export class ExecutionDialog {
this.domBtnKill = document.getElementById('execution-dialog-kill-action')
- this.domDatetimeStarted = document.getElementById('execution-dialog-datetime-started')
- this.domDatetimeFinished = document.getElementById('execution-dialog-datetime-finished')
- this.domExitCode = document.getElementById('execution-dialog-exit-code')
- this.domStatus = document.getElementById('execution-dialog-status')
+ this.domDuration = document.getElementById('execution-dialog-duration')
+ this.domStatus = new ActionStatusDisplay(document.getElementById('execution-dialog-status'))
this.domExecutionBasics = document.getElementById('execution-dialog-basics')
this.domExecutionDetails = document.getElementById('execution-dialog-details')
- this.domExecutionOutput = document.getElementById('execution-dialog-output')
window.terminal.open(this.domOutput)
}
showOutput () {
this.domOutput.hidden = false
- this.domOutputDetails.open = true
- this.domExecutionOutput.hidden = false
+ this.domOutput.hidden = false
}
toggleSize () {
if (this.dlg.classList.contains('big')) {
this.dlg.classList.remove('big')
- this.domOutputDetails.open = false
} else {
this.dlg.classList.add('big')
- this.domOutputDetails.open = true
}
+
+ window.terminal.fit.fit()
}
reset () {
@@ -54,10 +51,7 @@ export class ExecutionDialog {
this.domIcon.innerText = ''
this.domTitle.innerText = 'Waiting for result... '
- this.domExitCode.innerText = '?'
- this.domStatus.className = ''
- this.domDatetimeStarted.innerText = ''
- this.domDatetimeFinished.innerText = ''
+ this.domDuration.innerText = ''
// window.terminal.close()
@@ -68,12 +62,9 @@ export class ExecutionDialog {
this.domExecutionBasics.hidden = false
this.domExecutionDetails.hidden = true
- this.domOutputDetails.open = false
window.terminal.reset()
window.terminal.fit.fit()
-
- this.domExecutionOutput.hidden = true
}
show (actionButton) {
@@ -122,7 +113,7 @@ export class ExecutionDialog {
executionTick () {
this.executionSeconds++
- this.domDatetimeStarted.innerText = this.executionSeconds + ' seconds ago'
+ this.updateDuration(this.executionSeconds + ' seconds ago', '')
}
hideEverythingApartFromOutput () {
@@ -158,50 +149,52 @@ export class ExecutionDialog {
})
}
+ updateDuration (started, finished) {
+ if (finished === '') {
+ this.domDuration.innerHTML = started
+ } else {
+ let delta = ''
+
+ try {
+ delta = (new Date(finished) - new Date(started)) / 1000
+ delta = new Intl.RelativeTimeFormat().format(delta, 'seconds').replace('in ', '').replace('ago', '')
+ } catch (e) {
+ console.warn('Failed to calculate delta', e)
+ }
+
+ this.domDuration.innerHTML = started + ' → ' + finished
+
+ if (delta !== '') {
+ this.domDuration.innerHTML += ' (' + delta + ')'
+ }
+ }
+ }
+
renderExecutionResult (res) {
this.res = res
clearInterval(window.executionDialogTicker)
- this.domExecutionOutput.hidden = false
+ this.domOutput.hidden = false
if (!this.hideDetailsOnResult) {
this.domExecutionDetails.hidden = false
- } else {
- this.domOutputDetails.open = true
}
this.executionTrackingId = res.logEntry.executionTrackingId
this.domBtnKill.disabled = res.logEntry.executionFinished
- if (res.logEntry.executionFinished) {
- this.domStatus.innerText = 'Completed'
- this.domStatus.classList.add('action-success')
- this.domDatetimeFinished.innerText = res.logEntry.datetimeFinished
-
- if (res.logEntry.timedOut) {
- this.domExitCode.innerText = 'Timed out'
- this.domStatus.classList.add('action-timeout')
- } else if (res.logEntry.blocked) {
- this.domStatus.innerText = 'Blocked'
- this.domStatus.classList.add('action-blocked')
- } else if (res.logEntry.exitCode !== 0) {
- this.domStatus.innerText = 'Non-Zero Exit'
- this.domStatus.classList.add('action-nonzero-exit')
- } else {
- this.domExitCode.innerText = res.logEntry.exitCode
- }
- } else {
- this.domDatetimeFinished.innerText = 'Still running...'
- this.domExitCode.innerText = 'Still running...'
- this.domStatus.innerText = 'Still running...'
- }
+ this.domStatus.update(res.logEntry)
this.domIcon.innerHTML = res.logEntry.actionIcon
this.domTitle.innerText = res.logEntry.actionTitle
- this.domDatetimeStarted.innerText = res.logEntry.datetimeStarted
+ if (res.logEntry.executionFinished) {
+ this.updateDuration(res.logEntry.datetimeStarted, res.logEntry.datetimeFinished)
+ } else {
+ this.updateDuration(res.logEntry.datetimeStarted, 'Still running...')
+ }
window.terminal.reset()
window.terminal.write(res.logEntry.output, () => {
diff --git a/webui.dev/js/marshaller.js b/webui.dev/js/marshaller.js
index fce3add..2d91a25 100644
--- a/webui.dev/js/marshaller.js
+++ b/webui.dev/js/marshaller.js
@@ -2,6 +2,7 @@ import './ActionButton.js' // To define action-button
import { ExecutionDialog } from './ExecutionDialog.js'
import { Terminal } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
+import { ActionStatusDisplay } from './ActionStatusDisplay.js'
/**
* This is a weird function that just sets some globals.
@@ -510,22 +511,14 @@ export function marshalLogsJsonToHtml (json) {
const tpl = document.getElementById('tplLogRow')
const row = tpl.content.querySelector('tr').cloneNode(true)
- let logTableExitCode = logEntry.exitCode
-
- if (logEntry.exitCode === 0) {
- logTableExitCode = 'OK'
- }
-
- if (logEntry.timedOut) {
- logTableExitCode += ' (timed out)'
- }
-
row.querySelector('.timestamp').innerText = logEntry.datetimeStarted
row.querySelector('.content').innerText = logEntry.actionTitle
row.querySelector('.icon').innerHTML = logEntry.actionIcon
- row.querySelector('.exit-code').innerText = logTableExitCode
row.setAttribute('title', logEntry.actionTitle)
+ const exitCodeDisplay = new ActionStatusDisplay(row.querySelector('.exit-code'))
+ exitCodeDisplay.update(logEntry)
+
row.querySelector('.content').onclick = () => {
window.executionDialog.reset()
window.executionDialog.show()
diff --git a/webui.dev/style.css b/webui.dev/style.css
index 551a23b..a4fb604 100644
--- a/webui.dev/style.css
+++ b/webui.dev/style.css
@@ -17,10 +17,16 @@ dialog {
padding: 0;
}
+dialog[open] {
+ display: flex;
+ flex-direction: column;
+}
+
dialog.big {
max-width: 100vw;
width: 100vw;
height: 100vh;
+ max-height: 100dvh;
border: none;
margin: 0;
}
@@ -226,11 +232,6 @@ details {
flex-grow: 1;
}
-details[open] {
- margin-top: 1em;
- display: block;
-}
-
/* General Buttons */
button,
@@ -471,24 +472,19 @@ div.display {
font-size: small;
}
-#execution-dialog-output {
- display: flex;
- flex-direction: row;
- justify-content: start;
-}
-
#execution-dialog-xterm {
- overflow: auto;
-}
-
-.xterm .xterm-viewport {
- overflow-y: auto !important;
+ flex-grow: 1;
}
.padded-content {
padding: 1em;
}
+.padded-content-sides {
+ padding-left: 1em;
+ padding-right: 1em;
+}
+
.ta-left {
text-align: left;
}