Compare commits

..

17 Commits

Author SHA1 Message Date
Ben Visness 2980d66d4b Big WIP, project settings 2024-05-31 21:25:52 -05:00
Asaf Gartner 69c1cf1011 EsBuild with on-request rebuild and hot-reloading 2024-05-30 07:19:31 +03:00
Ben Visness e6dc21f058 Start reworking project edit page 2024-05-24 21:09:28 -05:00
Ben Visness 2fbc1a5d5a Nearly complete design of project home page 2024-05-24 20:43:01 -05:00
Ben Visness 40e5e39acb Scaffold new project home pages 2024-05-20 22:39:58 -05:00
Ben Visness 78884cb641 Implement new header / footer (mostly) 2024-05-20 21:37:28 -05:00
Ben Visness 7e01dae78e WIP cleanup styles dunno 2024-05-13 19:00:09 -05:00
Ben Visness b1b97e760e Port _projects.scss (deleting most of it!) 2024-05-13 18:47:07 -05:00
Ben Visness 38a2979f82 Port the content styles (easier than I thought!) 2024-05-13 18:33:15 -05:00
Ben Visness 379ad520de Port education styles 2024-05-13 17:43:39 -05:00
Ben Visness f9d3fc6c6b Do all the easy stuff 2024-05-10 22:22:31 -05:00
Ben Visness 54008b0c81 Convert the header / nav styles 2024-05-10 20:22:20 -05:00
Ben Visness 9286e5701d Wahoo more WIP haha 2024-04-29 18:41:00 -05:00
Ben Visness 3181805dd4 Finish cleaning up core.css for now (still a couple old things) 2024-04-29 17:40:24 -05:00
Ben Visness b4f9d379f4 WIP I dunno why do I do this 2024-03-08 18:46:17 -06:00
Ben Visness 7215e83650 WIP 2024-02-06 19:52:41 -06:00
Ben Visness f6691cee24 Start converting styles to plain CSS
wow there is a lot of garbage in here
2024-02-04 20:55:46 -06:00
166 changed files with 25519 additions and 14369 deletions

1
.gitignore vendored
View File

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

2
go.mod
View File

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

3
go.sum
View File

@ -90,6 +90,8 @@ 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/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 h1:feGUUxxvOtWVOhTko8Cbmp33a+tU0IMZxMEmnkoAISQ=
github.com/emersion/go-ical v0.0.0-20220601085725-0864dccc089f/go.mod h1:2MKFUgfNMULRxqZkadG1Vh44we3y5gJAtTBlVsx1BKQ= 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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -369,6 +371,7 @@ 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-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-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-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.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 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

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

View File

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

3
public/js/script.js Normal file
View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 KiB

View File

@ -1 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 2.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

View File

@ -1 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because it is too large Load Diff

14171
public/style.old.css Normal file

File diff suppressed because it is too large Load Diff

View File

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

39
src/buildscss/cmd/cmd.go Normal file
View File

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

View File

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

View File

