Compare commits

..

31 Commits

Author SHA1 Message Date
Ben Visness 6dd2a32780 Add Unwind URL 2024-05-24 19:15:18 -05:00
Ben Visness b681bacc16 Disable Freyabot 2024-05-21 15:56:32 -05:00
Ben Visness 9ecb65985e Don't show blog thing on project blogs 2024-05-20 17:02:06 -05:00
Ben Visness 04db4ad709 Add email newsletter signup to blogs 2024-05-20 17:00:03 -05:00
Asaf Gartner 1aab3b47b3 FreyaMode won't affect other message processing steps 2024-05-14 17:55:23 +03:00
Ben Visness 23b1b30d22 Less wrong Freya mode, I hope 2024-05-14 09:40:05 -05:00
Ben Visness ac54793fd0 Freya mode 2024-05-14 09:32:49 -05:00
Ben Visness 852ff7e53f Un-typo 2024-05-08 21:03:49 -05:00
Ben Visness 22265c9081 Add upcoming jam page 2024-05-08 21:01:04 -05:00
Asaf Gartner 7fd57f692b Handmade Cities banner 2024-05-01 07:15:28 +03:00
Asaf Gartner 71d236f1c2 Replaced twitch embed with youtube embed for the LJ2024 recap show. 2024-04-03 19:11:40 +03:00
Asaf Gartner 4568def378 Added a bunch of discord debugging 2024-03-28 21:24:46 +02:00
Ben Visness e8201a254e no the jam does not end now I refuse 2024-03-24 16:11:51 -05:00
Asaf Gartner 2065bad860 Added tooltip to project owners on the LJ page. 2024-03-14 02:31:59 +02:00
Ben Visness 5dd4880d4c heaaaghghghghg jam time yayaya 2024-03-12 23:20:35 -05:00
Ben Visness ee491c7696 Learning jam final design phase 1 2024-03-12 21:13:05 -05:00
Asaf Gartner f085858e9e Merge remote-tracking branch 'origin/beta' 2024-03-11 20:17:44 +02:00
Asaf Gartner 639ea17a88 Timeline and projects for learning jam 2024-03-11 20:15:32 +02:00
Asaf Gartner 2eb3288b2a LJ jam feed basics 2024-03-11 18:27:34 +02:00
Ben Visness 27dcdb4704 Also opengraph 2024-03-08 19:10:45 -06:00
Ben Visness 5aa9dde8a1 Soften language about "teaching" for the learning jam 2024-03-08 19:03:51 -06:00
Asaf Gartner cd36eb44eb Don't require url/asset for snippet creation from discord. 2024-03-04 21:19:56 +02:00
Asaf Gartner 444f43a195 Added !til for discord messages 2024-03-04 21:09:40 +02:00
Ben Visness 6fba490392 Add Twitter metadata 2024-02-14 12:13:59 -06:00
Asaf Gartner 7800cd9fe1 Added -webkit- prefix to mask and clip css props 2024-02-12 20:41:26 +02:00
Ben Visness 1e5c0c7b42 Icons, favicons, jam index fix 2024-02-11 10:15:13 -06:00
Ben Visness 032d6c435e Temporarily remove icons 2024-02-10 22:08:02 -06:00
Ben Visness 16e4b0327f Opengraph and banner 2024-02-10 22:06:43 -06:00
Ben Visness 97e6c74c52 Many change for great good yes 2024-02-10 20:54:13 -06:00
Ben Visness c5e458be8c Lay out top section 2024-02-09 15:02:58 -06:00
Asaf Gartner b5d4fe9ba2 Learning jam scaffolding 2024-02-08 22:21:01 +02:00
166 changed files with 14601 additions and 25751 deletions

1
.gitignore vendored
View File

@ -16,6 +16,7 @@ local/backups
/tmp
*.exe
.DS_Store
__debug_bin*
# vim session saves
Session.vim

2
go.mod
View File

