Added WRJ2023

This commit is contained in:
Asaf Gartner 2023-09-01 17:35:40 +03:00
parent 74f438afad
commit fdc7582701
19 changed files with 1069 additions and 23 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,36 @@
<?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 475 283" 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-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
<g id="Handlebars" transform="matrix(1,0,0,1,-100,-167)">
<path d="M404,196C407.778,166.333 384.333,169 364,184" style="fill:none;stroke:white;stroke-width:9px;"/>
</g>
<g id="Seat">
</g>
<path d="M120,42L160,42C177.006,42.052 186.77,48.289 191,58" style="fill:none;stroke:white;stroke-width:9px;"/>
<g id="Frame" transform="matrix(1,0,0,1,-100,-167)">
<path d="M314,390L445,274" style="fill:none;stroke:white;stroke-width:9px;"/>
<path d="M500,375L404,196" style="fill:none;stroke:white;stroke-width:9px;stroke-linecap:butt;"/>
<path d="M266,259L437,259" style="fill:none;stroke:white;stroke-width:9px;"/>
<path d="M314,390L248,209" style="fill:none;stroke:white;stroke-width:9px;stroke-linecap:butt;"/>
<g transform="matrix(0.815396,-0.0212494,-0.0212494,0.997554,66.2531,7.62625)">
<path d="M143.044,371.322L314,390" style="fill:none;stroke:white;stroke-width:10.11px;"/>
</g>
<path d="M175,375L266,259" style="fill:none;stroke:white;stroke-width:9px;"/>
</g>
<g id="Wheel" transform="matrix(1,0,0,1,-100,-167)">
<g transform="matrix(0.980392,0,0,0.980392,-46.0784,31.3725)">
<path d="M302,293.019C302,282.522 293.478,274 282.981,274L168.019,274C157.522,274 149,282.522 149,293.019L149,407.981C149,418.478 157.522,427 168.019,427L282.981,427C293.478,427 302,418.478 302,407.981L302,293.019ZM292.82,293.019L292.82,407.981C292.82,413.411 288.411,417.82 282.981,417.82C282.981,417.82 168.019,417.82 168.019,417.82C162.589,417.82 158.18,413.411 158.18,407.981L158.18,293.019C158.18,287.589 162.589,283.18 168.019,283.18L282.981,283.18C288.411,283.18 292.82,287.589 292.82,293.019Z" style="fill:white;"/>
</g>
<g transform="matrix(1,0,0,1,-50,26)">
<path d="M237,343C237,339.689 234.311,337 231,337L219,337C215.689,337 213,339.689 213,343L213,355C213,358.311 215.689,361 219,361L231,361C234.311,361 237,358.311 237,355L237,343Z" style="fill:white;"/>
</g>
</g>
<g id="Wheel1" serif:id="Wheel" transform="matrix(1,0,0,1,225,-167)">
<g transform="matrix(0.980392,0,0,0.980392,-46.0784,31.3725)">
<path d="M302,293.019C302,282.522 293.478,274 282.981,274L168.019,274C157.522,274 149,282.522 149,293.019L149,407.981C149,418.478 157.522,427 168.019,427L282.981,427C293.478,427 302,418.478 302,407.981L302,293.019ZM292.82,293.019L292.82,407.981C292.82,413.411 288.411,417.82 282.981,417.82C282.981,417.82 168.019,417.82 168.019,417.82C162.589,417.82 158.18,413.411 158.18,407.981L158.18,293.019C158.18,287.589 162.589,283.18 168.019,283.18L282.981,283.18C288.411,283.18 292.82,287.589 292.82,293.019Z" style="fill:white;"/>
</g>
<g transform="matrix(1,0,0,1,-50,26)">
<path d="M237,343C237,339.689 234.311,337 231,337L219,337C215.689,337 213,339.689 213,343L213,355C213,358.311 215.689,361 219,361L231,361C234.311,361 237,358.311 237,355L237,343Z" style="fill:white;"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@ -48,6 +48,15 @@ var VJ2023 = Jam{
Slug: "VJ2023",
}
var WRJ2023 = Jam{
Event: Event{
StartTime: time.Date(2023, 9, 25, 10, 0, 0, 0, utils.Must1(time.LoadLocation("Europe/London"))),
EndTime: time.Date(2023, 10, 1, 20, 0, 0, 0, utils.Must1(time.LoadLocation("Europe/London"))),
},
Name: "Wheel Reinvention Jam 2023",
Slug: "WRJ2023",
}
var HMS2022 = Event{
StartTime: time.Date(2022, 11, 16, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
EndTime: time.Date(2022, 11, 18, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
@ -63,7 +72,7 @@ var HMBoston2023 = Event{
EndTime: time.Date(2023, 8, 4, 0, 0, 0, 0, utils.Must1(time.LoadLocation("America/Los_Angeles"))),
}
var AllJams = []Jam{WRJ2021, WRJ2022, VJ2023}
var AllJams = []Jam{WRJ2021, WRJ2022, VJ2023, WRJ2023}
func CurrentJam() *Jam {
now := time.Now()

View File

@ -84,6 +84,27 @@ func BuildJamIndex2022() string {
return Url("/jam/2022", nil)
}
var RegexJamFeed2022 = regexp.MustCompile("^/jam/2022/feed$")
func BuildJamFeed2022() string {
defer CatchPanic()
return Url("/jam/2022/feed", nil)
}
var RegexJamIndex2023 = regexp.MustCompile("^/jam/2023$")
func BuildJamIndex2023() string {
defer CatchPanic()
return Url("/jam/2023", nil)
}
var RegexJamFeed2023 = regexp.MustCompile("^/jam/2023/feed$")
func BuildJamFeed2023() string {
defer CatchPanic()
return Url("/jam/2023/feed", nil)
}
var RegexJamIndex2023_Visibility = regexp.MustCompile("^/jam/visibility-2023$")
func BuildJamIndex2023_Visibility() string {
@ -105,13 +126,6 @@ func BuildJamRecap2023_Visibility() string {
return Url("/jam/visibility-2023/recap", nil)
}
var RegexJamFeed2022 = regexp.MustCompile("^/jam/2022/feed$")
func BuildJamFeed2022() string {
defer CatchPanic()
return Url("/jam/2022/feed", nil)
}
var RegexTimeMachine = regexp.MustCompile("^/timemachine$")
func BuildTimeMachine() string {

View File

@ -0,0 +1,47 @@
{{ template "wheeljam_2023_base.html" . }}
{{ define "content" }}
<style>
:root {
--theme-color: white;
--theme-color-dimmer: rgba(255, 255, 255, 0.1);
}
#title {
margin-top: 0;
}
</style>
<div id="top-container" class="flex flex-column items-center ph3">
<h1 id="title">Wheel Reinvention Jam</h1>
<h2 id="dates">September 25 - October 1, 2023</h2>
<div id="tagline" class="center">
A one-week jam to change the status quo.
</div>
</div>
<div class="section bg-black-20 pt4 pb3 pb4-ns">
<div class="mw8 margin-center ph3 ph4-l flex flex-column flex-row-ns g3">
<div class="flex-grow-1 overflow-hidden">
{{ if eq .DaysUntilEnd 0 }}
<h3 class="mt0 mb3">Project updates</h3>
{{ else }}
<h3 class="mt0 mb3">Recent updates</h3>
{{ end }}
<div class="timeline">
{{ range .TimelineItems }}
{{ template "timeline_item.html" . }}
{{ end }}
</div>
</div>
<div class="w-40-ns flex-shrink-0">
<h3 class="mt0 mb3">Projects</h3>
<div class="projects flex flex-column g3 back-to-normal">
{{ range .JamProjects }}
{{ template "project_card.html" projectcarddata . "" }}
{{ end }}
</div>
</div>
</div>
</div>
{{ end }}

View File

@ -0,0 +1,415 @@
{{ template "wheeljam_2023_base.html" . }}
{{ define "content" }}
{{ $discordInviteURL := "https://discord.gg/hmn" }}
<style>
.projects {
display: grid;
grid-template-columns: 1fr;
}
@media screen and (min-width: 30em) {
/* not small styles */
.projects {
grid-template-columns: 1fr 1fr;
}
}
</style>
<div id="top-container" class="flex flex-column items-center ph3">
<img id="logo" src="{{ static "wheeljam2023/logo.svg" }}">
<h1 id="title">Wheel Reinvention Jam</h1>
<h2 id="dates">September 25 - October 1, 2023</h2>
<div id="tagline" class="center">
A one-week jam to change the status quo.
{{ if gt .DaysUntilEnd 0 }}
{{ if eq .DaysUntilStart 0 }}
<b>Happening now.</b>
{{ else if eq .DaysUntilStart 1 }}
<b>Starting tomorrow.</b>
{{ else }}
<b>In {{ .DaysUntilStart }} days.</b>
{{ end }}
{{ end }}
</div>
<div class="actions flex justify-center">
{{ if gt .DaysUntilStart 0 }}
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns" target="_blank" href="https://github.com/HandmadeNetwork/wishlist/discussions">Find a project</a>
{{ else if gt .DaysUntilEnd 0 }}
{{ if .SubmittedProjectUrl }}
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns" target="_blank" href="{{ .SubmittedProjectUrl }}">Share your progress</a>
{{ else }}
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ .ProjectSubmissionUrl }}">Create your project</a>
{{ end }}
{{ else }}
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" href="{{ .ShowcaseFeedUrl }}">See the results</a>
{{ end }}
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ $discordInviteURL }}">Join the Discord</a>
</div>
</div>
<div class="section mw8 margin-center ph3 ph4-l mv4">
<p>
The <strong>Wheel Reinvention Jam</strong> is a one-week-long jam where we turn a fresh eye to "solved problems".
</p>
<p>
The tools we use every day are broken. Software is slow, unreliable, and bloated with thoughtless features. It <a href="https://twitter.com/ryanjfleury/status/1537278864111464448" target="_blank">disrespects the user</a> and forces settings that <a href="https://twitter.com/ra/status/1151988912845234178" target="_blank">no one wants</a>. And yet, people defend the status quo, claiming that what we have is fine, and that trying to change software is "reinventing the wheel".
</p>
<p>
Screw that. Progress is only made by inventing new things. It's not "reinventing" to break new ground. Nor is it "reinventing" to take a broken thing and design something better.
</p>
<p>
This is your chance to reinvent something.
</p>
</div>
{{ if eq .DaysUntilEnd 0 }}
<div class="section bg-black-20 pv4 overflow-hidden">
<div class="mw8 margin-center ph3 ph4-l">
<h2>Submitted projects</h2>
<div class="mt3 projects g3 back-to-normal">
{{ range .JamProjects }}
{{ template "project_card.html" projectcarddata . "" }}
{{ end }}
</div>
<div class="actions flex justify-center">
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" href="{{ .ShowcaseFeedUrl }}">See all updates</a>
</div>
</div>
</div>
{{ else if and (eq .DaysUntilStart 0) (not (eq .ShowcaseJson "[]")) }}
<div id="showcase-outer-container" class="bg-black-20 pt4 pb3 pb4-ns">
<div class="section mw8 margin-center ph3 ph4-l">
{{ if gt .DaysUntilEnd 0 }}
<h2>Recent updates</h2>
<p>
These screenshots and videos were shared by jam participants in <b>#project-showcase</b> on our <a href="{{ $discordInviteURL }}" target="_blank">Discord</a>. Join us and share what you're working on! <a class="b" href="{{ .ShowcaseFeedUrl }}">See all ➜</a>
</p>
{{ else }}
<h2>Community showcase</h2>
<p>
These screenshots and videos were shared by jam participants in <b>#project-showcase</b> on our <a href="https://discord.gg/hmn" target="_blank">Discord</a> during the jam. Join us and chat about your favorites!
</p>
{{ end }}
<div id="showcase-container" class="mw8 center-layout mh2 mh0-ns"></div>
<div class="actions flex justify-center">
<a class="ba b--white br2 pv2 pv3-ns ph3 ph4-ns ml3" target="_blank" href="{{ .ShowcaseFeedUrl }}">See all</a>
</div>
</div>
</div>
{{ template "showcase_templates.html" }}
<!-- Copy-pasted and mangled from showcase.html -->
<script>
const ROW_HEIGHT = 300;
const ITEM_SPACING = 4;
const showcaseItems = JSON.parse("{{ .ShowcaseJson }}");
const addThumbnailFuncs = new Array(showcaseItems.length);
const showcaseOuterContainer = document.querySelector('#showcase-outer-container');
let showcaseContainer = document.querySelector('#showcase-container');
// showcaseOuterContainer.classList.toggle('dn', showcaseItems.length === 0);
const itemElements = []; // array of arrays
for (let i = 0; i < showcaseItems.length; i++) {
const item = showcaseItems[i];
const [itemEl, addThumbnail] = makeShowcaseItem(item);
itemEl.container.setAttribute('data-index', i);
itemEl.container.setAttribute('data-date', item.date);
addThumbnailFuncs[i] = addThumbnail;
itemElements.push(itemEl.container);
}
function layout() {
const width = showcaseContainer.getBoundingClientRect().width;
showcaseContainer = emptyElement(showcaseContainer);
function addRow(itemEls, rowWidth, container) {
const totalSpacing = ITEM_SPACING * (itemEls.length - 1);
const scaleFactor = (width / Math.max(rowWidth, width));
const row = document.createElement('div');
row.classList.add('flex');
row.classList.toggle('justify-between', rowWidth >= width);
row.style.marginBottom = `${ITEM_SPACING}px`;
for (const itemEl of itemEls) {
const index = parseInt(itemEl.getAttribute('data-index'), 10);
const item = showcaseItems[index];
const aspect = item.width / item.height;
const baseWidth = (aspect * ROW_HEIGHT) * scaleFactor;
const actualWidth = baseWidth - (totalSpacing / itemEls.length);
itemEl.style.width = `${actualWidth}px`;
itemEl.style.height = `${scaleFactor * ROW_HEIGHT}px`;
itemEl.style.marginRight = `${ITEM_SPACING}px`;
row.appendChild(itemEl);
}
container.appendChild(row);
}
let rowItemEls = [];
let rowWidth = 0;
let numRows = 0;
for (const itemEl of itemElements) {
const index = parseInt(itemEl.getAttribute('data-index'), 10);
const item = showcaseItems[index];
const aspect = item.width / item.height;
rowWidth += aspect * ROW_HEIGHT;
rowItemEls.push(itemEl);
if (rowWidth > width) {
addRow(rowItemEls, rowWidth, showcaseContainer);
numRows += 1;
if (numRows == 3) {
return;
}
rowItemEls = [];
rowWidth = 0;
}
}
addRow(rowItemEls, rowWidth, showcaseContainer);
}
function loadImages() {
const items = showcaseContainer.querySelectorAll('.showcase-item');
for (const item of items) {
const i = parseInt(item.getAttribute('data-index'), 10);
addThumbnailFuncs[i]();
}
}
layout();
layout(); // scrollbars are fun!!
loadImages();
window.addEventListener('resize', () => {
layout();
});
</script>
{{ else }}
<div class="section bg-black-20 pv4 overflow-hidden">
<div class="mw8 margin-center ph3 ph4-l">
<h2>Last year's entries</h2>
<p>
We had many incredible entries last year. Here are a few of our favorites:
</p>
<div class="carousel-container">
<div class="carousel bg-white-10 br3 pa3 pa4-ns">
<div class="carousel-item active">
<img class="br2" src="{{ static "wheeljam2023/scroll.png" }}">
<h3>Scroll</h3>
<p>
Scroll is an experimental new typesetting format and editor. The document structure is inherently non-textual; in fact, even words within paragraphs are individual nodes that can easily be selected and moved as a whole. It's a great proof-of-concept of what "word processors" could be—and it even has a PDF export.
</p>
<a class="b db" href="https://handmade.network/forums/jam/t/8116-jam_submition_-_scroll%252C_a_experiment_in_a_non_text_typesetting_file_format">Full Submission ➜</a>
<a class="b db" href="https://youtu.be/1RjU5XJqysc?t=1083" target="_blank">Recap Interview ➜</a>
</div>
<div class="carousel-item">
<img class="br2" src="{{ static "wheeljam2023/nearmanager.gif" }}">
<h3>Near</h3>
<p>
Near (or Near Manager) is an experimental file viewer that breaks away from a plain hierarchy. By allowing you to flatten folder hierarchies, create custom groups, and reorder your files, Near allows you to tame any complex file structure and view it in a way that works for you.
</p>
<a class="b db" href="https://handmade.network/forums/jam/t/8120-jam_submission_-_near%252C_a_file_explorer_with_interesting_ideas">Full Submission ➜</a>
<a class="b db" href="https://youtu.be/1RjU5XJqysc?t=435" target="_blank">Recap Interview ➜</a>
</div>
<div class="carousel-item">
<img class="br2" src="{{ static "wheeljam2023/visaviz.png" }}">
<h3>Twitter Thread Graph Explorer</h3>
<p>
This project extended an existing personal project with a unique way of exploring Twitter threads. When the author found existing layout algorithms insufficient, he decided to roll his own. The project submission is an insightful look at why you sometimes need to do things yourself.
</p>
<p>
This project was featured as a demo at Handmade Seattle 2021.
</p>
<a class="b db" href="https://handmade.network/forums/jam/t/8137-jam_submission_-_twitter_thread_graph_explorer">Full Submission ➜</a>
<a class="b db" href="https://youtu.be/1RjU5XJqysc?t=7519" target="_blank">Recap Interview ➜</a>
<a class="b db" href="https://media.handmade-seattle.com/visa-viz/" target="_blank">Handmade Seattle Demo ➜</a>
</div>
<div class="carousel-item">
<img class="br2" src="{{ static "wheeljam2023/databaseexplorer.png" }}">
<h3>Database Explorer</h3>
<p>
This project explores a new way of querying SQL databases, by throwing away SQL in favor of a visual node graph. It allows you to incrementally build queries, seeing the data at every point along the way, and to reuse smaller queries in a way SQL cannot.
</p>
<a class="b db" href="https://handmade.network/forums/jam/t/8127-jam_submission__database_explorer">Full Submission ➜</a>
<a class="b db" href="https://youtu.be/1RjU5XJqysc?t=6390" target="_blank">Recap Interview ➜</a>
</div>
<div class="carousel-thinger next"></div>
<div class="carousel-thinger prev"></div>
</div>
<div class="carousel-buttons mt2 pv2"></div>
</div>
</div>
</div>
{{ end }}
<div class="pt4 pb3 pb4-ns">
<div class="section mw8 margin-center ph3 ph4-l">
<h2>How to participate</h2>
<p>
The jam takes place from Monday, September 25 through Sunday, October 1. Here's how you can participate:
</p>
<div class="{{ if gt .DaysUntilStart 0 }}emphasized{{ end }}">
<h3>Pick a project and form a team.</h3>
<p>
Find a project idea that excites you! Join the conversation over on our <a href="https://github.com/HandmadeNetwork/wishlist/discussions" target="_blank">Wishlist</a>, brainstorm ideas in <b>#jam</b> on <a href="{{ $discordInviteURL }}" target="_blank">Discord</a>, or just invite some friends to jam with you.
</p>
</div>
<div class="{{ if and (eq .DaysUntilStart 0) (gt .DaysUntilEnd 1) }}emphasized{{ end }}">
<h3>Jam.</h3>
<p>
{{ if and (eq .DaysUntilStart 0) (not .SubmittedProjectUrl) }}
<a href="{{ .ProjectSubmissionUrl }}" target="_blank"><b>Create a Handmade Network project</b></a>
{{ else }}
After the jam starts, create a Handmade Network project
{{ end }}
to track your work. Then, build your program! Share your work in progress in #project-showcase on Discord, or directly from your project page.
</p>
</div>
<div class="{{ if eq .DaysUntilEnd 1 }}emphasized{{ end }}">
<h3>Submit your work!</h3>
<p>
<b>Your Handmade Network project is your submission.</b> Fill out the project description, making sure to explain the goals of the project and how it improves on what came before. Also consider posting an update with video of your program in action!
</p>
{{ if and (eq .DaysUntilStart 0) (gt .DaysUntilEnd 0) }}
<p>
Submissions close <b><span class="countdown" data-deadline="{{ .EndTimeUnix }}"></span></b>.
</p>
{{ else if eq .DaysUntilEnd 0 }}
<p>
<b>Submissions are now closed.</b>
</p>
{{ end }}
</div>
</div>
</div>
<div class="bg-black-20 pt4 pb3 pb4-ns">
<div class="section mw8 margin-center ph3 ph4-l">
<h2>Rules</h2>
<ul>
<li>Any tech is allowed, but we encourage you to use only use what you really need. If you want some lightweight templates to get you started, check out our <a href="https://github.com/HandmadeNetwork/jam_templates" target="_blank">app templates</a>.</li>
<li>You may work solo or in a team. (But we encourage you to work with a team!)</li>
<li>Submit your work by the end of the day on October 1.</li>
</ul>
<p>There are no explicit winners, but we will be selecting a few of our favorite projects to highlight in a recap stream following the jam.</p>
<h3>Submission rules</h3>
<p>
<b>{{ with .SubmittedProjectUrl }}
<a href="{{ . }}" target="_blank">Your Handmade Network project</a>
{{ else }}
Your Handmade Network project
{{ end }}
is your submission.</b> We will be looking at the project's description and any extra updates you share toward the end of the jam.
</p>
<ul>
<li>
Explain the project's goals and how it improves on what came before. Also share some closing thoughts - did it turn out how you hoped? What did you learn? If you continue the project, what will you do differently?
</li>
<li>
<b>Your description must contain multiple screenshots of your software in action.</b> You should ideally also share a project update with a demo video. We recommend Mārtiņš Možeiko's <a href="https://wcap.handmade.network/" target="_blank">wcap</a> for recording desktop video on Windows. On Mac, just press ⌘-Option-5 and record a video, or use QuickTime.
</li>
<li>If at all possible, please provide a way for people to either build or download your program.</li>
</ul>
</div>
</div>
<div class="pt4">
<div class="flex-ns flex-row-ns mw8 margin-center ph3 ph4-l">
<div class="section flex-fair mb4 mb0-ns">
<h2>Make it by hand.</h2>
<p>
The Handmade ethos and Handmade community are software development superpowers. Don't be afraid to question your foundations and rebuild what needs rebuilding. The community is here to help you take on those challenges and do what others might consider impossible.
</p>
<p>
Of course, this is a jam, so focus on what matters to your project. There are many excellent libraries in the community that can save you time and help you focus on your core ideas. Don't be afraid to use them. But don't be afraid to do your own thing if they're holding you back.
</p>
</div>
<div class="section flex-fair ml4-m ml5-l">
<h2>Don't just rebuild. Reinvent.</h2>
<p>
This is a chance to build something <em>truly new</em>. Learn from previous work, but don't settle for “the same, but better”. It would be a huge shame to spend a week building nothing more than a clone of the same broken software we use today.
</p>
<p>
This is where working with a team can really help. Bounce ideas off each other, do some research, and brainstorm before the jam starts. The software you end up building might be pretty different from your original ideas.
</p>
<p>
In the end, this is a jam. Get weird and try something different.
</p>
</div>
</div>
</div>
<script>
const carouselContainer = document.querySelector('.carousel-container');
if (carouselContainer) {
const { next, prev } = initCarousel(carouselContainer, {
onChange() {
if (carouselContainer.getBoundingClientRect().top < 0) {
carouselContainer.scrollIntoView({ behavior: 'smooth' });
}
},
});
document.querySelector('.carousel-thinger.next')
.addEventListener('click', () => {
next();
});
document.querySelector('.carousel-thinger.prev')
.addEventListener('click', () => {
prev();
});
}
</script>
<script>
for (const countdown of document.querySelectorAll('.countdown')) {
const deadline = countdown.getAttribute('data-deadline');
const deadlineDate = new Date(parseInt(deadline, 10) * 1000);
function updateCountdown() {
const remainingMs = deadlineDate.getTime() - new Date().getTime();
const remainingMinutes = remainingMs / 1000 / 60;
const remainingHours = remainingMinutes / 60;
const remainingDays = remainingHours / 24; // no daylight savings transitions during the jam mmkay
let str = 'imminently';
if (remainingMinutes < 60) {
str = `in ${Math.ceil(remainingMinutes)} ${remainingMinutes === 1 ? 'minute' : 'minutes'}`;
} else if (remainingHours < 24) {
str = `in ${Math.ceil(remainingHours)} ${remainingHours === 1 ? 'hour' : 'hours'}`;
} else {
str = `in ${Math.ceil(remainingDays)} ${remainingDays === 1 ? 'day' : 'days'}`;
}
countdown.innerText = str;
}
updateCountdown();
setInterval(updateCountdown, 1000 * 60);
}
</script>
{{ end }}

View File

@ -16,6 +16,11 @@
<p>Since 2020, we have been running programming jams to encourage community members to explore new ideas and start projects. You can view all the past submissions and results here.</p>
<div class="jam-grid g3">
<a href="{{ .WRJ2023Url }}">
<div class="br2 overflow-hidden flex">
<img src="{{ static "wheeljam2023/TwitterCard.png" }}">
</div>
</a>
<a href="{{ .VJ2023Url }}">
<div class="br2 overflow-hidden flex">
<img src="{{ static "visjam2023/TwitterCard.png" }}">

View File

@ -111,7 +111,6 @@
</div>
*/}}
{{/*
<div class="mb3 ph3 ph0-ns">
<style>
#jam-banner {
@ -153,11 +152,11 @@
}
</style>
<a id="jam-banner" class="pv3 ph3 ph4-l br3 flex flex-column flex-row-ns items-center" href="{{ .JamUrl }}">
<img class="h3" src="{{ static "visjam2023/logo.svg" }}">
<img class="h3" src="{{ static "wheeljam2023/logo.svg" }}">
<div id="jam-title-container" class="flex flex-column pl3-m pl4-l pv3 pv0-ns">
<h3 id="jam-title">Visibility Jam</h3>
<h3 id="jam-title">Wheel Reinvention Jam</h3>
<div id="jam-details">
April 14 - 16.
September 25 - October 1.
{{ if gt .JamDaysUntilEnd 0 }}
{{ if eq .JamDaysUntilStart 0 }}
<b>Happening now.</b>
@ -178,7 +177,6 @@
</div>
</a>
</div>
*/}}
<div class="mb3 ph3 ph0-ns">
<style>

