Compare commits

..
7 Commits
Author SHA1 Message Date
justin 5be9ff4862 Shorten Windows app card text
Build and deploy / deploy (push) Canceled after 0s
Build and deploy / build (push) Canceled after 0s
2026-09-28 16:46:43 -07:00
justin c1bbfa06d5 List PowerShell scripts as the third install option in the README 2026-09-28 16:36:18 -07:00
justin 375a2a54fc Credit SignPath for code signing in the site footer 2026-09-28 16:19:10 -07:00
justin 8057ba9a09 Reword page intro 2026-09-28 16:04:17 -07:00
justin 952895f359 Say my mods in the page intro 2026-09-28 15:57:56 -07:00
justin c3726f7af5 Label option 1 as Recommended in parentheses 2026-09-28 15:55:45 -07:00
justin 967bd58f00 Show the three install options as numbered cards 2026-09-28 15:53:46 -07:00
4 changed files with 61 additions and 32 deletions

No files matched your search

+2 -1
View File
@@ -6,10 +6,11 @@ Live site: https://justinoros.github.io/sotf-modloader/
## For players ## For players
The site offers two ways to install: The site offers three ways to install:
1. **Windows app (recommended).** Download `SotfModLoader.exe` from the site and run it. It finds your game through Steam, including installs inside Program Files, and installs RedLoader and mods with one click. 1. **Windows app (recommended).** Download `SotfModLoader.exe` from the site and run it. It finds your game through Steam, including installs inside Program Files, and installs RedLoader and mods with one click.
2. **Zip download.** One zip with RedLoader and every mod. Extract it into your Sons of the Forest folder, the one that contains `SonsOfTheForest.exe`. 2. **Zip download.** One zip with RedLoader and every mod. Extract it into your Sons of the Forest folder, the one that contains `SonsOfTheForest.exe`.
3. **PowerShell scripts.** Each mod has a **Copy PowerShell install** button on the site. Paste the command into PowerShell to install that mod, plus RedLoader if needed. Run it again to update.
Close the game before installing or updating. Close the game before installing or updating.
+35 -20
View File
@@ -32,31 +32,44 @@ function link(href, text) {
return el("a", { href, target: "_blank", rel: "noopener" }, text); return el("a", { href, target: "_blank", rel: "noopener" }, text);
} }
function optionCard(number, title, text, action, note, recommended) {
return el("div", { class: recommended ? "get-card recommended" : "get-card" },
el("p", { class: "get-option" }, recommended ? `Option ${number} (Recommended)` : `Option ${number}`),
el("h3", {}, title),
el("p", {}, text),
action,
note ? el("p", { class: "get-note" }, note) : null,
);
}
function renderGet() { function renderGet() {
const node = $("get"); const node = $("get");
const { app, bundle } = state.manifest; const { app, bundle, mods } = state.manifest;
const cards = []; const options = [];
if (app) { if (app) {
cards.push( options.push((n) => optionCard(n, "Windows app",
el("div", { class: "get-card" }, "Finds your game automatically. Installs, updates and removes RedLoader and mods with one click.",
el("h2", {}, "Windows app"), el("a", { class: "button primary", href: app.url }, `Download app (${formatSize(app.size)})`),
el("p", {}, "Finds your game automatically, even inside Program Files, and installs RedLoader and mods with one click."), `Version ${app.version}. If Windows shows a warning, click More info, then Run anyway.`,
el("a", { class: "button primary", href: app.url }, `Download the app (${formatSize(app.size)})`), true));
el("p", { class: "get-note" }, `Version ${app.version}. If Windows shows a warning, click More info, then Run anyway.`),
),
);
} }
if (bundle) { if (bundle) {
cards.push( options.push((n) => optionCard(n, "Zip download",
el("div", { class: "get-card" }, "RedLoader and every mod in one zip. Extract it into your game folder, the one with SonsOfTheForest.exe.",
el("h2", {}, "Zip download"), el("a", { class: "button secondary", href: bundle.url, download: "sotf-mods.zip" }, `Download zip (${formatSize(bundle.size)})`),
el("p", {}, "RedLoader and every mod in one zip. Extract it into your Sons of the Forest folder, the one that contains SonsOfTheForest.exe."), "Download it again to update. Remove mods by deleting them from the Mods folder."));
el("a", { class: "button secondary", href: bundle.url, download: "sotf-mods.zip" }, `Download zip (${formatSize(bundle.size)})`),
),
);
} }
node.hidden = !cards.length; if ((mods || []).some((mod) => mod.installCommand)) {
node.replaceChildren(...cards); options.push((n) => optionCard(n, "PowerShell scripts",
"Install one mod at a time. Copy a mod's command from the list below and paste it into PowerShell.",
el("a", { class: "button secondary", href: "#mods" }, "Go to the mod list"),
"Installs RedLoader too if needed. Run the command again to update."));
}
node.hidden = !options.length;
node.replaceChildren(
el("div", { class: "block-head" }, el("h2", { id: "get-title" }, "Choose how to install")),
el("div", { class: "get-grid" }, options.map((make, i) => make(i + 1))),
);
} }
function renderRedloader() { function renderRedloader() {
@@ -70,7 +83,7 @@ function renderRedloader() {
el("li", { class: "row" }, el("li", { class: "row" },
el("div", {}, el("div", {},
el("h3", {}, link(rl.repo, "RedLoader")), el("h3", {}, link(rl.repo, "RedLoader")),
el("p", { class: "desc" }, "Every mod on this page runs on RedLoader. The app and the zip include it."), el("p", { class: "desc" }, "Every mod on this page runs on RedLoader. All three install options include it."),
el("p", { class: "meta" }, el("span", {}, `Latest ${rl.version}`), link(rl.releaseUrl, "Release notes")), el("p", { class: "meta" }, el("span", {}, `Latest ${rl.version}`), link(rl.releaseUrl, "Release notes")),
), ),
), ),
@@ -146,6 +159,8 @@ function renderFooter() {
? el("p", {}, "RedLoader by ToniMacaroni, redistributed unmodified under the ", ? el("p", {}, "RedLoader by ToniMacaroni, redistributed unmodified under the ",
link(`${redloader.repo}/blob/main/LICENSE.md`, "Apache License 2.0"), ".") link(`${redloader.repo}/blob/main/LICENSE.md`, "Apache License 2.0"), ".")
: null, : null,
el("p", {}, "Free code signing provided by ", link("https://about.signpath.io", "SignPath.io"),
", certificate by ", link("https://signpath.org", "SignPath Foundation"), "."),
el("p", {}, `Mod list updated ${updated}.`), el("p", {}, `Mod list updated ${updated}.`),
); );
} }
+2 -2
View File
@@ -15,9 +15,9 @@
<main class="page"> <main class="page">
<header class="masthead"> <header class="masthead">
<h1>Sons of the Forest mod loader</h1> <h1>Sons of the Forest mod loader</h1>
<p class="lede">Install, update and remove Sons of the Forest mods with one click.</p> <p class="lede">A one-stop shop to install, update or remove my Sons of the Forest mods. Choose an option below.</p>
</header> </header>
<section id="get" class="get" aria-label="Ways to install" hidden></section> <section id="get" class="block get" aria-labelledby="get-title" hidden></section>
<section id="redloader" class="block" aria-labelledby="redloader-title" hidden></section> <section id="redloader" class="block" aria-labelledby="redloader-title" hidden></section>
<section id="mods" class="block" aria-labelledby="mods-title"></section> <section id="mods" class="block" aria-labelledby="mods-title"></section>
<p id="status" class="status" role="status" aria-live="polite"></p> <p id="status" class="status" role="status" aria-live="polite"></p>
+22 -9
View File
@@ -52,7 +52,7 @@ a:hover {
} }
.page { .page {
max-width: 54rem; max-width: 62rem;
margin: 0 auto; margin: 0 auto;
padding: clamp(2.5rem, 7vw, 5rem) clamp(1.25rem, 4vw, 2.5rem) 2rem; padding: clamp(2.5rem, 7vw, 5rem) clamp(1.25rem, 4vw, 2.5rem) 2rem;
} }
@@ -74,32 +74,44 @@ a:hover {
font-size: 1.15rem; font-size: 1.15rem;
} }
.get { .get-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
gap: 1rem; gap: 1rem;
margin-top: 2.75rem; margin-top: 1.25rem;
} }
.get-card { .get-card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
padding: 1.35rem 1.5rem 1.4rem; padding: 1.25rem 1.35rem 1.35rem;
background: var(--bark); background: var(--bark);
border: 1px solid var(--bark-line); border: 1px solid var(--bark-line);
border-top: 5px solid var(--bark-line);
border-radius: 6px; border-radius: 6px;
} }
.get-card:first-child { .get-card.recommended {
border-top: 5px solid var(--flare); border-top-color: var(--flare);
} }
.get-card h2 { .get-card .get-option {
margin: 0; margin: 0;
color: var(--lichen);
font-size: 0.9rem;
}
.get-card.recommended .get-option {
color: var(--flare);
font-weight: 600;
}
.get-card h3 {
margin: 0.15rem 0 0;
font-family: var(--display); font-family: var(--display);
font-weight: 700; font-weight: 700;
font-size: 1.75rem; font-size: 1.65rem;
line-height: 1.1; line-height: 1.1;
} }
@@ -123,6 +135,7 @@ a.button {
font-size: 0.95rem; font-size: 0.95rem;
line-height: 1.2; line-height: 1.2;
text-decoration: none; text-decoration: none;
white-space: nowrap;
} }
a.button.primary { a.button.primary {