aboutsummaryrefslogtreecommitdiff
path: root/src/app
diff options
context:
space:
mode:
author0neGal <mail@0negal.com>2022-05-02 21:49:20 +0200
committerGitHub <noreply@github.com>2022-05-02 21:49:20 +0200
commitc40e331bdc4b2375d3802a515c9b7a032118dea7 (patch)
tree77c8d84c0fc9d0fbb742cfeeb80efa4762784a9d /src/app
parent9f2f77558238c28ceb8ff4fca2096602671779e5 (diff)
parent847a2178e7823749e3096daf24dfcd3df8b236cb (diff)
downloadViper-c40e331bdc4b2375d3802a515c9b7a032118dea7.tar.gz
Viper-c40e331bdc4b2375d3802a515c9b7a032118dea7.zip
Merge branch 'main' into enabledmods
Diffstat (limited to 'src/app')
-rw-r--r--src/app/browser.js95
-rw-r--r--src/app/icons/apply.pngbin0 -> 2555 bytes
-rw-r--r--src/app/icons/check.pngbin0 -> 373 bytes
-rw-r--r--src/app/icons/download.pngbin0 -> 2200 bytes
-rw-r--r--src/app/icons/filter.pngbin0 -> 1074 bytes
-rw-r--r--src/app/icons/settings.pngbin0 -> 2496 bytes
-rw-r--r--src/app/index.html115
-rw-r--r--src/app/launcher.js6
-rw-r--r--src/app/main.css288
-rw-r--r--src/app/main.js78
-rw-r--r--src/app/settings.js137
-rw-r--r--src/app/toast.js2
12 files changed, 659 insertions, 62 deletions
diff --git a/src/app/browser.js b/src/app/browser.js
index ded12fa..e22ab3c 100644
--- a/src/app/browser.js
+++ b/src/app/browser.js
@@ -4,6 +4,58 @@ var packages = [];
var Browser = {
maxentries: 50,
+ filters: {
+ get: () => {
+ let filtered = [];
+ let unfiltered = [];
+ let checks = browser.querySelectorAll("#filters .check");
+
+ for (let i = 0; i < checks.length; i++) {
+ if (! checks[i].classList.contains("checked")) {
+ filtered.push(checks[i].getAttribute("value"));
+ } else {
+ unfiltered.push(checks[i].getAttribute("value"));
+ }
+ }
+
+ return {
+ filtered,
+ unfiltered
+ };
+ },
+ isfiltered: (categories) => {
+ let filtered = Browser.filters.get().filtered;
+ let unfiltered = Browser.filters.get().unfiltered;
+ let state = false;
+ if (categories.length == 0) {return true}
+ for (let i = 0; i < categories.length; i++) {
+ if (filtered.includes(categories[i])) {
+ state = true;
+ continue
+ } else if (unfiltered.includes(categories[i])) {
+ state = false;
+ continue
+ }
+
+ state = true;
+ }
+
+ return state;
+ },
+ toggle: (state) => {
+ if (state == false) {
+ filters.classList.remove("shown");
+ return
+ }
+
+ filters.classList.toggle("shown");
+ let filterRect = filter.getBoundingClientRect();
+ let spacing = parseInt(getComputedStyle(filters).getPropertyValue("--spacing"));
+
+ filters.style.top = filterRect.bottom - spacing;
+ filters.style.right = filterRect.right - filterRect.left + filterRect.width - (spacing / 2);
+ },
+ },
toggle: (state) => {
if (state) {
browser.scrollTo(0, 0);
@@ -16,6 +68,7 @@ var Browser = {
return
} else if (! state) {
if (state != undefined) {
+ Browser.filters.toggle(false);
overlay.classList.remove("shown")
browser.classList.remove("shown")
return
@@ -38,11 +91,14 @@ var Browser = {
}
for (let i in packages) {
- if (i == Browser.maxentries) {Browser.endoflist();break}
new BrowserElFromObj(packages[i]);
}
},
loading: (string) => {
+ if (Browser.filters.get().unfiltered.length == 0) {
+ string = lang("gui.browser.noresults");
+ }
+
if (string) {
browserEntries.innerHTML = `<div class="loading">${string}</div>`;
}
@@ -52,6 +108,7 @@ var Browser = {
}
},
endoflist: () => {
+ if (browserEntries.querySelector(".message")) {return}
browserEntries.innerHTML += `<div class="message">${lang('gui.browser.endoflist')}</div>`
},
search: (string) => {
@@ -59,26 +116,25 @@ var Browser = {
let res = fuse.search(string);
if (res.length < 1) {
- Browser.loading("No results...")
+ Browser.loading(lang("gui.browser.noresults"))
return
}
for (let i = 0; i < res.length; i++) {
- if (i == Browser.maxentries) {Browser.endoflist();break}
new BrowserElFromObj(res[i].item);
}
},
setbutton: (mod, string) => {
mod = normalize(mod);
- if (document.getElementById(mod)) {
- let elems = document.querySelectorAll(`#${mod}`);
+ if (browserEntries.querySelector(`#mod-${mod}`)) {
+ let elems = browserEntries.querySelectorAll(`.el#mod-${mod}`);
for (let i = 0; i < elems.length; i++) {
elems[i].querySelector(".text button").innerHTML = string;
}
} else {
let make = (str) => {
- if (document.getElementById(str)) {
+ if (browserEntries.querySelector(`#mod-${str}`)) {
return Browser.setbutton(str, string);
} else {
return false;
@@ -104,10 +160,6 @@ var Browser = {
}
}
-document.body.addEventListener("keyup", (e) => {
- if (e.key == "Escape") {Browser.toggle(false)}
-})
-
function BrowserElFromObj(obj) {
let pkg = {...obj, ...obj.versions[0]};
@@ -118,11 +170,17 @@ function BrowserElFromObj(obj) {
url: pkg.package_url,
download: pkg.download_url,
version: pkg.version_number,
+ categories: pkg.categories,
description: pkg.description
})
}
function BrowserEl(properties) {
+ if (Browser.filters.isfiltered(properties.categories)) {return}
+
+ let entries = browser.querySelectorAll(".el").length;
+ if (entries == Browser.maxentries) {Browser.endoflist();return}
+
properties = {
title: "No name",
version: "1.0.0",
@@ -174,15 +232,16 @@ function BrowserEl(properties) {
}
browserEntries.innerHTML += `
- <div class="el" id="${normalize(properties.title)}">
+ <div class="el" id="mod-${normalize(properties.title)}">
<div class="image">
<img src="${properties.image}">
+ <img class="blur" src="${properties.image}">
</div>
<div class="text">
<div class="title">${properties.title}</div>
<div class="description">${properties.description}</div>
- <button onclick="installFromURL('${properties.download}')">${installstr}</button>
- <button onclick="require('electron').shell.openExternal('${properties.url}')">${lang('gui.browser.info')}</button>
+ <button class="install" onclick="installFromURL('${properties.download}')">${installstr}</button>
+ <button class="info" onclick="require('electron').shell.openExternal('${properties.url}')">${lang('gui.browser.info')}</button>
<button class="visual">${properties.version}</button>
<button class="visual">${lang("gui.browser.madeby")} ${properties.author}</button>
</div>
@@ -247,6 +306,7 @@ function normalize(items) {
let searchtimeout;
let searchstr = "";
search.addEventListener("keyup", () => {
+ Browser.filters.toggle(false);
clearTimeout(searchtimeout);
if (searchstr != search.value) {
@@ -262,3 +322,12 @@ search.addEventListener("keyup", () => {
}, 500)
}
})
+
+browser.addEventListener("scroll", () => {
+ Browser.filters.toggle(false);
+})
+
+let checks = document.querySelectorAll(".check");
+for (let i = 0; i < checks.length; i++) {
+ checks[i].setAttribute("onclick", "this.classList.toggle('checked');Browser.loadfront();search.value = ''")
+}
diff --git a/src/app/icons/apply.png b/src/app/icons/apply.png
new file mode 100644
index 0000000..915f809
--- /dev/null
+++ b/src/app/icons/apply.png
Binary files differ
diff --git a/src/app/icons/check.png b/src/app/icons/check.png
new file mode 100644
index 0000000..6c7f43f
--- /dev/null
+++ b/src/app/icons/check.png
Binary files differ
diff --git a/src/app/icons/download.png b/src/app/icons/download.png
new file mode 100644
index 0000000..189c50d
--- /dev/null
+++ b/src/app/icons/download.png
Binary files differ
diff --git a/src/app/icons/filter.png b/src/app/icons/filter.png
new file mode 100644
index 0000000..ade45ef
--- /dev/null
+++ b/src/app/icons/filter.png
Binary files differ
diff --git a/src/app/icons/settings.png b/src/app/icons/settings.png
new file mode 100644
index 0000000..3f7715a
--- /dev/null
+++ b/src/app/icons/settings.png
Binary files differ
diff --git a/src/app/index.html b/src/app/index.html
index 8222416..02a150f 100644
--- a/src/app/index.html
+++ b/src/app/index.html
@@ -10,15 +10,121 @@
<div id="toasts"></div>
<div id="winbtns">
+ <div id="settings" onclick="Settings.toggle(true)"></div>
<div id="minimize" onclick="ipcRenderer.send('minimize')"></div>
<div id="close" onclick="ipcRenderer.send('exit')"></div>
</div>
+ <div id="dragUI">
+ <div id="dragitems">
+ <div id="icon"></div>
+ <div id="text">%%gui.mods.dragdrop%%</div>
+ </div>
+ </div>
- <div id="overlay" onclick="Browser.toggle(false)"></div>
- <div id="browser">
- <div id="misc">
+ <div id="overlay" onclick="Browser.toggle(false);Settings.toggle(false)"></div>
+ <div class="popup" id="options">
+ <div class="misc">
+ <div style="width:100%"></div>
+ <button id="apply" onclick="Settings.toggle(false);Settings.apply()">
+ <img src="icons/apply.png">
+ %%gui.settings.save%%
+ </button>
+ <button id="close" onclick="Settings.toggle(false);Settings.load()">
+ <img src="icons/close.png">
+ %%gui.settings.discard%%
+ </button>
+ </div>
+ <div class="options">
+ <h2>%%gui.settings.title.ns%%</h2>
+ <div class="option" name="nsargs">
+ <div class="text">
+ %%gui.settings.nsargs.title%%
+ <div class="desc">
+ %%gui.settings.nsargs.desc%%
+ </div>
+ </div>
+ <div class="actions">
+ <input>
+ </div>
+ </div>
+ <h2>%%gui.settings.title.language%%</h2>
+ <div class="option" name="autolang">
+ <div class="text">
+ %%gui.settings.autolang.title%%
+ <div class="desc">
+ %%gui.settings.autolang.desc%%
+ </div>
+ </div>
+ <div class="actions">
+ <button class="switch off"></button>
+ </div>
+ </div>
+ <div class="option" name="forcedlang">
+ <div class="text">
+ %%gui.settings.forcedlang.title%%
+ <div class="desc">
+ %%gui.settings.forcedlang.desc%%
+ </div>
+ </div>
+ <div class="actions">
+ <select onchange="Settings.switch(document.querySelector(`.option[name='autolang'] button`), false)">
+ <option></option>
+ </select>
+ </div>
+ </div>
+ <h2>%%gui.settings.title.updates%%</h2>
+ <div class="option" name="autoupdate">
+ <div class="text">
+ %%gui.settings.autoupdate.title%%
+ <div class="desc">
+ %%gui.settings.autoupdate.desc%%
+ </div>
+ </div>
+ <div class="actions">
+ <button class="switch on"></button>
+ </div>
+ </div>
+ <div class="option" name="nsupdate">
+ <div class="text">
+ %%gui.settings.nsupdate.title%%
+ <div class="desc">
+ %%gui.settings.nsupdate.desc%%
+ </div>
+ </div>
+ <div class="actions">
+ <button class="switch on"></button>
+ </div>
+ </div>
+ <div class="option" name="excludes" type="array">
+ <div class="text">
+ %%gui.settings.excludes.title%%
+ <div class="desc">
+ %%gui.settings.excludes.desc%%
+ </div>
+ </div>
+ <div class="actions">
+ <input type="text">
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="popup" id="browser">
+ <div class="overlay" id="filters">
+ <div class="checks">
+ <div class="check checked" value="Mods">%%gui.browser.filter.mods%%</div>
+ <div class="check checked" value="Skins">%%gui.browser.filter.skins%%</div>
+ <div class="check checked" value="Client-side">%%gui.browser.filter.client%%</div>
+ <div class="check" value="Server-side">%%gui.browser.filter.server%%</div>
+ </div>
+ </div>
+
+ <div class="misc">
<input id="search" placeholder="%%gui.browser.search%%">
+ <button id="filter" onclick="Browser.filters.toggle()">
+ <img src="icons/filter.png">
+ </button>
<button id="close" onclick="Browser.toggle(false)">
<img src="icons/close.png">
</button>
@@ -94,7 +200,7 @@
<button id="togglemod" onclick="selected().toggle()">%%gui.mods.toggle%%</button>
<button id="toggleall" onclick="selected(true).toggle(true)">%%gui.mods.toggleall%%</button>
<button id="installmod" onclick="installmod()">%%gui.mods.install%%</button>
- <button id="installmod" onclick="Browser.toggle(true)">%%gui.mods.find%%</button>
+ <button id="findmod" onclick="Browser.toggle(true)">%%gui.mods.find%%</button>
</div>
</div>
</div>
@@ -120,6 +226,7 @@
<script src="main.js"></script>
<script src="toast.js"></script>
<script src="browser.js"></script>
+ <script src="settings.js"></script>
<script src="launcher.js"></script>
</body>
</html>
diff --git a/src/app/launcher.js b/src/app/launcher.js
index 60c0d18..7b49dc4 100644
--- a/src/app/launcher.js
+++ b/src/app/launcher.js
@@ -17,7 +17,7 @@ function page(page) {
pages[page].classList.remove("hidden");
btns[page].classList.remove("inactive");
bgHolder.setAttribute("bg", page);
-}; page(0)
+}; page(1)
// Updates the Viper release notes
@@ -25,6 +25,8 @@ ipcRenderer.on("vp-notes", (event, response) => {
let content = "";
for (const release of response) {
+ if (release.prerelease) {continue}
+
content += "# " + release.name + "\n\n"
+ release.body.replaceAll("\r\n", "\n") + "\n\n\n";
}
@@ -42,6 +44,8 @@ ipcRenderer.on("ns-notes", (event, response) => {
let content = "";
for (let release of response) {
+ if (release.prerelease) {continue}
+
content += "# " + release.name + "\n\n"
+ release.body.replaceAll("\r\n", "\nhtmlbreak") + "\n\n\n";
}
diff --git a/src/app/main.css b/src/app/main.css
index a532490..99d806c 100644
--- a/src/app/main.css
+++ b/src/app/main.css
@@ -1,17 +1,23 @@
:root {
- --red: #C7777F;
- --blue: #81A1C1;
- --orange: #D59783;
+ --red: 199, 119, 127;
+ --red2: 181 97 105;
+
+ --blue: 129, 161, 193;
+ --blue2: 139, 143, 185;
+
+ --orange: 213, 151, 131;
+ --orange2: 197 129 107;
+
--padding: 25px;
--bg: rgba(0, 0, 0, 0.5);
--selbg: rgba(80, 80, 80, 0.5);
- --redbg: linear-gradient(45deg, var(--red), #FA4343);
- --bluebg: linear-gradient(45deg, var(--blue), #7380ED);
+ --redbg: linear-gradient(45deg, rgb(var(--red)), #FA4343);
+ --bluebg: linear-gradient(45deg, rgb(var(--blue)), #7380ED);
}
-#browser, #modsdiv {
+.popup, #modsdiv {
outline: 1px solid #444444;
border: 3px solid var(--bg);
}
@@ -27,22 +33,26 @@
::-webkit-scrollbar-thumb {
border-radius: 10px;
- background: var(--red);
+ background: rgb(var(--red));
}
::selection {
color: black;
- background: var(--red);
+ background: rgb(var(--red));
}
body {
margin: 0;
overflow: hidden;
- user-select: none;
}
body, button, input {font-family: "Roboto", sans-serif}
+body, button, img, a {
+ -webkit-user-drag: none;
+ user-select: none;
+}
+
button {outline: none}
b, strong {font-weight: 700}
body, input, button {font-weight: 500}
@@ -62,7 +72,7 @@ button {
cursor: pointer;
}
-#browser {
+.popup {
--spacing: var(--padding);
z-index: 2;
@@ -81,7 +91,7 @@ button {
transition: opacity 0.15s ease-in-out, transform 0.15s ease-in-out;
}
-#browser.shown {
+.popup.shown {
opacity: 1.0;
pointer-events: all;
transform: scale(1.0);
@@ -110,7 +120,7 @@ button {
100% {opacity: 1.0}
}
-#browser .el, #browser #misc, #browser .loading {
+.popup .el, .popup .misc, .popup .loading {
--spacing: calc(var(--padding) / 2);
--height: calc(var(--padding) * 3);
--mischeight: calc(var(--padding) * 1.5);
@@ -125,7 +135,7 @@ button {
transition: 0.15s ease-in-out;
}
-#browser .el, #browser #search, #browser #close {
+.popup .el, .popup #search, .option .actions select, .option .actions input, .popup #close, .popup .misc button {
color: white;
display: flex;
align-items: center;
@@ -137,41 +147,52 @@ button {
width: calc(100% - var(--spacing) * 4);
}
-#browser #misc, #browser #search {
+.popup .misc, .popup #search, .option .actions input {
--height: var(--mischeight);
}
-#browser #misc {
+.popup .misc {
display: flex;
}
-#browser #search {
+.popup #search, .option .actions input, .option .actions select {
border: none;
outline: none;
transition: filter 0.15s ease-in-out;
width: calc(100% - var(--spacing) * 2);
}
-#browser #search:focus {
+.popup #search:focus, .option .actions input:focus, .option .actions button:active {
filter: brightness(1.5);
}
-#browser #close {
+.popup .misc button {
--height: calc(var(--padding) * 1.5);
padding: 0px;
margin-left: 0px;
- width: var(--height);
+ padding: 0px !important;
+ width: var(--height) !important;
}
-#browser #close img {
+.popup .misc button img {
opacity: 0.6;
width: var(--height);
- height: var(--height);
- transform: scale(0.6);
+ transform: scale(0.5);
+ height: var(--height) !important;
+}
+
+#options.popup .misc button {
+ margin-left: 0px;
+ width: auto !important;
+ padding-right: calc(var(--padding) / 2) !important;
}
-#browser .loading {
+.popup .misc button:last-child {
+ margin-left: 0px !important;
+}
+
+.popup .loading {
width: 100%;
color: white;
display: flex;
@@ -181,38 +202,166 @@ button {
height: calc(100% - var(--mischeight) - var(--height));
}
-#browser .message {
+.popup .message {
color: white;
text-align: center;
margin: var(--padding);
width: calc(100% - var(--padding));
}
-#browser .el .image, #browser .el .image img {
+.popup .el .image, .popup .el .image img {
width: var(--height);
height: var(--height);
margin-right: var(--spacing);
border-radius: var(--spacing);
}
-#browser .el .text {
+.popup .el .image img.blur {
+ z-index: -1;
+ position: relative;
+ filter: blur(10px);
+ top: calc(var(--height) * -1 + 5px);
+}
+
+.popup .el .text {
overflow: hidden;
}
-#browser .el .title, #browser .el .description {
+.popup .el .title, .popup .el .description {
height: 1.2em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
-#browser .el .title {font-size: 1.2em}
-#browser .el .description {font-size: 0.8em}
-#browser .el button {
- background: var(--blue);
+.popup .el .title {
+ font-size: 1.2em;
+ font-weight: 700;
+}
+
+.popup .el .description {font-size: 0.8em}
+.popup .el button {
+ background: rgb(var(--blue));
margin-top: var(--spacing);
}
+.popup .el button.info {
+ background: rgb(var(--blue2));
+}
+
+.popup .options {
+ color: white;
+ margin: calc(var(--padding) / 2);
+}
+
+.popup .options .option {
+ width: 100%;
+ display: flex;
+ margin-bottom: var(--padding);
+ justify-content: space-between;
+}
+
+.popup .overlay {
+ z-index: 1;
+ color: white;
+ opacity: 0.0;
+ position: fixed;
+ pointer-events: none;
+ transform: scale(0.9);
+ background: var(--selbg);
+ backdrop-filter: blur(15px);
+ transition: 0.15s ease-in-out;
+ padding: calc(var(--spacing) / 2);
+ border-radius: calc(var(--spacing) / 2);
+}
+
+.popup .overlay.shown {
+ opacity: 1.0;
+ pointer-events: all;
+ transform: scale(1.0);
+}
+
+.checks {
+}
+
+.check {
+ display:flex;
+ cursor: pointer;
+}
+
+.check::before {
+ width: 1em;
+ height: 1em;
+ content: " ";
+ background-size: 75%;
+ filter: brightness(1.3);
+ background-position: center;
+ background-repeat: no-repeat;
+ transition: 0.15s ease-in-out;
+ background-color: var(--selbg);
+ margin-right: calc(var(--spacing) / 3);
+ border-radius: calc(var(--spacing) / 4);
+}
+
+.check.checked::before {
+ background-color: rgb(var(--red));
+ background-image: url(icons/check.png);
+}
+
+.option .text {font-weight: 600}
+.option .text .desc {
+ opacity: 0.8;
+ font-weight: 500;
+ font-size: 0.9em;
+ max-width: 400px;
+ margin-top: calc(var(--padding) / 3);
+}
+
+.option .actions input, .option .actions select {
+ width: 100%;
+ margin: 0px;
+ --spacing: calc(var(--padding) / 3);
+}
+
+.option[type=array] .actions input {
+ word-spacing: 15px;
+ margin-right: 15vw;
+}
+
+.option .actions button {
+ background: var(--selbg);
+}
+
+.switch {
+ width: 50px;
+ height: 25px;
+ border-radius: 50px;
+}
+
+.switch.on {
+ background: rgba(var(--red), 0.2) !important;
+}
+
+.switch::after {
+ left: -5px;
+ width: 15px;
+ height: 15px;
+ content: " ";
+ display: block;
+ background: red;
+ position: relative;
+ border-radius: 50px;
+ background: var(--bg);
+ transition: 0.2s ease-in-out;
+}
+
+.switch.on::after {
+ left: 15px;
+ width: 20px;
+ opacity: 0.5;
+ background: rgb(var(--red));
+}
+
#winbtns {
z-index: 2;
display: flex;
@@ -233,9 +382,10 @@ button {
#winbtns #close {background-image: url("icons/close.png")}
#winbtns #minimize {background-image: url("icons/minimize.png")}
+#winbtns #settings {background-image: url("icons/settings.png")}
#winbtns div:hover {opacity: 1.0}
-#winbtns div:active {transform: scale(0.98)}
+#winbtns div:active {transform: scale(0.95)}
button:hover {filter: brightness(110%)}
button:active {filter: brightness(90%)}
@@ -252,7 +402,7 @@ img {pointer-events: none}
background-size: cover;
background-position: center;
background-repeat: no-repeat;
- transition: background-image 0.5s ease-in-out;
+ transition: background-image 0.1s ease-in-out;
filter: brightness(0.4) blur(2px) grayscale(0.6);
}
@@ -384,7 +534,7 @@ img {pointer-events: none}
text-align: center;
position: relative;
border-radius: 50px;
- background: var(--red);
+ background: rgb(var(--red));
left: calc(50% - 15px);
transition: 0.2s ease-in-out;
}
@@ -430,7 +580,7 @@ img {pointer-events: none}
margin-bottom: 10px;
border-radius: 10px;
background: var(--redbg);
- transition: 0.3s ease-in-out;
+ transition: 0.2s ease-in-out;
filter: drop-shadow(0px 8px 5px rgba(0, 0, 0, 0.1));
}
@@ -449,7 +599,7 @@ img {pointer-events: none}
}
a {
- color: var(--red);
+ color: rgb(var(--red));
text-decoration: none;
transition: filter 0.2s ease-in;
}
@@ -496,12 +646,17 @@ a:hover {
}
.simplebar-scrollbar:before {
- background: var(--red) !important;
+ background: rgb(var(--red)) !important;
}
-#installmod {background: var(--blue)}
-#togglemod, #toggleall {background: var(--orange)}
-#northstar, #removeall, #removemod {background: var(--red)}
+#installmod {background: rgb(var(--blue))}
+#findmod {background: rgb(var(--blue2))}
+
+#togglemod {background: rgb(var(--orange))}
+#toggleall {background: rgb(var(--orange2))}
+
+#removemod {background: rgb(var(--red))}
+#removeall {background: rgb(var(--red2))}
button:disabled {
opacity: 0.5;
pointer-events: none;
@@ -509,6 +664,7 @@ button:disabled {
button.visual {
opacity: 1.0;
+ padding-right: 0px;
pointer-events: none;
background: transparent !important;
}
@@ -612,6 +768,58 @@ code {
font-weight: 600;
}
+#dragUI {
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ color: white;
+ opacity: 0.0;
+ position: fixed;
+ z-index: 1000000;
+ pointer-events: none;
+ background: var(--bg);
+ backdrop-filter: blur(15px);
+ transition: 0.1s ease-in-out;
+}
+
+#dragUI.shown {
+ opacity: 1.0;
+ pointer-events: all;
+}
+
+#dragUI #dragitems {
+ --size: 25vw;
+ top: 50%;
+ left: 50%;
+ opacity: 0.6;
+ position: absolute;
+ text-align: center;
+ width: var(--size);
+ height: var(--size);
+ margin-top: calc(var(--size) / 2 * -1);
+ margin-left: calc(var(--size) / 2 * -1);
+}
+
+#dragUI #dragitems #icon {
+ width: 100%;
+ height: 100%;
+ filter: invert(1);
+ transform: scale(0.45);
+ background-size: cover;
+ background-image: url("icons/download.png");
+ transition: 0.1s ease-in-out;
+}
+
+#dragUI.shown #dragitems #icon {
+ transform: scale(0.5);
+}
+
+#dragUI #dragitems #text {
+ top: -5vw;
+ position: relative;
+}
+
/* drag control */
#bgHolder,
diff --git a/src/app/main.js b/src/app/main.js
index 169f86f..95b6f4c 100644
--- a/src/app/main.js
+++ b/src/app/main.js
@@ -8,7 +8,11 @@ let shouldInstallNorthstar = false;
// Base settings
var settings = {
+ nsargs: "",
gamepath: "",
+ nsupdate: true,
+ autolang: true,
+ forcedlang: "en",
autoupdate: true,
zip: "/northstar.zip",
lang: navigator.language,
@@ -23,7 +27,24 @@ ipcRenderer.send("setlang", settings.lang);
// Loads the settings
if (fs.existsSync("viper.json")) {
- settings = {...settings, ...JSON.parse(fs.readFileSync("viper.json", "utf8"))};
+ let conf = fs.readFileSync("viper.json", "utf8");
+ let json = {};
+
+ // Validates viper.json
+ try {
+ json = JSON.parse(conf);
+ }catch (e) {
+ let reset = confirm(lang("general.invalidconfig", navigator.language) + e);
+ if (! reset) {
+ ipcRenderer.send("exit")
+ } else {
+ fs.writeFileSync("viper.json", "{}")
+ ipcRenderer.send("relaunch");
+ }
+
+ }
+
+ settings = {...settings, ...json};
settings.zip = path.join(settings.gamepath + "/northstar.zip");
if (settings.gamepath.length === 0) {
@@ -31,6 +52,11 @@ if (fs.existsSync("viper.json")) {
} else {
setpath(true);
}
+
+ let args = path.join(settings.gamepath, "ns_startup_args.txt");
+ if (fs.existsSync(args)) {
+ settings.nsargs = fs.readFileSync(args, "utf8");
+ }
} else {
setpath();
}
@@ -74,14 +100,22 @@ function setButtons(state) {
}
}
+ disablearray(document.querySelectorAll(".playBtnContainer .playBtn"))
disablearray(document.querySelectorAll("#nsMods .buttons.modbtns button"))
disablearray(document.querySelectorAll("#browser #browserEntries .text button"))
}
+ipcRenderer.on("setbuttons", (event, state) => {setButtons(state)})
+ipcRenderer.on("gamepathlost", (event, state) => {
+ page(0);
+ setButtons(false);
+ alert(lang("gui.gamepath.lost"));
+})
+
// Frontend part of updating Northstar
ipcRenderer.on("ns-update-event", (event, key) => {
document.getElementById("update").innerText = `(${lang(key)})`;
- console.log(key);
+ console.log(lang(key));
switch(key) {
case "cli.update.uptodate.short":
setButtons(true);
@@ -156,12 +190,18 @@ function selected(all) {
}
}
-// Tells the main process to install a mod
+// Tells the main process to install a mod through the file selector
function installmod() {
setButtons(false);
ipcRenderer.send("installmod")
}
+// Tells the main process to directly install a mod from this path
+function installFromPath(path) {
+ setButtons(false);
+ ipcRenderer.send("installfrompath", path)
+}
+
// Tells the main process to install a mod from a URL
function installFromURL(url) {
setButtons(false);
@@ -242,6 +282,38 @@ ipcRenderer.on("wrongpath", () => {
setlang();
+let dragtimer;
+document.addEventListener("dragover", (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ dragUI.classList.add("shown");
+
+ clearTimeout(dragtimer);
+ dragtimer = setTimeout(() => {
+ dragUI.classList.remove("shown");
+ }, 5000)
+});
+
+document.addEventListener("mouseover", (e) => {
+ clearTimeout(dragtimer);
+ dragUI.classList.remove("shown");
+});
+
+document.addEventListener("drop", (e) => {
+ event.preventDefault();
+ event.stopPropagation();
+
+ dragUI.classList.remove("shown");
+ installFromPath(event.dataTransfer.files[0].path)
+});
+
+document.body.addEventListener("keyup", (e) => {
+ if (e.key == "Escape") {
+ Browser.toggle(false);
+ Settings.toggle(false);
+ }
+})
+
document.body.addEventListener("click", event => {
if (event.target.tagName.toLowerCase() === "a" && event.target.protocol != "file:") {
event.preventDefault();
diff --git a/src/app/settings.js b/src/app/settings.js
new file mode 100644
index 0000000..23b38c9
--- /dev/null
+++ b/src/app/settings.js
@@ -0,0 +1,137 @@
+var Settings = {
+ toggle: (state) => {
+ if (state) {
+ Settings.load();
+ options.scrollTo(0, 0);
+ overlay.classList.add("shown")
+ options.classList.add("shown")
+
+ return
+ } else if (! state) {
+ if (state != undefined) {
+ overlay.classList.remove("shown")
+ options.classList.remove("shown")
+ return
+ }
+ }
+
+ Settings.load();
+ options.scrollTo(0, 0);
+ overlay.classList.toggle("shown")
+ options.classList.toggle("shown")
+ },
+ apply: () => {
+ settings = {...settings, ...Settings.get()};
+ ipcRenderer.send("savesettings", Settings.get());
+ },
+ reloadSwitches: () => {
+ let switches = document.querySelectorAll(".switch");
+
+ for (let i = 0; i < switches.length; i++) {
+ switches[i].setAttribute("onclick", `Settings.switch(${i})`);
+ }
+ },
+ switch: (element, state) => {
+ let switches = document.querySelectorAll(".switch");
+ if (switches[element]) {
+ element = switches[element];
+ }
+
+ let on = () => {
+ element.classList.add("on");
+ element.classList.remove("off");
+ }
+
+ let off = () => {
+ element.classList.add("off");
+ element.classList.remove("on");
+ }
+
+ if (state != undefined) {
+ if (state) {on()} else {off()}
+ } else {
+ if (element.classList.contains("on")) {off()} else {on()}
+ }
+
+ Settings.reloadSwitches();
+ },
+ get: () => {
+ let opts = {};
+ let options = document.querySelectorAll(".option");
+
+ for (let i = 0; i < options.length; i++) {
+ let optName = options[i].getAttribute("name");
+ if (options[i].querySelector(".actions input")) {
+ let input = options[i].querySelector(".actions input").value;
+ if (options[i].getAttribute("type")) {
+ opts[optName] = input.split(" ");
+ } else {
+ opts[optName] = input;
+ }
+ } else if (options[i].querySelector(".actions select")) {
+ opts[optName] = options[i].querySelector(".actions select").value;
+ } else if (options[i].querySelector(".actions .switch")) {
+ if (options[i].querySelector(".actions .switch.on")) {
+ opts[optName] = true;
+ } else {
+ opts[optName] = false;
+ }
+ }
+ }
+
+ return opts;
+ },
+ load: () => {
+ let options = document.querySelectorAll(".option");
+
+ for (let i = 0; i < options.length; i++) {
+ let optName = options[i].getAttribute("name");
+ if (optName == "forcedlang") {
+ let div = options[i].querySelector("select");
+
+ div.innerHTML = "";
+ let langs = fs.readdirSync(__dirname + "/../lang");
+ for (let i in langs) {
+ title = JSON.parse(fs.readFileSync(__dirname + `/../lang/${langs[i]}`, "utf8"))["lang.title"];
+ if (title) {
+ div.innerHTML += `<option value="${langs[i].replace(/\..*$/, '')}">${title}</option>`
+ }
+
+ }
+
+ div.value = settings.forcedlang;
+ continue;
+ }
+
+ if (settings[optName] != undefined) {
+ switch(typeof settings[optName]) {
+ case "string":
+ options[i].querySelector(".actions input").value = settings[optName];
+ break
+ case "object":
+ options[i].querySelector(".actions input").value = settings[optName].join(" ");
+ break
+ case "boolean":
+ let switchDiv = options[i].querySelector(".actions .switch");
+ if (settings[optName]) {
+ switchDiv.classList.add("on");
+ switchDiv.classList.remove("off");
+ } else {
+ switchDiv.classList.add("off");
+ switchDiv.classList.remove("on");
+ }
+ break
+
+ }
+ }
+ }
+
+ ipcRenderer.send("can-autoupdate");
+ ipcRenderer.on("cant-autoupdate", () => {
+ document.querySelector(".option[name=autoupdate]").style.display = "none";
+ })
+ }
+}
+
+Settings.reloadSwitches();
+Settings.load();
diff --git a/src/app/toast.js b/src/app/toast.js
index 2a8555e..9cb8996 100644
--- a/src/app/toast.js
+++ b/src/app/toast.js
@@ -11,7 +11,7 @@ function Toast(properties) {
switch(toast.scheme) {
case "error":
toast.fg = "#FFFFFF";
- toast.bg = "var(--red)";
+ toast.bg = "rgb(var(--red))";
break
case "success":
toast.fg = "#FFFFFF";