Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ In all cases, a tooltip is added to a date+time field that shows the full repres
When using very old browsers, the required date/time functions may not be present. In that case SaltGUI reverts to simply displaying the reported time from the Salt system. The tooltip is then not shown.


## Theme
SaltGUI follows the browser color-scheme preference.
When SaltGUI is embedded, it also uses theme hints from the parent frame when those are available.


## Templates
SaltGUI supports command templates for easier command entry into the command-box.
The menu item for that becomes visible there when you define one or more templates
Expand Down
6 changes: 4 additions & 2 deletions saltgui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

<head>
<title>SaltGUI</title>
<script src="static/scripts/theme.js"></script>
<link rel='stylesheet' type='text/css' href='static/stylesheets/theme.css'/>
<link rel='stylesheet' type='text/css' href='static/stylesheets/controls.css'/>
<link rel='stylesheet' type='text/css' href='static/stylesheets/main.css'/>
<link rel='stylesheet' type='text/css' href='static/stylesheets/login.css'/>
Expand All @@ -26,8 +28,8 @@
<h1 class='logo'><div id="logo">SaltGUI</div></h1>
<h1 class='fab'>
<svg width="36" height="36">
<circle id='button-manual-run' cx="18" cy="18" r="18" fill="#4caf50" />
<text style='pointer-events: none' fill="white" font-weight="bold" font-size="20" x="6" y="23">&gt;_</text>
<circle id='button-manual-run' cx="18" cy="18" r="18" fill="var(--color-text-accent)" />
<text style='pointer-events: none' fill="var(--color-text-inverse)" font-weight="bold" font-size="20" x="6" y="23">&gt;_</text>
</svg>
</h1>
<!-- 1F4D6 = OPEN BOOK -->
Expand Down
2 changes: 1 addition & 1 deletion saltgui/static/scripts/Character.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ export class Character {
}