View File

@ -0,0 +1,355 @@
{{/*
This is a copy-paste from base.html because we want to preserve the unique
style of this page no matter what future changes we make to the base.
*/}}
<!DOCTYPE html{{ if .OpenGraphItems }} prefix="og: http://ogp.me/ns#"{{ end }}>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" sizes="16x16" href="{{ static "wheeljam2023/favicon-16x16.png" }}">
<link rel="icon" type="image/png" sizes="32x32" href="{{ static "wheeljam2023/favicon-32x32.png" }}">
{{ if .CanonicalLink }}<link rel="canonical" href="{{ .CanonicalLink }}">{{ end }}
{{ range .OpenGraphItems }}
{{ if .Property }}
<meta property="{{ .Property }}" content="{{ .Value }}" />
{{ else }}
<meta name="{{ .Name }}" content="{{ .Value }}" />
{{ end }}
{{ end }}
{{ if .Title }}
<title>{{ .Title }} | Handmade Network</title>
{{ else }}
<title>Handmade Network</title>
{{ end }}
<meta name="theme-color" content="#23CE76">
<script src="{{ static "js/templates.js" }}"></script>
<link rel="stylesheet" href="{{ static "fonts/mohave/stylesheet.css" }}">
<link href='https://fonts.googleapis.com/css?family=Fira+Sans:300,400,500,600' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Fira+Mono:300,400,500,700' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="{{ static "style.css" }}">
<style>
/* Copy-paste from project.css yay */
{{ $c := hex2color "23CE76" }}
{{ $themeDim := eq .Theme "dark" | ternary (lightness 0.35 $c) (lightness 0.75 $c) | color2css }}
{{ $themeDimmer := eq .Theme "dark" | ternary (lightness 0.3 $c) (lightness 0.8 $c) | color2css }}
{{ $themeDimmest := eq .Theme "dark" | ternary (lightness 0.2 $c) (lightness 0.85 $c) | color2css }}
{{ $themeDark := eq .Theme "dark" | ternary (lightness 0.30 $c) (lightness 0.35 $c) | color2css }}
{{ $linkColor := eq .Theme "dark" | ternary (lightness 0.55 $c) (lightness 0.35 $c) | color2css }}
{{ $linkHoverColor := eq .Theme "dark" | ternary (lightness 0.65 $c) (lightness 0.45 $c) | color2css }}
:root {
--content-background: #f8f8f8;
--card-background: rgba(255, 255, 255, 0.1);
--card-background-hover: rgba(255, 255, 255, 0.16);
--theme-color: {{ $c | color2css }};
--theme-color-dim: {{ $themeDim }};
--theme-color-dimmer: {{ $themeDimmer }};
--theme-color-dimmest: {{ $themeDimmest }};
--timeline-content-background: rgba(255, 255, 255, 0.1);
}
body {
background: linear-gradient(#23CE76, #299CE0)
}
.user-options,
header form,
header .menu-bar .wiki,
header .menu-bar .library
{
display: none !important;
}
header {
border-bottom-color: white;
margin-bottom: 0 !important;
}
.hmn-logo {
background-color: rgba(255, 255, 255, 0.1) !important;
}
header a, footer a {
color: white !important;
}
header .submenu {
background-color: #23CE76;
}
#top-container {
margin: 3rem 0;
}
#logo {
width: 16rem;
}
h1, h2, h3 {
font-family: 'MohaveHMN', sans-serif;
margin-bottom: 0;
font-weight: normal;
}
#title {
color: white;
font-size: 2.4rem;
line-height: 0.8;
margin-top: 2rem;
letter-spacing: -0.06rem;
text-transform: uppercase;
}
#dates {
font-variant: small-caps;
font-size: 1.6rem;
margin-top: 0.2rem;
}
#tagline {
font-size: 1rem;
margin-top: 1rem;
line-height: 1.4;
}
#top-container a {
color: white !important;
text-decoration: underline;
}
.actions {
margin-top: 1.5rem;
}
.actions a {
text-decoration: none !important;
line-height: 1.4;
font-weight: 500;
transition: background-color 50ms ease-in-out;
background-color:rgba(255, 255, 255, 0.1);
}
.actions a:hover {
background-color: rgba(255, 255, 255, 0.2);
}
.actions a:active {
background-color: rgba(255, 255, 255, 0.15);
}
.section {
font-size: 1rem;
line-height: 1.4;
}
.section h2 {
font-variant: small-caps;
font-size: 2.2rem;
line-height: 1.1;
}
.section h3 {
font-variant: small-caps;
font-size: 2rem;
line-height: 0.8;
margin-top: 1.4rem;
}
.section p {
margin-top: 1em;
margin-bottom: 1em;
}
.section a {
text-decoration: underline;
}
.emphasized {
padding-left: 1rem;
border-left: 0.3rem solid white;
}
.flex-fair {
flex-basis: 1px;
flex-grow: 1;
flex-shrink: 1;
}
ul {
list-style-type: disc;
}
li {
margin-top: 0.6rem;
margin-bottom: 0.6rem;
}
.section li p {
margin-top: 0.6rem;
margin-bottom: 0.6rem;
}
footer {
border-top: 2px solid white;
margin-top: 2rem;
text-align: center;
}
footer h2 {
text-transform: uppercase;
}
.showcase-item {
background-color: rgba(0, 0, 0, 0.2);
border-color: rgba(255, 255, 255, 0.5);
}
.carousel-thinger {
position: absolute;
top: 0;
width: 6rem;
height: 100%;
background-color: rgba(255, 255, 255, 0.1); /* bg-white-10 */
border-radius: 0.5rem; /* br3 */
cursor: pointer;
}
.carousel-thinger.prev {
left: -7rem;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
background: linear-gradient(to left, rgba(255, 255, 255, 0.1), transparent);
}
.carousel-thinger.next {
right: -7rem;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background: linear-gradient(to right, rgba(255, 255, 255, 0.1), transparent);
}
@media screen and (min-width: 30em) {
/* not small styles */
#top-container {
margin: 5.4rem 0;
}
#logo {
width: 31rem;
}
#title {
font-size: 5.2rem;
margin-top: 4rem;
}
#dates {
font-size: 2.8rem;
}
#tagline {
font-size: 1.2rem;
margin-top: 1.2rem;
}
.actions {
margin-top: 2.2rem;
}
.actions a {
font-size: 1.2rem;
}
.section h2 {
font-size: 3.4rem;
}
.section h3 {
font-size: 2.4rem;
margin-top: 1.6rem;
}
}
h3.mt0 {
margin-top: 0; /* ugh seriously */
}
.back-to-normal * {
font-family: "Fira Sans", sans-serif;
}
.back-to-normal h1,
.back-to-normal h2,
.back-to-normal h3,
.back-to-normal h4,
.back-to-normal h5
{
font-weight: 500;
margin: 0;
margin-bottom: 0.5rem;
font-size: 1.5rem;
line-height: 1.25em;
}
.back-to-normal a {
text-decoration: none;
}
.snippet-project {
background-color: rgba(255, 255, 255, 0.1);
text-decoration: none !important;
}
/* More copy-paste from project.css */
.bg-theme {
background-color: {{ $c | color2css }};
background-color: var(--theme-color);
}
.bg-theme-dim {
background-color: {{ $themeDim }};
background-color: var(--theme-color-dim);
}
.bg-theme-dimmer {
background-color: {{ $themeDimmer }};
background-color: var(--theme-color-dimmer);
}
.bg-theme-dimmest {
background-color: {{ $themeDimmest }};
background-color: var(--theme-color-dimmest);
}
</style>
<script src="{{ static "js/carousel.js" }}"></script>
</head>
<body>
<div class="left white">
<div class="mt4-ns mw8 margin-center ph3-m ph4-l">
{{ template "header.html" . }}
</div>
{{ block "content" . }}{{ end }}
<div class="mw8 margin-center ph3-m ph4-l">
{{ template "footer.html" . }}
</div>
</div>
</body>
</html>