@ -23,34 +23,6 @@ import (
"github.com/jpillora/backoff" "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 { func RunDiscordBot(ctx context.Context, dbConn *pgxpool.Pool) jobs.Job {
log := logging.ExtractLogger(ctx).With().Str("module", "discord").Logger() log := logging.ExtractLogger(ctx).With().Str("module", "discord").Logger()
ctx = logging.AttachLoggerToContext(&log, ctx) ctx = logging.AttachLoggerToContext(&log, ctx)
@ -84,11 +56,6 @@ func RunDiscordBot(ctx context.Context, dbConn *pgxpool.Pool) jobs.Job {
log.Info().Msg("Connecting to the Discord gateway") log.Info().Msg("Connecting to the Discord gateway")
bot := newBotInstance(dbConn) bot := newBotInstance(dbConn)
err := bot.Run(ctx) err := bot.Run(ctx)
disconnectMessage := ""
if err != nil {
disconnectMessage = err.Error()
}
RecordBotEvent("Disconnected", disconnectMessage)
if err != nil { if err != nil {
dur := boff.Duration() dur := boff.Duration()
log.Error(). log.Error().
@ -134,8 +101,6 @@ type botInstance struct {
conn *websocket.Conn conn *websocket.Conn
dbConn *pgxpool.Pool dbConn *pgxpool.Pool
resuming bool
heartbeatIntervalMs int heartbeatIntervalMs int
forceHeartbeat chan struct{} forceHeartbeat chan struct{}
@ -228,7 +193,6 @@ func (bot *botInstance) Run(ctx context.Context) (err error) {
logging.ExtractLogger(ctx).Info().Msg("Discord asked us to reconnect to the gateway") logging.ExtractLogger(ctx).Info().Msg("Discord asked us to reconnect to the gateway")
return nil return nil
case OpcodeInvalidSession: case OpcodeInvalidSession:
RecordBotEvent("Failed to resume - invalid session", "")
// We tried to resume but the session was invalid. // We tried to resume but the session was invalid.
// Delete the session and reconnect from scratch again. // Delete the session and reconnect from scratch again.
_, err := bot.dbConn.Exec(ctx, `DELETE FROM discord_session`) _, err := bot.dbConn.Exec(ctx, `DELETE FROM discord_session`)
@ -300,11 +264,8 @@ func (bot *botInstance) connect(ctx context.Context) error {
} }
} }
RecordBotEvent("Connected", "")
if shouldResume { if shouldResume {
RecordBotEvent("Resuming with session ID", session.ID)
// Reconnect to the previous session // Reconnect to the previous session
bot.resuming = true
err := bot.sendGatewayMessage(ctx, GatewayMessage{ err := bot.sendGatewayMessage(ctx, GatewayMessage{
Opcode: OpcodeResume, Opcode: OpcodeResume,
Data: Resume{ Data: Resume{
@ -579,20 +540,11 @@ 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)) 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 { switch *msg.EventName {
case "RESUMED": case "RESUMED":
// Nothing to do, but at least we can log something // Nothing to do, but at least we can log something
logging.ExtractLogger(ctx).Info().Msg("Finished resuming gateway session") logging.ExtractLogger(ctx).Info().Msg("Finished resuming gateway session")
bot.resuming = false
RecordBotEvent("Done resuming", "")
bot.createApplicationCommands(ctx) bot.createApplicationCommands(ctx)
case "MESSAGE_CREATE": case "MESSAGE_CREATE":
newMessage := *MessageFromMap(msg.Data, "") newMessage := *MessageFromMap(msg.Data, "")

View File

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

View File

@ -27,11 +27,6 @@ import (
"github.com/google/uuid" "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 { func HandleIncomingMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message, createSnippets bool) error {
deleted := false deleted := false
var err error var err error
@ -58,11 +53,6 @@ func HandleIncomingMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message
err = HandleInternedMessage(ctx, dbConn, msg, deleted, createSnippets) 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 return err
} }
@ -75,7 +65,17 @@ func CleanUpShowcase(ctx context.Context, dbConn db.ConnOrTx, msg *Message) (boo
return deleted, nil return deleted, nil
} }
if !messageShouldBeStored(msg) { 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 {
err := DeleteMessage(ctx, msg.ChannelID, msg.ID) err := DeleteMessage(ctx, msg.ChannelID, msg.ID)
if err != nil { if err != nil {
return deleted, oops.New(err, "failed to delete message") 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{ err = SendMessages(ctx, dbConn, MessageToSend{
ChannelID: channel.ID, ChannelID: channel.ID,
Req: CreateMessageRequest{ Req: CreateMessageRequest{
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.", Content: "Posts in #project-showcase are required to have either an image/video or a link. Discuss showcase content in #projects.",
}, },
}) })
if err != nil { if err != nil {
@ -146,70 +146,6 @@ func CleanUpLibrary(ctx context.Context, dbConn db.ConnOrTx, msg *Message) (bool
return deleted, nil 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 { func ShareToMatrix(ctx context.Context, msg *Message) error {
if msg.Flags&MessageFlagCrossposted == 0 { if msg.Flags&MessageFlagCrossposted == 0 {
return nil return nil
@ -302,7 +238,7 @@ func ShareToMatrix(ctx context.Context, msg *Message) error {
} }
func MaybeInternMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message) error { func MaybeInternMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message) error {
if messageShouldBeStored(msg) { if msg.ChannelID == config.Config.Discord.ShowcaseChannelID {
err := InternMessage(ctx, dbConn, msg) err := InternMessage(ctx, dbConn, msg)
if errors.Is(err, errNotEnoughInfo) { if errors.Is(err, errNotEnoughInfo) {
logging.ExtractLogger(ctx).Warn(). logging.ExtractLogger(ctx).Warn().
@ -354,8 +290,8 @@ func InternMessage(
_, err = dbConn.Exec(ctx, _, err = dbConn.Exec(ctx,
` `
INSERT INTO discord_message (id, channel_id, guild_id, url, user_id, sent_at, snippet_created, backfilled) INSERT INTO discord_message (id, channel_id, guild_id, url, user_id, sent_at, snippet_created)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8) VALUES ($1, $2, $3, $4, $5, $6, $7)
`, `,
msg.ID, msg.ID,
msg.ChannelID, msg.ChannelID,
@ -364,7 +300,6 @@ func InternMessage(
msg.Author.ID, msg.Author.ID,
msg.Time(), msg.Time(),
false, false,
msg.Backfilled,
) )
if err != nil { if err != nil {
return oops.New(err, "failed to save new discord message") return oops.New(err, "failed to save new discord message")
@ -406,7 +341,7 @@ func FetchInternedMessage(ctx context.Context, dbConn db.ConnOrTx, msgId string)
// 1. Saves/updates content // 1. Saves/updates content
// 2. Saves/updates snippet // 2. Saves/updates snippet
// 3. Deletes content/snippet // 3. Deletes content/snippet
func HandleInternedMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message, removeInternedMessage bool, createSnippet bool) error { func HandleInternedMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message, deleted bool, createSnippet bool) error {
tx, err := dbConn.Begin(ctx) tx, err := dbConn.Begin(ctx)
if err != nil { if err != nil {
return oops.New(err, "failed to start transaction") return oops.New(err, "failed to start transaction")
@ -417,11 +352,7 @@ func HandleInternedMessage(ctx context.Context, dbConn db.ConnOrTx, msg *Message
if err != nil && !errors.Is(err, db.NotFound) { if err != nil && !errors.Is(err, db.NotFound) {
return err return err
} else if err == nil { } else if err == nil {
if !removeInternedMessage { if !deleted {
removeInternedMessage = !messageShouldBeStored(msg)
}
if !removeInternedMessage {
err = SaveMessageContents(ctx, tx, interned, msg) err = SaveMessageContents(ctx, tx, interned, msg)
if err != nil { if err != nil {
return err return err
@ -854,7 +785,7 @@ func HandleSnippetForInternedMessage(ctx context.Context, dbConn db.ConnOrTx, in
if interned.MessageContent == nil { if interned.MessageContent == nil {
// NOTE(asaf): Can't have a snippet without content // NOTE(asaf): Can't have a snippet without content
// NOTE(asaf): Messages that only have an attachment also have a content struct with an empty content string // NOTE(asaf): Messages that only have an attachment also have blank content
// TODO(asaf): Do we need to delete existing snippets in this case??? Not entirely sure how to trigger this through discord // TODO(asaf): Do we need to delete existing snippets in this case??? Not entirely sure how to trigger this through discord
return nil return nil
} }
@ -904,42 +835,43 @@ func HandleSnippetForInternedMessage(ctx context.Context, dbConn db.ConnOrTx, in
if shouldCreate { if shouldCreate {
// Get an asset ID or URL to make a snippet from // Get an asset ID or URL to make a snippet from
assetId, url, err := getSnippetAssetOrUrl(ctx, tx, &interned.Message) assetId, url, err := getSnippetAssetOrUrl(ctx, tx, &interned.Message)
if assetId != nil || url != nil {
contentMarkdown := interned.MessageContent.LastContent
contentHTML := parsing.ParseMarkdown(contentMarkdown, parsing.DiscordMarkdown)
contentMarkdown := interned.MessageContent.LastContent _, err = tx.Exec(ctx,
contentHTML := parsing.ParseMarkdown(contentMarkdown, parsing.DiscordMarkdown) `
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, existingSnippet, err = FetchSnippetForMessage(ctx, tx, interned.Message.ID)
` if err != nil {
INSERT INTO snippet (url, "when", description, _description_html, asset_id, discord_message_id, owner_id) return oops.New(err, "failed to fetch newly-created snippet")
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")
}
existingSnippet, err = FetchSnippetForMessage(ctx, tx, interned.Message.ID) _, err = tx.Exec(ctx,
if err != nil { `
return oops.New(err, "failed to fetch newly-created snippet") UPDATE discord_message
} SET snippet_created = TRUE
WHERE id = $1
_, err = tx.Exec(ctx, `,
` interned.Message.ID,
UPDATE discord_message )
SET snippet_created = TRUE if err != nil {
WHERE id = $1 return oops.New(err, "failed to mark message as having snippet")
`, }
interned.Message.ID,
)
if err != nil {
return oops.New(err, "failed to mark message as having snippet")
} }
} }
} }
@ -1081,36 +1013,3 @@ func messageHasLinks(content string) bool {
return false 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,7 +275,6 @@ type Message struct {
Embeds []Embed `json:"embeds"` Embeds []Embed `json:"embeds"`
originalMap map[string]interface{} originalMap map[string]interface{}
Backfilled bool
} }
func (m *Message) JumpURL() string { func (m *Message) JumpURL() string {

View File

@ -62,17 +62,6 @@ var WRJ2023 = Jam{
UrlSlug: "2023", 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{ var HMS2022 = Event{
StartTime: time.Date(2022, 11, 16, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))), 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"))), EndTime: time.Date(2022, 11, 18, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
@ -88,17 +77,7 @@ var HMBoston2023 = Event{
EndTime: time.Date(2023, 8, 4, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))), EndTime: time.Date(2023, 8, 4, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
} }
var HMS2024 = Event{ var AllJams = []Jam{WRJ2021, WRJ2022, VJ2023, WRJ2023}
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 { func CurrentJam() *Jam {
now := time.Now() now := time.Now()

View File

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

View File

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

View File

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

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(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

@ -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(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

@ -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(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

@ -1,23 +0,0 @@
.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

@ -1,70 +0,0 @@
.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

@ -1,186 +0,0 @@
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;
}
}

View File

@ -1,834 +0,0 @@
// 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

@ -1,130 +0,0 @@
// 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,269 +0,0 @@
.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

@ -1,360 +0,0 @@
.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

@ -1,150 +0,0 @@
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

@ -1,206 +0,0 @@
.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

@ -1,24 +0,0 @@
.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

@ -1,21 +0,0 @@
.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

@ -1,45 +0,0 @@
.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

@ -1,52 +0,0 @@
.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

@ -1,15 +0,0 @@
.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

@ -1,272 +0,0 @@
.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

@ -1,7 +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,51 +0,0 @@
.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

@ -1,53 +0,0 @@
.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

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

View File

@ -0,0 +1,176 @@
.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;
}
}
}

740
src/rawdata/scss/core.css Normal file
View File

@ -0,0 +1,740 @@
/* 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

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

95
src/rawdata/scss/form.css Normal file
View File

@ -0,0 +1,95 @@
.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);
}
}
}

173
src/rawdata/scss/forum.css Normal file
View File

@ -0,0 +1,173 @@
.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);
}
}

179
src/rawdata/scss/header.css Normal file
View File

@ -0,0 +1,179 @@
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

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

View File

@ -0,0 +1,28 @@
.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

@ -0,0 +1,15 @@
.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

@ -0,0 +1,87 @@
.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

@ -0,0 +1,5 @@
.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,19 @@
@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 'episodes';
@import 'education'; @import 'education';
@import 'forms'; @import 'forms';
@import 'forum'; // @import 'forum';
@import 'header'; @import 'header';
@import 'icons'; @import 'icons';
@import 'irc'; @import 'irc';
@import 'landing'; // @import 'landing';
@import 'library'; @import 'library';
@import 'profile'; @import 'profile';
@import 'projects'; @import 'projects';
@import 'showcase'; @import 'showcase';
@import 'streams'; @import 'streams';
@import 'timeline'; // @import 'timeline';
@import 'carousel'; @import 'carousel';
@import 'notices'; @import 'notices';
@import 'progress_bar'; @import 'progress_bar';

View File

@ -0,0 +1,39 @@
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

@ -0,0 +1,140 @@
/* 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)';
}

3
src/rawdata/scss/tachyons.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

@ -0,0 +1,43 @@
.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);
}
}
}

105
src/rawdata/scss/vars.css Normal file
View File

@ -0,0 +1,105 @@
/*
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

@ -0,0 +1 @@
<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>

After

Width:  |  Height:  |  Size: 171 B

View File

@ -1,11 +1 @@
<?xml version="1.0" encoding="iso-8859-1"?> <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>
<!-- 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: 776 B

After

Width:  |  Height:  |  Size: 257 B

View File

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

View File

@ -2,7 +2,7 @@
{{ define "content" }} {{ define "content" }}
<div class="post-content"> <div class="post-content">
<div class="mw7 ph3 ph0-ns margin-center"> <div class="mw7 ph3 ph0-ns m--center">
<h3> <h3>
We're trying to put programming back on the right track. We're trying to put programming back on the right track.
</h3> </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. 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> -->
<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. <a href="{{ .RolesUrl }}"><b>Join us.</b></a> 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>
</p> </p>
</div> </div>
<div class="ph3 ph0-ns mt4"> <div class="ph3 ph0-ns mt4">
<h2>Leadership</h2> <h2>Leadership</h2>
<div class="flex flex-column flex-row-ns g3"> <div class="flex flex-column flex-row-ns g3">
<div class="flex-fair pa3 bg--dim br3"> <div class="flex-fair pa3 bg3 br3">
<h3>Ben Visness</h3> <h3>Ben Visness</h3>
<h4>Lead<!--, Foundation President--></h4> <h4>Lead<!--, Foundation President--></h4>
<p> <p>
@ -40,7 +40,7 @@
</p> --> </p> -->
</div> </div>
<div class="flex-fair pa3 bg--dim br3"> <div class="flex-fair pa3 bg3 br3">
<h3>Asaf Gartner</h3> <h3>Asaf Gartner</h3>
<h4>Admin<!--, Foundation Secretary--></h4> <h4>Admin<!--, Foundation Secretary--></h4>
<p> <p>
@ -51,7 +51,7 @@
</p> </p>
</div> </div>
<div class="flex-fair pa3 bg--dim br3"> <div class="flex-fair pa3 bg3 br3">
<h3>Colin Davidson</h3> <h3>Colin Davidson</h3>
<h4>Admin<!--, Foundation Treasurer--></h4> <h4>Admin<!--, Foundation Treasurer--></h4>
<p> <p>
@ -67,34 +67,11 @@
<div class="ph3 ph0-ns mt4"> <div class="ph3 ph0-ns mt4">
<h2>Key Contributors</h2> <h2>Key Contributors</h2>
<div class="flex flex-column flex-row-ns g3"> <div class="flex flex-column flex-row-ns g3">
<div class="flex-fair pa3 bg--dim br3"> <div class="flex-fair pa3 bg3 br3">
<h3>Martin Fouilleul</h3> <h3>Martin Fouilleul</h3>
<h4>[Secret Project] Lead</h4> <h4>Orca 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>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>Right now he is working behind the scenes on a secret Handmade Network project, to be revealed at a later date.</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>
</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> </div>
</div> </div>

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
{{ template "base.html" . }} {{ template "base.html" . }}
{{ define "content" }} {{ define "content" }}
<div class="center-layout mw7 mv3 ph3 ph0-ns post-content"> <div class="m--center mw7 mv3 ph3 ph0-ns post-content">
<h1>Handmade Guide to Community Interaction</h1> <h1>Handmade Guide to Community Interaction</h1>
<h2>Our Philosophy</h2> <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> <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" }} {{ define "content" }}
{{ $bannerclass := "overflow-hidden br2 mt2 h4 h5-ns cover bg-center" }} {{ $bannerclass := "overflow-hidden br2 mt2 h4 h5-ns cover bg-center" }}
<div class="center-layout mw7 ph3 ph0-ns post-content flex flex-column g3"> <div class="m--center mw7 ph3 ph0-ns post-content flex flex-column g3">
<div class="fronke"> <div class="fronke">
<a href="https://handmadehero.org/conference"> <a href="https://handmadehero.org/conference">
<h2>HandmadeCon</h2> <h2>HandmadeCon</h2>

View File

@ -1,22 +0,0 @@
{{ 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,28 +29,6 @@
{{ if .CanEditPostTitle }} {{ if .CanEditPostTitle }}
<input id="title" class="b w-100 mb1" name="title" type="text" placeholder="Post title..." value="{{ .PostTitle }}" /> <input id="title" class="b w-100 mb1" name="title" type="text" placeholder="Post title..." value="{{ .PostTitle }}" />
{{ end }} {{ 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> <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"> <div class="flex justify-end items-center mt2">
@ -82,7 +60,7 @@
{{ if .ShowEduOptions }} {{ if .ShowEduOptions }}
{{/* Hope you have a .Article field! */}} {{/* Hope you have a .Article field! */}}
<div class="bg--dim br3 pa3 mt3"> <div class="bg3 br3 pa3 mt3">
<h4>Education Options</h4> <h4>Education Options</h4>
<div class="mb2"> <div class="mb2">
<label for="slug">Slug:</label> <label for="slug">Slug:</label>
@ -108,7 +86,7 @@
</div> </div>
{{ end }} {{ end }}
</form> </form>
<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-container" class="post post-preview mathjax flex-fair-ns overflow-auto mv3 mv0-ns ml3-ns pa3 br3 bg3 {{ .PreviewClass }}">
<div id="preview" class="post-content"></div> <div id="preview" class="post-content"></div>
</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 */}} <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" . }} {{ template "base.html" . }}
{{ define "content" }} {{ define "content" }}
<div class="mw7 margin-center"> <div class="mw7 m--center">
<h3 class="mb3">Are you sure you want to delete this article?</h3> <h3 class="mb3">Are you sure you want to delete this article?</h3>
<div class="bg--dim pa3 br3 tl post-content"> <div class="bg3 pa3 br3 tl post-content">
<h1>{{ .Article.Title }}</h1> <h1>{{ .Article.Title }}</h1>
{{ .Article.Content }} {{ .Article.Content }}
</div> </div>

View File

@ -7,7 +7,7 @@
<p>Dive into one of these topics and start learning.</p> <p>Dive into one of these topics and start learning.</p>
<div class="flex flex-column flex-row-ns g3 mt3 mb4"> <div class="flex flex-column flex-row-ns g3 mt3 mb4">
<a href="#compilers" class="edu-topic db flex-fair-ns bg--dim br3 overflow-hidden c--inherit flex flex-column"> <a href="#compilers" class="edu-topic db flex-fair-ns bg3 br3 overflow-hidden c--inherit flex flex-column">
<img src="{{ static "education/compilers.jpg" }}"> <img src="{{ static "education/compilers.jpg" }}">
<div class="pa3"> <div class="pa3">
<h2>Compilers</h2> <h2>Compilers</h2>
@ -16,7 +16,7 @@
</div> </div>
</div> </div>
</a> </a>
<a href="#networking" class="edu-topic db flex-fair-ns bg--dim br3 overflow-hidden c--inherit flex flex-column"> <a href="#networking" class="edu-topic db flex-fair-ns bg3 br3 overflow-hidden c--inherit flex flex-column">
<img src="{{ static "education/networking.jpg" }}"> <img src="{{ static "education/networking.jpg" }}">
<div class="pa3"> <div class="pa3">
<h2>Networking</h2> <h2>Networking</h2>
@ -25,7 +25,7 @@
</div> </div>
</div> </div>
</a> </a>
<a href="#time" class="edu-topic db flex-fair-ns bg--dim br3 overflow-hidden c--inherit flex flex-column"> <a href="#time" class="edu-topic db flex-fair-ns bg3 br3 overflow-hidden c--inherit flex flex-column">
<img src="{{ static "education/time.jpg" }}"> <img src="{{ static "education/time.jpg" }}">
<div class="pa3"> <div class="pa3">
<h2>Time</h2> <h2>Time</h2>
@ -39,17 +39,17 @@
<h2>What makes us different?</h2> <h2>What makes us different?</h2>
<div class="flex flex-column flex-row-ns g3 mb4"> <div class="flex flex-column flex-row-ns g3 mb4">
<div class="flex-fair bg--dim pa3 br2"> <div class="flex-fair bg3 pa3 br2">
<h3>Real material.</h3> <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. 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>
<div class="flex-fair bg--dim pa3 br3"> <div class="flex-fair bg3 pa3 br3">
<h3>For any skill level.</h3> <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. 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>
<div class="flex-fair bg--dim pa3 br3"> <div class="flex-fair bg3 pa3 br3">
<h3>Designed for programmers.</h3> <h3>Designed for programmers.</h3>
We're not here to teach you how to program. We're here to teach you a specific topic. 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> <h2>All Topics</h2>
{{ range .Courses }} {{ range .Courses }}
<div id="{{ .Slug }}" class="edu-course mv3 bg--dim pa3 br3"> <div id="{{ .Slug }}" class="edu-course mv3 bg3 pa3 br3">
<h3>{{ .Name }}</h3> <h3>{{ .Name }}</h3>
<div class="overflow-hidden"> <div class="overflow-hidden">
<div class="edu-articles ml3 pl3"> <div class="edu-articles ml3 pl3">

View File

@ -1,39 +1,6 @@
{{ template "base.html" . }} {{ template "base.html" . }}
{{ define "extrahead" }} {{ 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 "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 (strjoin "annotations/cinera__" .Project.Subdomain ".css") }}">
<link rel="stylesheet" type="text/css" href="{{ static "annotations/cinera_topics.css" }}"> <link rel="stylesheet" type="text/css" href="{{ static "annotations/cinera_topics.css" }}">

View File

@ -18,7 +18,7 @@
</div> </div>
{{ range .Posts }} {{ range .Posts }}
<div class="post-list-item flex items-center ph3 pv2 {{ if .Unread }}unread{{ else }}read{{ end }} {{ .Classes }}"> <div class="post-list-item flex items-center ph3 pv2 {{ if .Unread }}unread{{ else }}read{{ end }} {{ .Classes }}">
<img class="avatar-icon mr2" src="{{ .User.AvatarUrl }}"> <img class="avatar mr2" src="{{ .User.AvatarUrl }}">
<div class="flex-grow-1 overflow-hidden"> <div class="flex-grow-1 overflow-hidden">
{{ template "breadcrumbs.html" .Breadcrumbs }} {{ template "breadcrumbs.html" .Breadcrumbs }}
<div class="title nowrap truncate"><a href="{{ .Url }}" title="{{ .Preview }}">{{ if .PostTypePrefix }}{{ .PostTypePrefix }}: {{ end }}{{ .Title }}</a></div> <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"> <div class="mt3 mw7">
{{ range .Fishbowls }} {{ range .Fishbowls }}
<div class="br2 bg--dim pa3 mb2"> <div class="br2 bg3 pa3 mb2">
{{ if .Valid }} {{ if .Valid }}
<a href="{{ .Url }}"><h3 class="f4 ma0">{{ .Fishbowl.Title }}</h3></a> <a href="{{ .Url }}"><h3 class="f4 ma0">{{ .Fishbowl.Title }}</h3></a>
{{ else }} {{ else }}

View File

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

View File

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

View File

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

View File

@ -6,29 +6,26 @@
{{ $footerClasses := "ma0 pa0 dib-ns" }} {{ $footerClasses := "ma0 pa0 dib-ns" }}
<li class="{{ $footerClasses }}"> <li class="{{ $footerClasses }}">
<a href="{{ .Footer.HomepageUrl }}">Main Page</a> <a href="{{ .Footer.HomepageUrl }}">Main Page</a>
<span class="c--dimmer dn di-ns"> / </span>
</li> </li>
<li class="{{ $footerClasses }}"> <li class="{{ $footerClasses }}">
<a href="{{ .Footer.ManifestoUrl }}">Manifesto</a> <a href="{{ .Footer.ManifestoUrl }}">Manifesto</a>
<span class="c--dimmer dn di-ns"> / </span>
</li> </li>
<li class="{{ $footerClasses }}"> <li class="{{ $footerClasses }}">
<a href="{{ .Footer.AboutUrl }}">About</a> <a href="{{ .Footer.AboutUrl }}">About</a>
</li> <span class="c--dimmer dn di-ns"> / </span>
<li class="{{ $footerClasses }}">
<a href="{{ .Footer.RolesUrl }}">Roles</a>
</li> </li>
<li class="{{ $footerClasses }}"> <li class="{{ $footerClasses }}">
<a href="{{ .Footer.ProjectIndexUrl }}">Projects</a> <a href="{{ .Footer.ProjectIndexUrl }}">Projects</a>
<span class="c--dimmer dn di-ns"> / </span>
</li> </li>
<li class="{{ $footerClasses }}"> <li class="{{ $footerClasses }}">
<a href="{{ .Footer.CommunicationGuidelinesUrl }}">Communication Guidelines</a> <a href="{{ .Footer.CommunicationGuidelinesUrl }}">Communication Guidelines</a>
<span class="c--dimmer dn di-ns"> / </span>
</li> </li>
<li class="{{ $footerClasses }}"> <li class="{{ $footerClasses }}">
<a href="{{ .Footer.ContactUrl }}">Contact</a> <a href="{{ .Footer.ContactUrl }}">Contact</a>
</li> </li>
</ul> </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> </footer>

View File

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

View File

@ -0,0 +1,103 @@
<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,43 +1,21 @@
<header id="site-header" class="mb3 bb bw1 b--theme-dark"> <header id="site-header" class="old mb3 bb bw1 b--theme-dark">
<a href="#content-start" class="sr-focusable" id="content-jump">Jump to Content</a> <a href="#content-start" class="sr sr-focusable" id="content-jump">Jump to Content</a>
<div class="user-options flex justify-center justify-end-ns relative"> <div class="flex justify-center justify-end-ns relative">
{{ if .User }} {{ if .User }}
{{ if .User.IsStaff }} {{ if .User.IsStaff }}
<a class="admin-panel pa2" href="{{ .Header.AdminUrl }}"><span class="icon-settings"> Admin</span></a> <a class="pa2" href="{{ .Header.AdminUrl }}"><span class="icon-settings"> Admin</span></a>
{{ end }} {{ end }}
<div> <div>
<a class="dib pv2 pl2" href="{{ .Header.UserProfileUrl }}">{{ .User.Username }}</a> <a class="dib pv2 pl2" href="{{ .Header.UserProfileUrl }}">{{ .User.Username }}</a>
<a class="dib pv2 pr2" href="{{ .Header.UserSettingsUrl }}">(settings)</a> <a class="dib pv2 pr2" href="{{ .Header.UserSettingsUrl }}">(settings)</a>
</div> </div>
<a class="logout pa2" href="{{ .Header.LogoutActionUrl }}"><span class="icon-logout"></span> Log Out</a> <a class="pa2" href="{{ .Header.LogoutActionUrl }}"><span class="icon-logout"></span> Log Out</a>
{{ else }} {{ else }}
<a class="register pa2" id="register-link" href="{{ .Header.RegisterUrl }}">Register</a> <a class="pa2" id="register-link" href="{{ .Header.RegisterUrl }}">Register</a>
<a class="login pa2" id="login-link" href="{{ .LoginPageUrl }}">Log in</a> <a class="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 }} {{ end }}
</div> </div>
<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 justify-between w-100 {{ if .IsProjectPage }}project{{ end }}">
<div class="flex flex-column flex-row-ns items-center w-100"> <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" }} {{ $itemsClass := "items self-stretch flex items-center justify-center justify-start-ns ml2-ns ml3-l" }}
{{ if .Header.Project }} {{ if .Header.Project }}
@ -79,30 +57,30 @@
</a> </a>
<div class="{{ $itemsClass }}"> <div class="{{ $itemsClass }}">
<div class="root-item"> <div class="root-item">
<a href="{{ .Header.ProjectIndexUrl }}">Projects</a> <a class="pa2 ph3-l" href="{{ .Header.ProjectIndexUrl }}">Projects</a>
</div> </div>
<div class="root-item"> <div class="root-item">
<a aria-expanded="false" aria-controls="events-submenu" class="menu-dropdown-js" href="#"> <a class="pa2 ph3-l" aria-expanded="false" aria-controls="events-submenu" class="menu-dropdown-js" href="#">
Events <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div> Events <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div>
</a> </a>
<div class="submenu b--theme-dark" id="events-submenu"> <div class="submenu bw1 b--theme-dark bg--main" id="events-submenu">
<a href="{{ .Header.JamsUrl }}">Jams</a> <a class="pa2 ph3-l" href="{{ .Header.JamsUrl }}">Jams</a>
<a href="{{ .Header.ConferencesUrl }}">Conferences</a> <a class="pa2 ph3-l" href="{{ .Header.ConferencesUrl }}">Conferences</a>
<a href="{{ .Header.FishbowlUrl }}">Fishbowls</a> <a class="pa2 ph3-l" href="{{ .Header.FishbowlUrl }}">Fishbowls</a>
<a href="{{ .Header.PodcastUrl }}">Podcast</a> <a class="pa2 ph3-l" href="{{ .Header.PodcastUrl }}">Podcast</a>
<a href="https://guide.handmade-seattle.com/s" target="_blank">Handmade Dev Show</a> <a class="pa2 ph3-l" href="https://guide.handmade-seattle.com/s" target="_blank">Handmade Dev Show</a>
{{/*<a href="{{ .Header.CalendarUrl }}">Calendar</a>*/}} <a class="pa2 ph3-l" href="{{ .Header.CalendarUrl }}">Calendar</a>
</div> </div>
</div> </div>
<div class="root-item"> <div class="root-item">
<a href="{{ .Header.ForumsUrl }}">Forums</a> <a class="pa2 ph3-l" href="{{ .Header.ForumsUrl }}">Forums</a>
</div> </div>
<div class="root-item"> <div class="root-item">
<a aria-expanded="false" aria-controls="resource-submenu" class="menu-dropdown-js" href="#"> <a class="pa2 ph3-l" aria-expanded="false" aria-controls="resource-submenu" class="menu-dropdown-js" href="#">
Resources <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div> Resources <div class="dib svgicon ml1">{{ svg "chevron-down-thick" }}</div>
</a> </a>
<div class="submenu b--theme-dark" id="resource-submenu"> <div class="submenu bw1 b--theme-dark bg--main" id="resource-submenu">
<a href="{{ .Header.EducationUrl }}">Education</a> <a class="pa2 ph3-l" href="{{ .Header.EducationUrl }}">Education</a>
</div> </div>
</div> </div>
</div> </div>
@ -171,32 +149,5 @@
}); });
} }
} }
// 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> </script>

View File

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

View File

@ -1,23 +0,0 @@
<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

@ -1,19 +0,0 @@
<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