static buttonInText (txt) {
return "<span style=\"background-color:#eee\">&nbsp;" + txt + "&nbsp;</span>";
return "<span class=\"button-in-text\">&nbsp;" + txt + "&nbsp;</span>";
}
}
4 changes: 2 additions & 2 deletions saltgui/static/scripts/Router.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class Router {
this.pages = [];
Router.currentPage = undefined;

this._registerPage(new LoginPage(this));
this._registerPage(Router.loginPage = new LoginPage(this));
this._registerPage(Router.minionsPage = new MinionsPage(this));
this._registerPage(Router.keysPage = new KeysPage(this));
this._registerPage(Router.grainsPage = new GrainsPage(this));
Expand Down Expand Up @@ -272,7 +272,7 @@ export class Router {
// perform the hiding/showing
for (let nr = 1; nr <= 2; nr++) {
const item = document.getElementById("button-" + pPage.path + nr);
item.style.color = !visible && hasVisibleChild ? "lightgray" : "black";
item.classList.toggle("menu-item-dimmed", !visible && hasVisibleChild);
if (!visible) {
// hide the shortcut indicator
item.classList.remove("menu-item-first-letter");
Expand Down
4 changes: 2 additions & 2 deletions saltgui/static/scripts/output/OutputDocumentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,12 +270,12 @@ export class OutputDocumentation {
// replace ``......``
// e.g. in "sys.doc state.apply"
// named groups are only introduced in ES9/2018
out = out.replace(/``([^`]*)``/g, "<span style='background-color: #575757'>$1</span>");
out = out.replace(/``([^`]*)``/g, "<span class='doc-inline-code'>$1</span>");

// replace `......`
// e.g. in "sys.doc state.apply"
// named groups are only introduced in ES9/2018
out = out.replace(/`([^`]*)`/g, "<span style='color: yellow'>$1</span>");
out = out.replace(/`([^`]*)`/g, "<span class='doc-inline-highlight'>$1</span>");

// remove whitespace at end of lines
out = out.replace(/ *\n/gm, "");
Expand Down
12 changes: 4 additions & 8 deletions saltgui/static/scripts/output/OutputHighstateSummaryOriginal.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ export class OutputHighstateSummaryOriginal {

let txt = "\nSummary for " + pMinionId;
txt += "\n------------";
const summarySpan = Utils.createSpan("", txt);
summarySpan.style.color = "aqua";
const summarySpan = Utils.createSpan("text-info", txt);
pDiv.append(summarySpan);

const total = pSucceeded + pSkipped + pFailed;
Expand All @@ -24,7 +23,6 @@ export class OutputHighstateSummaryOriginal {
if (pChangesSummary > 0) {
txt = " (";
const oSpan = Utils.createSpan("", txt);
oSpan.style.color = "white";
pDiv.append(oSpan);

txt = "changed=" + pChangesSummary;
Expand All @@ -33,7 +31,6 @@ export class OutputHighstateSummaryOriginal {

txt = ")";
const cSpan = Utils.createSpan("", txt);
cSpan.style.color = "white";
pDiv.append(cSpan);
}

Expand All @@ -42,7 +39,7 @@ export class OutputHighstateSummaryOriginal {
if (pFailed > 0) {
failedSpan.classList.add("task-failure");
} else {
failedSpan.style.color = "aqua";
failedSpan.classList.add("text-info");
}
pDiv.append(failedSpan);

Expand All @@ -57,16 +54,15 @@ export class OutputHighstateSummaryOriginal {
if (pFailed > 0) {
failureSpan.classList.add("task-failure");
} else {
failureSpan.style.color = "aqua";
failureSpan.classList.add("text-info");
}
pDiv.append(failureSpan);
}

txt = "\n------------";
txt += "\nTotal states run: " + total;
txt += "\nTotal run time: " + Output.getDuration(pTotalMilliSeconds);
const totalsSpan = Utils.createSpan("", txt);
totalsSpan.style.color = "aqua";
const totalsSpan = Utils.createSpan("text-info", txt);
pDiv.append(totalsSpan);
pDiv.style.cursor = "pointer";
}
Expand Down
2 changes: 0 additions & 2 deletions saltgui/static/scripts/panels/HighState.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,6 @@ export class HighStatePanel extends Panel {
// for information (keys.length > this._maxHighstateStates)

const span = Utils.createSpan("task");
span.style.backgroundColor = "black";

// this also sets the span's class(es)
Output._setTaskToolTip(span, data);
Expand Down Expand Up @@ -516,7 +515,6 @@ export class HighStatePanel extends Panel {

// remove the priority indicator from the key
const itemSpan = Utils.createSpan(["tasksummary", className], character);
itemSpan.style.backgroundColor = "black";
summarySpan.append(itemSpan);
Utils.addToolTip(itemSpan, className.replace("task-", "").replace("-", " with "));
}
Expand Down
7 changes: 4 additions & 3 deletions saltgui/static/scripts/panels/Jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,12 +302,13 @@ export class JobsPanel extends Panel {
}
if (newLevel > oldLevel) {
span.dataset.level = newLevel;
span.classList.remove("text-success", "text-warning", "text-error");
if (newLevel === 1) {
span.style.color = "green";
span.classList.add("text-success");
} else if (newLevel === 2) {
span.style.color = "orange";
span.classList.add("text-warning");
} else if (newLevel === 3) {
span.style.color = "red";
span.classList.add("text-error");
}
}
span.style.removeProperty("display");
Expand Down
12 changes: 6 additions & 6 deletions saltgui/static/scripts/panels/JobsDetails.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,16 +266,16 @@ export class JobsDetailsPanel extends JobsPanel {
if (pData.Minions.length === 0) {
detailsHTML += "<span>";
} else if (keyCount === pData.Minions.length) {
detailsHTML += "<span style='color: green'>";
detailsHTML += "<span class='job-details-success'>";
} else {
detailsHTML += "<span style='color: red'>";
detailsHTML += "<span class='job-details-failure'>";
}
detailsHTML += Utils.txtZeroOneMany(keyCount,
"no results", "{0} result", "{0} results");
detailsHTML += "</span>";

if (keyCount < pData.Minions.length) {
detailsHTML += ", <span style='color: red'>";
detailsHTML += ", <span class='job-details-failure'>";
detailsHTML += pData.Minions.length - keyCount;
detailsHTML += " missing</span>";
}
Expand All @@ -299,13 +299,13 @@ export class JobsDetailsPanel extends JobsPanel {
for (const key of keys) {
detailsHTML += ", ";
if (key === "0-0") {
detailsHTML += "<span style='color: green'>";
detailsHTML += "<span class='job-details-success'>";
detailsHTML += Utils.txtZeroOneMany(summary[key], "", "{0} success", "{0} successes");
} else if (key.startsWith("0-")) {
detailsHTML += "<span style='color: orange'>";
detailsHTML += "<span class='job-details-warning'>";
detailsHTML += Utils.txtZeroOneMany(summary[key], "", "{0} success", "{0} successes");
} else if (key.startsWith("1-")) {
detailsHTML += "<span style='color: red'>";
detailsHTML += "<span class='job-details-failure'>";
detailsHTML += Utils.txtZeroOneMany(summary[key], "", "{0} failure", "{0} failures");
} else {
// if (key.startsWith("2-"))
Expand Down
75 changes: 40 additions & 35 deletions saltgui/static/scripts/panels/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,21 +260,21 @@ export class LoginPanel extends Panel {
break;
case "no-session":
// gray because we cannot prove that the user was/wasnt logged in
this._showNoticeText("gray", "Not logged in", "notice_not_logged_in");
this._showNoticeText("var(--color-notice-muted)", "Not logged in", "notice_not_logged_in");
break;
case "session-cancelled":
this._showNoticeText("#F44336", "Session cancelled", "notice-session-cancelled");
this._showNoticeText("var(--color-notice-danger)", "Session cancelled", "notice-session-cancelled");
break;
case "session-expired":
this._showNoticeText("#F44336", "Session expired", "notice-session-expired");
this._showNoticeText("var(--color-notice-danger)", "Session expired", "notice-session-expired");
break;
case "logout":
// gray because this is the result of a user action
this._showNoticeText("gray", "Logout", "notice_logout");
this._showNoticeText("var(--color-notice-muted)", "Logout", "notice_logout");
break;
default:
// should not occur
this._showNoticeText("#F44336", reason, "notice_other:" + reason);
this._showNoticeText("var(--color-notice-danger)", reason, "notice_other:" + reason);
}

this._enableLoginControls(true);
Expand Down Expand Up @@ -303,11 +303,40 @@ export class LoginPanel extends Panel {
}

_onLoginSuccess () {
this._showNoticeText("#4CAF50", "Please wait" + Character.HORIZONTAL_ELLIPSIS, "notice_please_wait");
this._showNoticeText("var(--color-text-accent)", "Please wait" + Character.HORIZONTAL_ELLIPSIS, "notice_please_wait");

Utils.setStorageItem("local", "salt-motd-txt", "");
Utils.setStorageItem("local", "salt-motd-html", "");

this.bootstrapSession();

// allow the success message to be seen
window.setTimeout(() => {
// erase credentials since we don't do page-refresh
this.usernameField.value = "";
this.passwordField.value = "";
if (Utils.getStorageItem("session", "login_response") !== null) {
// we might have been logged out in this first second
// e.g. when clock between client and server differs more than the session timout
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get("page")) {
// a redirect page is specified
const params = {};
for (const pair of urlParams.entries()) {
params[pair[0]] = pair[1];
}
const page = params["page"];
delete params["page"];
this.router.goTo(page, params);
} else {
this.router.goTo("");
}
}
}, 1000);

}

bootstrapSession () {
// We need these functions to populate the dropdown boxes
const wheelConfigValuesPromise = this.api.getWheelConfigValues();
const runnerStateOrchestrateShowSlsPromise = this.api.getRunnerStateOrchestrateShowSls();
Expand Down Expand Up @@ -339,30 +368,6 @@ export class LoginPanel extends Panel {
});
/* eslint-enable no-unused-vars */

// allow the success message to be seen
window.setTimeout(() => {
// erase credentials since we don't do page-refresh
this.usernameField.value = "";
this.passwordField.value = "";
if (Utils.getStorageItem("session", "login_response") !== null) {
// we might have been logged out in this first second
// e.g. when clock between client and server differs more than the session timout
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get("page")) {
// a redirect page is specified
const params = {};
for (const pair of urlParams.entries()) {
params[pair[0]] = pair[1];
}
const page = params["page"];
delete params["page"];
this.router.goTo(page, params);
} else {
this.router.goTo("");
}
}
}, 1000);

BeaconsMinionPanel.getAvailableBeacons(this.api);
}

Expand Down Expand Up @@ -517,19 +522,19 @@ export class LoginPanel extends Panel {
_onLoginFailure (error) {
if (typeof error === "string") {
// something detected before trying to login
this._showNoticeText("#F44336", error, "notice_login_string_error");
this._showNoticeText("var(--color-notice-danger)", error, "notice_login_string_error");
} else if (error && error.status === 503) {
// Service Unavailable
// e.g. salt-api running but salt-master not running
this._showNoticeText("#F44336", error.message, "notice_login_service_unavailable");
this._showNoticeText("var(--color-notice-danger)", error.message, "notice_login_service_unavailable");
} else if (error && error.status === -1) {
// No permissions: login valid, but no api functions executable
// e.g. PAM says OK and /etc/salt/master says NO
this._showNoticeText("#F44336", error.message, "notice_login_other_error");
this._showNoticeText("var(--color-notice-danger)", error.message, "notice_login_other_error");
} else if (error.toString().startsWith("TypeError: NetworkError")) {
this._showNoticeText("#F44336", "Network Error", "notice_login_other_error");
this._showNoticeText("var(--color-notice-danger)", "Network Error", "notice_login_other_error");
} else {
this._showNoticeText("#F44336", "Authentication failed", "notice_auth_failed");
this._showNoticeText("var(--color-notice-danger)", "Authentication failed", "notice_auth_failed");
}

this._enableLoginControls(true);
Expand Down
2 changes: 1 addition & 1 deletion saltgui/static/scripts/panels/Nodegroups.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export class NodegroupsPanel extends Panel {

_addNodegroupRow (pNodegroup, pAllNodegroups) {
const tr = Utils.createTr("no-search", null, "ng-" + pNodegroup);
tr.style.borderTop = "4px double #ddd";
tr.style.borderTop = "4px double var(--color-border-default)";

const menuTd = Utils.createTd();
tr.dropdownmenu = new DropDownMenu(menuTd, "smaller");
Expand Down
2 changes: 1 addition & 1 deletion saltgui/static/scripts/panels/Stats.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class StatsPanel extends Panel {

_handleStats (pStatsData) {
if (this.showErrorRowInstead(pStatsData)) {
this.statsTd.innerHTML = "<span style='color:red'>this error is typically caused by using the <tt>collect_stats: True</tt> setting in the master configuration file, which is broken in at least the recent versions of salt-api</span>";
this.statsTd.innerHTML = "<span class='text-error'>this error is typically caused by using the <tt>collect_stats: True</tt> setting in the master configuration file, which is broken in at least the recent versions of salt-api</span>";
window.clearInterval(this.updateStatsInterval);
this.updateStatsInterval = null;
return;
Expand Down
20 changes: 20 additions & 0 deletions saltgui/static/scripts/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
(function initializeTheme () {
const context = globalThis;
const root = document.documentElement;
const mediaQuery = context.matchMedia ? context.matchMedia("(prefers-color-scheme: dark)") : null;

function wantsDarkTheme () {
return mediaQuery ? mediaQuery.matches : false;
}

function applyTheme () {
root.dataset.theme = wantsDarkTheme() ? "dark" : "light";
}

applyTheme();
if (mediaQuery && typeof mediaQuery.addEventListener === "function") {
mediaQuery.addEventListener("change", applyTheme);
} else if (mediaQuery && typeof mediaQuery.addListener === "function") {
mediaQuery.addListener(applyTheme);
}
})();
2 changes: 1 addition & 1 deletion saltgui/static/stylesheets/beacons.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@

.beacon-disabled,
.beacon-waiting {
color: gray;
color: var(--color-text-muted);
}
Loading
Loading