View File

@ -22,6 +22,7 @@ func JamsIndex(c *RequestContext) ResponseData {
WRJ2021Url string
WRJ2022Url string
VJ2023Url string
WRJ2023Url string
}
res.MustWriteTemplate("jams_index.html", TemplateData{
@ -31,6 +32,170 @@ func JamsIndex(c *RequestContext) ResponseData {
WRJ2021Url: hmnurl.BuildJamIndex2021(),
WRJ2022Url: hmnurl.BuildJamIndex2022(),
VJ2023Url: hmnurl.BuildJamIndex2023_Visibility(),
WRJ2023Url: hmnurl.BuildJamIndex2023(),
}, c.Perf)
return res
}
func JamIndex2023(c *RequestContext) ResponseData {
var res ResponseData
daysUntilStart := daysUntil(hmndata.WRJ2023.StartTime)
daysUntilEnd := daysUntil(hmndata.WRJ2023.EndTime)
baseData := getBaseDataAutocrumb(c, hmndata.WRJ2023.Name)
baseData.OpenGraphItems = []templates.OpenGraphItem{
{Property: "og:site_name", Value: "Handmade Network"},
{Property: "og:type", Value: "website"},
{Property: "og:image", Value: hmnurl.BuildPublic("wheeljam2023/opengraph.png", true)},
{Property: "og:description", Value: "A one-week jam to change the status quo. September 25 - October 1 on Handmade Network."},
{Property: "og:url", Value: hmnurl.BuildJamIndex2023()},
}
type JamPageData struct {
templates.BaseData
DaysUntilStart, DaysUntilEnd int
StartTimeUnix, EndTimeUnix int64
SubmittedProjectUrl string
ProjectSubmissionUrl string
ShowcaseFeedUrl string
ShowcaseJson string
JamProjects []templates.Project
}
var showcaseItems []templates.TimelineItem
submittedProjectUrl := ""
if c.CurrentUser != nil {
projects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
OwnerIDs: []int{c.CurrentUser.ID},
JamSlugs: []string{hmndata.WRJ2023.Slug},
Limit: 1,
})
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user"))
}
if len(projects) > 0 {
urlContext := hmndata.UrlContextForProject(&projects[0].Project)
submittedProjectUrl = urlContext.BuildHomepage()
}
}
jamProjects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
JamSlugs: []string{hmndata.WRJ2023.Slug},
})
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user"))
}
pageProjects := make([]templates.Project, 0, len(jamProjects))
for _, p := range jamProjects {
pageProjects = append(pageProjects, templates.ProjectAndStuffToTemplate(&p, hmndata.UrlContextForProject(&p.Project).BuildHomepage(), c.Theme))
}
projectIds := make([]int, 0, len(jamProjects))
for _, jp := range jamProjects {
projectIds = append(projectIds, jp.Project.ID)
}
if len(projectIds) > 0 {
snippets, err := hmndata.FetchSnippets(c, c.Conn, c.CurrentUser, hmndata.SnippetQuery{
ProjectIDs: projectIds,
Limit: 12,
})
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets for jam showcase"))
}
showcaseItems = make([]templates.TimelineItem, 0, len(snippets))
for _, s := range snippets {
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, c.Theme, false)
if timelineItem.CanShowcase {
showcaseItems = append(showcaseItems, timelineItem)
}
}
}
showcaseJson := templates.TimelineItemsToJSON(showcaseItems)
res.MustWriteTemplate("jam_2023_wrj_index.html", JamPageData{
BaseData: baseData,
DaysUntilStart: daysUntilStart,
DaysUntilEnd: daysUntilEnd,
StartTimeUnix: hmndata.WRJ2023.StartTime.Unix(),
EndTimeUnix: hmndata.WRJ2023.EndTime.Unix(),
ProjectSubmissionUrl: hmnurl.BuildProjectNewJam(),
SubmittedProjectUrl: submittedProjectUrl,
ShowcaseFeedUrl: hmnurl.BuildJamFeed2023(),
ShowcaseJson: showcaseJson,
JamProjects: pageProjects,
}, c.Perf)
return res
}
func JamFeed2023(c *RequestContext) ResponseData {
jamProjects, err := hmndata.FetchProjects(c, c.Conn, c.CurrentUser, hmndata.ProjectsQuery{
JamSlugs: []string{hmndata.WRJ2023.Slug},
})
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch jam projects for current user"))
}
projectIds := make([]int, 0, len(jamProjects))
for _, jp := range jamProjects {
projectIds = append(projectIds, jp.Project.ID)
}
var timelineItems []templates.TimelineItem
if len(projectIds) > 0 {
snippets, err := hmndata.FetchSnippets(c, c.Conn, c.CurrentUser, hmndata.SnippetQuery{
ProjectIDs: projectIds,
})
if err != nil {
return c.ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to fetch snippets for jam showcase"))
}
timelineItems = make([]templates.TimelineItem, 0, len(snippets))
for _, s := range snippets {
timelineItem := SnippetToTimelineItem(&s.Snippet, s.Asset, s.DiscordMessage, s.Projects, s.Owner, c.Theme, false)
timelineItem.SmallInfo = true
timelineItems = append(timelineItems, timelineItem)
}
}
pageProjects := make([]templates.Project, 0, len(jamProjects))
for _, p := range jamProjects {
pageProjects = append(pageProjects, templates.ProjectAndStuffToTemplate(&p, hmndata.UrlContextForProject(&p.Project).BuildHomepage(), c.Theme))
}
type JamFeedData struct {
templates.BaseData
DaysUntilStart, DaysUntilEnd int
JamProjects []templates.Project
TimelineItems []templates.TimelineItem
}
daysUntilStart := daysUntil(hmndata.WRJ2023.StartTime)
daysUntilEnd := daysUntil(hmndata.WRJ2023.EndTime)
baseData := getBaseDataAutocrumb(c, hmndata.WRJ2023.Name)
baseData.OpenGraphItems = []templates.OpenGraphItem{
{Property: "og:site_name", Value: "Handmade Network"},
{Property: "og:type", Value: "website"},
{Property: "og:image", Value: hmnurl.BuildPublic("wheeljam2023/opengraph.png", true)},
{Property: "og:description", Value: "A one-week jam to change the status quo. September 25 - October 1 on Handmade Network."},
{Property: "og:url", Value: hmnurl.BuildJamFeed2023()},
}
var res ResponseData
res.MustWriteTemplate("jam_2023_wrj_feed.html", JamFeedData{
BaseData: baseData,
DaysUntilStart: daysUntilStart,
DaysUntilEnd: daysUntilEnd,
JamProjects: pageProjects,
TimelineItems: timelineItems,
}, c.Perf)
return res
}
@ -48,7 +213,7 @@ func JamIndex2023_Visibility(c *RequestContext) ResponseData {
{Property: "og:type", Value: "website"},
{Property: "og:image", Value: hmnurl.BuildPublic("visjam2023/opengraph.png", true)},
{Property: "og:description", Value: "See things in a new way. April 14 - 16."},
{Property: "og:url", Value: hmnurl.BuildJamIndex()},
{Property: "og:url", Value: hmnurl.BuildJamIndex2023_Visibility()},
{Name: "twitter:card", Value: "summary_large_image"},
{Name: "twitter:image", Value: hmnurl.BuildPublic("visjam2023/TwitterCard.png", true)},
}
@ -192,7 +357,7 @@ func JamFeed2023_Visibility(c *RequestContext) ResponseData {
{Property: "og:type", Value: "website"},
{Property: "og:image", Value: hmnurl.BuildPublic("visjam2023/opengraph.png", true)},
{Property: "og:description", Value: "See things in a new way. April 14 - 16."},
{Property: "og:url", Value: hmnurl.BuildJamIndex()},
{Property: "og:url", Value: hmnurl.BuildJamFeed2023_Visibility()},
{Name: "twitter:card", Value: "summary_large_image"},
{Name: "twitter:image", Value: hmnurl.BuildPublic("visjam2023/TwitterCard.png", true)},
}
@ -237,7 +402,7 @@ func JamRecap2023_Visibility(c *RequestContext) ResponseData {
{Property: "og:type", Value: "website"},
{Property: "og:image", Value: hmnurl.BuildPublic("visjam2023/opengraph.png", true)},
// {Property: "og:description", Value: "See things in a new way. April 14 - 16."},
{Property: "og:url", Value: hmnurl.BuildJamIndex()},
{Property: "og:url", Value: hmnurl.BuildJamRecap2023_Visibility()},
{Name: "twitter:card", Value: "summary_large_image"},
{Name: "twitter:image", Value: hmnurl.BuildPublic("visjam2023/TwitterCard.png", true)},
}
@ -265,7 +430,7 @@ func JamIndex2022(c *RequestContext) ResponseData {
{Property: "og:type", Value: "website"},
{Property: "og:image", Value: hmnurl.BuildPublic("wheeljam2022/opengraph.png", true)},
{Property: "og:description", Value: "A one-week jam to change the status quo. August 15 - 21 on Handmade Network."},
{Property: "og:url", Value: hmnurl.BuildJamIndex()},
{Property: "og:url", Value: hmnurl.BuildJamIndex2022()},
}
type JamPageData struct {
@ -402,7 +567,7 @@ func JamFeed2022(c *RequestContext) ResponseData {
{Property: "og:type", Value: "website"},
{Property: "og:image", Value: hmnurl.BuildPublic("wheeljam2022/opengraph.png", true)},
{Property: "og:description", Value: "A one-week jam to change the status quo. August 15 - 21 on Handmade Network."},
{Property: "og:url", Value: hmnurl.BuildJamIndex()},
{Property: "og:url", Value: hmnurl.BuildJamFeed2022()},
}
var res ResponseData
@ -459,7 +624,7 @@ func JamIndex2021(c *RequestContext) ResponseData {
{Property: "og:type", Value: "website"},
{Property: "og:image", Value: hmnurl.BuildPublic("wheeljam2021/opengraph.png", true)},
{Property: "og:description", Value: "A one-week jam to bring a fresh perspective to old ideas. September 27 - October 3 on Handmade Network."},
{Property: "og:url", Value: hmnurl.BuildJamIndex()},
{Property: "og:url", Value: hmnurl.BuildJamIndex2021()},
}
type JamPageData struct {

View File

@ -156,9 +156,9 @@ func Index(c *RequestContext) ResponseData {
AtomFeedUrl: hmnurl.BuildAtomFeed(),
MarkAllReadUrl: hmnurl.HMNProjectContext.BuildForumMarkRead(0),
JamUrl: hmnurl.BuildJamIndex2023_Visibility(),
JamDaysUntilStart: daysUntil(hmndata.VJ2023.StartTime),
JamDaysUntilEnd: daysUntil(hmndata.VJ2023.EndTime),
JamUrl: hmnurl.BuildJamIndex2023(),
JamDaysUntilStart: daysUntil(hmndata.WRJ2023.StartTime),
JamDaysUntilEnd: daysUntil(hmndata.WRJ2023.EndTime),
HMSDaysUntilStart: daysUntil(hmndata.HMS2023.StartTime),
HMSDaysUntilEnd: daysUntil(hmndata.HMS2023.EndTime),

View File

@ -61,7 +61,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexJamsIndex, JamsIndex)
hmnOnly.GET(hmnurl.RegexJamIndex, func(c *RequestContext) ResponseData {
return c.Redirect(hmnurl.BuildJamIndex2023_Visibility(), http.StatusFound)
return c.Redirect(hmnurl.BuildJamIndex2023(), http.StatusFound)
})
hmnOnly.GET(hmnurl.RegexJamIndex2021, JamIndex2021)
hmnOnly.GET(hmnurl.RegexJamIndex2022, JamIndex2022)
@ -69,6 +69,8 @@ func NewWebsiteRoutes(conn *pgxpool.Pool) http.Handler {
hmnOnly.GET(hmnurl.RegexJamIndex2023_Visibility, JamIndex2023_Visibility)
hmnOnly.GET(hmnurl.RegexJamFeed2023_Visibility, JamFeed2023_Visibility)
hmnOnly.GET(hmnurl.RegexJamRecap2023_Visibility, JamRecap2023_Visibility)
hmnOnly.GET(hmnurl.RegexJamIndex2023, JamIndex2023)
hmnOnly.GET(hmnurl.RegexJamFeed2023, JamFeed2023)
hmnOnly.GET(hmnurl.RegexTimeMachine, TimeMachine)
hmnOnly.GET(hmnurl.RegexTimeMachineSubmissions, TimeMachineSubmissions)