@ -24,6 +24,7 @@ require (
github.com/stretchr/testify v1.8.1
github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300
github.com/teacat/noire v1.1.0
github.com/wellington/go-libsass v0.9.2
github.com/yuin/goldmark v1.4.13
github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01
golang.org/x/crypto v0.6.0
@ -44,7 +45,6 @@ require (
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/evanw/esbuild v0.21.4 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect

3
go.sum
View File

@ -90,8 +90,6 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f h1:feGUUxxvOtWVOhTko8Cbmp33a+tU0IMZxMEmnkoAISQ=
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f/go.mod h1:2MKFUgfNMULRxqZkadG1Vh44we3y5gJAtTBlVsx1BKQ=
github.com/evanw/esbuild v0.21.4 h1:pe4SEQMoR1maEjhgWPEPWmUy11Jp6nidxd1mOvMrFFU=
github.com/evanw/esbuild v0.21.4/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -371,7 +369,6 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

@ -3,7 +3,7 @@ package main
import (
_ "git.handmade.network/hmn/hmn/src/admintools"
_ "git.handmade.network/hmn/hmn/src/assets"
_ "git.handmade.network/hmn/hmn/src/buildscss/cmd"
_ "git.handmade.network/hmn/hmn/src/buildscss"
_ "git.handmade.network/hmn/hmn/src/discord/cmd"
_ "git.handmade.network/hmn/hmn/src/initimage"
_ "git.handmade.network/hmn/hmn/src/migration"

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

0
public/icons.ttf Normal file → Executable file
View File

View File

@ -1,15 +1,12 @@
function ImageSelector(form, maxFileSize, container, defaultImageUrl) {
this.form = form;
this.maxFileSize = maxFileSize;
this.fileInput = container.querySelector(".imginput");
this.removeImageInput = container.querySelector(".imginput-remove");
this.fileInput = container.querySelector(".image_input");
this.removeImageInput = container.querySelector(".remove_input");
this.imageEl = container.querySelector("img");
this.container = container.querySelector(".imginput-container");
this.resetLink = container.querySelector(".imginput-reset-link");
this.removeLink = container.querySelector(".imginput-remove-link");
this.filenameText = container.querySelector(".imginput-filename");
this.originalImageUrl = this.imageEl.getAttribute("data-imginput-original");
this.originalImageFilename = this.imageEl.getAttribute("data-imginput-original-filename");
this.resetLink = container.querySelector(".reset");
this.removeLink = container.querySelector(".remove");
this.originalImageUrl = this.imageEl.getAttribute("data-original");
this.currentImageUrl = this.originalImageUrl;
this.defaultImageUrl = defaultImageUrl || "";
@ -17,7 +14,7 @@ function ImageSelector(form, maxFileSize, container, defaultImageUrl) {
this.removeImageInput.value = "";
this.setImageUrl(this.originalImageUrl);
this.updatePreview();
this.updateButtons();
this.fileInput.addEventListener("change", function(ev) {
if (this.fileInput.files.length > 0) {
@ -36,16 +33,12 @@ function ImageSelector(form, maxFileSize, container, defaultImageUrl) {
}
}
ImageSelector.prototype.openFileInput = function() {
this.fileInput.click();
}
ImageSelector.prototype.handleNewImageFile = function(file) {
if (file) {
this.updateSizeLimit(file.size);
this.removeImageInput.value = "";
this.setImageUrl(URL.createObjectURL(file));
this.updatePreview(file);
this.updateButtons();
}
};
@ -54,7 +47,7 @@ ImageSelector.prototype.removeImage = function() {
this.fileInput.value = "";
this.removeImageInput.value = "true";
this.setImageUrl(this.defaultImageUrl);
this.updatePreview(null);
this.updateButtons();
};
ImageSelector.prototype.resetImage = function() {
@ -62,7 +55,7 @@ ImageSelector.prototype.resetImage = function() {
this.fileInput.value = "";
this.removeImageInput.value = "";
this.setImageUrl(this.originalImageUrl);
this.updatePreview(null);
this.updateButtons();
};
ImageSelector.prototype.updateSizeLimit = function(size) {
@ -89,24 +82,19 @@ ImageSelector.prototype.setImageUrl = function(url) {
}
};
ImageSelector.prototype.updatePreview = function(file) {
const showReset = (
this.originalImageUrl
&& this.originalImageUrl != this.defaultImageUrl
&& this.originalImageUrl != this.currentImageUrl
);
const showRemove = (
!this.fileInput.required
&& this.currentImageUrl != this.defaultImageUrl
);
this.resetLink.hidden = !showReset;
this.removeLink.hidden = !showRemove;
ImageSelector.prototype.updateButtons = function() {
if ((this.originalImageUrl.length > 0 && this.originalImageUrl != this.defaultImageUrl)
&& this.currentImageUrl != this.originalImageUrl) {
if (this.currentImageUrl == this.originalImageUrl) {
this.filenameText.innerText = this.originalImageFilename;
this.resetLink.style.display = "inline-block";
} else {
this.filenameText.innerText = file ? file.name : "";
this.resetLink.style.display = "none";
}
this.container.hidden = !this.currentImageUrl;
if (!this.fileInput.required && this.currentImageUrl != this.defaultImageUrl) {
this.removeLink.style.display = "inline-block";
} else {
this.removeLink.style.display = "none";
}
};

View File

@ -1,3 +0,0 @@
function rem2px(rem) {
return rem * parseFloat(getComputedStyle(document.documentElement).fontSize);
}

View File

@ -9,7 +9,7 @@ function readableByteSize(numBytes) {
"gb"
];
let scale = 0;
while (numBytes > 1024 && scale < scales.length - 1) {
while (numBytes > 1024 && scale < scales.length-1) {
numBytes /= 1024;
scale++;
}
@ -28,7 +28,7 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
snippetEdit.avatarLink.href = ownerUrl;
snippetEdit.username.textContent = ownerName;
snippetEdit.username.href = ownerUrl;
snippetEdit.date.textContent = new Intl.DateTimeFormat([], { month: "2-digit", day: "2-digit", year: "numeric" }).format(date);
snippetEdit.date.textContent = new Intl.DateTimeFormat([], {month: "2-digit", day: "2-digit", year: "numeric"}).format(date);
snippetEdit.text.value = text;
if (attachmentElement) {
originalAttachment = attachmentElement.cloneNode(true);
@ -56,7 +56,7 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
updateProjectSelector();
if (originalSnippetEl) {
snippetEdit.cancelLink.addEventListener("click", function () {
snippetEdit.cancelLink.addEventListener("click", function() {
cancel();
});
} else {
@ -78,7 +78,7 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
if (proj.id == stickyProjectId) {
projEl.removeButton.remove();
} else {
projEl.removeButton.addEventListener("click", function (ev) {
projEl.removeButton.addEventListener("click", function(ev) {
projEl.root.remove();
updateProjectSelector();
});
@ -126,7 +126,7 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
option.textContent = remainingProjects[i].name;
projectSelector.appendChild(option);
}
projectSelector.addEventListener("change", function (ev) {
projectSelector.addEventListener("change", function(ev) {
if (projectSelector.selectedOptions.length > 0) {
let selected = projectSelector.selectedOptions[0];
if (selected.value != "") {
@ -237,33 +237,33 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
}
}
snippetEdit.uploadLink.addEventListener("click", function () {
snippetEdit.uploadLink.addEventListener("click", function() {
snippetEdit.file.click();
});
snippetEdit.removeLink.addEventListener("click", function () {
snippetEdit.removeLink.addEventListener("click", function() {
clearAttachment(false);
});
snippetEdit.replaceLink.addEventListener("click", function () {
snippetEdit.replaceLink.addEventListener("click", function() {
snippetEdit.file.click();
});
snippetEdit.resetLink.addEventListener("click", function () {
snippetEdit.resetLink.addEventListener("click", function() {
clearAttachment(true);
});
snippetEdit.uploadResetLink.addEventListener("click", function () {
snippetEdit.uploadResetLink.addEventListener("click", function() {
clearAttachment(true);
});
snippetEdit.file.addEventListener("change", function () {
snippetEdit.file.addEventListener("change", function() {
if (snippetEdit.file.files.length > 0) {
setFile(snippetEdit.file.files[0]);
}
});
snippetEdit.root.addEventListener("dragover", function (ev) {
snippetEdit.root.addEventListener("dragover", function(ev) {
let effect = "none";
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
@ -277,7 +277,7 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
let enterCounter = 0;
snippetEdit.root.addEventListener("dragenter", function (ev) {
snippetEdit.root.addEventListener("dragenter", function(ev) {
enterCounter++;
let droppable = Array.from(ev.dataTransfer.items).some(
item => item.kind.toLowerCase() === "file"
@ -287,14 +287,14 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
}
});
snippetEdit.root.addEventListener("dragleave", function (ev) {
snippetEdit.root.addEventListener("dragleave", function(ev) {
enterCounter--;
if (enterCounter == 0) {
snippetEdit.root.classList.remove("drop");
}
});
snippetEdit.root.addEventListener("drop", function (ev) {
snippetEdit.root.addEventListener("drop", function(ev) {
enterCounter = 0;
snippetEdit.root.classList.remove("drop");
@ -305,18 +305,18 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
ev.preventDefault();
});
snippetEdit.text.addEventListener("paste", function (ev) {
snippetEdit.text.addEventListener("paste", function(ev) {
const files = ev.clipboardData?.files ?? [];
if (files.length > 0) {
setFile(files[0]);
}
});
snippetEdit.text.addEventListener("input", function (ev) {
snippetEdit.text.addEventListener("input", function(ev) {
validate();
});
snippetEdit.saveButton.addEventListener("click", function (ev) {
snippetEdit.saveButton.addEventListener("click", function(ev) {
let projectsChanged = false;
let projInputs = snippetEdit.projectList.querySelectorAll("input[name=project_id]");
let assignedIds = [];
@ -349,8 +349,8 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
cancel();
}
});
snippetEdit.deleteButton.addEventListener("click", function (ev) {
snippetEdit.deleteButton.addEventListener("click", function(ev) {
if (!window.confirm("Are you sure you want to delete this snippet?")) {
ev.preventDefault();
return;
@ -367,7 +367,7 @@ function makeSnippetEdit(ownerName, ownerAvatar, ownerUrl, date, text, attachmen
function editTimelineSnippet(timelineItemEl, stickyProjectId) {
let ownerName = timelineItemEl.querySelector(".user")?.textContent;
let ownerUrl = timelineItemEl.querySelector(".user")?.href;
let ownerAvatar = timelineItemEl.querySelector(".avatar")?.src;
let ownerAvatar = timelineItemEl.querySelector(".avatar-icon")?.src;
let creationDate = new Date(timelineItemEl.querySelector("time").dateTime);
let rawDesc = timelineItemEl.querySelector(".rawdesc").textContent;
let attachment = timelineItemEl.querySelector(".timeline-content-box")?.children?.[0];

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 236 253" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="books" x="0" y="0" width="235.74" height="252.97" style="fill:none;"/><path d="M77.61,252.704l0,-230.569l80.52,-0l-0,230.569l-80.52,0Zm59.686,-188.853l0,-20.883l-38.853,-0l0,20.883l38.853,0Zm0,128.926l0,-108.092l-38.853,-0l0,108.092l38.853,-0Zm-38.853,20.833l0,18.261l38.853,-0l0,-18.261l-38.853,0Zm-98.032,39.094l-0,-252.438l70.429,-0l-0,252.438l-70.429,0Zm49.596,-206.372l-0,-25.233l-28.763,-0l0,25.233l28.763,0Zm-0,142.369l-0,-121.536l-28.763,0l0,121.536l28.763,-0Zm-28.763,20.833l0,22.337c0,-0 28.763,-0 28.763,-0l-0,-22.337l-28.763,-0Zm143.656,43.17l-0,-252.438l70.429,-0l-0,252.438l-70.429,0Zm49.596,-206.372l-0,-25.233l-28.763,-0l0,25.233l28.763,0Zm-0,142.369l-0,-121.536l-28.763,0l0,121.536l28.763,-0Zm-28.763,20.833l0,22.337l28.763,-0l-0,-22.337l-28.763,-0Z"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 336 233" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="lightbulb" x="0" y="0" width="335.159" height="232.656" style="fill:none;"/><path d="M197.613,202.015c0,16.576 -13.457,30.034 -30.034,30.034c-16.576,-0 -30.034,-13.458 -30.034,-30.034l60.068,-0Zm0.06,-9.97l-60.206,-0c-3.155,-16.04 -5.152,-23.793 -32.39,-57.373c-10.513,-12.96 -16.178,-29.812 -16.178,-47.773c0,-43.425 35.256,-78.68 78.68,-78.68c43.425,-0 78.68,34.8 78.68,78.224c0,17.961 -5.665,34.814 -16.177,47.774c-27.238,33.58 -29.254,41.788 -32.409,57.828Zm-16.343,-20.834c3.88,-11.276 11.569,-24.225 32.572,-50.118c7.611,-9.383 11.524,-21.647 11.524,-34.65c-0,-31.893 -25.954,-57.391 -57.847,-57.391c-31.926,0 -57.846,25.92 -57.846,57.847c-0,13.003 3.913,25.266 11.524,34.649c20.946,25.823 28.639,38.556 32.521,49.663l27.552,0Zm-160.921,-151.684c-4.694,-3.321 -5.808,-9.827 -2.488,-14.52c3.321,-4.693 9.827,-5.808 14.52,-2.488l31.239,22.101c4.694,3.321 5.808,9.827 2.488,14.52c-3.32,4.693 -9.827,5.808 -14.52,2.488l-31.239,-22.101Zm12.032,153.97c-4.693,3.32 -11.199,2.205 -14.52,-2.488c-3.32,-4.693 -2.206,-11.199 2.488,-14.52l31.239,-22.101c4.693,-3.32 11.2,-2.205 14.52,2.488c3.32,4.693 2.206,11.199 -2.488,14.52l-31.239,22.101Zm-21.856,-74.77c-5.749,-0 -10.416,-4.668 -10.416,-10.417c-0,-5.749 4.667,-10.417 10.416,-10.417l38.267,0c5.749,0 10.417,4.668 10.417,10.417c-0,5.749 -4.668,10.417 -10.417,10.417l-38.267,-0Zm292.133,-95.906c4.693,-3.32 11.199,-2.205 14.519,2.488c3.321,4.693 2.206,11.199 -2.487,14.52l-31.239,22.101c-4.694,3.32 -11.2,2.206 -14.52,-2.488c-3.321,-4.693 -2.206,-11.199 2.487,-14.52l31.24,-22.101Zm12.032,153.97c4.693,3.321 5.808,9.827 2.487,14.52c-3.32,4.694 -9.826,5.808 -14.519,2.488l-31.24,-22.101c-4.693,-3.321 -5.808,-9.827 -2.487,-14.52c3.32,-4.693 9.826,-5.808 14.52,-2.488l31.239,22.101Zm9.823,-78.595c5.75,-0 10.417,4.667 10.417,10.416c0,5.749 -4.667,10.417 -10.417,10.417l-38.266,-0c-5.75,-0 -10.417,-4.668 -10.417,-10.417c-0,-5.749 4.667,-10.416 10.417,-10.416l38.266,-0Z"/></svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 301 229" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><rect id="presentation" x="-0" y="0" width="300.716" height="228.578" style="fill:none;"/><path d="M124.979,14.95l175.159,0l0,152.429l-137.896,0c-1.49,-7.262 -3.898,-14.19 -7.097,-20.659l124.334,-0l-0,-111.11l-143.514,-0c-2.302,-7.632 -6.074,-14.628 -10.986,-20.66Zm-119.77,196.191c-2.997,-8.252 -4.631,-17.155 -4.631,-26.437c0,-42.797 34.745,-77.542 77.541,-77.542c42.796,0 77.541,34.745 77.541,77.542c0,9.282 -1.634,18.185 -4.631,26.437c-22.091,10.548 -46.816,16.455 -72.91,16.455c-26.094,0 -50.819,-5.907 -72.91,-16.455Zm15.522,-13.612c17.693,7.296 37.075,11.317 57.388,11.317c20.314,0 39.695,-4.021 57.389,-11.317c0.919,-4.129 1.402,-8.421 1.402,-12.825c0,-32.448 -26.343,-58.792 -58.791,-58.792c-32.448,0 -58.791,26.344 -58.791,58.792c0,4.404 0.484,8.696 1.403,12.825Zm57.388,-196.547c28.741,-0 52.075,23.334 52.075,52.074c-0,28.741 -23.334,52.075 -52.075,52.075c-28.74,-0 -52.074,-23.334 -52.074,-52.075c-0,-28.74 23.334,-52.074 52.074,-52.074Zm0,18.75c-18.392,-0 -33.324,14.932 -33.324,33.324c-0,18.392 14.932,33.325 33.324,33.325c18.392,-0 33.325,-14.933 33.325,-33.325c-0,-18.392 -14.933,-33.324 -33.325,-33.324Z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -2,63 +2,94 @@ package buildscss
import (
"context"
"encoding/base64"
"fmt"
"os"
"path/filepath"
"git.handmade.network/hmn/hmn/src/config"
"git.handmade.network/hmn/hmn/src/jobs"
"git.handmade.network/hmn/hmn/src/logging"
"github.com/evanw/esbuild/pkg/api"
color "git.handmade.network/hmn/hmn/src/ansicolor"
"git.handmade.network/hmn/hmn/src/oops"
"git.handmade.network/hmn/hmn/src/website"
"github.com/spf13/cobra"
"github.com/wellington/go-libsass"
)
var ActiveServerPort uint16
var compressed bool
func RunServer(ctx context.Context) jobs.Job {
job := jobs.New()
if config.Config.Env != config.Dev {
job.Done()
return job
}
logger := logging.ExtractLogger(ctx).With().Str("module", "EsBuild").Logger()
esCtx, ctxErr := BuildContext()
if ctxErr != nil {
panic(ctxErr)
}
logger.Info().Msg("Starting esbuild server and watcher")
err := esCtx.Watch(api.WatchOptions{})
serverResult, err := esCtx.Serve(api.ServeOptions{
Port: config.Config.EsBuild.Port,
Servedir: "./",
OnRequest: func(args api.ServeOnRequestArgs) {
logger.Info().Interface("args", args).Msg("Response from esbuild server")
},
func init() {
libsass.RegisterSassFunc("base64($filename)", func(ctx context.Context, in libsass.SassValue) (*libsass.SassValue, error) {
var filename string
err := libsass.Unmarshal(in, &filename)
if err != nil {
return nil, err
}
fileBytes, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
encoded, _ := libsass.Marshal(base64.StdEncoding.EncodeToString(fileBytes))
return &encoded, nil
})
buildCommand := &cobra.Command{
Use: "buildscss",
Short: "Build the website CSS",
Run: func(cmd *cobra.Command, args []string) {
style := libsass.NESTED_STYLE
if compressed {
style = libsass.COMPRESSED_STYLE
}
err := compile("src/rawdata/scss/style.scss", "public/style.css", "light", style)
if err != nil {
fmt.Println(color.Bold + color.Red + "Failed to compile main SCSS." + color.Reset)
fmt.Println(err)
os.Exit(1)
}
for _, theme := range []string{"light", "dark"} {
err := compile("src/rawdata/scss/theme.scss", fmt.Sprintf("public/themes/%s/theme.css", theme), theme, style)
if err != nil {
fmt.Println(color.Bold + color.Red + "Failed to compile theme SCSS." + color.Reset)
fmt.Println(err)
os.Exit(1)
}
}
},
}
buildCommand.Flags().BoolVar(&compressed, "compressed", false, "Minify the output CSS")
website.WebsiteCommand.AddCommand(buildCommand)
}
func compile(inpath, outpath string, theme string, style int) error {
err := os.MkdirAll(filepath.Dir(outpath), 0755)
if err != nil {
panic(err)
panic(oops.New(err, "failed to create directory for CSS file"))
}
ActiveServerPort = serverResult.Port
go func() {
<-ctx.Done()
logger.Info().Msg("Shutting down esbuild server and watcher")
esCtx.Dispose()
job.Done()
}()
return job
}
outfile, err := os.OpenFile(outpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
panic(oops.New(err, "failed to open CSS file for writing"))
}
defer outfile.Close()
func BuildContext() (api.BuildContext, *api.ContextError) {
return api.Context(api.BuildOptions{
EntryPoints: []string{
"src/rawdata/scss/style.css",
},
Outbase: "src/rawdata/scss",
Outdir: "public",
External: []string{"/public/*"},
Bundle: true,
Write: true,
Engines: []api.Engine{
{Name: api.EngineChrome, Version: "109"},
{Name: api.EngineFirefox, Version: "109"},
{Name: api.EngineSafari, Version: "12"},
},
})
infile, err := os.Open(inpath)
if err != nil {
panic(oops.New(err, "failed to open SCSS file"))
}
compiler, err := libsass.New(outfile, infile,
libsass.IncludePaths([]string{
"src/rawdata/scss",
fmt.Sprintf("src/rawdata/scss/themes/%s", theme),
}),
libsass.OutputStyle(style),
)
if err != nil {
panic(oops.New(err, "failed to create SCSS compiler"))
}
return compiler.Run()
}

View File

@ -1,39 +0,0 @@
package cmd
import (
"fmt"
"os"
"git.handmade.network/hmn/hmn/src/buildscss"
"git.handmade.network/hmn/hmn/src/logging"
"git.handmade.network/hmn/hmn/src/website"
"github.com/spf13/cobra"
)
func init() {
buildCommand := &cobra.Command{
Use: "buildscss",
Short: "Build the website CSS",
Run: func(cmd *cobra.Command, args []string) {
ctx, err := buildscss.BuildContext()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
res := ctx.Rebuild()
outputFilenames := make([]string, 0)
for _, o := range res.OutputFiles {
outputFilenames = append(outputFilenames, o.Path)
}
logging.Info().
Interface("Errors", res.Errors).
Interface("Warnings", res.Warnings).
Msg("Ran esbuild")
if len(outputFilenames) > 0 {
logging.Info().Interface("Files", outputFilenames).Msg("Wrote files")
}
},
}
website.WebsiteCommand.AddCommand(buildCommand)
}

View File

@ -91,7 +91,4 @@ var Config = HMNConfig{
FFMpegPath: "", // Will not generate asset video thumbnails if ffmpeg is not specified
CPULimitPath: "", // Not mandatory. FFMpeg will not limited if this is not provided
},
EsBuild: EsBuildConfig{
Port: 9004,
},
}

View File

@ -32,8 +32,7 @@ type HMNConfig struct {
EpisodeGuide EpisodeGuide
DevConfig DevConfig
PreviewGeneration PreviewGenerationConfig
Calendars []CalendarSource
EsBuild EsBuildConfig
Calendars []CalendarSource
}
type PostgresConfig struct {
@ -105,7 +104,7 @@ type MatrixConfig struct {
type CalendarSource struct {
Name string
Url string
Url string
}
type EpisodeGuide struct {
@ -127,10 +126,6 @@ type PreviewGenerationConfig struct {
CPULimitPath string
}
type EsBuildConfig struct {
Port uint16
}
func init() {
if Config.EpisodeGuide.Projects == nil {
Config.EpisodeGuide.Projects = make(map[string]string)

View File

@ -23,6 +23,34 @@ import (
"github.com/jpillora/backoff"
)
type BotEvent struct {
Timestamp time.Time
Name string
Extra string
}
var botEvents = make([]BotEvent, 0, 1000)
var botEventsMutex = sync.Mutex{}
func RecordBotEvent(name, extra string) {
botEventsMutex.Lock()
defer botEventsMutex.Unlock()
if len(botEvents) > 1000 {
botEvents = botEvents[len(botEvents)-500:]
}
botEvents = append(botEvents, BotEvent{
Timestamp: time.Now(),
Name: name,
Extra: extra,
})
}
func GetBotEvents() []BotEvent {
botEventsMutex.Lock()
defer botEventsMutex.Unlock()
return botEvents[:]
}
func RunDiscordBot(ctx context.Context, dbConn *pgxpool.Pool) jobs.Job {
log := logging.ExtractLogger(ctx).With().Str("module", "discord").Logger()
ctx = logging.AttachLoggerToContext(&log, ctx)
@ -56,6 +84,11 @@ func RunDiscordBot(ctx context.Context, dbConn *pgxpool.Pool) jobs.Job {
log.Info().Msg("Connecting to the Discord gateway")
bot := newBotInstance(dbConn)
err := bot.Run(ctx)
disconnectMessage := ""
if err != nil {
disconnectMessage = err.Error()
}
RecordBotEvent("Disconnected", disconnectMessage)
if err != nil {
dur := boff.Duration()
log.Error().
@ -101,6 +134,8 @@ type botInstance struct {
conn *websocket.Conn
dbConn *pgxpool.Pool
resuming bool
heartbeatIntervalMs int
forceHeartbeat chan struct{}
@ -193,6 +228,7 @@ func (bot *botInstance) Run(ctx context.Context) (err error) {
logging.ExtractLogger(ctx).Info().Msg("Discord asked us to reconnect to the gateway")
return nil
case OpcodeInvalidSession:
RecordBotEvent("Failed to resume - invalid session", "")
// We tried to resume but the session was invalid.
// Delete the session and reconnect from scratch again.
_, err := bot.dbConn.Exec(ctx, `DELETE FROM discord_session`)
@ -264,8 +300,11 @@ func (bot *botInstance) connect(ctx context.Context) error {
}
}
RecordBotEvent("Connected", "")
if shouldResume {
RecordBotEvent("Resuming with session ID", session.ID)
// Reconnect to the previous session
bot.resuming = true
err := bot.sendGatewayMessage(ctx, GatewayMessage{
Opcode: OpcodeResume,
Data: Resume{
@ -540,11 +579,20 @@ func (bot *botInstance) processEventMsg(ctx context.Context, msg *GatewayMessage
panic(fmt.Sprintf("processEventMsg must only be used on Dispatch messages (opcode %d). Validate this before you call this function.", OpcodeDispatch))
}
if bot.resuming {
name := ""
if msg.EventName != nil {
name = *msg.EventName
}
RecordBotEvent("Got event while resuming", name)
}
switch *msg.EventName {
case "RESUMED":
// Nothing to do, but at least we can log something
logging.ExtractLogger(ctx).Info().Msg("Finished resuming gateway session")
bot.resuming = false
RecordBotEvent("Done resuming", "")
bot.createApplicationCommands(ctx)
case "MESSAGE_CREATE":
newMessage := *MessageFromMap(msg.Data, "")

View File

@ -185,6 +185,7 @@ func Scrape(ctx context.Context, dbConn *pgxpool.Pool, channelID string, earlies
return true
}
msg.Backfilled = true
err := HandleIncomingMessage(ctx, dbConn, &msg, createSnippets)
if err != nil {

View File

@ -27,6 +27,11 @@ import (
"github.com/google/uuid"
)
var autostoreChannels = []string{
config.Config.Discord.ShowcaseChannelID,
// TODO(asaf): Add jam channel
}
func HandleIncomingMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message, createSnippets bool) error {
deleted := false
var err error
@ -53,6 +58,11 @@ func HandleIncomingMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message
err = HandleInternedMessage(ctx, dbConn, msg, deleted, createSnippets)
}
// when we needed her most...she vanished
// if !deleted && err == nil {
// err = FreyaMode(ctx, dbConn, msg)
// }
return err
}
@ -65,17 +75,7 @@ func CleanUpShowcase(ctx context.Context, dbConn db.ConnOrTx, msg *Message) (boo
return deleted, nil
}
hasGoodContent := true
if msg.OriginalHasFields("content") && !messageHasLinks(msg.Content) {
hasGoodContent = false
}
hasGoodAttachments := true
if msg.OriginalHasFields("attachments") && len(msg.Attachments) == 0 {
hasGoodAttachments = false
}
if !hasGoodContent && !hasGoodAttachments {
if !messageShouldBeStored(msg) {
err := DeleteMessage(ctx, msg.ChannelID, msg.ID)
if err != nil {
return deleted, oops.New(err, "failed to delete message")
@ -91,7 +91,7 @@ func CleanUpShowcase(ctx context.Context, dbConn db.ConnOrTx, msg *Message) (boo
err = SendMessages(ctx, dbConn, MessageToSend{
ChannelID: channel.ID,
Req: CreateMessageRequest{
Content: "Posts in #project-showcase are required to have either an image/video or a link. Discuss showcase content in #projects.",
Content: "Posts in #project-showcase are required to have either an image/video or a link, or start with `!til`. Discuss showcase content in #projects.",
},
})
if err != nil {
@ -146,6 +146,70 @@ func CleanUpLibrary(ctx context.Context, dbConn db.ConnOrTx, msg *Message) (bool
return deleted, nil
}
func FreyaMode(ctx context.Context, dbConn db.ConnOrTx, msg *Message) error {
if msg.Author.IsBot {
return nil
}
if msg.ChannelID == config.Config.Discord.ShowcaseChannelID || msg.ChannelID == config.Config.Discord.LibraryChannelID {
return nil
}
twitteryUrls := []string{
"https://twitter.com",
"https://x.com",
"https://vxtwitter.com",
"https://fxtwitter.com",
}
isTwittery := false
for _, url := range twitteryUrls {
if strings.Contains(msg.Content, url) {
isTwittery = true
}
}
if !isTwittery {
return nil
}
// FREYA MODE ENGAGED
approvedTweets := []string{
"https://vxtwitter.com/FreyaHolmer/status/1757836988495847568",
"https://vxtwitter.com/FreyaHolmer/status/1752441092501361103",
"https://vxtwitter.com/FreyaHolmer/status/1753813557966217268",
"https://vxtwitter.com/FreyaHolmer/status/1746228932188295579",
"https://vxtwitter.com/FreyaHolmer/status/1732687685850894799",
"https://vxtwitter.com/FreyaHolmer/status/1761487879178736048",
"https://vxtwitter.com/FreyaHolmer/status/1733820461492863442",
"https://vxtwitter.com/FreyaHolmer/status/1732845451701871101",
"https://vxtwitter.com/FreyaHolmer/status/1765680355657359585",
"https://vxtwitter.com/FreyaHolmer/status/1784678195997852129",
"https://vxtwitter.com/FreyaHolmer/status/1741468609044508831",
"https://vxtwitter.com/FreyaHolmer/status/1759306434053870012",
"https://vxtwitter.com/FreyaHolmer/status/1754929898492162178",
"https://vxtwitter.com/FreyaHolmer/status/1782498313511534822",
"https://vxtwitter.com/FreyaHolmer/status/1623737764041695232",
"https://vxtwitter.com/FreyaHolmer/status/1718979996125925494",
"https://vxtwitter.com/FreyaHolmer/status/1675945798448607248",
"https://vxtwitter.com/FreyaHolmer/status/1662229911375953922",
"https://vxtwitter.com/FreyaHolmer/status/1652235944752185345",
"https://vxtwitter.com/FreyaHolmer/status/1386408507218427905",
"https://vxtwitter.com/FreyaHolmer/status/1436696408506212353",
"https://vxtwitter.com/FreyaHolmer/status/1444755552777670657",
"https://vxtwitter.com/FreyaHolmer/status/1232826293902888960",
}
tweet := approvedTweets[rand.Intn(len(approvedTweets))]
err := SendMessages(ctx, dbConn, MessageToSend{
ChannelID: msg.ChannelID,
Req: CreateMessageRequest{
Content: fmt.Sprintf("No. Only Freya is allowed to tweet. %s", tweet),
},
})
if err != nil {
return oops.New(err, "failed to send Freya tweet")
}
return nil
}
func ShareToMatrix(ctx context.Context, msg *Message) error {
if msg.Flags&MessageFlagCrossposted == 0 {
return nil
@ -238,7 +302,7 @@ func ShareToMatrix(ctx context.Context, msg *Message) error {
}
func MaybeInternMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message) error {
if msg.ChannelID == config.Config.Discord.ShowcaseChannelID {
if messageShouldBeStored(msg) {
err := InternMessage(ctx, dbConn, msg)
if errors.Is(err, errNotEnoughInfo) {
logging.ExtractLogger(ctx).Warn().
@ -290,8 +354,8 @@ func InternMessage(
_, err = dbConn.Exec(ctx,
`
INSERT INTO discord_message (id, channel_id, guild_id, url, user_id, sent_at, snippet_created)
VALUES ($1, $2, $3, $4, $5, $6, $7)
INSERT INTO discord_message (id, channel_id, guild_id, url, user_id, sent_at, snippet_created, backfilled)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
`,
msg.ID,
msg.ChannelID,
@ -300,6 +364,7 @@ func InternMessage(
msg.Author.ID,
msg.Time(),
false,
msg.Backfilled,
)
if err != nil {
return oops.New(err, "failed to save new discord message")
@ -341,7 +406,7 @@ func FetchInternedMessage(ctx context.Context, dbConn db.ConnOrTx, msgId string)
// 1. Saves/updates content
// 2. Saves/updates snippet
// 3. Deletes content/snippet
func HandleInternedMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message, deleted bool, createSnippet bool) error {
func HandleInternedMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message, removeInternedMessage bool, createSnippet bool) error {
tx, err := dbConn.Begin(ctx)
if err != nil {
return oops.New(err, "failed to start transaction")
@ -352,7 +417,11 @@ func HandleInternedMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message
if err != nil && !errors.Is(err, db.NotFound) {
return err
} else if err == nil {
if !deleted {
if !removeInternedMessage {
removeInternedMessage = !messageShouldBeStored(msg)
}
if !removeInternedMessage {
err = SaveMessageContents(ctx, tx, interned, msg)
if err != nil {
return err
@ -785,7 +854,7 @@ func HandleSnippetForInternedMessage(ctx context.Context, dbConn db.ConnOrTx, in
if interned.MessageContent == nil {
// NOTE(asaf): Can't have a snippet without content
// NOTE(asaf): Messages that only have an attachment also have blank content
// NOTE(asaf): Messages that only have an attachment also have a content struct with an empty content string
// TODO(asaf): Do we need to delete existing snippets in this case??? Not entirely sure how to trigger this through discord
return nil
}
@ -835,43 +904,42 @@ func HandleSnippetForInternedMessage(ctx context.Context, dbConn db.ConnOrTx, in
if shouldCreate {
// Get an asset ID or URL to make a snippet from
assetId, url, err := getSnippetAssetOrUrl(ctx, tx, &interned.Message)
if assetId != nil || url != nil {
contentMarkdown := interned.MessageContent.LastContent
contentHTML := parsing.ParseMarkdown(contentMarkdown, parsing.DiscordMarkdown)
_, err = tx.Exec(ctx,
`
INSERT INTO snippet (url, "when", description, _description_html, asset_id, discord_message_id, owner_id)
VALUES ($1, $2, $3, $4, $5, $6, $7)
`,
url,
interned.Message.SentAt,
contentMarkdown,
contentHTML,
assetId,
interned.Message.ID,
interned.HMNUser.ID,
)
if err != nil {
return oops.New(err, "failed to create snippet from attachment")
}
contentMarkdown := interned.MessageContent.LastContent
contentHTML := parsing.ParseMarkdown(contentMarkdown, parsing.DiscordMarkdown)
existingSnippet, err = FetchSnippetForMessage(ctx, tx, interned.Message.ID)
if err != nil {
return oops.New(err, "failed to fetch newly-created snippet")
}
_, err = tx.Exec(ctx,
`
INSERT INTO snippet (url, "when", description, _description_html, asset_id, discord_message_id, owner_id)
VALUES ($1, $2, $3, $4, $5, $6, $7)
`,
url,
interned.Message.SentAt,
contentMarkdown,
contentHTML,
assetId,
interned.Message.ID,
interned.HMNUser.ID,
)
if err != nil {
return oops.New(err, "failed to create snippet from attachment")
}
_, err = tx.Exec(ctx,
`
UPDATE discord_message
SET snippet_created = TRUE
WHERE id = $1
`,
interned.Message.ID,
)
if err != nil {
return oops.New(err, "failed to mark message as having snippet")
}
existingSnippet, err = FetchSnippetForMessage(ctx, tx, interned.Message.ID)
if err != nil {
return oops.New(err, "failed to fetch newly-created snippet")
}
_, err = tx.Exec(ctx,
`
UPDATE discord_message
SET snippet_created = TRUE
WHERE id = $1
`,
interned.Message.ID,
)
if err != nil {
return oops.New(err, "failed to mark message as having snippet")
}
}
}
@ -1013,3 +1081,36 @@ func messageHasLinks(content string) bool {
return false
}
func messageShouldBeStored(msg *Message) bool {
if msg == nil {
return false
}
if strings.HasPrefix(strings.ToLower(strings.TrimSpace(msg.Content)), "!til") {
return true
}
autostore := false
for _, cid := range autostoreChannels {
if msg.ChannelID == cid {
autostore = true
break
}
}
if autostore {
hasGoodContent := true
if msg.OriginalHasFields("content") && !messageHasLinks(msg.Content) {
hasGoodContent = false
}
hasGoodAttachments := true
if msg.OriginalHasFields("attachments") && len(msg.Attachments) == 0 {
hasGoodAttachments = false
}
return hasGoodContent || hasGoodAttachments
}
return false
}

View File

@ -275,6 +275,7 @@ type Message struct {
Embeds []Embed `json:"embeds"`
originalMap map[string]interface{}
Backfilled bool
}
func (m *Message) JumpURL() string {

View File

@ -62,6 +62,17 @@ var WRJ2023 = Jam{
UrlSlug: "2023",
}
var LJ2024 = Jam{
Event: Event{
StartTime: time.Date(2024, 3, 15, 17, 0, 0, 0, time.UTC),
EndTime: time.Date(2024, 3, 25, 0, 0, 0, 0, time.UTC),
},
Name: "Learning Jam 2024",
Slug: "LJ2024",
UrlSlug: "learning-2024",
}
// Conferences
var HMS2022 = Event{
StartTime: time.Date(2022, 11, 16, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
EndTime: time.Date(2022, 11, 18, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
@ -77,7 +88,17 @@ var HMBoston2023 = Event{
EndTime: time.Date(2023, 8, 4, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
}
var AllJams = []Jam{WRJ2021, WRJ2022, VJ2023, WRJ2023}
var HMS2024 = Event{
StartTime: time.Date(2024, 11, 20, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
EndTime: time.Date(2024, 11, 22, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
}
var HMBoston2024 = Event{
StartTime: time.Date(2024, 8, 9, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
EndTime: time.Date(2024, 8, 10, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
}
var AllJams = []Jam{WRJ2021, WRJ2022, VJ2023, WRJ2023, LJ2024}
func CurrentJam() *Jam {
now := time.Now()

View File

@ -38,9 +38,8 @@ type ProjectsQuery struct {
type ProjectAndStuff struct {
Project models.Project
LogoLightAsset *models.Asset
LogoDarkAsset *models.Asset
HeaderImage *models.Asset
LogoLightAsset *models.Asset `db:"logolight_asset"`
LogoDarkAsset *models.Asset `db:"logodark_asset"`
Owners []*models.User
Tag *models.Tag
}
@ -73,7 +72,6 @@ func FetchProjects(
Project models.Project `db:"project"`
LogoLightAsset *models.Asset `db:"logolight_asset"`
LogoDarkAsset *models.Asset `db:"logodark_asset"`
HeaderAsset *models.Asset `db:"header_asset"`
Tag *models.Tag `db:"tag"`
}
@ -88,7 +86,6 @@ func FetchProjects(
project
LEFT JOIN asset AS logolight_asset ON logolight_asset.id = project.logolight_asset_id
LEFT JOIN asset AS logodark_asset ON logodark_asset.id = project.logodark_asset_id
LEFT JOIN asset AS header_asset ON header_asset.id = project.header_asset_id
LEFT JOIN tag ON project.tag = tag.id
`)
if len(q.OwnerIDs) > 0 {
@ -222,7 +219,6 @@ func FetchProjects(
Project: p.Project,
LogoLightAsset: p.LogoLightAsset,
LogoDarkAsset: p.LogoDarkAsset,
HeaderImage: p.HeaderAsset,
Owners: owners,
Tag: p.Tag,
})

View File

@ -44,10 +44,6 @@ func TestShowcase(t *testing.T) {
AssertRegexMatch(t, BuildShowcase(), RegexShowcase, nil)
}
func TestStreams(t *testing.T) {
AssertRegexMatch(t, BuildStreams(), RegexStreams, nil)
}
func TestWhenIsIt(t *testing.T) {
AssertRegexMatch(t, BuildWhenIsIt(), RegexWhenIsIt, nil)
}
@ -143,7 +139,6 @@ func TestFeed(t *testing.T) {
func TestProjectIndex(t *testing.T) {
AssertRegexMatch(t, BuildProjectIndex(1, ""), RegexProjectIndex, nil)
AssertRegexMatch(t, BuildProjectIndex(2, ""), RegexProjectIndex, map[string]string{"page": "2"})
AssertRegexMatch(t, BuildProjectIndex(1, "test"), RegexProjectIndex, map[string]string{"category": "test"})
AssertRegexMatch(t, BuildProjectIndex(2, "test"), RegexProjectIndex, map[string]string{"page": "2", "category": "test"})
assert.Panics(t, func() { BuildProjectIndex(0, "") })
@ -427,6 +422,16 @@ func TestJamFeed2023(t *testing.T) {
AssertSubdomain(t, BuildJamFeed2023(), "")
}
func TestJamIndex2024_Learning(t *testing.T) {
AssertRegexMatch(t, BuildJamIndex2024_Learning(), RegexJamIndex2024_Learning, nil)
AssertSubdomain(t, BuildJamIndex2024_Learning(), "")
}
func TestJamFeed2024_Learning(t *testing.T) {
AssertRegexMatch(t, BuildJamFeed2024_Learning(), RegexJamFeed2024_Learning, nil)
AssertSubdomain(t, BuildJamFeed2024_Learning(), "")
}
func TestTimeMachine(t *testing.T) {
AssertRegexMatch(t, BuildTimeMachine(), RegexTimeMachine, nil)
AssertSubdomain(t, BuildTimeMachine(), "")
@ -454,8 +459,8 @@ func TestTimeMachineFormDone(t *testing.T) {
}
func TestNewsletterSignup(t *testing.T) {
AssertRegexMatch(t, BuildNewsletterSignup(), RegexNewsletterSignup, nil)
AssertSubdomain(t, BuildNewsletterSignup(), "")
AssertRegexMatch(t, BuildAPINewsletterSignup(), RegexNewsletterSignup, nil)
AssertSubdomain(t, BuildAPINewsletterSignup(), "")
}
func TestProjectNewJam(t *testing.T) {

View File

@ -35,13 +35,6 @@ func BuildShowcase() string {
return Url("/showcase", nil)
}
var RegexStreams = regexp.MustCompile("^/streams$")
func BuildStreams() string {
defer CatchPanic()
return Url("/streams", nil)
}
var RegexWhenIsIt = regexp.MustCompile("^/whenisit$")
func BuildWhenIsIt() string {
@ -49,13 +42,6 @@ func BuildWhenIsIt() string {
return Url("/whenisit", nil)
}
var RegexNewsletterSignup = regexp.MustCompile("^/newsletter$")
func BuildNewsletterSignup() string {
defer CatchPanic()
return Url("/newsletter", nil)
}
var RegexJamsIndex = regexp.MustCompile("^/jams$")
func BuildJamsIndex() string {
@ -126,6 +112,34 @@ func BuildJamRecap2023_Visibility() string {
return Url("/jam/visibility-2023/recap", nil)
}
var RegexJamIndex2024_Learning = regexp.MustCompile("^/jam/learning-2024$")
func BuildJamIndex2024_Learning() string {
defer CatchPanic()
return Url("/jam/learning-2024", nil)
}
var RegexJamFeed2024_Learning = regexp.MustCompile("^/jam/learning-2024/feed$")
func BuildJamFeed2024_Learning() string {
defer CatchPanic()
return Url("/jam/learning-2024/feed", nil)
}
var RegexJamGuidelines2024_Learning = regexp.MustCompile("^/jam/learning-2024/guidelines$")
func BuildJamGuidelines2024_Learning() string {
defer CatchPanic()
return Url("/jam/learning-2024/guidelines", nil)
}
var RegexJamSaveTheDate = regexp.MustCompile("^/jam/upcoming$")
func BuildJamSaveTheDate() string {
defer CatchPanic()
return Url("/jam/upcoming", nil)
}
func BuildJamIndexAny(slug string) string {
defer CatchPanic()
return Url(fmt.Sprintf("/jam/%s", slug), nil)
@ -333,6 +347,24 @@ func BuildConferences() string {
return Url("/conferences", nil)
}
/*
* Volunteer/Staff Roles
*/
var RegexStaffRolesIndex = regexp.MustCompile(`^/roles$`)
func BuildStaffRolesIndex() string {
defer CatchPanic()
return Url("/roles", nil)
}
var RegexStaffRole = regexp.MustCompile(`^/roles/(?P<slug>[^/]+)$`)
func BuildStaffRole(slug string) string {
defer CatchPanic()
return Url(fmt.Sprintf("/roles/%s", slug), nil)
}
/*
* User
*/
@ -617,12 +649,6 @@ func BuildEducationRerender() string {
return Url("/education/rerender", nil)
}
/*
* Style test
*/
var RegexStyleTest = regexp.MustCompile(`^/debug/styles$`)
/*
* Forums
*/
@ -896,6 +922,12 @@ func BuildDiscordShowcaseBacklog() string {
return Url("/discord_showcase_backlog", nil)
}
var RegexDiscordBotDebugPage = regexp.MustCompile("^/discord_bot_debug$")
func BuildDiscordBotDebugPage() string {
return Url("/discord_bot_debug", nil)
}
/*
* API
*/
@ -906,6 +938,12 @@ func BuildAPICheckUsername() string {
return Url("/api/check_username", nil)
}
var RegexAPINewsletterSignup = regexp.MustCompile("^/api/newsletter_signup$")
func BuildAPINewsletterSignup() string {
return Url("/api/newsletter_signup", nil)
}
/*
* Twitch stuff
*/
@ -955,13 +993,6 @@ func BuildS3Asset(s3key string) string {
return res
}
var RegexEsBuild = regexp.MustCompile("^/esbuild$")
func BuildEsBuild() string {
defer CatchPanic()
return Url("/esbuild", nil)
}
var RegexPublic = regexp.MustCompile("^/public/.+$")
func BuildPublic(filepath string, cachebust bool) string {
@ -1009,6 +1040,12 @@ func BuildUserFile(filepath string) string {
return BuildPublic(fmt.Sprintf("media/%s", filepath), false)
}
/*
* Redirects
*/
var RegexUnwind = regexp.MustCompile(`^/unwind$`)
/*
* Other
*/

View File

@ -0,0 +1,47 @@
package migrations
import (
"context"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"
"github.com/jackc/pgx/v5"
)
func init() {
registerMigration(AddBackfillToDiscordMessage{})
}
type AddBackfillToDiscordMessage struct{}
func (m AddBackfillToDiscordMessage) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2024, 3, 28, 18, 41, 7, 0, time.UTC))
}
func (m AddBackfillToDiscordMessage) Name() string {
return "AddBackfillToDiscordMessage"
}
func (m AddBackfillToDiscordMessage) Description() string {
return "Add a backfill flag to discord messages"
}
func (m AddBackfillToDiscordMessage) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
ALTER TABLE discord_message
ADD COLUMN backfilled BOOLEAN NOT NULL default FALSE;
`,
)
return err
}
func (m AddBackfillToDiscordMessage) Down(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
ALTER TABLE discord_message
DROP COLUMN backfilled;
`,
)
return err
}

View File

@ -0,0 +1,47 @@
package migrations
import (
"context"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"
"github.com/jackc/pgx/v5"
)
func init() {
registerMigration(newsletter{})
}
type newsletter struct{}
func (m newsletter) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2024, 5, 7, 1, 34, 32, 0, time.UTC))
}
func (m newsletter) Name() string {
return "newsletter"
}
func (m newsletter) Description() string {
return "Adds the newsletter signup"
}
func (m newsletter) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
CREATE TABLE newsletter_emails (
email VARCHAR(255) NOT NULL PRIMARY KEY
);
`,
)
return err
}
func (m newsletter) Down(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
DROP TABLE newsletter_emails;
`,
)
return err
}

View File

@ -1,47 +0,0 @@
package migrations
import (
"context"
"time"
"git.handmade.network/hmn/hmn/src/migration/types"
"github.com/jackc/pgx/v5"
)
func init() {
registerMigration(AddHeaderImage{})
}
type AddHeaderImage struct{}
func (m AddHeaderImage) Version() types.MigrationVersion {
return types.MigrationVersion(time.Date(2024, 6, 1, 2, 1, 18, 0, time.UTC))
}
func (m AddHeaderImage) Name() string {
return "AddHeaderImage"
}
func (m AddHeaderImage) Description() string {
return "Adds a header image to projects"
}
func (m AddHeaderImage) Up(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
ALTER TABLE project
ADD COLUMN header_asset_id UUID REFERENCES asset (id) ON DELETE SET NULL;
`,
)
return err
}
func (m AddHeaderImage) Down(ctx context.Context, tx pgx.Tx) error {
_, err := tx.Exec(ctx,
`
ALTER TABLE project
DROP COLUMN header_asset_id;
`,
)
return err
}

View File

@ -0,0 +1,23 @@
.unapproved-project {
border-bottom: 1px solid transparent;
padding: 10px;
padding-bottom: 20px;
&.hidden-project {
background: -moz-linear-gradient(left, rgba(132,26,26,0.4) 0%,rgba(132,26,26,0.1) 15%, rgba(132,26,26,0.0) 50%); /* FF3.6-15 */
background: -webkit-linear-gradient(left, rgba(132,26,26,0.4) 0%,rgba(132,26,26,0.1) 15%, rgba(132,26,26,0.0) 50%); /* Chrome10-25,Safari5.1-6 */
background: linear-gradient(to right, rgba(132,26,26,0.4) 0%,rgba(132,26,26,0.1) 15%, rgba(132,26,26,0.0) 50%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
}
&:last-child {
border-bottom-width: 0px;
}
}
.hidden-project-tag {
color:red;
}
a.new-user {
display:block;
}

View File

@ -0,0 +1,70 @@
.userlist {
text-align:center;
width:100%;
margin:0px auto;
padding-top:30px;
position:relative;
line-height:0em;
.user {
margin:5px;
display:inline-block;
height:200px;
max-height:180px;
text-align:left;
position:relative;
width:296px;
border-radius:3px;
line-height:1.4em;
overflow:hidden;
box-shadow:0px 2px 5px rgba(0,0,0,0.20);
top:0px;
transition: box-shadow 0.2s, background-color 0.2s;
&:hover {
box-shadow:0px 3px 7px rgba(0,0,0,0.20);
}
table {
/* Border and background color given by theme */
border:1px solid transparent;
width:100%;
height:100%;
}
.bottom {
transition: background-color 0.2s;
}
td {
vertical-align:top;
&.avatar {
width:100px;
height:80px;
}
}
.name {
text-align:left;
.username {
vertical-align:middle;
}
}
.avatar .image {
display:block;
float:left;
width:80px;
height:80px;
background-size:cover;
margin-right:10px;
}
.bio {
padding:5px;
}
}
}

View File

@ -14,22 +14,23 @@
.carousel-button {
border: 1px solid;
border-color: var(--dimmer-color);
@include usevar(border-color, dimmer-color);
cursor: pointer;
transition: all 100ms ease-in-out;
&:hover {
background-color: var(--dimmest-color);
@include usevar(background-color, dimmest-color);
}
&.active {
border-color: var(--theme-color);
width: var(--width-2);
@include usevar(border-color, theme-color);
@extend .w2;
&:hover {
background-color: var(--theme-color-dimmest);
@include usevar(background-color, theme-color-dimmest);
}
}
}
}
}

View File

@ -0,0 +1,186 @@
strong {
font-weight: 500;
}
.text {
@include usevar(background-color, text-background);
}
.underline {
text-decoration:underline;
}
.monospace {
font-family: $monospace-fonts;
background-color: rgba(0, 0, 0, 0.1);
padding: 0.2em 0 0.05em;
border-radius: 3px;
&::before, &::after {
content: "\00a0";
letter-spacing: -0.2em;
}
}
.center {
text-align: center;
}
.right {
text-align: right;
}
.left {
text-align: left;
}
.justify {
text-align:justify;
-webkit-hyphens:manual;
-moz-hyphens:manual;
-ms-hyphens:manual;
hyphens:manual;
}
.spoiler {
@include usevar(border-color, spoiler-border);
border-width: 1px;
border-style: dashed;
color: transparent;
&::selection {
color: white;
background-color: black;
}
}
blockquote {
@extend .b--dimmest;
@extend .ml2, .pl2, .mr0;
@extend .bl;
.quotewho {
line-height: 2em;
&::after {
content: " said:";
}
}
}
pre {
font-family: $monospace-fonts;
}
.post-content {
* {
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.25rem;
}
h4 {
font-size: 1rem;
}
h5 {
font-size: 0.8rem;
}
h1, h2, h3, h4, h5 {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
li:not(:last-child) {
margin-bottom: 0.2em;
}
img {
max-width: 100%;
}
hr {
@extend .mh3;
}
div.code {
@extend .b--dimmer;
max-width: 100%;
max-height: 20em;
max-height: 80vh;
overflow: auto;
-moz-tab-size: 4;
tab-size: 4;
border-width: 1px;
border-style: solid;
}
div + br,
blockquote + br,
ul + br,
ol + br,
{
display: none;
}
table {
@extend .mv2;
}
th, td {
@extend .ph2, .pv1;
@extend .ba, .b--dimmest;
}
code {
@extend .bg--dim;
@extend .br2;
padding: .2em 0;
white-space: nowrap;
&::before, &::after {
content: "\00a0";
letter-spacing: -0.2em;
display: inline-block;
}
}
pre > code, pre.hmn-code {
@extend .bg--dim;
@extend .br2;
padding: 0.7em;
overflow-x: auto;
}
}
figure {
@extend .mh0, .mv2, .mv3-ns, .pa2, .pa3-ns, .bg--dim, .br3;
display: flex;
flex-direction: column;
padding-bottom: 0;
figcaption {
@extend .pv1, .pv2-ns;
}
}

834
src/rawdata/scss/_core.scss Normal file
View File

@ -0,0 +1,834 @@
// Global variables
$input-padding: 0.3rem;
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Chrome/Safari/Opera */
-khtml-user-select: none; /* Konqueror */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE/Edge */
user-select: none; /* non-prefixed version, currently */
}
* {
box-sizing: border-box;
}
br {
border-style: none; // why, IE...
}
body {
@include usevar(background-color, main-background-color);
@include usevar(color, main-color);
font-family: "Fira Sans", sans-serif;
background-size: cover;
min-height: 100vh;
box-sizing: border-box;
font-size: px2rem(14px);
line-height: 1.5;
font-weight: 400;
}
a, .link {
@include usevar(color, link-color);
@include usevar(border-bottom-color, link-border-color);
border-bottom: none;
text-decoration: none;
&:hover {
/* text-decoration:underline; */
}
&.external::after {
font-family: "icons";
content:" 1";
vertical-align:middle;
}
}
ol {
list-style-type: decimal;
}
ul {
list-style-type: circle;
padding: 0;
margin: 0;
}
.list-none {
list-style-type: none;
}
li {
padding: 0;
margin: 0;
padding-left: 5px;
margin-left: 15px;
}
h1, h2, h3, h4, h5 {
font-weight: 500;
margin: 0;
margin-bottom: 0.5rem;
font-size: 1.5rem;
line-height: 1.25em;
}
strong {
font-weight: 600;
}
em {
font-style: italic;
}
p {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.p-spaced p {
$spacing: 0.6em;
&:not(:first-child) {
margin-top: $spacing;
}
&:not(:last-child) {
margin-bottom: $spacing;
}
}
table {
border-collapse: collapse;
table-layout: fixed;
}
th, td {
@include usevar(color, fg-font-color);
}
td {
vertical-align:baseline;
}
hr {
@include usevar(border-top-color, hr-color);
@extend .mv3;
border-width: 1px 0 0;
border-top-style: solid;
}
$monospace-fonts: "Fira Mono", monospace;
.mono {
font-family: $monospace-fonts;
}
article code {
font-family: $monospace-fonts;
}
.big { font-size:120%; }
.title {
font-weight: bold;
}
.clear {
width: 0;
}
.full {
width: 100%;
}
.hidden {
display: none;
}
.empty {
padding-left: 20px;
}
.column h2 {
text-align: center;
margin-bottom: 20px;
}
.margin-center {
margin-left: auto;
margin-right: auto;
}
.flex-fair {
flex-basis: 1px;
flex-grow: 1;
flex-shrink: 1;
}
@media #{$breakpoint-not-small} {
.flex-fair-ns {
flex-basis: 1px;
flex-grow: 1;
flex-shrink: 1;
}
}
@media #{$breakpoint-large} {
.flex-fair-l {
flex-basis: 1px;
flex-grow: 1;
flex-shrink: 1;
}
}
.c--normal {
@include usevar('color', 'fg-font-color');
}
.c--inherit {
color: inherit;
&:hover, &:active {
color: inherit;
}
}
.b--theme {
@include usevar(border-color, theme-color);
}
.c--dim {
@include usevar(color, dim-color);
}
.c--theme-dim {
@include usevar(color, theme-color-dim);
}
.b--dim {
@include usevar(border-color, dim-color);
}
.b--theme-dim {
@include usevar(border-color, theme-color-dim);
}
.c--dimmer {
@include usevar(color, dimmer-color);
}
.c--theme-dimmer {
@include usevar(color, theme-color-dimmer);
}
.b--dimmer {
@include usevar(border-color, dimmer-color);
}
.b--theme-dimmer {
@include usevar(border-color, theme-color-dimmer);
}
.c--dimmest {
@include usevar(color, dimmest-color);
}
.c--theme-dimmest {
@include usevar(color, theme-color-dimmest);
}
.b--dimmest {
@include usevar(border-color, dimmest-color);
}
.b--theme-dimmest {
@include usevar(border-color, theme-color-dimmest);
}
.bg--dim {
@include usevar(background-color, dim-background);
}
.bg--content {
@include usevar(background-color, content-background);
}
.bg--card {
@include usevar(background-color, card-background);
}
.f8 {
font-size: 0.65rem;
}
.mw-site {
max-width: 80rem;
}
.mh-3 {
max-height: $height-3;
}
.mh-4 {
max-height: $height-4;
}
.mh-5 {
max-height: $height-5;
}
.mh-6 {
max-height: $height-6;
}
.mh-100 {
max-height: 100%;
}
.mh-50vh {
max-height: 50vh;
}
.mh-60vh {
max-height: 60vh;
}
.mh-70vh {
max-height: 70vh;
}
.mh-80vh {
max-height: 80vh;
}
.minw-100 {
min-width: 100%;
}
.minh-1 {
min-height: $height-1;
}
.minh-2 {
min-height: $height-2;
}
.minh-3 {
min-height: $height-3;
}
.minh-4 {
min-height: $height-4;
}
.minh-5 {
min-height: $height-5;
}
.minh-6 {
min-height: $height-6;
}
.h1-5 {
height: 1.5rem;
}
.fira {
font-family: "Fira Sans", sans-serif;
}
.bi-avoid { break-inside: avoid; }
.cc-auto { column-count: auto; }
.cc1 { column-count: 1; }
.cc2 { column-count: 2; }
.cc3 { column-count: 3; }
.cg0 { column-gap: $spacing-none; }
.cg1 { column-gap: $spacing-extra-small; }
.cg2 { column-gap: $spacing-small; }
.cg3 { column-gap: $spacing-medium; }
.cg4 { column-gap: $spacing-large; }
.cg5 { column-gap: $spacing-extra-large; }
.g0 { gap: $spacing-none; }
.g1 { gap: $spacing-extra-small; }
.g2 { gap: $spacing-small; }
.g3 { gap: $spacing-medium; }
.g4 { gap: $spacing-large; }
.g5 { gap: $spacing-extra-large; }
.grid {
display: grid;
}
.grid-1 {
grid-template-columns: 1fr;
}
.grid-2 {
grid-template-columns: 1fr 1fr;
}
.aspect-ratio--2x1 {
padding-bottom: 50%;
}
.hide-if-empty:empty {
display: none !important;
}
@media #{$breakpoint-not-small} {
.bi-avoid-ns { break-inside: avoid; }
.cc-auto-ns { column-count: auto; }
.cc1-ns { column-count: 1; }
.cc2-ns { column-count: 2; }
.cc3-ns { column-count: 3; }
.cg0-ns { column-gap: $spacing-none; }
.cg1-ns { column-gap: $spacing-extra-small; }
.cg2-ns { column-gap: $spacing-small; }
.cg3-ns { column-gap: $spacing-medium; }
.cg4-ns { column-gap: $spacing-large; }
.cg5-ns { column-gap: $spacing-extra-large; }
.grid-1-ns { grid-template-columns: 1fr; }
.grid-2-ns { grid-template-columns: 1fr 1fr; }
.bg--dim-ns {
@include usevar(background-color, dim-background);
}
.g0-ns { gap: $spacing-none; }
.g1-ns { gap: $spacing-extra-small; }
.g2-ns { gap: $spacing-small; }
.g3-ns { gap: $spacing-medium; }
.g4-ns { gap: $spacing-large; }
.g5-ns { gap: $spacing-extra-large; }
}
@media #{$breakpoint-medium} {
.bi-avoid-m { break-inside: avoid; }
.cc-auto-m { column-count: auto; }
.cc1-m { column-count: 1; }
.cc2-m { column-count: 2; }
.cc3-m { column-count: 3; }
.cg1-m { column-gap: $spacing-extra-small; }
.cg2-m { column-gap: $spacing-small; }
.cg3-m { column-gap: $spacing-medium; }
.cg4-m { column-gap: $spacing-large; }
.cg5-m { column-gap: $spacing-extra-large; }
.grid-1-m { grid-template-columns: 1fr; }
.grid-2-m { grid-template-columns: 1fr 1fr; }
.bg--dim-m {
@include usevar(background-color, dim-background);
}
.g0-m { gap: $spacing-none; }
.g1-m { gap: $spacing-extra-small; }
.g2-m { gap: $spacing-small; }
.g3-m { gap: $spacing-medium; }
.g4-m { gap: $spacing-large; }
.g5-m { gap: $spacing-extra-large; }
}
@media #{$breakpoint-large} {
.bi-avoid-l { break-inside: avoid; }
.cc-auto-l { column-count: auto; }
.cc1-l { column-count: 1; }
.cc2-l { column-count: 2; }
.cc3-l { column-count: 3; }
.cg1-l { column-gap: $spacing-extra-small; }
.cg2-l { column-gap: $spacing-small; }
.cg3-l { column-gap: $spacing-medium; }
.cg4-l { column-gap: $spacing-large; }
.cg5-l { column-gap: $spacing-extra-large; }
.grid-1-l { grid-template-columns: 1fr; }
.grid-2-l { grid-template-columns: 1fr 1fr; }
.bg--dim-l {
@include usevar(background-color, dim-background);
}
.g0-l { gap: $spacing-none; }
.g1-l { gap: $spacing-extra-small; }
.g2-l { gap: $spacing-small; }
.g3-l { gap: $spacing-medium; }
.g4-l { gap: $spacing-large; }
.g5-l { gap: $spacing-extra-large; }
}
.not-first:first-child {
display: none;
}
.not-first-of-type:first-of-type {
display: none;
}
.not-last:last-child {
display: none;
}
.not-last-of-type:last-of-type {
display: none;
}
.svgicon {
svg {
fill: currentColor;
stroke: currentColor;
width: 1em;
height: 1em;
}
&:not(.svgicon-nofix) svg {
transform: translate(0px,0.1em);
}
}
// Tachyons' `center` is unfortunately overloaded by a .center
// class we have in our own CSS.
.center-layout {
margin-right: auto;
margin-left: auto;
}
footer {
.list li:not(:last-child)::after {
@extend .c--dimmer;
@media #{$breakpoint-not-small} {
& {
content: ' / ';
}
}
}
}
.content {
@include usevar(background-color, content-background);
margin: auto;
p {
-moz-text-size-adjust:auto;
-webkit-text-size-adjust:auto;
text-size-adjust:auto;
margin: 0.6rem 0;
}
.description {
line-height: 1.42em;
text-align: left;
margin: auto;
p {
margin-bottom: $spacing-small;
text-align: left;
}
}
> .top-bar {
text-align: left;
}
}
.content-block {
// TODO: What the heck are these styles
/* Background color given by theme */
background-repeat:repeat-x;
border-radius:2px;
text-align:left;
width:100%;
/* box-shadow: 0px 4px 7px rgba(0,0,0,0.5); /* Not themed */
position:relative;
box-sizing:border-box;
&.top-bar {
background-image:none;
}
&.language-desc {
padding: 10px;
h3 {
margin-left: 10px;
}
}
.no-bg {
background-image:none;
background-color:transparent;
box-shadow:none;
}
}
.sidebar {
.content-block {
&.single {
padding:0px; /* for project list, TODO */
margin-top:20px;
}
&.top-bar {
/* box-shadow: 0px 2px 4px rgba(0,0,0,0.2); /* Not themed */
width:80%;
display:block;
margin:10px auto;
}
}
br.sidebar-filler {
line-height:20px;
}
.projectlist {
background-color:transparent;
}
}
.content-block .bottom-padding, .sidebar .bottom-padding {
margin-top: $spacing-medium;
}
.breadcrumb {
&:hover {
text-decoration:underline;
}
&.current {
text-overflow: clip ellipsis;
}
}
.breadcrumb-before:nth-of-type(n+2)::before {
content: '';
}
.optionbar {
@extend .b--dimmest;
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
text-align: center;
align-items: center;
border-style: dashed;
border-width: 0px;
border-bottom-width: 1px;
padding-bottom: $spacing-small;
@media #{$breakpoint-not-small} {
flex-direction: row;
text-align: left;
padding-bottom: 0;
}
&.bottom {
border-bottom-width: 0px;
border-top-width: 1px;
padding-bottom: 0;
padding-top: $spacing-small;
@media #{$breakpoint-not-small} {
padding-top: 0;
}
}
&.center {
text-align: center; // TODO: find this and kill it
}
.options {
display: flex;
flex-direction: column;
@media #{$breakpoint-not-small} {
flex-direction: row;
}
& {
#{$buttons} {
@include lite-button;
@extend .ph2;
@extend .pv1;
@extend .pv2-ns;
}
}
}
.group {
display: inline-block;
height: 100%;
margin: auto;
}
}
.tab {
@include usevar(background-color, tab-background);
@extend .pa2;
}
.tab-bar {
@include usevar(border-color, tab-border-color);
@extend .flex, .flex-row;
width: 100%;
.tab-button {
@include usevar(background-color, tab-button-background);
@include usevar(border-color, tab-border-color);
@extend .ph3, .pv2;
cursor: pointer; // TODO: Should this be a link?
&:hover {
@include usevar(background-color, tab-button-background-hover);
}
&.current {
@include usevar(background-color, tab-button-background-current);
font-weight: 500;
}
}
}
.pagination {
.page.current {
cursor: default;
font-weight: 600;
&:hover {
text-decoration: none;
}
}
.button {
@extend .pv0, .ph2;
}
}
.user-link {
position:relative;
}
// TODO(ben): It appears that this code is inactive because the JS that would
// create the popups is commented out.
.user-popup {
opacity:0;
max-height:0px;
width:340px;
text-align:center;
transition:max-height 0.2s, opacity 0.1s;
box-shadow:0px 2px 5px rgba(0,0,0,0.3);
overflow:hidden;
position:absolute;
bottom:0px;
&.expanded {
visibility:visible;
max-height:250px;
opacity:1;
}
table {
/* Background color given by theme */
width:100%;
position:relative;
border-radius:3px;
z-index:10;
}
td {
vertical-align:top;
padding:15px 9px;
}
#avatar {
width:100px;
height:100px;
background-size:contain;
background-position:center center;
background-repeat:no-repeat;
border-radius:3px;
margin:0px auto;
}
.username {
font-weight:bold;
}
.bottom {
/* Border color given by theme */
border-top: 1px solid transparent;
padding: 15px;
.bio {
vertical-align:top;
width:90%;
}
}
}
.site-search {
&[type=text].lite {
// wow CSS selector priority sucks
// First transition copied from input .lite
transition: border-bottom-color 60ms ease-in-out, width 300ms ease;
}
}
#search_button_homepage {
margin:0px;
height:100%;
height:calc(100% - 2px);
border-radius:0px;
display:inline-block;
display:none;
}
.background-even:nth-of-type(even) {
// this is the default, and should be overridden by dynamic colors.
@include usevar(background-color, background-even-background);
@include usevar(--fade-color, background-even-background);
}
.sr-only {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
word-wrap: normal !important;
transition: 0.2s all;
}
.sr-focusable {
@extend .sr-only;
&:focus {
padding: 15px 10px;
height: auto;
width: auto;
background: var(--content-background);
clip: initial;
clip-path: initial;
z-index: 99999;
}
}

View File

@ -0,0 +1,130 @@
// TODO: Old and wrong and bad
.toolbar {
@include usevar('background-color', 'editor-toolbar-background');
@include usevar('border-color', 'editor-toolbar-border-color');
border-radius:3px;
height: 2.2em;
overflow-y:scroll;
transition:all 0.3s;
border: 1px solid transparent;
.button, input[type=button] {
@include usevar('background-color', 'editor-toolbar-button-background');
@include usevar('border-color', 'editor-toolbar-button-border-color');
line-height: 2.2em;
box-shadow:none;
border:0px;
border-right:1px solid transparent;
border-radius:0px;
margin:0px;
padding:0px 10px;
height:100%;
vertical-align:bottom;
text-transform:none;
font-weight:300;
&:hover {
@include usevar('background-color', 'editor-toolbar-button-background-hover');
}
}
input[type="text"] {
height:2.2em;
border:0px solid transparent; /* Not themed */
}
}
#preview:empty::after {
content: 'A preview of your post will appear here.';
@include usevar(color, dimmer-color);
font-style: italic;
}
@media #{$breakpoint-not-small} {
#preview-container {
max-height: calc(100vh - 20rem);
overflow: auto;
}
}
.edit-form {
.edit-form-row {
@extend .flex;
@extend .flex-column;
@extend .flex-row-ns;
@extend .mv3;
> :first-child {
@extend .w-100;
@extend .w4-ns;
@extend .flex-grow-0;
@extend .flex-shrink-0;
@extend .tl;
@extend .tr-ns;
@extend .pr0;
@extend .pr2-ns;
@extend .pb1;
@extend .pb0-ns;
font-weight: 500;
}
> :nth-child(2) {
@extend .flex-grow-1;
@extend .overflow-hidden;
}
.pt-input-ns {
// NOTE(ben): This could maybe be more general someday?
@media #{$breakpoint-not-small} {
padding-top: $input-padding;
}
}
}
input[type=text] {
@extend .w-100;
@extend .mw5-ns;
}
input[type=text]:invalid {
@include usevar(border-color, form-error-color);
}
textarea {
@extend .w-100;
@extend .w6-ns;
@extend .mw-100;
@extend .h3;
}
&.project-edit {
.project_description {
width: 100%;
min-height: 400px;
height: 30vh;
}
input.project_blurb,
input.project_name,
{
min-width: 300px;
width: 50%;
}
.quota-bar {
// @include usevar(border-color, 'project-edit-quota-bar-border-color');
width: 500px;
border-width: 1px;
margin-bottom: 10px;
.quota-filled {
// @include usevar(background-color, 'project-edit-quota-bar-filled-background');
height: 100%;
}
}
}
}

View File

@ -1,26 +1,26 @@
.edu-course {
&.highlight {
border-left: 0.25rem solid black;
border-color: var(--link-color);
background-color: var(--theme-color-dimmest);
@include usevar(border-color, link-color);
@include usevar(background-color, theme-color-dimmest);
}
.edu-article.coming-soon {
color: var(--dimmer-color);
@extend .c--dimmer;
font-style: italic;
}
.edu-article::after {
--height: 10rem;
$height: 10rem;
content: '';
position: absolute;
width: 1rem;
height: var(--height);
height: $height;
border-width: 0 0 1px 1px;
border-style: solid;
border-color: var(--dimmest-color);
@include usevar(border-color, dimmest-color);
left: -1.5rem;
top: calc(1rem - var(--height));
top: 1rem - $height;
border-bottom-left-radius: 0.5rem;
}
}
@ -37,4 +37,4 @@
.note {
color: red;
}
}
}

View File

@ -0,0 +1,269 @@
.episode-list {
.description p {
line-height:1.42em;
}
h2, h3 {
font-size:1em;
font-weight:bold;
}
ul {
margin-bottom:30px;
list-style-type:none;
margin-top:-2px;
}
}
// UNUSED(outer_holder)
#outer_holder .back {
margin: 0px auto 20px auto;
.outer {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
align-items: flex-start;
-webkit-justify-content: center;
justify-content: center;
& > .sidebar {
max-width: 260px;
& > div {
margin: 6px;
background-color: #ddd;
width: 200px;
margin-bottom: 12px;
}
}
}
article {
-webkit-box-flex: 1 1 40%;
-moz-box-flex: 1 1 40%;
-webkit-flex: 1 1 40%;
-ms-flex: 1 1 40%;
flex: 1 1 40%;
}
}
// UNUSED
#player-wrapper {
display: inline-block;
-webkit-box-flex: 0 1 auto;
-moz-box-flex: 0 1 auto;
-webkit-flex: 0 1 auto;
-ms-flex: 0 1 auto;
flex: 0 1 auto;
box-shadow: 0px 0px 4px #000;
}
@media screen {
#player-wrapper #player {
width: 320px;
height: 210px;
}
}
@media screen and (min-width: 500px) {
#player-wrapper #player {
width: 480px;
height: 300px;
}
}
@media screen and (min-width: 670px) {
#player-wrapper #player {
width: 640px;
height: 390px;
}
}
@media screen and (min-width: 890px) {
#player-wrapper #player {
width: 854px;
height: 510px;
}
}
@media screen and (min-width: 1320px) {
#player-wrapper #player {
width: 1280px;
height: 750px;
}
}
.annotation {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
align-items: flex-start;
.navigation {
width: 450px;
display: inline-block;
}
.notes {
display: inline-block;
}
}
// UNUSED(timecodes)
nav.timecodes {
margin: 0px;
max-width: 450px;
min-height: 700px;
overflow-y: auto;
-webkit-box-flex: 0 0 auto;
-moz-box-flex: 0 0 auto;
-webkit-flex: 0 0 auto;
-ms-flex: 0 0 auto;
flex: 0 0 auto;
a {
color: #ccc;
}
ul[name=markers] {
margin: 0px;
padding: 0px;
list-style-type: none;
font-size: 15px;
& > li {
padding: 4px;
display: block;
border-bottom: 1px solid #333;
&:hover {
background-color: rgba(0,90,0,0.3);
}
a {
display: block;
text-decoration: none;
span.timecode {
color: #777777;
float: left;
font-size: 86%;
margin-right: .5em;
text-align: right;
width: 42px;
}
}
}
}
}
// UNUSED
#video-notes {
min-height: 700px;
margin-top: 0px;
pre {
margin-top:1.5em;
margin-left: 10px;
margin-right: 30px;
margin-bottom: 1.5em;
padding: 10px;
overflow: auto;
}
}
// UNUSED
#game_icon {
background-position: 0px 0px;
background-repeat: no-repeat;
background-image: url("/images/hero_home_200x114.png");
background-size: cover;
height: 113px;
margin: 0px 0px 4px 0px;
}
// UNUSED
.sidebar-heading {
margin: 0px 0px 16px;
font-size: 18px;
line-height: 24px;
font-weight: bold;
color: #00547c;
white-space: nowrap;
text-shadow: 1px 1px 2px rgba(0,0,0,0.25);
text-align: center;
}
// Episode Search
.queryContainer {
width: 1000px;
margin: 15px auto;
display: flex;
flex-direction: horizontal;
label {
flex-grow: 0;
flex-shrink: 0;
padding-right: 15px;
}
}
#query {
flex-grow: 1;
}
#results {
width: 800px;
margin: 0 auto;
}
.dayContainer:nth-child(2n) {
background-color: rgba(0,0,0,0.05);
}
.dayName {
width: 200px;
display: inline-block;
vertical-align: top;
line-height: 16px;
box-sizing: border-box;
padding: 5px;
}
.markerList {
display: inline-block;
width: 600px;
box-sizing: border-box;
vertical-align: top;
}
.marker {
cursor: pointer;
border-top: 1px solid rgba(255, 255, 255, 0.1);
display: block;
text-decoration: none;
&:first-child {
border: none;
}
&:hover {
background-color: rgba(0, 0, 0, 0.1);
}
b {
color: black;
background-color: rgb(255, 155, 0);
}
}
#resultsSummary {
text-align: center;
margin: 10px 0;
}

View File

@ -0,0 +1,360 @@
.display-options {
height:100%;
padding:0px 20px 0px 20px;
> * {
display:inline-block;
vertical-align:middle;
}
}
.forum h3 {
margin:0px 80px 0px 20px;
font-weight:bold;
}
.forum-narrow .forum-narrow-hide {
display: none;
}
.post-bg-alternate:nth-of-type(odd) {
@include usevar('background-color', 'forum-even-background');
}
.thread-list-item .latestpost {
width: 16.5rem;
}
.thread {
@include usevar('color', 'fg-font-color');
@extend .ma0;
.profile & {
padding-left:15px;
}
.title {
font-weight: bold;
transition: border-bottom-color 0.1s;
border-bottom-color: transparent;
&:hover {
border-bottom-color: initial;
}
}
.forum & .info th {
width: 50px;
}
}
.avatar-icon {
width: 40px;
height: 40px;
flex-shrink: 0;
object-fit: cover;
border-radius: 100%;
overflow: hidden;
object-fit: cover;
@include usevar(background-color, dimmest-color);
}
:root {
@include usevar(--fade-color, 'content-background');
}
.excerpt-fade {
background-image: linear-gradient(to top, var(--fade-color), rgba(0, 0, 0, 0));
pointer-events: none;
}
.read {
@include usevar('color', 'forum-thread-read-color');
td {
@include usevar('color', 'forum-thread-read-color');
}
a {
@include usevar('color', 'forum-thread-read-link-color');
}
.title {
font-weight: 500;
}
}
.goto {
font-size: 200%;
width: 30px;
a {
display: block;
padding: 0px 10px;
box-sizing: border-box;
position: relative;
line-height: 100%;
background-color: transparent;
}
}
.badge {
display: inline-block;
border-radius: 1000em;
padding: 0 0.8em;
font-size: 0.9em;
line-height: 1.8em;
font-weight: bold;
&.staff {
background-color: #17b2c6;
color: white;
&::before {
content: 'Staff';
}
}
}
.postid a {
margin-top: -4rem;
padding-top: 4rem;
outline: none;
}
.codeblocktable {
font-family: $monospace-fonts;
font-size:14px;
overflow:auto;
line-height:1.5em;
tbody {
width:100%;
}
.linenos {
@include usevar(color, 'code-line-number-color');
font-weight:500;
padding: 5px;
background: rgba(0, 0, 0, 0.15);
}
.code {
/* Background color given by theme */
padding-right:20px;
padding-left:10px;
max-width:80em;
}
}
.post {
.action.button {
padding:0px 10px;
margin-top:0px;
margin-right:4px;
background-color:transparent;
font-size:130%;
border-radius:0px;
border-width:0px;
border-bottom-width:2px;
transition:border-bottom-width 0.1s;
&:hover {
border-bottom-width:4px;
}
}
}
.blog {
.post {
.meta {
position:relative;
}
&.op .meta {
margin-bottom:10px;
}
.badges {
width: 40px;
font-size: 0.7em;
text-align: center;
}
}
.sidebar {
.post > .author {
padding:15px;
text-align:center;
}
.recent-posts {
text-align:center;
padding:15px;
h2 {
display:inline;
}
}
.archive ul {
list-style-type:none;
text-align:left;
}
}
.post-list {
.post {
&:nth-child(even) {
background-color: transparent;
}
}
}
}
.featured-post .meta .avatar-icon {
left:-60px;
bottom:-5px;
}
.content-block.blog {
p.title {
font-weight:bold;
a {
border-bottom-width:0px;
&:hover {
border-bottom-width:1px;
}
}
}
}
a.mark_as_read {
float: right;
}
.mark_as_read_toplevel_forum {
margin-left: auto;
margin-right: auto;
width: 100%;
text-align: center;
a {
text-decoration: underline;
}
}
div.mark_as_read_toplevel_blog {
margin-left: auto;
margin-right: auto;
width: 100%;
text-align: center;
a {
text-decoration: none;
}
}
.mark_as_read_toplevel_blog
div.mark_as_read_site {
margin-left: auto;
margin-right: auto;
width: 100%;
text-align: center;
a {
text-decoration: none;
}
}
.mark_as_read_site
li.post-entry {
margin-left:0px;
margin-bottom: 3px;
}
.diff {
width:100%;
max-height:100vh;
overflow-y:scroll;
margin-top:20px;
> .post-source {
@include usevar(color, 'fg-font-color');
@include usevar(background-color, 'forum-diff-source-background');
@include usevar(border-color, 'forum-diff-source-border-color');
border-width: 1px;
border-radius: 2px;
font-family: $monospace-fonts;
width:49%;
box-sizing:border-box;
padding: 4px;
display:inline-block;
vertical-align: top;
}
.diff-replace,
.diff-delete,
.diff-insert,
{
border-width: 1px;
border-radius: 2px;
padding: 1px;
}
.diff-replace {
@include usevar(background-color, 'forum-diff-replace-background');
@include usevar(border-color, 'forum-diff-replace-border-color');
}
.diff-delete {
@include usevar(background-color, 'forum-diff-delete-background');
@include usevar(border-color, 'forum-diff-delete-border-color');
}
.diff-insert {
@include usevar(background-color, 'forum-diff-insert-background');
@include usevar(border-color, 'forum-diff-insert-border-color');
}
}
.index {
list-style-type:none;
-webkit-column-count: 3;
-moz-column-count: 3;
column-count: 3;
margin-top:20px;
.index-category {
margin-bottom:20px;
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid-column;
}
}
.bbtable {
width: 100%;
border: 1px solid;
td, th {
border: 1px solid;
padding: 4px;
}
th {
background: rgba(0, 0, 0, 0.15);
font-weight: bold;
border-bottom-width: 2px;
}
tbody tr:nth-child(even) {
background: rgba(0, 0, 0, 0.05);
}
}

View File

@ -0,0 +1,150 @@
header {
$logo-height: px2rem(60px);
.hmn-logo {
height: $logo-height;
width: 100%;
text-transform: uppercase;
font-family: 'MohaveHMN', sans-serif;
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
color: white !important;
&.big {
@media #{$breakpoint-not-small} {
width: px2rem(180px);
}
}
&.small {
@media #{$breakpoint-not-small} {
width: $logo-height;
padding: 0.8rem;
text-align: justify;
text-align-last: justify;
text-justify: inter-character;
flex-direction: column;
font-size: 1rem;
line-height: 1em;
align-items: stretch;
}
}
}
.items {
position: relative; // will be used on mobile, when .root-item is not relative
}
.root-item {
@media #{$breakpoint-not-small} {
& {
position: relative; // makes submenus align to this item instead of the screen
height: $logo-height;
}
}
&:not(:hover):not(.clicked) > .submenu {
display: none;
}
&.clicked .svgicon {
transform: rotate(180deg);
}
> a {
@extend .pa2, .ph3-l;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-weight: bold;
}
.svgicon {
font-size: 0.7em;
}
}
&:not(.clicked) .root-item:not(:hover),
&.clicked .root-item:not(.clicked) {
> .submenu {
display: none;
}
}
.submenu {
@extend .bw1;
@include usevar(background-color, content-background);
display: flex;
flex-direction: column;
position: absolute;
left: 0;
right: 0;
z-index: 100;
min-width: 10rem;
border-top-style: solid;
border-bottom-style: solid;
@media #{$breakpoint-not-small} {
& {
border-top-style: none;
border-left-style: solid;
border-right-style: solid;
left: initial;
right: initial;
}
}
> a {
@extend .pa2, .ph3-l;
display: block;
white-space: nowrap;
z-index: 1;
font-weight: bold;
text-align: center;
@media #{$breakpoint-not-small} {
& {
text-align: left;
}
}
}
}
.menu-bar {
width: 100%;
z-index: 10;
}
#login-link{
cursor: pointer;
}
#login-popup {
@include usevar(background-color, login-popup-background);
@include usevar(color, fg-font-color);
visibility: hidden;
position: absolute;
z-index: 12;
overflow: hidden;
right: 0;
top: 100%;
width: 100%;
&.open {
visibility: visible;
}
@media #{$breakpoint-not-small} {
top: 2.2rem;
width: 17rem;
}
}
}

View File

@ -9,65 +9,65 @@ span.icon {
span.icon-settings::before {
font-family: "icons";
content: "0";
content:"0";
}
span.icon-link::before {
font-family: "icons";
content: "1";
content:"1";
}
span.icon-logout::before {
font-family: "icons";
content: "2";
content:"2";
}
span.icon-twitter::before {
font-family: "icons";
content: "#";
content:"#";
}
span.icon-twitch::before {
font-family: "icons";
content: "$";
content:"$";
}
span.icon-github::before {
font-family: "icons";
content: "%";
content:"%";
}
span.icon-patreon::before {
font-family: "icons";
content: "&";
content:"&";
}
span.icon-youtube::before {
font-family: "icons";
content: "'";
content:"'";
}
span.icon-soundcloud::before {
font-family: "icons";
content: "*";
content:"*";
}
span.icon-web::before {
font-family: "icons";
content: "3";
content:"3";
}
span.icon-itchio::before {
font-family: "icons";
content: "+";
content:"+";
}
span.icon-hitbox::before {
font-family: "icons";
content: ",";
content:",";
}
span.icon-rss::before {
font-family: "icons";
content: "4";
content:"4";
}

206
src/rawdata/scss/_irc.scss Normal file
View File

@ -0,0 +1,206 @@
.chat {
@include usevar(border-color, 'irc-border-color');
margin: auto;
width: 100%;
border: 1px solid transparent;
position: relative;
overflow: hidden;
padding-right: 8px;
box-sizing: border-box;
#tabs {
overflow-y: scroll;
box-sizing: border-box;
.hidden {
display: none;
}
.current {
@include usevar(box-shadow, 'irc-tab-current-shadow');
}
.button {
padding-left: 10px;
padding-right: 3px;
transition: width 0.2s;
text-transform: none;
vertical-align: top;
.close-btn {
visibility: hidden;
opacity: 0;
transition: opacity 0.2s;
display: inline-block;
font-size: 9pt;
vertical-align: middle;
height: 13px;
line-height: 11px;
width: 13px;
margin-left: 5px;
padding-left: 2px;
padding-right: 3px;
border-radius: 3px;
border: 1px solid transparent; /* Not themed */
position: relative;
top: -2px;
}
&:hover .close-btn {
@include usevar(color, 'irc-tab-close-button-color');
@include usevar(background-color, 'irc-tab-close-button-background');
visibility: visible;
opacity: 1;
}
}
.new-tab {
padding: 0px;
padding-left: 10px;
}
}
#history {
width: 100%;
min-height: 300px;
height: 50vh;
overflow-y: auto;
padding-top: 3px;
box-sizing: border-box;
.channel {
width: 100%;
display: none;
&.current {
display: table;
}
&.hidden {
display: none;
}
}
.entry {
display: table-row;
width: 100%;
font-size: 11pt;
&.minor {
color: #777;
font-size: 10pt;
}
&.alert {
color: #dd683b;
}
&.error {
color: #880000;
font-style: italic;
}
&.action {
font-style: italic;
.msg .action-name {
font-weight: bold;
}
}
.nick, .msg {
display: table-cell;
padding: 0px 4px;
}
.nick {
@include usevar(border-color, 'irc-nick-border-color');
font-weight: bold;
text-align: right;
border-right-width: 1px;
border-right-style: solid;
}
.msg {
width: 100%;
.highlight {
background-color: #dd683b;
color: #fff;
padding: 0px 5px;
}
.emote {
height: 1.6em;
vertical-align: middle;
}
}
}
}
#chatbox {
width: 100%;
}
#users {
@include usevar(color, 'irc-users-color');
@include usevar(background-color, 'irc-users-background');
@include usevar(border-color, 'irc-users-border-color');
position: absolute;
border-left-width: 1px;
border-left-style: solid;
padding: 5px;
right: 0px;
top: 0px;
color: #333;
height: 100%;
max-width: 20%;
width: 20%;
transition: right 0.2s;
max-height: 100%;
overflow-y: auto;
box-sizing: border-box;
&.collapsed {
right:-20%;
}
.popout {
@include usevar(background-color, 'irc-users-popout-background');
@include usevar(border-left-color, 'irc-users-popout-border-color-left');
@include usevar(border-right-color, 'irc-users-popout-border-color-right');
width: 8px;
font-size: 7pt;
height: 100%;
border-radius: 2px;
border: 0px solid transparent; /* Not themed */
border-left-width: 1px;
border-left-style: solid;
border-right-width: 1px;
border-right-style: solid;
position: absolute;
top: 0px;
left: -8px;
box-shadow: 0px 0px 0px transparent; /* Not themed */
text-align: center;
margin: 0px;
cursor: pointer;
}
.op {
font-weight: bold;
}
.user {
cursor: pointer;
}
}
}

View File

@ -0,0 +1,24 @@
.landing-layout {
display: grid;
gap: $spacing-medium;
> * {
overflow: hidden;
}
}
@media #{$breakpoint-large} {
.landing-layout {
grid-template-columns: 1fr;
grid-auto-columns: 1fr;
> * {
grid-column: 1 / 2;
&.landing-right {
grid-column: 2 / 3;
grid-row: 1 / 20; // increase this number if somehow you ever add that much garbage to the home page :)
}
}
}
}

View File

@ -0,0 +1,21 @@
.star-btn {
border-bottom-width: 2px;
@include usevar('background-color', 'library-star-btn-background');
@include usevar('border-color', 'library-star-btn-border-color');
a {
@include usevar('border-color', 'library-star-btn-a-border-color');
&:hover {
@include usevar('background-color', 'library-star-btn-a-hover-background');
}
}
}
.library-dropcap {
width: 1em;
height: 1.1em;
padding-top: 0.19em;
text-align: center;
font-size: 2rem;
}

View File

@ -0,0 +1,45 @@
.notice {
@include usevar(color, notice-text-color);
@extend .ph3, .pv2;
@extend .br2-ns;
a {
@include usevar(color, notice-text-color);
@include usevar(border-bottom-color, notice-text-color);
}
}
.notice-unapproved {
@include usevar(background-color, notice-unapproved-color);
}
.notice-hidden {
@include usevar(background-color, notice-hidden-color);
}
.notice-hiatus {
@include usevar(background-color, notice-hiatus-color);
}
.notice-dead {
@include usevar(background-color, notice-dead-color);
}
.notice-lts {
@include usevar(background-color, notice-lts-color);
}
.notice-lts-reqd {
@include usevar(background-color, notice-lts-reqd-color);
}
.notice-success {
@include usevar(background-color, notice-success-color);
}
.notice-warn {
@include usevar(background-color, notice-warn-color);
}
.notice-failure {
@include usevar(background-color, notice-failure-color);
}

View File

@ -0,0 +1,52 @@
.profile {
.content {
.description {
margin:0px auto;
max-width:40em;
}
h2 {
margin-bottom:10px;
}
}
ul.recent-posts, ul.recent-comments {
list-style-type:none;
}
ul .entry {
margin-left:20px;
}
.entry {
.context {
font-weight:bold;
border-bottom:0px;
}
}
.content-block.projects {
background-image:none;
overflow-y:visible;
}
}
.profile .content-block.avatar,
.project .content-block.logo {
background-color:transparent;
background-image:none;
box-shadow:none;
min-height:200px;
img {
// TODO(ben): Check if this was actually set to anything before
// box-shadow:
}
}
.project .content-block.screenshots,
.profile .content-block.projects
{
padding:0px;
min-height:0em;
}

View File

@ -0,0 +1,15 @@
.upload_bar.uploading .instructions, .upload_bar:not(.uploading) .progress {
display: none;
}
.upload_bar .progress_bar {
border: 2px solid;
@include usevar('border-color', 'link-color');
padding: 2px;
}
.upload_bar .progress_bar > div {
@include usevar('background-color', 'link-color');
height: 100%;
}

View File

@ -0,0 +1,272 @@
.project-carousel {
.carousel-item {
position: absolute;
top: 0;
left: 0;
br {
line-height: 0.6em;
}
}
.carousel-item-small {
position: absolute;
top: 0;
left: 0;
&:not(.active) {
display: none;
}
}
.carousel-description {
max-height: 14rem;
overflow: hidden;
}
.carousel-fade {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 30px;
@include usevar(background, "linear-gradient(" dim-background-transparent "," dim-background ")")
}
}
.project {
.pair {
display: flex;
align-items: flex-start;
.key {
font-weight: bold;
flex-shrink: 0;
}
.value {
text-align: right;
flex-grow: 1;
}
}
.settings-icon {
z-index: 10;
position: absolute;
width: 25px;
height: 25px;
right: 5px;
top: 5px;
margin: 0px;
text-align: center;
padding: 3px;
border-radius: 5px;
line-height: 100%;
box-sizing: border-box;
}
.screenshots .slideshow {
background-color:black;
}
.tags {
text-align:center;
h2 {
margin-bottom:5px;
}
li {
display:inline-block;
}
}
.button-bar {
border: none;
}
.forum {
h3 {
margin: 0;
}
.thread-entry-right {
display: none;
}
}
}
.project-card {
@include usevar(color, 'fg-font-color');
@include usevar(background-color, 'card-background');
@include usevar(border-color, 'project-card-border-color');
transition: box-shadow 0.2s, background-color 0.2s;
.slideshow & {
margin-top:8px;
}
&:hover {
@include usevar(background-color, 'card-background-hover');
& > .title {
text-decoration:underline;
}
}
.image-container {
$image-size: 8rem;
width: $image-size;
min-height: $image-size;
.image {
position: absolute;
width: $image-size;
top: 0;
bottom: 0;
}
}
.details {
/* Background color given by theme */
transition: background-color 0.2s;
}
.badges:empty {
display: none;
}
.badge {
color: white;
border-radius: 5px;
}
}
.projectlist {
.sidebar & {
padding:0px;
width:340px;
// background-image:none;
// background-color:transparent;
// box-shadow:none;
.project-card.more {
height:40px;
width:326px;
padding-top:5px;
}
}
}
.screenshots .slide {
max-width:100%;
}
.slideshow {
/* Background color and color given by theme */
position:relative;
background-image:none;
overflow:hidden;
.optionbar {
height:30px;
text-align:center;
}
.buttons {
display:inline-block;
height:100%;
}
.button {
line-height:100%;
padding:5px 8px;
font-weight:400;
&.current {
font-weight:600;
}
}
#slide-deck {
width:100%;
position:relative;
transition:left 0.3s;
left:0px;
text-align:center;
display:flex;
flex-direction:horizontal;
justify-content: space-around;
align-items: center;
}
.slide {
flex: 1 1 100%;
position:relative;
img {
margin:auto;
max-height:60vh;
max-width:100%;
}
}
&.cards {
#slide-deck {
justify-content: flex-start;
}
.slide {
flex: 0 1 auto;
}
}
}
#project_owner_suggestions {
max-height:300px;
overflow:auto;
position:absolute;
border:1px solid transparent;
#user_template {
display:none;
}
}
.user_suggestions {
@include usevar(background-color, 'project-user-suggestions-background');
padding-top: 5px;
padding-bottom: 5px;
&#project_owner_suggestions .user {
@include usevar(border-color, 'project-user-suggestions-border-color');
border-bottom-width: 1px;
width: 200px;
box-sizing:border-box;
cursor:pointer;
}
.user {
position:relative;
margin-left:50px;
display: inline-block;
margin-right:10px;
.avatar-icon {
left:-50px;
bottom:0px;
border-radius:50%;
border:2px solid transparent;
}
}
}
#project_owners {
min-width:50%;
}

View File

@ -0,0 +1,7 @@
.showcase-item {
.gradient {
width: 100%;
height: 114px;
background-image: linear-gradient(rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0));
}
}

View File

@ -0,0 +1,51 @@
.streams-container {
#empty-message {
display: none;
}
.streams {
&:empty {
display: none;
+ #empty-message {
display: block;
}
}
}
.stream {
.live {
position: absolute;
left: $spacing-small;
top: $spacing-small;
color: white;
background-color: #e91916;
font-weight: bold;
}
.viewers {
position: absolute;
left: $spacing-small;
bottom: $spacing-small;
color: white;
background-color: rgba(0, 0, 0, 0.7);
}
.avatar {
padding: 1.5rem;
}
}
.twitch-embed-container {
padding-bottom: 0%;
transition: padding-bottom 400ms ease-in-out;
&.open {
padding-bottom: 150%;
@media screen and (min-width: 713px) { // magic numbers, Twitch has weird breakpoints
padding-bottom: 40.6%;
}
}
}
}

View File

@ -0,0 +1,53 @@
.timeline-item {
@include usevar(background-color, card-background);
@include usevar(--fade-color, card-background);
@include usevar(color, main-color);
.avatar-icon {
border: 2px solid;
@include usevar(border-color, theme-color);
&.big {
width: 3rem;
height: 3rem;
@media #{$breakpoint-not-small} {
width: px2rem(62px);
height: px2rem(62px);
}
}
}
.timeline-content-box {
&.timeline-item-bg {
@include usevar(background-color, timeline-content-background);
}
> * {
display: block;
max-width: 100%;
max-height: 80vh;
}
&.embed {
@extend .aspect-ratio, .aspect-ratio--16x9;
> iframe {
@extend .aspect-ratio--object;
}
}
}
}
.timeline-modal {
.container {
max-height: 100vh;
max-width: 100%;
@media #{$breakpoint-not-small} {
width: auto;
max-width: calc(100% - 2rem);
max-height: calc(100vh - 2rem);
}
}
}

View File

@ -1,176 +0,0 @@
.post-content {
line-height: 1.4;
* {
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
}
h1 {
font-size: 2rem;
}
h2 {
font-size: 1.5rem;
}
h3 {
font-size: 1.25rem;
}
h4 {
font-size: 1rem;
}
h5 {
font-size: 0.8rem;
}
h1,
h2,
h3,
h4,
h5 {
line-height: 1.2;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
li:not(:last-child) {
margin-bottom: 0.6em;
}
li p {
margin-top: 0.6em;
margin-bottom: 0.6em;
}
img {
max-width: 100%;
}
hr {
margin-left: var(--spacing-medium);
margin-right: var(--spacing-medium);
}
div.code {
border-color: var(--dimmer-color);
max-width: 100%;
max-height: 20em;
max-height: 80vh;
overflow: auto;
-moz-tab-size: 4;
tab-size: 4;
border-width: 1px;
border-style: solid;
}
div+br,
blockquote+br,
ul+br,
ol+br {
display: none;
}
table {
margin-top: var(--spacing-small);
margin-bottom: var(--spacing-small);
}
th,
td {
padding: var(--spacing-extra-small) var(--spacing-small);
border: 1px solid var(--dimmest-color);
}
code {
background-color: var(--dim-background);
padding: .2em 0;
white-space: nowrap;
&::before,
&::after {
content: "\00a0";
letter-spacing: -0.2em;
display: inline-block;
}
}
pre>code,
pre.hmn-code {
background-color: var(--dim-background);
padding: 0.7em;
overflow-x: auto;
}
blockquote {
border-color: var(--dimmest-color);
margin-left: var(--spacing-small);
padding-left: var(--spacing-small);
margin-right: 0;
border-left-style: solid;
border-left-width: 1px;
.quotewho {
line-height: 2em;
&::after {
content: " said:";
}
}
}
figure {
margin: var(--spacing-small) 0;
padding: var(--spacing-small) var(--spacing-small) 0;
background-color: var(--dim-background);
border-radius: var(--border-radius-3);
display: flex;
flex-direction: column;
@media screen and (min-width: 35em) {
margin-top: var(--spacing-medium);
margin-bottom: var(--spacing-medium);
padding: var(--spacing-medium) var(--spacing-medium) 0;
}
figcaption {
margin: var(--spacing-extra-small) 0;
@media screen and (min-width: 35em) {
margin: var(--spacing-small) 0;
}
}
}
table {
border-collapse: collapse;
table-layout: fixed;
}
td {
vertical-align: baseline;
}
.spoiler {
border-color: var(--spoiler-border);
border-width: 1px;
border-style: dashed;
color: transparent;
&::selection {
color: white;
background-color: black;
}
}
}

View File

@ -1,740 +0,0 @@
/* Bare minimum styles */
* {
/* It's aggressive, but we like it aggressive */
box-sizing: border-box;
border-color: var(--border-color);
}
br {
/* why, IE... */
border-style: none;
}
body {
background-color: var(--background-color);
color: var(--color);
font-family: "Inter", sans-serif;
min-height: 100vh;
box-sizing: border-box;
}
a,
.link {
color: var(--link-color);
border-bottom: none;
text-decoration: none;
&.external::after {
font-family: "icons";
content: " 1";
vertical-align: middle;
}
}
.link--normal {
--link-color: var(--color);
}
b,
strong {
font-weight: 600;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
margin: 0;
line-height: 1;
font-weight: 600;
}
code,
pre,
.mono {
font-family: "Fira Mono", monospace;
}
/* Utility */
.bg--main {
background-color: var(--main-background-color);
}
.m--center {
margin-left: auto;
margin-right: auto;
}
.flex-shrink-0 {
flex-shrink: 0;
}
.flex-grow-1 {
flex-grow: 1;
}
.flex-fair {
flex-basis: 1px;
flex-grow: 1;
flex-shrink: 1;
}
@media screen and (min-width: 35em) {
.flex-fair-ns {
flex-basis: 1px;
flex-grow: 1;
flex-shrink: 1;
}
}
@media screen and (min-width: 60em) {
.flex-fair-l {
flex-basis: 1px;
flex-grow: 1;
flex-shrink: 1;
}
}
.b--dim {
border-color: var(--dim-color);
}
.b--dimmer {
border-color: var(--dimmer-color);
}
.b--dimmest {
border-color: var(--dimmest-color);
}
.b--theme {
border-color: var(--theme-color);
}
.b--theme-dim {
border-color: var(--theme-color-dim);
}
.b--theme-dimmer {
border-color: var(--theme-color-dimmer);
}
.b--theme-dimmest {
border-color: var(--theme-color-dimmest);
}
.b--theme-dark {
border-color: var(--theme-color-dark);
}
.b--theme-light {
border-color: var(--theme-color-light);
}
.bg1 {
background-color: var(--bg-1);
}
.bg2 {
background-color: var(--bg-2);
}
.bg3 {
background-color: var(--bg-3);
}
.bg4 {
background-color: var(--bg-4);
}
.bg5 {
background-color: var(--bg-5);
}
.bg-theme {
background-color: var(--theme-color);
}
.bg-theme-dim {
background-color: var(--theme-color-dim);
}
.bg-theme-dimmer {
background-color: var(--theme-color-dimmer);
}
.bg-theme-dimmest {
background-color: var(--theme-color-dimmest);
}
.bg-theme-dark {
background-color: var(--theme-color-dark);
}
.bg-theme-light {
background-color: var(--theme-color-light);
}
.c--normal {
color: var(--color);
}
.c--inherit {
color: inherit;
&:hover,
&:active {
color: inherit;
}
}
.c--dim {
color: var(--dim-color);
}
.c--theme-dim {
color: var(--theme-color-dim);
}
.c--dimmer {
color: var(--dimmer-color);
}
.c--theme-dimmer {
color: var(--theme-color-dimmer);
}
.c--dimmest {
color: var(--dimmest-color);
}
.c--theme-dimmest {
color: var(--theme-color-dimmest);
}
.f8 {
font-size: 0.65rem;
}
.mw-site {
max-width: var(--site-width);
}
.mw-site-narrow {
max-width: var(--site-width-narrow);
}
.mh-3 {
max-height: var(--height-3);
}
.mh-4 {
max-height: var(--height-4);
}
.mh-5 {
max-height: var(--height-5);
}
.mh-6 {
max-height: var(--height-6);
}
.mh-100 {
max-height: 100%;
}
.mh-50vh {
max-height: 50vh;
}
.mh-60vh {
max-height: 60vh;
}
.mh-70vh {
max-height: 70vh;
}
.mh-80vh {
max-height: 80vh;
}
.minw-100 {
min-width: 100%;
}
.minh-1 {
min-height: var(--height-1);
}
.minh-2 {
min-height: var(--height-2);
}
.minh-3 {
min-height: var(--height-3);
}
.minh-4 {
min-height: var(--height-4);
}
.minh-5 {
min-height: var(--height-5);
}
.minh-6 {
min-height: var(--height-6);
}
.h1-5 {
height: 1.5rem;
}
.fira {
font-family: "Fira Sans", sans-serif;
}
.bi-avoid {
break-inside: avoid;
}
.cc-auto {
column-count: auto;
}
.cc1 {
column-count: 1;
}
.cc2 {
column-count: 2;
}
.cc3 {
column-count: 3;
}
.cg0 {
column-gap: var(--spacing-none);
}
.cg1 {
column-gap: var(--spacing-extra-small);
}
.cg2 {
column-gap: var(--spacing-small);
}
.cg3 {
column-gap: var(--spacing-medium);
}
.cg4 {
column-gap: var(--spacing-large);
}
.cg5 {
column-gap: var(--spacing-extra-large);
}
.g0 {
gap: var(--spacing-none);
}
.g1 {
gap: var(--spacing-extra-small);
}
.g2 {
gap: var(--spacing-small);
}
.g3 {
gap: var(--spacing-medium);
}
.g4 {
gap: var(--spacing-large);
}
.g5 {
gap: var(--spacing-extra-large);
}
.grid {
display: grid;
}
.grid-1 {
grid-template-columns: 1fr;
}
.grid-2 {
grid-template-columns: 1fr 1fr;
}
.aspect-ratio--2x1 {
padding-bottom: 50%;
}
.hide-if-empty:empty {
display: none !important;
}
.fill-current {
fill: currentColor;
}
.rot-180 {
transform: rotate(180deg);
}
:not([hidden])+.show-when-sibling-hidden {
display: none;
}
@media screen and (min-width: 35em) {
.bi-avoid-ns {
break-inside: avoid;
}
.cc-auto-ns {
column-count: auto;
}
.cc1-ns {
column-count: 1;
}
.cc2-ns {
column-count: 2;
}
.cc3-ns {
column-count: 3;
}
.cg0-ns {
column-gap: var(--spacing-none);
}
.cg1-ns {
column-gap: var(--spacing-extra-small);
}
.cg2-ns {
column-gap: var(--spacing-small);
}
.cg3-ns {
column-gap: var(--spacing-medium);
}
.cg4-ns {
column-gap: var(--spacing-large);
}
.cg5-ns {
column-gap: var(--spacing-extra-large);
}
.grid-1-ns {
grid-template-columns: 1fr;
}
.grid-2-ns {
grid-template-columns: 1fr 1fr;
}
.bg1-ns {
background-color: var(--bg-1);
}
.bg2-ns {
background-color: var(--bg-2);
}
.bg3-ns {
background-color: var(--bg-3);
}
.bg4-ns {
background-color: var(--bg-4);
}
.bg5-ns {
background-color: var(--bg-5);
}
}
@media screen and (min-width: 35em) and (max-width: 60em) {
.bi-avoid-m {
break-inside: avoid;
}
.cc-auto-m {
column-count: auto;
}
.cc1-m {
column-count: 1;
}
.cc2-m {
column-count: 2;
}
.cc3-m {
column-count: 3;
}
.cg1-m {
column-gap: var(--spacing-extra-small);
}
.cg2-m {
column-gap: var(--spacing-small);
}
.cg3-m {
column-gap: var(--spacing-medium);
}
.cg4-m {
column-gap: var(--spacing-large);
}
.cg5-m {
column-gap: var(--spacing-extra-large);
}
.grid-1-m {
grid-template-columns: 1fr;
}
.grid-2-m {
grid-template-columns: 1fr 1fr;
}
.bg1-m {
background-color: var(--bg-1);
}
.bg2-m {
background-color: var(--bg-2);
}
.bg3-m {
background-color: var(--bg-3);
}
.bg4-m {
background-color: var(--bg-4);
}
.bg5-m {
background-color: var(--bg-5);
}
}
@media screen and (min-width: 60em) {
.bi-avoid-l {
break-inside: avoid;
}
.cc-auto-l {
column-count: auto;
}
.cc1-l {
column-count: 1;
}
.cc2-l {
column-count: 2;
}
.cc3-l {
column-count: 3;
}
.cg1-l {
column-gap: var(--spacing-extra-small);
}
.cg2-l {
column-gap: var(--spacing-small);
}
.cg3-l {
column-gap: var(--spacing-medium);
}
.cg4-l {
column-gap: var(--spacing-large);
}
.cg5-l {
column-gap: var(--spacing-extra-large);
}
.grid-1-l {
grid-template-columns: 1fr;
}
.grid-2-l {
grid-template-columns: 1fr 1fr;
}
.bg1-l {
background-color: var(--bg-1);
}
.bg2-l {
background-color: var(--bg-2);
}
.bg3-l {
background-color: var(--bg-3);
}
.bg4-l {
background-color: var(--bg-4);
}
.bg5-l {
background-color: var(--bg-5);
}
}
.svgicon {
svg {
fill: currentColor;
stroke: currentColor;
width: 1em;
height: 1em;
overflow: visible;
}
&:not(.svgicon-nofix) svg {
transform: translate(0px, 0.1em);
}
}
.sr {
border: 0;
clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
width: 1px;
word-wrap: normal !important;
transition: 0.2s all;
}
.sr-focusable:focus {
padding: 15px 10px;
height: auto;
width: auto;
background: var(--content-background);
clip: initial;
clip-path: initial;
z-index: 99999;
}
.breadcrumb {
&:hover {
text-decoration: underline;
}
&.current {
text-overflow: clip ellipsis;
}
}
/* Old stuff that should probably be moved? */
.optionbar {
width: 100%;
padding-bottom: var(--spacing-small);
display: flex;
flex-direction: column;
justify-content: space-between;
text-align: center;
align-items: center;
border-style: dashed;
border-width: 0 0 1px;
border-color: var(--dimmest-color);
@media screen and (min-width: 35em) {
flex-direction: row;
text-align: left;
padding-bottom: 0;
}
&.bottom {
border-bottom-width: 0;
border-top-width: 1px;
padding-bottom: 0;
padding-top: var(--spacing-small);
@media screen and (min-width: 35em) {
padding-top: 0;
}
}
&.center {
/* TODO: find this and kill it */
text-align: center;
}
.options {
display: flex;
flex-direction: column;
@media screen and (min-width: 35em) {
flex-direction: row;
}
.option {
padding: var(--spacing-extra-small) var(--spacing-small);
display: inline-flex;
align-items: center;
justify-content: center;
@media screen and (min-width: 35em) {
padding-top: var(--spacing-small);
padding-bottom: var(--spacing-small);
}
}
}
.group {
display: inline-block;
height: 100%;
margin: auto;
}
}
.tab {
background-color: var(--tab-background);
padding: var(--spacing-small);
}
.background-even:nth-of-type(even) {
background-color: var(--background-even-background);
--fade-color: var(--background-even-background);
}

View File

@ -1,75 +0,0 @@
#preview:empty::after {
content: 'A preview of your post will appear here.';
color: var(--dimmer-color);
font-style: italic;
}
@media screen and (min-width: 35em) {
#preview-container {
max-height: calc(100vh - 20rem);
overflow: auto;
}
}
.edit-form {
.edit-form-row {
display: flex;
flex-direction: column;
margin-top: var(--spacing-medium);
margin-bottom: var(--spacing-medium);
@media screen and (min-width: 35em) {
flex-direction: row;
}
> :first-child {
width: 100%;
font-weight: 500;
flex-grow: 0;
flex-shrink: 0;
text-align: left;
padding-right: 0;
padding-bottom: var(--spacing-extra-small);
@media screen and (min-width: 35em) {
width: var(--width-4);
text-align: right;
padding-right: var(--spacing-small);
padding-bottom: 0;
}
}
> :nth-child(2) {
flex-grow: 1;
overflow: hidden;
}
.pt-input-ns {
@media screen and (min-width: 35em) {
padding-top: var(--spacing-extra-small);
}
}
}
input[type=text] {
width: 100%;
@media screen and (min-width: 35em) {
max-width: var(--width-5);
}
}
input[type=text]:invalid {
border-color: var(--red);
}
textarea {
width: 100%;
max-width: 100%;
height: var(--height-3);
@media screen and (min-width: 35em) {
width: var(--width-6);
}
}
}

View File

@ -1,95 +0,0 @@
.hmn-form {
input,
textarea,
select {
color: var(--color);
accent-color: var(--button-color-primary);
background-color: var(--bg-3);
padding: 0.75rem;
outline: none;
&:not(.no-border) {
border: 1px solid var(--border-color);
}
&:focus {
border-color: var(--border-color-focused);
~.also-focus {
border-color: var(--border-color-focused);
}
}
.error &,
&.error,
&:invalid {
border-color: var(--border-color-error);
}
&:disabled {
background-color: var(--bg-5);
color: var(--border-color);
}
}
textarea {
resize: vertical;
}
button,
input[type=submit] {
color: var(--color);
background-color: var(--bg-3);
cursor: pointer;
font-weight: 500;
line-height: 1.5rem;
&.btn-primary {
background-color: var(--button-color-primary);
}
&:not(.no-border) {
border: none;
}
&:not(.no-padding) {
padding: 0.5rem 1.5rem;
}
}
label {
font-weight: 600;
}
.input-group {
display: flex;
flex-direction: column;
gap: var(--spacing-extra-small);
line-height: 1.4;
}
.error .error-msg {
color: var(--color-error);
}
fieldset {
margin: 0;
padding: 0;
border: 1px solid var(--border-color);
background-color: var(--bg-2);
display: flex;
flex-direction: column;
}
legend {
background-color: var(--bg-1);
font-weight: bold;
float: left;
padding: var(--spacing-medium);
&:not(:last-child) {
border-bottom: 1px solid var(--border-color);
}
}
}

View File

@ -1,173 +0,0 @@
.thread-list-item .latestpost {
width: 16.5rem;
}
.excerpt-fade {
background-image: linear-gradient(to top, var(--fade-color), rgba(0, 0, 0, 0));
pointer-events: none;
}
.read {
color: var(--forum-thread-read-color);
td {
color: var(--forum-thread-read-color);
}
a {
color: var(--forum-thread-read-link-color);
}
.title {
font-weight: 500;
}
}
.goto {
font-size: 200%;
width: 30px;
a {
display: block;
padding: 0px 10px;
box-sizing: border-box;
position: relative;
line-height: 100%;
background-color: transparent;
}
}
.badge {
display: inline-block;
border-radius: 1000em;
padding: 0 0.8em;
font-size: 0.9em;
line-height: 1.8em;
font-weight: bold;
&.staff {
background-color: #17b2c6;
color: white;
&::before {
content: 'Staff';
}
}
}
.postid a {
margin-top: -4rem;
padding-top: 4rem;
outline: none;
}
.codeblocktable {
font-family: monospace;
font-size: 14px;
overflow: auto;
line-height: 1.5em;
tbody {
width: 100%;
}
.linenos {
color: var(--theme-dim);
font-weight: 500;
padding: 5px;
background: rgba(0, 0, 0, 0.15);
}
.code {
/* Background color given by theme */
padding-right: 20px;
padding-left: 10px;
max-width: 80em;
}
}
.post {
.action.button {
padding: 0px 10px;
margin-top: 0px;
margin-right: 4px;
background-color: transparent;
font-size: 130%;
border-radius: 0px;
border-width: 0px;
border-bottom-width: 2px;
transition: border-bottom-width 0.1s;
&:hover {
border-bottom-width: 4px;
}
}
}
.blog {
.post {
.meta {
position: relative;
}
&.op .meta {
margin-bottom: 10px;
}
.badges {
width: 40px;
font-size: 0.7em;
text-align: center;
}
}
.sidebar {
.post>.author {
padding: 15px;
text-align: center;
}
.recent-posts {
text-align: center;
padding: 15px;
h2 {
display: inline;
}
}
.archive ul {
list-style-type: none;
text-align: left;
}
}
.post-list {
.post {
&:nth-child(even) {
background-color: transparent;
}
}
}
}
.bbtable {
width: 100%;
border: 1px solid;
td,
th {
border: 1px solid;
padding: 4px;
}
th {
background: rgba(0, 0, 0, 0.15);
font-weight: bold;
border-bottom-width: 2px;
}
tbody tr:nth-child(even) {
background: rgba(0, 0, 0, 0.05);
}
}

View File

@ -1,179 +0,0 @@
header.old {
--logo-height: 3.75rem;
.hmn-logo {
height: var(--logo-height);
width: 100%;
text-transform: uppercase;
font-family: 'MohaveHMN', sans-serif;
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
color: white !important;
&.big {
@media screen and (min-width: 35em) {
width: 11.25rem;
}
}
&.small {
@media screen and (min-width: 35em) {
width: var(--logo-height);
padding: 0.8rem;
text-align: justify;
text-align-last: justify;
text-justify: inter-character;
flex-direction: column;
font-size: 1rem;
line-height: 1em;
align-items: stretch;
}
}
}
.items {
position: relative;
/* will be used on mobile, when .root-item is not relative */
}
.root-item {
@media screen and (min-width: 35em) {
& {
position: relative;
/* makes submenus align to this item instead of the screen */
height: var(--logo-height);
}
}
&:not(:hover):not(.clicked)>.submenu {
display: none;
}
&.clicked .svgicon {
transform: rotate(180deg);
}
>a {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-weight: bold;
}
.svgicon {
font-size: 0.7em;
}
}
&:not(.clicked) .root-item:not(:hover),
&.clicked .root-item:not(.clicked) {
>.submenu {
display: none;
}
}
.submenu {
display: flex;
flex-direction: column;
position: absolute;
left: 0;
right: 0;
z-index: 100;
min-width: 10rem;
border-top-style: solid;
border-bottom-style: solid;
@media screen and (min-width: 35em) {
& {
border-top-style: none;
border-left-style: solid;
border-right-style: solid;
left: initial;
right: initial;
}
}
>a {
display: block;
white-space: nowrap;
z-index: 1;
font-weight: bold;
text-align: center;
@media screen and (min-width: 35em) {
& {
text-align: left;
}
}
}
}
}
header {
background-color: var(--bg-4);
.hmn-logo {
font-family: 'MohaveHMN', sans-serif;
text-transform: uppercase;
font-size: 1.6rem;
padding: 0.6rem 0.8rem;
line-height: 1;
}
.menu-chevron {
/* ensure that it also has .svgicon */
display: inline-block;
margin-left: var(--spacing-extra-small);
font-size: var(--font-size-7);
}
.avatar {
width: 1.8rem;
}
.header-nav {
>a,
>.root-item>a {
display: block;
padding: var(--spacing-medium);
}
.submenu {
display: flex;
flex-direction: column;
position: absolute;
z-index: 100;
min-width: 8rem;
background-color: var(--card-background);
>a {
padding: var(--spacing-small) var(--spacing-medium);
display: block;
white-space: nowrap;
z-index: 1;
}
}
.root-item {
&:not(:hover):not(.clicked)>.submenu {
display: none;
}
&.clicked .svgicon {
transform: rotate(180deg);
}
}
}
&:not(.clicked) .root-item:not(:hover),
&.clicked .root-item:not(.clicked) {
>.submenu {
display: none;
}
}
}

View File

@ -1,28 +0,0 @@
.notice a {
color: inherit;
border-bottom-color: inherit;
}
.notice-hiatus {
background-color: var(--notice-hiatus-color);
}
.notice-dead {
background-color: var(--notice-dead-color);
}
.notice-lts {
background-color: var(--notice-lts-color);
}
.notice-success {
background-color: var(--notice-success-color);
}
.notice-warn {
background-color: var(--notice-warn-color);
}
.notice-failure {
background-color: var(--notice-failure-color);
}

View File

@ -1,15 +0,0 @@
.upload_bar.uploading .instructions,
.upload_bar:not(.uploading) .progress {
display: none;
}
.upload_bar .progress_bar {
border: 2px solid;
border-color: var(--link-color);
padding: 2px;
}
.upload_bar .progress_bar>div {
background-color: var(--link-color);
height: 100%;
}

View File

@ -1,87 +0,0 @@
.pair {
display: flex;
align-items: flex-start;
.key {
font-weight: bold;
flex-shrink: 0;
}
.value {
text-align: right;
flex-grow: 1;
}
}
.project-carousel {
.carousel-item {
position: absolute;
top: 0;
left: 0;
br {
line-height: 0.6em;
}
}
.carousel-item-small {
position: absolute;
top: 0;
left: 0;
&:not(.active) {
display: none;
}
}
.carousel-description {
max-height: 14rem;
overflow: hidden;
}
.carousel-fade {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 30px;
background: linear-gradient(var(--dim-background-transparent), var(--dim-background));
}
}
.project-card {
color: var(--fg-font-color);
background-color: var(--card-background);
&.project-card-black {
background-color: var(--card-background-transparent);
}
.slideshow & {
margin-top: 8px;
}
.image-container {
--image-size: 8rem;
width: var(--image-size);
min-height: var(--image-size);
.image {
position: absolute;
width: var(--image-size);
top: 0;
bottom: 0;
}
}
.badges:empty {
display: none;
}
.badge {
color: white;
border-radius: 5px;
}
}

View File

@ -1,5 +0,0 @@
.showcase-item .gradient {
width: 100%;
height: 114px;
background-image: linear-gradient(rgba(0, 0, 0, 0.82), rgba(0, 0, 0, 0));
}

View File

@ -1,19 +0,0 @@
@import "tachyons.min.css";
@import "vars.css";
@import "core.css";
@import "carousel.css";
@import "content.css";
@import "editor.css";
@import "education.css";
@import "form.css";
@import "forum.css";
@import "header.css";
@import "icons.css";
@import "notices.css";
@import "progress_bar.css";
@import "projects.css";
@import "showcase.css";
@import "syntax.css";
@import "timeline.css";

View File

@ -14,17 +14,17 @@
@import 'episodes';
@import 'education';
@import 'forms';
// @import 'forum';
@import 'forum';
@import 'header';
@import 'icons';
@import 'irc';
// @import 'landing';
@import 'landing';
@import 'library';
@import 'profile';
@import 'projects';
@import 'showcase';
@import 'streams';
// @import 'timeline';
@import 'timeline';
@import 'carousel';
@import 'notices';
@import 'progress_bar';

View File

@ -1,39 +0,0 @@
pre, code, .codeblock {
.hll { background-color: #ffffcc }
.c { color: #008000 } /* Comment */
.err { border: 1px solid #FF0000 } /* Error */
.k { color: #0000ff } /* Keyword */
.ch { color: #008000 } /* Comment.Hashbang */
.cm { color: #008000 } /* Comment.Multiline */
.cp { color: #0000ff } /* Comment.Preproc */
.cpf { color: #008000 } /* Comment.PreprocFile */
.c1 { color: #008000 } /* Comment.Single */
.cs { color: #008000 } /* Comment.Special */
.ge { font-style: italic } /* Generic.Emph */
.gh { font-weight: bold } /* Generic.Heading */
.gp { font-weight: bold } /* Generic.Prompt */
.gs { font-weight: bold } /* Generic.Strong */
.gu { font-weight: bold } /* Generic.Subheading */
.kc { color: #0000ff } /* Keyword.Constant */
.kd { color: #0000ff } /* Keyword.Declaration */
.kn { color: #0000ff } /* Keyword.Namespace */
.kp { color: #0000ff } /* Keyword.Pseudo */
.kr { color: #0000ff } /* Keyword.Reserved */
.kt { color: #2b91af } /* Keyword.Type */
.s { color: #a31515 } /* Literal.String */
.nc { color: #2b91af } /* Name.Class */
.ow { color: #0000ff } /* Operator.Word */
.sa { color: #a31515 } /* Literal.String.Affix */
.sb { color: #a31515 } /* Literal.String.Backtick */
.sc { color: #a31515 } /* Literal.String.Char */
.dl { color: #a31515 } /* Literal.String.Delimiter */
.sd { color: #a31515 } /* Literal.String.Doc */
.s2 { color: #a31515 } /* Literal.String.Double */
.se { color: #a31515 } /* Literal.String.Escape */
.sh { color: #a31515 } /* Literal.String.Heredoc */
.si { color: #a31515 } /* Literal.String.Interpol */
.sx { color: #a31515 } /* Literal.String.Other */
.sr { color: #a31515 } /* Literal.String.Regex */
.s1 { color: #a31515 } /* Literal.String.Single */
.ss { color: #a31515 } /* Literal.String.Symbol */
}

View File

@ -1,140 +0,0 @@
/* Variables copied from tachyons/variables.scss and adapted to CSS variables. */
:root {
--sans-serif: -apple-system, BlinkMacSystemFont, 'avenir next', avenir, helvetica, 'helvetica neue', ubuntu, roboto, noto, 'segoe ui', arial, sans-serif;
--serif: georgia, serif;
--code: consolas, monaco, monospace;
--font-size-headline: 6rem;
--font-size-subheadline: 5rem;
--font-size-1: 3rem;
--font-size-2: 2.25rem;
--font-size-3: 1.5rem;
--font-size-4: 1.25rem;
--font-size-5: 1rem;
--font-size-6: .875rem;
--font-size-7: .75rem;
--letter-spacing-tight: -.05em;
--letter-spacing-1: .1em;
--letter-spacing-2: .25em;
--line-height-solid: 1;
--line-height-title: 1.25;
--line-height-copy: 1.5;
--measure: 30em;
--measure-narrow: 20em;
--measure-wide: 34em;
--spacing-none: 0;
--spacing-extra-small: .25rem;
--spacing-small: .5rem;
--spacing-medium: 1rem;
--spacing-large: 2rem;
--spacing-extra-large: 4rem;
--spacing-extra-extra-large: 8rem;
--spacing-extra-extra-extra-large: 16rem;
--spacing-copy-separator: 1.5em;
--height-1: 1rem;
--height-2: 2rem;
--height-3: 4rem;
--height-4: 8rem;
--height-5: 16rem;
--height-6: 32rem;
--width-1: 1rem;
--width-2: 2rem;
--width-3: 4rem;
--width-4: 8rem;
--width-5: 16rem;
--width-6: 32rem;
--width-7: 48rem;
--width-8: 64rem;
--max-width-1: 1rem;
--max-width-2: 2rem;
--max-width-3: 4rem;
--max-width-4: 8rem;
--max-width-5: 16rem;
--max-width-6: 32rem;
--max-width-7: 48rem;
--max-width-8: 64rem;
--max-width-9: 96rem;
--border-radius-none: 0;
--border-radius-1: .125rem;
--border-radius-2: .25rem;
--border-radius-3: .5rem;
--border-radius-4: 1rem;
--border-radius-circle: 100%;
--border-radius-pill: 9999px;
--border-width-none: 0;
--border-width-1: .125rem;
--border-width-2: .25rem;
--border-width-3: .5rem;
--border-width-4: 1rem;
--border-width-5: 2rem;
--box-shadow-1: 0px 0px 4px 2px rgba(0, 0, 0, 0.2);
--box-shadow-2: 0px 0px 8px 2px rgba(0, 0, 0, 0.2);
--box-shadow-3: 2px 2px 4px 2px rgba(0, 0, 0, 0.2);
--box-shadow-4: 2px 2px 8px 0px rgba(0, 0, 0, 0.2);
--box-shadow-5: 4px 4px 8px 0px rgba(0, 0, 0, 0.2);
--black: #000;
--near-black: #111;
--dark-gray: #333;
--mid-gray: #555;
--gray: #777;
--silver: #999;
--light-silver: #aaa;
--moon-gray: #ccc;
--light-gray: #eee;
--near-white: #f4f4f4;
--white: #fff;
--transparent: transparent;
--black-90: rgba(0, 0, 0, .9);
--black-80: rgba(0, 0, 0, .8);
--black-70: rgba(0, 0, 0, .7);
--black-60: rgba(0, 0, 0, .6);
--black-50: rgba(0, 0, 0, .5);
--black-40: rgba(0, 0, 0, .4);
--black-30: rgba(0, 0, 0, .3);
--black-20: rgba(0, 0, 0, .2);
--black-10: rgba(0, 0, 0, .1);
--black-05: rgba(0, 0, 0, .05);
--black-025: rgba(0, 0, 0, .025);
--black-0125: rgba(0, 0, 0, .0125);
--white-90: rgba(255, 255, 255, .9);
--white-80: rgba(255, 255, 255, .8);
--white-70: rgba(255, 255, 255, .7);
--white-60: rgba(255, 255, 255, .6);
--white-50: rgba(255, 255, 255, .5);
--white-40: rgba(255, 255, 255, .4);
--white-30: rgba(255, 255, 255, .3);
--white-20: rgba(255, 255, 255, .2);
--white-10: rgba(255, 255, 255, .1);
--white-05: rgba(255, 255, 255, .05);
--white-025: rgba(255, 255, 255, .025);
--white-0125: rgba(255, 255, 255, .0125);
--dark-red: #e7040f;
--red: #ff4136;
--light-red: #ff725c;
--orange: #ff6300;
--gold: #ffb700;
--yellow: #ffd700;
--light-yellow: #fbf1a9;
--purple: #5e2ca5;
--light-purple: #a463f2;
--dark-pink: #d5008f;
--hot-pink: #ff41b4;
--pink: #ff80cc;
--light-pink: #ffa3d7;
--dark-green: #137752;
--green: #19a974;
--light-green: #9eebcf;
--navy: #001b44;
--dark-blue: #00449e;
--blue: #357edd;
--light-blue: #96ccff;
--lightest-blue: #cdecff;
--washed-blue: #f6fffe;
--washed-green: #e8fdf5;
--washed-yellow: #fffceb;
--washed-red: #ffdfdf;
--breakpoint-not-small: 'screen and (min-width: 35em)';
--breakpoint-medium: 'screen and (min-width: 35em) and (max-width: 60em)';
--breakpoint-large: 'screen and (min-width: 60em)';
}

File diff suppressed because one or more lines are too long

View File

@ -25,6 +25,8 @@ $vars: (
menu-bottom-border-color: #444,
login-popup-background: #181818,
content-background: #202020,
content-background-transparent: rgba(#202020, 0),
dim-background: #252525,

View File

@ -25,6 +25,8 @@ $vars: (
menu-bottom-border-color: black,
login-popup-background: #fbfbfb,
content-background: #f8f8f8,
content-background-transparent: rgba(#f8f8f8, 0),
dim-background: #f0f0f0,

View File

@ -1,43 +0,0 @@
.avatar {
object-fit: cover;
border-radius: 100%;
overflow: hidden;
background-color: var(--dimmest-color);
flex-shrink: 0;
}
.timeline-item {
background-color: var(--card-background);
--fade-color: var(--card-background);
color: var(--main-color);
.timeline-content-box {
&.timeline-item-bg {
background-color: var(--timeline-content-background);
}
>* {
display: block;
max-width: 100%;
max-height: 80vh;
}
}
.avatar {
/* 40px */
width: 2.5rem;
}
}
.timeline-modal {
.container {
max-height: 100vh;
max-width: 100%;
@media screen and (min-width: 35em) {
width: auto;
max-width: calc(100% - 2rem);
max-height: calc(100vh - 2rem);
}
}
}

View File

@ -1,105 +0,0 @@
/*
Media queries cannot have variables, so here are media queries you can copy-paste as necessary:
$breakpoint-not-small: screen and (min-width: 35em)
$breakpoint-medium: screen and (min-width: 35em) and (max-width: 60em)
$breakpoint-large: screen and (min-width: 60em)
*/
@import "tachyons-vars.css";
:root {
--background-color: white;
--color: black;
--link-color: #d12991;
--red: #c61d24;
--dim-color: #333;
--dimmer-color: #999;
--dimmest-color: #bbb;
/* Default theme colors in case the project.css is busted */
--theme-color: #666;
--theme-color-dim: #aaa;
--theme-color-dimmer: #bbb;
--theme-color-dimmest: #ccc;
--theme-color-dark: #666;
--theme-color-light: #666;
--main-background-color: #f8f8f8;
--main-background-color-transparent: rgba(#f8f8f8, 0);
--bg-1: #1f1f1f;
--bg-2: #2f2f2f;
--bg-3: #494949;
--forum-thread-read-color: #555;
--forum-thread-read-link-color: #888;
--notice-hiatus-color: #aa7d30;
--notice-dead-color: #b42222;
--notice-lts-color: #43a52f;
--notice-success-color: #43a52f;
--notice-warn-color: #aa7d30;
--notice-failure-color: #b42222;
--spoiler-border: #aaa;
--site-width: 80rem;
--site-width-narrow: 60rem;
}
@media (prefers-color-scheme: dark) {
:root {
--background-color: #2f2f2f;
--color: #eee;
--link-color: #ff5dc2;
--color-error: #ff6666;
--dim-color: #bbb;
--dimmer-color: #999;
--dimmest-color: #777;
--theme-color: #666;
--theme-color-dim: #444;
--theme-color-dimmer: #383838;
--theme-color-dimmest: #333;
--theme-color-dark: #666;
--theme-color-light: #666;
--main-background-color: #202020;
--main-background-color-transparent: rgba(#202020, 0);
/* --card-background: #282828; */
--card-background: #494949;
--card-background-hover: #333;
--card-background-transparent: #242424D8;
--bg-1: #1f1f1f;
--bg-2: #2f2f2f;
--bg-3: #494949;
--bg-4: #595959;
--bg-5: #cbcbcb;
--border-color: #595959;
--border-color-focused: #4e55ff;
--border-color-error: #ff3a3a;
--button-color-primary: #c900ea;
--forum-thread-read-color: #777;
--forum-thread-read-link-color: #999;
--notice-hiatus-color: #876327;
--notice-dead-color: #7a2020;
--notice-lts-color: #2a681d;
--notice-success-color: #2a681d;
--notice-warn-color: #876327;
--notice-failure-color: #7a2020;
--spoiler-border: #777;
}
}

View File

@ -1 +0,0 @@
<svg viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M6,6l0,-6l2,0l0,6l6,0l0,2l-6,0l0,6l-2,0l0,-6l-6,0l0,-2l6,0Z"/></svg>

Before

Width:  |  Height:  |  Size: 171 B

View File

@ -1 +1,11 @@
<svg viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M7,5.444l5.444,-5.444l1.556,1.556l-5.444,5.444l5.444,5.444l-1.556,1.556l-5.444,-5.444l-5.444,5.444l-1.556,-1.556l5.444,-5.444l-5.444,-5.444l1.556,-1.556l5.444,5.444Z"/></svg>
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 47.971 47.971">
<g>
<path d="M28.228,23.986L47.092,5.122c1.172-1.171,1.172-3.071,0-4.242c-1.172-1.172-3.07-1.172-4.242,0L23.986,19.744L5.121,0.88
c-1.172-1.172-3.07-1.172-4.242,0c-1.172,1.171-1.172,3.071,0,4.242l18.865,18.864L0.879,42.85c-1.172,1.171-1.172,3.071,0,4.242
C1.465,47.677,2.233,47.97,3,47.97s1.535-0.293,2.121-0.879l18.865-18.864L42.85,47.091c0.586,0.586,1.354,0.879,2.121,0.879
s1.535-0.293,2.121-0.879c1.172-1.171,1.172-3.071,0-4.242L28.228,23.986z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 257 B

After

Width:  |  Height:  |  Size: 776 B

View File

@ -103,8 +103,8 @@ func ProjectToTemplate(
func ProjectAndStuffToTemplate(p *hmndata.ProjectAndStuff, url string, theme string) Project {
res := ProjectToTemplate(&p.Project, url)
res.Logo = ProjectLogoUrl(&p.Project, p.LogoLightAsset, p.LogoDarkAsset, theme)
if p.HeaderImage != nil {
res.HeaderImage = hmnurl.BuildS3Asset(p.HeaderImage.S3Key)
for _, o := range p.Owners {
res.Owners = append(res.Owners, UserToTemplate(o, theme))
}
return res
}
@ -129,7 +129,7 @@ func ProjectToProjectSettings(
p *models.Project,
owners []*models.User,
tag string,
lightLogo, darkLogo, headerImage *models.Asset,
lightLogoUrl, darkLogoUrl string,
currentTheme string,
) ProjectSettings {
ownerUsers := make([]User, 0, len(owners))
@ -147,26 +147,8 @@ func ProjectToProjectSettings(
Blurb: p.Blurb,
Description: p.Description,
Owners: ownerUsers,
LightLogo: AssetToTemplate(lightLogo),
DarkLogo: AssetToTemplate(darkLogo),
HeaderImage: AssetToTemplate(headerImage),
}
}
func AssetToTemplate(a *models.Asset) *Asset {
if a == nil {
return nil
}
return &Asset{
Url: hmnurl.BuildS3Asset(a.S3Key),
ID: a.ID.String(),
Filename: a.Filename,
Size: a.Size,
MimeType: a.MimeType,
Width: a.Width,
Height: a.Height,
LightLogo: lightLogoUrl,
DarkLogo: darkLogoUrl,
}
}

View File

@ -2,7 +2,7 @@
{{ define "content" }}
<div class="post-content">
<div class="mw7 ph3 ph0-ns m--center">
<div class="mw7 ph3 ph0-ns margin-center">
<h3>
We're trying to put programming back on the right track.
</h3>
@ -16,14 +16,14 @@
The Handmade Network's activities are funded by the <a href="{{ .FoundationUrl }}">Handmade Software Foundation</a>, a (soon-to-be) 501(c)(3) nonprofit corporation.
</p> -->
<p>
We can teach programmers how things actually work. We can teach users to expect better. We can build the best software you've ever seen. <b>Join us.</b>
We can teach programmers how things actually work. We can teach users to expect better. We can build the best software you've ever seen. <a href="{{ .RolesUrl }}"><b>Join us.</b></a>
</p>
</div>
<div class="ph3 ph0-ns mt4">
<h2>Leadership</h2>
<div class="flex flex-column flex-row-ns g3">
<div class="flex-fair pa3 bg3 br3">
<div class="flex-fair pa3 bg--dim br3">
<h3>Ben Visness</h3>
<h4>Lead<!--, Foundation President--></h4>
<p>
@ -40,7 +40,7 @@
</p> -->
</div>
<div class="flex-fair pa3 bg3 br3">
<div class="flex-fair pa3 bg--dim br3">
<h3>Asaf Gartner</h3>
<h4>Admin<!--, Foundation Secretary--></h4>
<p>
@ -51,7 +51,7 @@
</p>
</div>
<div class="flex-fair pa3 bg3 br3">
<div class="flex-fair pa3 bg--dim br3">
<h3>Colin Davidson</h3>
<h4>Admin<!--, Foundation Treasurer--></h4>
<p>
@ -67,11 +67,34 @@
<div class="ph3 ph0-ns mt4">
<h2>Key Contributors</h2>
<div class="flex flex-column flex-row-ns g3">
<div class="flex-fair pa3 bg3 br3">
<div class="flex-fair pa3 bg--dim br3">
<h3>Martin Fouilleul</h3>
<h4>Orca Lead</h4>
<p>Martin is a systems programmer, researcher, and PhD with experience in programming languages and models for distributed temporal interactions in music and performing arts software. He is also a former sound engineer and computer music designer.</p>
<p>He is the project lead on <a href="https://orca-app.dev/">Orca</a>, a new platform for WebAssembly applications outside the browser.</p>
<h4>[Secret Project] Lead</h4>
<p>Martin is a PhD student at <a href="https://www.ircam.fr/">Ircam</a>, researching programming languages and models for distributed temporal interactions in music and performing arts software. He is also a former sound engineer and computer music designer.</p>
<p>Right now he is working behind the scenes on a secret Handmade Network project, to be revealed at a later date.</p>
</div>
<div class="flex-fair pa3 bg--dim br3">
<h3>???</h3>
<h4>Education Lead</h4>
<p>
<strong>This could be you!</strong> Education is one of our flagship initiatives, and we are looking for someone to lead it. If you are excited about programming education and want to help out, please <a href="{{ .EducationLeadUrl }}">learn more and get in touch!</a>
</p>
</div>
</div>
<div class="flex flex-column flex-row-ns g3 mt3">
<div class="flex-fair pa3 bg--dim br3">
<h3>???</h3>
<h4>Advocacy Lead</h4>
<p>
<strong>This could be you!</strong> We want to go out into the world and advocate for better software and better programming practices. We need a strong communicator who can lead this for us. If this excites you, please <a href="{{ .AdvocacyLeadUrl }}">learn more and get in touch!</a>
</p>
</div>
<div class="flex-fair pa3 bg--dim br3">
<h3>???</h3>
<h4>Design Lead</h4>
<p>
<strong>This could be you!</strong> Building software for people means designing software for people. We are looking for someone with visual and UX design skills to design website features, event art, livestreaming assets, educational graphics, and more. If any of this sounds interesting to you, <a href="{{ .DesignLeadUrl }}">learn more and get in touch!</a>
</p>
</div>
</div>
</div>

View File

@ -4,7 +4,7 @@
<div class="optionbar">
<div class="options">
{{ if .CanCreatePost }}
<a class="option" href="{{ .NewPostUrl }}"><span class="big pr1">+</span> Create Post</a>
<a class="button" href="{{ .NewPostUrl }}"><span class="big pr1">+</span> Create Post</a>
{{ end }}
</div>
<div class="options">
@ -14,7 +14,7 @@
{{ if .Posts }}
{{ range .Posts }}
<div class="flex items-start ph3 pv3 background-even">
<img class="avatar mr2" src="{{ .Author.AvatarUrl }}">
<img class="avatar-icon mr2" src="{{ .Author.AvatarUrl }}">
<div class="flex-grow-1 overflow-hidden">
<div class="title mb1"><a href="{{ .Url }}">{{ .Title }}</a></div>
<div class="details">

View File

@ -1,12 +1,12 @@
{{ template "base.html" . }}
{{ define "content" }}
<div class="mw7 m--center tl post ph3 ph0-ns">
<div class="mw7 margin-center tl post ph3 ph0-ns">
<h1>{{ .Thread.Title }}</h1>
{{ with .MainPost }}
<div class="flex justify-between items-center mt2 mb3">
<div class="flex items-center">
<div class="avatar contain bg-center" style="background-image:url('{{ .Author.AvatarUrl }}');"></div>
<div class="avatar-icon contain bg-center" style="background-image:url('{{ .Author.AvatarUrl }}');"></div>
<div class="flex flex-column ml2">
<div>
<a class="username" href="{{ .Author.ProfileUrl }}" target="_blank">{{ .Author.Name }}</a>
@ -39,18 +39,22 @@
{{ end }}
<!-- Main post -->
<div class="mb3">
<div class="{{ if .IsProjectPage }}mb3{{ end }}">
<div class="post-content overflow-x-auto">
{{ .MainPost.Content }}
</div>
</div>
{{ if not .IsProjectPage }}
{{ template "newsletter_signup.html" . }}
{{ end }}
<div class="optionbar"></div>
{{ range .Comments }}
<div class="pa3 flex items-start background-even">
<div>
<div class="avatar contain bg-center" style="background-image:url('{{ .Author.AvatarUrl }}');"></div>
<div class="avatar-icon contain bg-center" style="background-image:url('{{ .Author.AvatarUrl }}');"></div>
</div>
<div class="pl3 flex flex-column w-100">
<div class="flex justify-between">

View File

@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "content" }}
<div class="mw7 m--center">
<div class="mw7 margin-center">
<h3 class="mb3">Are you sure you want to delete this post?</h3>
{{ template "forum_post_standalone.html" .Post }}
<form action="{{ .SubmitUrl }}" method="POST" class="pv3 flex justify-end">

View File

@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "content" }}
<div class="m--center mw7 mv3 ph3 ph0-ns post-content">
<div class="center-layout mw7 mv3 ph3 ph0-ns post-content">
<h1>Handmade Guide to Community Interaction</h1>
<h2>Our Philosophy</h2>
<p>The Handmade community strives to create an environment conducive to innovation, education, and constructive discussion. To that end, we expect members of the community to respect the following set of principles to maintain civil discourse and create an inclusive environment.</p>

View File

@ -2,7 +2,7 @@
{{ define "content" }}
{{ $bannerclass := "overflow-hidden br2 mt2 h4 h5-ns cover bg-center" }}
<div class="m--center mw7 ph3 ph0-ns post-content flex flex-column g3">
<div class="center-layout mw7 ph3 ph0-ns post-content flex flex-column g3">
<div class="fronke">
<a href="https://handmadehero.org/conference">
<h2>HandmadeCon</h2>

View File

@ -0,0 +1,22 @@
{{ template "base.html" . }}
{{ define "content" }}
<table>
<thead>
<tr>
<td>Timestamp</td>
<td>Name</td>
<td>Extras</td>
</tr>
</thead>
<tbody>
{{ range .BotEvents }}
<tr>
<td>{{ rfc3339 .Timestamp }}</td>
<td>{{ .Name }}</td>
<td>{{ .Extra }}</td>
</tr>
{{ end }}
</tbody>
</table>
{{ end }}

View File

@ -29,6 +29,28 @@
{{ if .CanEditPostTitle }}
<input id="title" class="b w-100 mb1" name="title" type="text" placeholder="Post title..." value="{{ .PostTitle }}" />
{{ end }}
{{/* TODO: Reintroduce the toolbar in a way that makes sense for Markdown */}}
{{/*
<div class="toolbar" id="toolbar">
<input type="button" id="bold" value="B" />
<input type="button" id="italic" value="I" />
<input type="button" id="underline" value="U" />
<input type="button" id="monospace" value="monospace" />
<input type="button" id="url" value="url" />
<input type="button" id="img" value="img" />
<input type="button" id="code" value="code" />
<input type="button" id="quote_simple" value="quote (anon)" />
<input type="button" id="quote_member" value="quote (member)" />
<input type="button" id="spoiler" value="spoiler" />
<input type="button" id="lalign" value="Left" />
<input type="button" id="calign" value="Center" />
<input type="button" id="ralign" value="Right" />
<input type="button" id="ulist" value="ul" />
<input type="button" id="olist" value="ol" />
<input type="button" id="litem" value="li" />
<input type="button" id="youtube" value="youtube" />
</div>
*/}}
<textarea id="editor" class="w-100 h6 minh-6 pa2 mono lh-copy" name="body">{{ .EditInitialContents }}</textarea>
<div class="flex justify-end items-center mt2">
@ -60,7 +82,7 @@
{{ if .ShowEduOptions }}
{{/* Hope you have a .Article field! */}}
<div class="bg3 br3 pa3 mt3">
<div class="bg--dim br3 pa3 mt3">
<h4>Education Options</h4>
<div class="mb2">
<label for="slug">Slug:</label>
@ -86,7 +108,7 @@
</div>
{{ end }}
</form>
<div id="preview-container" class="post post-preview mathjax flex-fair-ns overflow-auto mv3 mv0-ns ml3-ns pa3 br3 bg3 {{ .PreviewClass }}">
<div id="preview-container" class="post post-preview mathjax flex-fair-ns overflow-auto mv3 mv0-ns ml3-ns pa3 br3 bg--dim {{ .PreviewClass }}">
<div id="preview" class="post-content"></div>
</div>
<input type="file" multiple name="file_input" id="file_input" class="dn" />{{/* NOTE(asaf): Placing this outside the form to avoid submitting it to the server by accident */}}

View File

@ -1,9 +1,9 @@
{{ template "base.html" . }}
{{ define "content" }}
<div class="mw7 m--center">
<div class="mw7 margin-center">
<h3 class="mb3">Are you sure you want to delete this article?</h3>
<div class="bg3 pa3 br3 tl post-content">
<div class="bg--dim pa3 br3 tl post-content">
<h1>{{ .Article.Title }}</h1>
{{ .Article.Content }}
</div>

View File

@ -7,7 +7,7 @@
<p>Dive into one of these topics and start learning.</p>
<div class="flex flex-column flex-row-ns g3 mt3 mb4">
<a href="#compilers" class="edu-topic db flex-fair-ns bg3 br3 overflow-hidden c--inherit flex flex-column">
<a href="#compilers" class="edu-topic db flex-fair-ns bg--dim br3 overflow-hidden c--inherit flex flex-column">
<img src="{{ static "education/compilers.jpg" }}">
<div class="pa3">
<h2>Compilers</h2>
@ -16,7 +16,7 @@
</div>
</div>
</a>
<a href="#networking" class="edu-topic db flex-fair-ns bg3 br3 overflow-hidden c--inherit flex flex-column">
<a href="#networking" class="edu-topic db flex-fair-ns bg--dim br3 overflow-hidden c--inherit flex flex-column">
<img src="{{ static "education/networking.jpg" }}">
<div class="pa3">
<h2>Networking</h2>
@ -25,7 +25,7 @@
</div>
</div>
</a>
<a href="#time" class="edu-topic db flex-fair-ns bg3 br3 overflow-hidden c--inherit flex flex-column">
<a href="#time" class="edu-topic db flex-fair-ns bg--dim br3 overflow-hidden c--inherit flex flex-column">
<img src="{{ static "education/time.jpg" }}">
<div class="pa3">
<h2>Time</h2>
@ -39,17 +39,17 @@
<h2>What makes us different?</h2>
<div class="flex flex-column flex-row-ns g3 mb4">
<div class="flex-fair bg3 pa3 br2">
<div class="flex-fair bg--dim pa3 br2">
<h3>Real material.</h3>
We equip you to go straight to the source. Our guides are structured around books and articles written by experts. We give you high-quality material to read, and the context to understand it. You do the rest.
</div>
<div class="flex-fair bg3 pa3 br3">
<div class="flex-fair bg--dim pa3 br3">
<h3>For any skill level.</h3>
Each guide runs the gamut from beginner to advanced. Whether you're new to a topic or have been practicing it for years, read through our guides and you'll find something new.
</div>
<div class="flex-fair bg3 pa3 br3">
<div class="flex-fair bg--dim pa3 br3">
<h3>Designed for programmers.</h3>
We're not here to teach you how to program. We're here to teach you a specific topic.
@ -59,7 +59,7 @@
<h2>All Topics</h2>
{{ range .Courses }}
<div id="{{ .Slug }}" class="edu-course mv3 bg3 pa3 br3">
<div id="{{ .Slug }}" class="edu-course mv3 bg--dim pa3 br3">
<h3>{{ .Name }}</h3>
<div class="overflow-hidden">
<div class="edu-articles ml3 pl3">

View File

@ -1,6 +1,39 @@
{{ template "base.html" . }}
{{ define "extrahead" }}
<style type="text/css">
.queryContainer {
width: 80%;
margin: 15px auto 20px auto;
display: flex;
flex-direction: horizontal;
}
.queryContainer input {
height:3em;
}
.queryContainer #query {
flex-grow: 1;
}
.queryContainer #submit_button {
flex-grow: 0;
flex-shrink: 0;
padding:0px 10px;
vertical-align:middle;
margin:0px;
}
#annotationContainer {
left: -10px;
margin: 0px;
margin-left: -20px;
padding: 0px;
width: 100%;
height: 100%;
border: 0px transparent none;
}
</style>
<link rel="stylesheet" type="text/css" href="{{ static "annotations/cinera.css" }}">
<link rel="stylesheet" type="text/css" href="{{ static (strjoin "annotations/cinera__" .Project.Subdomain ".css") }}">
<link rel="stylesheet" type="text/css" href="{{ static "annotations/cinera_topics.css" }}">

View File

@ -18,7 +18,7 @@
</div>
{{ range .Posts }}
<div class="post-list-item flex items-center ph3 pv2 {{ if .Unread }}unread{{ else }}read{{ end }} {{ .Classes }}">
<img class="avatar mr2" src="{{ .User.AvatarUrl }}">
<img class="avatar-icon mr2" src="{{ .User.AvatarUrl }}">
<div class="flex-grow-1 overflow-hidden">
{{ template "breadcrumbs.html" .Breadcrumbs }}
<div class="title nowrap truncate"><a href="{{ .Url }}" title="{{ .Preview }}">{{ if .PostTypePrefix }}{{ .PostTypePrefix }}: {{ end }}{{ .Title }}</a></div>

View File

@ -10,7 +10,7 @@
<div class="mt3 mw7">
{{ range .Fishbowls }}
<div class="br2 bg3 pa3 mb2">
<div class="br2 bg--dim pa3 mb2">
{{ if .Valid }}
<a href="{{ .Url }}"><h3 class="f4 ma0">{{ .Fishbowl.Title }}</h3></a>
{{ else }}

View File

@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "content" }}
<div class="mw7 m--center">
<div class="mw7 margin-center">
<h3 class="mb3">Are you sure you want to delete this post?</h3>
{{ template "forum_post_standalone.html" .Post }}
<form action="{{ .SubmitUrl }}" method="POST" class="pv3 flex justify-end">

View File

@ -1,7 +1,7 @@
{{ template "base.html" . }}
{{ define "content" }}
<div class="post-content mw7 ph3 ph0-ns m--center">
<div class="post-content mw7 ph3 ph0-ns margin-center">
<h1>Handmade Software Foundation</h1>
<!-- <div> -->
</div>

View File

@ -1,5 +0,0 @@
<footer>
<div class="mv5 h3 fill-current link--normal">
<a href="{{ .Header.HMNHomepageUrl }}">{{ svg "hmn_circuit" }}</a>
</div>
</footer>

View File

@ -6,26 +6,29 @@
{{ $footerClasses := "ma0 pa0 dib-ns" }}
<li class="{{ $footerClasses }}">
<a href="{{ .Footer.HomepageUrl }}">Main Page</a>
<span class="c--dimmer dn di-ns"> / </span>
</li>
<li class="{{ $footerClasses }}">
<a href="{{ .Footer.ManifestoUrl }}">Manifesto</a>
<span class="c--dimmer dn di-ns"> / </span>
</li>
<li class="{{ $footerClasses }}">
<a href="{{ .Footer.AboutUrl }}">About</a>
<span class="c--dimmer dn di-ns"> / </span>
</li>
<li class="{{ $footerClasses }}">
<a href="{{ .Footer.RolesUrl }}">Roles</a>
</li>
<li class="{{ $footerClasses }}">
<a href="{{ .Footer.ProjectIndexUrl }}">Projects</a>
<span class="c--dimmer dn di-ns"> / </span>
</li>
<li class="{{ $footerClasses }}">
<a href="{{ .Footer.CommunicationGuidelinesUrl }}">Communication Guidelines</a>
<span class="c--dimmer dn di-ns"> / </span>
</li>
<li class="{{ $footerClasses }}">
<a href="{{ .Footer.ContactUrl }}">Contact</a>
</li>
</ul>
<form onsubmit="this.querySelector('input[name=q]').value = this.querySelector('#searchstring').value + ' site:handmade.network';" class="ma0 mt3 bg--card pa1 br2 dib" method="GET" action="{{ .Footer.SearchActionUrl }}" target="_blank">
<input type="hidden" name="q" />
<input class="site-search bn lite pa2 fira" type="text" id="searchstring" value="" placeholder="Search with DuckDuckGo" size="18" />
<input id="search_button_homepage" type="submit" value="Go"/>
</form>
</footer>

View File

@ -1,4 +1,4 @@
<div class="bg3 pa3 br3 tl">
<div class="bg--dim pa3 br3 tl">
<div class="w-100 flex items-center">
<div class="w-20 mw3 w3">
<!-- Mobile avatar -->

View File

@ -1,103 +0,0 @@
<header id="site-header" class="flex flex-row items-center link--normal">
<a href="{{ .Header.HMNHomepageUrl }}" class="hmn-logo flex-shrink-0">
Handmade
</a>
<div class="flex-grow-1 flex-shrink-1"></div>
<div class="header-nav flex flex-row items-center lh-solid f6">
<a href="{{ .Header.ProjectIndexUrl }}">Projects</a>
<a href="{{ .Header.JamsUrl }}">Jams</a>
<div class="root-item">
<a aria-expanded="false" aria-controls="events-submenu" class="menu-dropdown-js" href="#">
Events <div class="menu-chevron svgicon">{{ svg "chevron-down-thick" }}</div>
</a>
<div class="submenu" id="events-submenu">
<a href="{{ .Header.JamsUrl }}">Jams</a>
<a href="https://twitch.tv/HandmadeNetwork">Unwind</a>
</div>
</div>
<div class="root-item">
<a aria-expanded="false" aria-controls="resource-submenu" class="menu-dropdown-js" href="#">
Resources <div class="menu-chevron svgicon">{{ svg "chevron-down-thick" }}</div>
</a>
<div class="submenu" id="resource-submenu">
<a href="{{ .Header.ForumsUrl }}">Forums</a>
<a href="{{ .Header.FishbowlUrl }}">Fishbowls</a>
<a href="{{ .Header.PodcastUrl }}">Podcast</a>
<!-- TODO: This is not a "resource", nor do we want to imply that we own Handmade Cities. -->
<a href="https://handmadecities.com/media/">Handmade Cities</a>
<!-- TODO: What about our existing conferences page? How to properly reference HandmadeCon? Does this give enough context? (Make sure to reference Casey's email to me and Abner.) -->
<a href="https://guide.handmadehero.org/hmcon/">HandmadeCon</a>
</div>
</div>
<div class="root-item">
<a aria-expanded="false" aria-controls="about-submenu" class="menu-dropdown-js" href="#">
About <div class="menu-chevron svgicon">{{ svg "chevron-down-thick" }}</div>
</a>
<div class="submenu" id="about-submenu">
<a href="{{ .Header.ManifestoUrl }}">Manifesto</a>
<a href="{{ .Header.AboutUrl }}">About the Team</a>
</div>
</div>
</div>
<a class="db ph3 pv2 flex" href="{{ or .Header.UserProfileUrl .LoginPageUrl }}">
<img class="avatar" src="{{ with .User }}{{ .AvatarUrl }}{{ end }}">
</a>
</header>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function() {
const header = document.querySelector('#site-header');
// set up dropdown stuff for mobile / touch
{
const rootItems = header.querySelectorAll('.root-item');
function clearDropdowns() {
for (const item of rootItems) {
item.classList.remove('clicked');
}
}
function clickDropdown(el) {
header.classList.add('clicked');
if (el.classList.contains('clicked')) {
clearDropdowns();
} else {
clearDropdowns();
el.classList.add('clicked');
}
}
for (const item of rootItems) {
if (item.querySelector('.submenu')) {
item.addEventListener('click', e => {
clickDropdown(item);
e.stopPropagation();
});
}
}
}
// dropdown accessiblity
{
const dropdowns = document.querySelectorAll('.menu-dropdown-js');
for(let i = 0; i < dropdowns.length; i++) {
let dropdown = dropdowns[i];
dropdown.addEventListener('click', e => {
e.preventDefault();
for(let j = 0; j < dropdowns.length; j++){
let each = dropdowns[j];
if(each != dropdown){
each.setAttribute("aria-expanded", false);
}
}
// getAttribute returns a string so we have to do it this way
var toSetTo = dropdown.getAttribute("aria-expanded") == "false" ? "true" : "false";
dropdown.setAttribute("aria-expanded", toSetTo);
console.log(dropdown);
});
}
}
});
</script>

View File

@ -1,21 +1,43 @@
<header id="site-header" class="old mb3 bb bw1 b--theme-dark">
<a href="#content-start" class="sr sr-focusable" id="content-jump">Jump to Content</a>
<div class="flex justify-center justify-end-ns relative">
<header id="site-header" class="mb3 bb bw1 b--theme-dark">
<a href="#content-start" class="sr-focusable" id="content-jump">Jump to Content</a>
<div class="user-options flex justify-center justify-end-ns relative">
{{ if .User }}
{{ if .User.IsStaff }}
<a class="pa2" href="{{ .Header.AdminUrl }}"><span class="icon-settings"> Admin</span></a>
<a class="admin-panel pa2" href="{{ .Header.AdminUrl }}"><span class="icon-settings"> Admin</span></a>
{{ end }}
<div>
<a class="dib pv2 pl2" href="{{ .Header.UserProfileUrl }}">{{ .User.Username }}</a>
<a class="dib pv2 pr2" href="{{ .Header.UserSettingsUrl }}">(settings)</a>
</div>
<a class="pa2" href="{{ .Header.LogoutActionUrl }}"><span class="icon-logout"></span> Log Out</a>
<a class="logout pa2" href="{{ .Header.LogoutActionUrl }}"><span class="icon-logout"></span> Log Out</a>
{{ else }}
<a class="pa2" id="register-link" href="{{ .Header.RegisterUrl }}">Register</a>
<a class="pa2" id="login-link" href="{{ .LoginPageUrl }}">Log in</a>
<a class="register pa2" id="register-link" href="{{ .Header.RegisterUrl }}">Register</a>
<a class="login pa2" id="login-link" href="{{ .LoginPageUrl }}">Log in</a>
<div id="login-popup" class="pa3 bt bb ba-ns bw1 b--theme-dark">
<form action="{{ .Header.LoginActionUrl }}" method="post" class="ma0 flex flex-column">
<input type="text" name="username" class="w-100" value="" placeholder="Username" />
<input type="password" name="password" class="w-100 mt1" value="" placeholder="Password" />
<a class="db mt1" href="{{ .Header.ForgotPasswordUrl }}">Forgot your password?</a>
<input type="hidden" name="redirect" value="{{ $.CurrentUrl }}">
<div class="mt2">
<input type="submit" value="Log In" class="w-100" />
</div>
<div class="mt3 tc">
<div class="b mb1">Third-party login</div>
<div class="flex flex-column g1">
<a href="{{ .Header.LoginWithDiscordUrl }}" class="db br2 overflow-hidden flex" title="Log in with Discord">
<img
src="{{ static "discord-login.svg" }}"
alt="Log in with Discord"
>
</a>
</div>
</div>
</form>
</div>
{{ end }}
</div>
<div class="flex flex-column flex-row-ns justify-between w-100 {{ if .IsProjectPage }}project{{ end }}">
<div class="menu-bar flex flex-column flex-row-ns justify-between w-100 {{ if .IsProjectPage }}project{{ end }}">
<div class="flex flex-column flex-row-ns items-center w-100">
{{ $itemsClass := "items self-stretch flex items-center justify-center justify-start-ns ml2-ns ml3-l" }}
{{ if .Header.Project }}
@ -57,30 +79,30 @@
</a>
<div class="{{ $itemsClass }}">
<div class="root-item">
<a class="pa2 ph3-l" href="{{ .Header.ProjectIndexUrl }}">Projects</a>
<a href="{{ .Header.ProjectIndexUrl }}">Projects</a>
</div>
<div class="root-item">
<a class="pa2 ph3-l" aria-expanded="false" aria-controls="events-submenu" class="menu-dropdown-js" href="#">
<a aria-expanded="false" aria-controls="events-submenu" class="menu-dropdown-js" href="#">
Events <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div>
</a>
<div class="submenu bw1 b--theme-dark bg--main" id="events-submenu">
<a class="pa2 ph3-l" href="{{ .Header.JamsUrl }}">Jams</a>
<a class="pa2 ph3-l" href="{{ .Header.ConferencesUrl }}">Conferences</a>
<a class="pa2 ph3-l" href="{{ .Header.FishbowlUrl }}">Fishbowls</a>
<a class="pa2 ph3-l" href="{{ .Header.PodcastUrl }}">Podcast</a>
<a class="pa2 ph3-l" href="https://guide.handmade-seattle.com/s" target="_blank">Handmade Dev Show</a>
<a class="pa2 ph3-l" href="{{ .Header.CalendarUrl }}">Calendar</a>
<div class="submenu b--theme-dark" id="events-submenu">
<a href="{{ .Header.JamsUrl }}">Jams</a>
<a href="{{ .Header.ConferencesUrl }}">Conferences</a>
<a href="{{ .Header.FishbowlUrl }}">Fishbowls</a>
<a href="{{ .Header.PodcastUrl }}">Podcast</a>
<a href="https://guide.handmade-seattle.com/s" target="_blank">Handmade Dev Show</a>
{{/*<a href="{{ .Header.CalendarUrl }}">Calendar</a>*/}}
</div>
</div>
<div class="root-item">
<a class="pa2 ph3-l" href="{{ .Header.ForumsUrl }}">Forums</a>
<a href="{{ .Header.ForumsUrl }}">Forums</a>
</div>
<div class="root-item">
<a class="pa2 ph3-l" aria-expanded="false" aria-controls="resource-submenu" class="menu-dropdown-js" href="#">
<a aria-expanded="false" aria-controls="resource-submenu" class="menu-dropdown-js" href="#">
Resources <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div>
</a>
<div class="submenu bw1 b--theme-dark bg--main" id="resource-submenu">
<a class="pa2 ph3-l" href="{{ .Header.EducationUrl }}">Education</a>
<div class="submenu b--theme-dark" id="resource-submenu">
<a href="{{ .Header.EducationUrl }}">Education</a>
</div>
</div>
</div>
@ -149,5 +171,32 @@
});
}
}
// set up login form
{
const loginPopup = document.getElementById("login-popup");
const loginLink = document.getElementById("login-link");
if (loginPopup !== null) {
loginLink.onclick = (e) => {
e.preventDefault();
loginPopup.classList.toggle("open");
}
}
const dtf = new Intl.DateTimeFormat([], {
dateStyle: "full",
timeStyle: "short",
});
for (const time of document.querySelectorAll('time')) {
const d = new Date(Date.parse(time.dateTime));
if (time.getAttribute("data-type") == "content") {
time.textContent = dtf.format(d);
} else {
time.title = dtf.format(d);
}
}
}
});
</script>

View File

@ -1,16 +1,11 @@
{{/* NOTE(asaf): Make sure to include js/image_selector.js */}}
<input {{ if .Required }}required{{ end }} class="imginput dn" type="file" accept="image/*" name="{{ .Name }}" />
<input class="imginput-remove" type="hidden" name="remove_{{ .Name }}" />
<input {{ if .Required }}required{{ end }} class="image_input" type="file" accept="image/*" name="{{ .Name }}" />
<input class="remove_input" type="hidden" name="remove_{{ .Name }}" />
<span class="error dn notice notice-failure mv2 mw6"></span>
<div class="imginput-container pa3 flex g3 items-center">
{{ $url := or (and .Asset .Asset.Url) "" }}
{{ $filename := or (and .Asset .Asset.Filename) "" }}
<img class="w4 flex-shrink-0" data-imginput-original="{{ $url }}" data-imginput-original-filename="{{ $filename }}" src="{{ $url }}" />
<div class="w1 flex-grow-1 flex flex-column g1">
<div class="flex f6">
<a href="javascript:;" class="imginput-reset-link">Reset</a>
<a href="javascript:;" class="imginput-remove-link">Remove</a>
</div>
<div class="imginput-filename b truncate"></div>
<div class="image_container">
<img class="mw6" data-original="{{ .Src }}" src="{{ .Src }}" />
<div >
<a href="javascript:;" class="reset">Reset</a>
<a href="javascript:;" class="remove">Remove</a>
</div>
</div>

View File

@ -0,0 +1,23 @@
<div class="mw7 margin-center link--white">
<div class="flex flex-column pv4 tc">
<img class="jam-logo margin-center" src="{{ static "learningjam2024/logo.svg" }}">
<div class="margin-center mt3 mt4-ns">
<a class="jam-title db mb3" href="{{ .JamUrl }}">Learning Jam</a>
<div class="flex flex-column flex-row-ns justify-between lh-solid g3 g0-ns">
<div>
<div class="fw7 f3 mb1">Learn</div>
<div class="fw3 f4">March 15-17, 2024</div>
</div>
<div>
<div class="fw7 f3 mb1">Share</div>
<div class="fw3 f4">March 22-24, 2024</div>
</div>
</div>
</div>
{{ if gt .DaysUntilEnd 0 }}
<div class="flex g3 justify-center mt4">
<a href="{{ .DiscordInviteUrl }}" class="btn--jam">Join the Discord</a>
</div>
{{ end }}
</div>
</div>

View File

@ -0,0 +1,19 @@
<div class="mw7 margin-center link--white">
<div class="flex flex-column flex-row-ns justify-center items-center pv4 g3 tc tl-ns">
<img class="jam-logo-small" src="{{ static "learningjam2024/logo.svg" }}">
<div class="flex flex-column justify-between">
<div>
<a class="jam-title db small" href="{{ .JamUrl }}">Learning Jam</a>
<div class="fw6 f5 mb3">March 15 - 24, 2024</div>
</div>
<div class="flex items-center g2">
{{ if and (not .SubmittedProjectUrl) (gt .DaysUntilEnd 0) }}
<a href="{{ .NewProjectUrl }}" class="btn--jam">Create your project</a>
{{ else }}
<a href="{{ .DiscordInviteUrl }}" class="btn--jam">Join the Discord</a>
{{ end }}
<a href="{{ .GuidelinesUrl }}" class="flex items-baseline pa2">Guidelines <div class="dib svgicon f8 ml1">{{ svg "chevron-right" }}</div></a>
</div>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More