Initial Release

This commit is contained in:
2026-05-24 21:58:23 -07:00
commit 0a782e0096
209 changed files with 11657 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
extends _TemplateSpecial
block pageHeader
meta(name='robots' content='noindex')
title suzzy - Page Not Found (404)
block pageContent
//- User Message
img(class='special-banner' src='file:///images/public-nopage.png')
p(class='text-header') Page Not Found
p(class='text-tip') Should've made a left instead of a right...
//- User Actions
div(class='divider-line')
div(class='special-alongside')
a(class='default' href='/') Homepage
a(class='default' href='javascript:window.history.back()') Go Back
@@ -0,0 +1,79 @@
each e in relevantArticle.elements
case e.type
when 'text'
p
each t in e.items
case t.tag
when ''
| #{t.content}
when 'h'
| #[span(class='text-hint') #{t.content}]
when 'c'
| #[span(class='text-code') #{t.content}]
when 'a'
| #[a(class='link-highlight' target=t.target href=t.href) #{t.content}]
when 's'
| #[s #{t.content}]
when 'b'
| #[b #{t.content}]
when 'i'
| #[i #{t.content}]
when 'header'
p(class='element-header' id=formatString(e.value)) #{e.value}
when 'subheader'
p(class='element-subheader' id=formatString(e.value)) #{e.value}
when 'quote'
p(class='text-quote') > #{e.value}
when 'code'
div(class='element-code')
button(class='default') Copy
pre !{e.content}
when 'image'
div(class='element-gallery')
div(class='gallery-item')
img(class='special-dropshadow' loading='lazy' src=e.resource alt=e.alt)
p #{e.caption}
when 'video'
div(class='element-gallery')
div(class='gallery-item')
video(class='special-dropshadow' controls preload='none' poster=e.cover)
source(src=e.resource)
p #{e.caption}
when 'audio'
div(class='element-audio special-shadow')
audio(preload='auto')
//- Audio Metadata
div(class='audio-container')
p #{e.name}
a(class='audio-download' title='Download Track' download=e.resource href=e.resource)
img(src='file://icons/icon-download.svg' alt='Icon for Download')
//- Audio Controls
div(class='audio-container')
button(class='audio-button' title='Play/Pause Audio')
img(src='file://icons/icon-play.svg' alt='Icon for Play/Pause')
input(class='audio-slider' type='range' value='0' disabled)
p(class='audio-time') -:--/-:--
when 'table'
table(class='element-table special-shadow')
- var firstRow = e.items.at(0)
- var otherRow = e.items.slice(1)
thead
tr
each column in firstRow
th #{column}
tbody
each row in otherRow
tr
each column in row
td #{column}
when 'list'
ul(class='element-list')
each listItem in e.items
li • #{listItem}
when 'banner'
img(class='element-banner' src=e.resource alt=e.alt)
when 'beanie'
a(href=e.resource download=(e.resource.split('/').at(-1).split('?').at(0)))
img(class='element-beanie' src=e.resource alt=e.alt)
default
- throw "Unknown Type: " + e.type
@@ -0,0 +1,67 @@
doctype html
html(lang='en')
head
meta(name='viewport' content='width=device-width, initial-scale=1')
link(rel='icon' type='image/png' href='file://images/favicon.png')
link(rel='stylesheet' href='file://fonts/font-poppins.css')
link(rel='stylesheet' href='file://styles/template-global.css')
link(rel='stylesheet' href='file://styles/template-modals.css')
link(rel='stylesheet' href='file://styles/template-profile.css')
meta(property='theme-color' content='#A1A8FF')
block pageHeader
body
noscript
style.
noscript div {
position: absolute;
width: 100%;
height: 100%;
display: grid;
justify-items: center;
align-content: center;
text-align: center;
}
noscript p {
padding: 24px;
box-sizing: border-box;
}
div.wrapper-layout {
display: none;
}
div
p This webpage requires JavaScript to be enabled.
a(href='/') Go to Homepage
div(class='wrapper-layout')
div(class='wrapper-navigation')
- var PAGE = filename.split('\\').at(-1).split('.').at(0)
a(href='/')
img(class='navigation-logo' src='file://images/public-logo.png' alt='Logo for suzzy games')
a(active=(PAGE === 'index') class='navigation-link' href='/profile')
img(src='file://icons/icon-user.svg' alt='Icon for Profile')
span Profile
a(active=(PAGE === 'security') class='navigation-link' href='/profile/security')
img(src='file://icons/icon-lock.svg' alt='Icon for Security')
span Security
a(active=(PAGE === 'devices') class='navigation-link' href='/profile/devices')
img(src='file://icons/icon-mobile.svg' alt='Icon for Devices')
span Devices
a(active=(PAGE === 'connections') class='navigation-link' href='/profile/connections')
img(src='file://icons/icon-plug.svg' alt='Icon for Connections')
span Connections
a(active=(PAGE === 'applications') class='navigation-link' href='/profile/applications')
img(src='file://icons/icon-app.svg' alt='Icon for Applications')
span Applications
div(class='divider-line')
a(class='navigation-link' href='/profile/redeem')
img(src='file://icons/icon-star.svg' alt='Icon for Redeem Code')
span Redeem Code
a(class='navigation-link' href='/')
img(src='file://icons/icon-exit.svg' alt='Icon for Sign Out')
span Sign Out
div(class='wrapper-content')
block pageContent
script(src='file://scripts/shared-utils.ts')
@@ -0,0 +1,15 @@
//- Collection Alerts and Errors
div(class='collection-alert' id='alert-loading')
img(src='file://icons/diagram-loading.svg' alt='Icon for Loading')
span Loading #{loading_message ||'Content'}
div(class='collection-alert' id='alert-error' hidden)
img(src='file://icons/icon-cross.svg' alt='Icon for Cross')
span An Error Has Occurred
if empty_message
div(class='collection-alert' id='alert-empty' hidden)
img(src=empty_source alt=`Icon for ${empty_alt}`)
span #{empty_message}
//- Collection Items
div(class='wrapper-collection')
@@ -0,0 +1,66 @@
doctype html
html(lang='en')
head
meta(charset='UTF-8')
meta(name='viewport' content='width=device-width, initial-scale=1.0')
link(rel='icon' type='image/png' href='file://images/favicon.png')
link(rel='stylesheet' href='file://fonts/font-poppins.css')
link(rel='stylesheet' href='file://styles/template-global.css')
link(rel='stylesheet' href='file://styles/template-public.css')
link(rel='stylesheet' href='file://styles/template-modals.css')
meta(property='theme-color' content='#A1A8FF')
block pageHeader
body
//- Navigation Bar
div(class='wrapper-navigation')
div(class='container-navigation')
//- Left-Hand: Site Navigation
div(class='navigation-side' id='navigation-left')
a(href="/" style="display:grid;" tabindex="0")
img(
class='navigation-logo'
src='file://images/public-logo.png'
alt='Logo for suzzy games'
)
p(class='text-muted') •
a(class='text-link' href='/blog') Blog
a(class='text-link' href='/library') Library
a(class='text-link' href='/projects') Projects
//- Right-Hand: User & Site Status
div(class='navigation-side' id='navigation-right')
a(class='special-shadow' id='navigation-status' href='https://status.suzzygames.com/' title='Server Status')
div(id='status-dot' class='status-dot special-pulse')
p(id='status-text' class='text-tip') Loading
a(id='navigation-profile' class='special-shadow' title='Manage Account')
//- Content Dividers
//- div.divider-navigation: Spacer for Navigation Bar on Desktop
//- div.divider-mobile: Line Divider only present on Mobile
div(class='divider-navigation')
div(class='divider-line divider-mobile')
//- Escape Content Wrapper for elements that required the full width of the page
block pageEscaped
//- Wrapper for Page Content
div(class='wrapper-content')
block pageContent
//- Wrapper for Page Footer
div(class='wrapper-footer')
div(class='container-footer')
div(class='footer-links')
a(class='footer-link' target='_blank' href='https://twitter.com/bakonpancakz' title='Twitter Account for bakonpancakz')
img(src='file://images/brands/logo-x.png' alt='Logo for X')
a(class='footer-link' target='_blank' href='https://discord.gg/invite/bvTCZavXUU' title='Discord Server for suzzy games')
img(src='file://images/brands/logo-discord.png' alt='Logo for Discord')
a(class='footer-link' target='_blank' href='https://www.roblox.com/groups/10781692' title='ROBLOX Group for suzzy games')
img(src='file://images/brands/logo-roblox.png' alt='Logo for Roblox')
p(class='text-tip')
| © 2021-2024 suzzy games. All Rights Reserved.
a(class='text-tip text-muted' href='/challenge-picross' style='text-decoration: none;')
| Developed with ♡ by bakonpancakz
//- Page Scripts
script(src='file://scripts/shared-utils.ts')
script(src='file://scripts/shared-public.ts')
@@ -0,0 +1,17 @@
doctype html
html(lang='en')
head
meta(name='viewport' content='width=device-width, initial-scale=1')
link(rel='icon' type='image/png' href='file://images/favicon.png')
link(rel='stylesheet' href='file://fonts/font-poppins.css')
link(rel='stylesheet' href='file://styles/template-global.css')
link(rel='stylesheet' href='file://styles/template-modals.css')
link(rel='stylesheet' href='file://styles/template-special.css')
meta(property='theme-color' content='#A1A8FF')
block pageHeader
body
div(class='wrapper-special')
div(class='container-special')
block pageContent
script(src='file://scripts/shared-utils.ts')
block pageEscaped
@@ -0,0 +1,182 @@
<article
id="hello-world"
created="Apr 17 2024"
categoryId="behind-the-scenes"
authorId="bakonpancakz"
title="Hello World!"
snippet="Breaking the radio silence what has bakonpancakz been up to? And what exactly is a suzzy?"
banner="file://blog/hello-world/banner.gif"
noindex="false"
>
<header>Welcome to suzzy games!</header>
<text>
I made this site to serve as a hub for my game development hobby. There
is no theme, there is no incentive, just pure passion and efficient code.
I'll be sharing previews, progress, and generally whatever comes to mind.
So that's what this site is for! And this post contains a bunch of previews
of things I've worked on that <b>might</b> become their own game in the
future.
</text>
<quote>Except for Cart Ride that one was just an update :P</quote>
<!-- Game: Cart Ride around a Fumo -->
<header>Cart Ride Around a Fumo</header>
<text>
<a href="https://www.roblox.com/games/9987852646/Cart-Ride-Around-a-Fumo">Cart Ride Around a Fumo</a>
is my own spin on the cart ride genre that was all over in the olden days
of ROBLOX. It's a labor of love with way too much effort put into it, but
I like to look back at it and try to match that level of quality for
everything I do.
</text>
<quote>Why Fumos? Because they're funny.</quote>
<text>
I spent a whole two months on it and again, put way more effort into it
than I should have, but that's just how I roll (⌐■_■). I actually got a
lot of my cousins to playtest it and everybody had a lot of fun blowing
each other up. Which I like to think is what kickstarted my passion.
<h>(my origin story)</h>
</text>
<image
caption="Home Sweet Home"
alt="Overhead view of the in-game track"
resource="file://blog/hello-world/craf-1.jpeg"
/>
<text>
Around ten people play it daily and people are still donating despite
its age, so I definitely did something right. (^_^) It even has a
couple videos on it, one of <a href="https://www.youtube.com/watch?v=Jjep6xt3PiM">friends just having fun</a>,
and a <a href="https://www.youtube.com/watch?v=fyoQxBuR1Mo">super duper edited one</a>.
</text>
<quote>Thanks guys &lt;3</quote>
<text>
I actually planned to release a major update for it which would have added
cart customization and a track editor. But the update grew too large in
scope and is now being developed as its own standalone game.
<h>(stay tuned!)</h>
</text>
<text>
It's nothing too interesting in my opinion but here's what one of the
menus looked like.
</text>
<image
caption="(Scrapped) Cart Selection Menu"
alt="Screenshot of a scrapped concept showcasing cart customization"
resource="file://blog/hello-world/craf-2.jpeg"
/>
<!-- Game: Five Nights at Kanye 2 -->
<header>Five Nights at Kanye 2</header>
<text>
The <a href="https://www.roblox.com/games/13051172029/Five-Nights-at-Kanyes-FNAK-CLASSIC">original FNAK</a>
was made in only a few hours but my friends really liked the idea so I fleshed it
out a bit more and released a <a href="https://www.roblox.com/games/11229358049/Five-Nights-at-Kanyes-FNAK">remastered version</a>
just for fun. A few people liked it and sent me messages asking for a second
entry so I actually worked on it for a bit.
</text>
<image
caption="Main Menu"
alt="Main Menu for Five Nights at Kanyes 2, taking place in a dimly lit restaurant"
resource="file://blog/hello-world/fnak-1.jpeg"
/>
<image
caption="Kanye's Mansion (Courtyard)"
alt="Wide angle view of the courtyard in Kanye's Mansion"
resource="file://blog/hello-world/fnak-2.jpeg"
/>
<image
caption="Kanye's Mansion (Overhead with Layout)"
alt="Overhead view of Kanye's Mansion with each room labelled"
resource="file://blog/hello-world/fnak-3.jpeg"
/>
<text>
The story I had in mind was that this was a prequel to the first game,
and this was the night you broke into Kanye's house to steal DONDA 3
for Twitter clout <s>(now called x lol)</s>. Over the course of five
nights you <h>(and up to 3 other friends)</h> were supposed to collect
story items that would unlock another room after each night.
</text>
<text>
Kanye was also intended to have a better than average AI that would actively
hunt you down as you made mistakes. Something as small as leaving a light on
would make him suspicious and have him start to slowly but surely hunt you
down <h>(working as a soft time limit)</h>.
</text>
<text>
For some added suspense, the music changes as Kanye's suspicious level changes.
All the way from clueless to chasing you down. It is based on Kanye's track
<a href="https://open.spotify.com/track/3JWiDGQX2eTlFvKj3Yssj3">Closed on Sunday</a>
and made by Dakota <h>(he'll get a proper shoutout later!)</h>
</text>
<audio name="FNAK Stage 1 - We are Here (Arrival)" resource="file://blog/hello-world/FNAK_Stage_1.mp3"/>
<audio name="FNAK Stage 2 - Something Isn't Right (Tracking)" resource="file://blog/hello-world/FNAK_Stage_2.mp3"/>
<audio name="FNAK Stage 3 - He's Near (Tracking, Close)" resource="file://blog/hello-world/FNAK_Stage_3.mp3"/>
<audio name="FNAK Stage 4 - Found you (Chasing)" resource="file://blog/hello-world/FNAK_Stage_4.mp3"/>
<!-- Game: Project Z -->
<header>Project Z</header>
<text>
Project Z is a <i>player-versus-player, first-person shooter, zombie survival</i>
game where you barricade yourself into a room and fight off zombified
players for six waves with each wave unlocking new zombie classes and
zombie bosses. <h>Did you get all that?</h>
</text>
<text>
I worked on a few systems <h>(UI, Social, Weapons, etc)</h> but never
got around to making an actual working prototype to fully test the
idea out. Even worse is that changes to ROBLOX suddenly broke my
scripts so I can't even show them off except for in this equally
terrible quality video.
</text>
<text>
It's definitely a cool idea and I will most likely revisit it in the
future and give the love it deserves. This idea actually borrows a ton
from the original <a href="https://steamcommunity.com/sharedfiles/filedetails/?id=105462463">Garry's Mod Zombie Survival</a>
which I used to play it all the time back in middle school and really
wanted to bring the idea to modern audiences.
</text>
<video
caption="Broken Weapons and Sprays"
cover="file://blog/hello-world/ProjectZ-Cover.jpeg"
resource="file://blog/hello-world/ProjectZ.mp4"
/>
<!-- Section: The Plan -->
<header>What now?</header>
<text>
Well, I'm going back to working on that standalone project I mentioned
earlier! It's called Project Monke and you can think of it as Super
Monkey Ball and Super Mario Maker put together. It has online services
<h>(which is why you can create an account btw)</h> so that should give
you an idea of how large of a scope this project has.
</text>
<text>
If you make an account there isn't really a whole lot you can do with it
now, except for some profile customization and security related features.
I'll be adding comments to the site at a later date, but for now the
foundation has been set.
</text>
<image
caption="And for all the nerds out there, the backend is written in Go, the frontend is hand-written PUG+CSS+JS, and served using NGINX, on my personal server (nicknamed miku)"
alt="Picture of a computer stuffed in the closest with a Hatsune Miku sticker slapped in the corner"
resource="file://blog/hello-world/peak-performance.jpeg"
/>
<!-- Section: Special Thanks -->
<header>Special Thanks to Dakota</header>
<text>
Before I go, I'd also like to share a couple more tracks from Dakota
<s>who I have locked in my basement</s> who graciously offered to make
music for my projects. He also made the Five Nights at Kanye tracks you
heard earlier. <a href="https://soundcloud.com/user-627340075">Check out his Soundcloud!</a> ^_^
</text>
<audio name="Project Monke - Settings" resource="file://blog/hello-world/ProjectMonke_Settings.mp3"/>
<audio name="FNAK - Settings (Original)" resource="file://blog/hello-world/FNAK_Options_Original.mp3"/>
<audio name="FNAK - Settings (In-Game)" resource="file://blog/hello-world/FNAK_Options_InGame.mp3"/>
<video
caption="inside joke :3"
cover="file://blog/hello-world/settings-Cover.jpeg"
resource="file://blog/hello-world/settings.mp4"
/>
<quote>p.s. wanna leave an encouraging comment or question? Leave a comment on my twitter!</quote>
<quote>p.p.s there's an easter egg hidden somewhere on this site for users, I hope you love picross as much as I do~</quote>
</article>
@@ -0,0 +1,103 @@
<article
id="repo-of-knowledge"
created="May 12 2024"
categoryId="behind-the-scenes"
authorId="bakonpancakz"
title="Repo of Knowledge"
snippet="Fresh paint, building libraries, and starting side-projects (again). Welcome to new suzzy games website!"
banner="file://blog/repo-of-knowledge/banner.png"
noindex="false"
>
<!-- Introduction -->
<header>A Fresh Coat of Paint</header>
<text>
<b>Welcome to the new and improved suzzy games website!</b>
</text>
<text>
Absolutely everything on this site has been either upgraded or replaced
entirely. The blog system went from janky to proper, pages are now full
of content and trivia instead of being half-baked, and a new library was
built. <h>More on this later!</h>
I dedicated an entire weekend to turn this site from potato to french
fry. I absolutely love how it looks now and I hope you do too.
</text>
<quote>Sometimes I open up the website just to stare at it @_@</quote>
<image
caption="New and Improved!"
alt="A 4 by 4 grid showing the old site on top and the new site design on the bottom"
resource="file://blog/repo-of-knowledge/SITE_SHOWCASE.jpg"
/>
<!-- New Feature: oAuth2 -->
<header>Introducing oAuth2</header>
<text>
<b>Are you a developer? Then this chapter is for you!</b> Applications
and oAuth2 are now available for everybody! This allows third-party
developers to safely access your profile and the game data that
<b>you</b> decide to share with them.
</text>
<text>
New features and subsequent documentation on those features will become
available for developers in the library.
<h>Which brings us to the next topic...</h>
</text>
<image
caption="oAuth2 User Interface (You've seen one of these before right?)"
alt="An example oAuth2 prompt asking the user to allow access to certain account permissions"
resource="file://blog/repo-of-knowledge/OAUTH2_SHOWCASE.png"
/>
<!-- New Feature: Developer Docs -->
<header>Building a Library</header>
<text>
<a href='/library'>The library</a> is a new section of the suzzy website!
It contains legal stuff, branding guidelines, and most importantly
documentation for developers. If you're really bored check out the
<a href="/library/home/branding" target="_blank">branding article</a>!
It contains little bits of trivia on all past suzzy projects.
</text>
<image
caption="Library Homepage (Its Boo!)"
alt="A preview for the suzzy games library with the mascot sitting on a bean bag in their room reading a book"
resource="file://blog/repo-of-knowledge/LIBRARY_SHOWCASE.png"
/>
<!-- Announcement: New 2D Game Engine -->
<header>Introducing BECCA2D</header>
<video
caption="BECCA2D Splash Screen"
resource="file://blog/repo-of-knowledge/BECCA2D_TEASER.mp4"
cover="file://blog/repo-of-knowledge/BECCA2D_TEASER_COVER.png"
/>
<text>
<b>Introducing BECCA2D!</b> An <i>in-house 2D Game Engine</i> designed
from the ground up to be lightweight, efficient, extensible, and run in
Google Chrome. <h>sorry firefox users :p</h>
</text>
<text>
I'm planning on using this engine in a new side-project, a side-scrolling
style platformer fangame based on a certain fish vtuber I've taken a liking
to recently. This fangame also shares a considerable amount of features with
Project Monke, so this will also serve as a personal training ground of sorts.
</text>
<text>
But that's all I'm sharing about this side-project for today! I'll be making
a dedicated post on the engine showcasing its features, design, and reasoning
soon™
</text>
<!-- Summary -->
<header>Epilogue</header>
<text>
So that's my content dump for the month. Again I'm still working on new
stuff and it'll all be done and released eventually
<h>gimme like six months or something :P</h>
</text>
<image
caption="ok that's it see you soon™"
alt="A man holding the ball standing in front a white board with the words soon trademarked"
resource="file://blog/repo-of-knowledge/conclusion.png"
/>
<quote>p.s. I got a drawing tablet! nothing crazy just a wacom one! expect more doodles all over the site now</quote>
<quote>p.p.s still no blog comments btw >.></quote>
</article>
+104
View File
@@ -0,0 +1,104 @@
extends ../_TemplatePublic
block pageHeader
link(rel='stylesheet' href='file://styles/browser-blog.css')
meta(property='og:image' content='file://images/public-logo-og.png')
meta(property='og:title' content='suzzy games - blog')
meta(property='og:description' content='News and Updates straight from the source.')
meta(name='description' content='News and Updates straight from the source.')
title suzzy games - blog
style.
div.divider-mobile {
display: none !important;
}
block pageEscaped
div(class='widget-wrapper background-scroll')
//- Widget: Latest Articles
div(class='widget')
p(class='text-header') > Latest <
div(class='widget-latest-articles')
- var recents = articles.slice(0, 4)
each article, index in recents
div(class='latest-item')
img(class='latest-banner special-dropshadow' alt=`Banner Image for Blog Post '${article.info.title}'` src=article.info.banner)
div(class='latest-content')
p(class='text-headline') #{article.info.title}
div(class='latest-info')
p(class='text-tip text-muted') #{article.author.name}
p(class='text-tip text-muted') &bull; #{formatDate(article.info.created)} &bull;
p(class='text-tip text-muted') #{article.category.name}
div(class='divider-header')
p(class='text-tip') #{article.info.snippet}
a(class='text-tip text-muted' href=`/blog/${article.category.id}/${article.info.id}`)
| Read More (#{calcReadTime(article.elements)} minutes)
//- Widget: Latest Controls
div(class='widget widget-latest-controls')
div(class='latest-spacer')
div(class='latest-controls')
button(class='default' id='latest-last' aria-label='Last Blog Post') <
each _, index in recents
div(class='dot-circle' + (index === 0 ? ' dot-active' : ''))
button(class='default' id='latest-next' aria-label='Next Blog Post') >
script(src='file://scripts/browser-blog.ts')
block pageContent
div(class='main-wrapper')
//- Browser Filters
div(class='main-aside')
p(class='text-header') > Browser <
a(class=`filter-link ${!relevantCategory ? 'filter-active' : ''}` href='/blog')
img(class='filter-icon' alt='Icon for Anything' src='file://blog/category-any.png')
p(class='filter-name text-tip') Anything
each category in categories
a(
href=`/blog/${category.id}`
class=`filter-link ${relevantCategory && (relevantCategory.id === category.id ? 'filter-active' : '')}`
)
img(class='filter-icon' alt=`Icon for Category ${category.name}` src=category.icon)
p(class='filter-name text-tip') #{category.name}
div(class='divider-dashed')
//- Browser Articles
div(class='main-content' role='main')
if relevantArticles.length === 0
//- No Articles Found
div(class='collection-empty')
img(aria-hidden='true' alt='Icon for No Posts' src='file://images/public-noposts.png')
p(class='text-muted') no blog posts found
else
//- Article Previews
each article, index in relevantArticles
a(class='collection-item' href=`/blog/${article.category.id}/${article.info.id}`)
img(class='item-banner' alt=`Banner Image for Blog Post ${article.info.title}` src=article.info.banner)
div(class='item-content')
p(class='text-header') #{article.info.title}
div(class='item-info')
img(class='filter-icon icon-small' alt=`Icon for Category ${article.category.name}` src=article.category.icon)
p(class='text-tip') #{article.category.name}
//- img(class='filter-icon icon-small' alt=`Avatar for Author ${article.author.name}` src=article.author.icon)
//- p(class='text-tip') #{article.author.name}
p(class='text-tip text-muted item-bull') &bull;
p(class='text-tip') #{formatDate(article.info.created)}
p(class='text-tip text-muted item-bull') &bull;
p(class='text-tip') #{calcReadTime(article.elements)} minute read
p(class='text-tip text-muted') #{article.info.snippet}
//- Page Select
if relevantArticles.length !== 0
div(class='main-wrapper')
div(class='main-aside')
div(class='main-content' role='main')
div(class='collection-pageselect')
- var isLastPage = (pageCurrent === 1)
- var noNextPage = (pageCurrent === pageTotal)
- var BASE_URL = '/blog' + (relevantCategory ? `/${relevantCategory.id}` : '')
- var hrefLastPage = BASE_URL + (pageCurrent === 2 ? '' : (isLastPage ? '' : `/${pageCurrent - 1}`))
- var hrefNextPage = BASE_URL + (noNextPage ? '' : `/${pageCurrent + 1}` )
a(class='default text-tip' href=(hrefLastPage) disabled=(isLastPage)) <
each _, index in Array(pageTotal)
div(class=('dot-circle ' + (index === (pageCurrent-1) ? 'dot-active': '')).trim())
a(class='default text-tip' href=(hrefNextPage) disabled=(noNextPage)) >
div(class='divider-spacer')
@@ -0,0 +1,26 @@
{
"itemsPerPage": 8,
"authors": [
{
"id": "bakonpancakz",
"name": "bakonpancakz",
"icon": "file://blog/author-bakonpancakz.png"
}
],
"categories": [
{
"id": "behind-the-scenes",
"name": "Behind The Scenes",
"icon": "file://blog/category-bts.png"
},
{
"id": "project-monke",
"name": "Project Monke",
"icon": "file://blog/category-pm.png"
}
],
"articles": [
"04.17.24.Hello World.xml",
"05.03.24.Repo of Knowledge.xml"
]
}
+55
View File
@@ -0,0 +1,55 @@
extends ../_TemplatePublic
block pageHeader
- const article = relevantArticle
- const author = relevantArticle.author
- const category = relevantArticle.category
link(rel='stylesheet' href='file://styles/browser-blog.css')
link(rel='stylesheet' href='file://styles/template-article.css')
meta(property='og:title' content=article.info.title)
meta(property='og:description' content=`By ${author.name} on ${formatDate(article.info.created)}\n---\n${article.info.snippet}`)
meta(name='description' content=`By ${author.name} on ${formatDate(article.info.created)}\n---\n${article.info.snippet}`)
meta(property='og:image' content=`${article.info.banner}?direct=true`)
meta(property='twitter:card' content='summary_large_image')
title blog | #{article.info.title} (#{category.name})
block pageContent
div(class='main-wrapper')
//- Layout: Sidebar
div(class='main-aside article-chapters')
p(class='text-headline') Chapters
each element in article.elements
if (element.type === 'header')
a(href=`#${formatString(element.value)}` class='text-tip') &bull; #{element.value}
a(class='text-tip text-muted' href='#') ▲ Back to Top
//- Mobile Users get a small triangle in the bottom right instead
a(class='default mobile-home' href='#home') ▲
//- Layout: Article
div(class='main-content' role='main' id='home')
//- Article Headeer
img(
class='article-banner special-dropshadow'
alt=`Banner for ${article.info.title}`
src=article.info.banner
)
p(class='text-hero article-header') #{article.info.title}
div(class='article-header')
div(class='item-info')
img(class='filter-icon' alt=`Icon for Category ${category.name}` src=category.icon)
a(class='text-tip' href=`/blog/${category.id}`) #{category.name}
p(class='text-tip')
| #{formatDate(article.info.created)}
p(class='text-tip')
| #{calcReadTime(article.elements)} minute read
div(class='item-info')
img(class='filter-icon' alt=`Avatar for Author ${author.name}` src=author.icon)
p(class='text-tip') #{author.name}
div(class='divider-line')
//- Article Content
include ../_TemplateArticle
div(style='height: 64px;')
script(src='file://scripts/shared-article.ts')
@@ -0,0 +1,57 @@
<article
id="applications"
authorId=""
categoryId="api"
title="Applications"
created="500"
snippet="Developer documentation for Applications"
banner=""
noindex="false"
>
<header>Introduction</header>
<text>
Applications allow you to integrate the suzzy API into your projects.
You can create and manage them while in the <a href='/profile/applications'>Applications</a>
section of your profile.
</text>
<subheader>Application Object</subheader>
<table>
<row>
<column>Field</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>id</column>
<column>integer</column>
<column>Unique Application ID</column>
</row>
<row>
<column>created</column>
<column>Date</column>
<column>ISO Timestamp of when application was created</column>
</row>
<row>
<column>flags</column>
<column>integer</column>
<column>the application's public flags</column>
</row>
<row>
<column>name</column>
<column>string</column>
<column>the application's name</column>
</row>
<row>
<column>description</column>
<column>string?</column>
<column>the application's description</column>
</row>
<row>
<column>icon</column>
<column>string?</column>
<column>the application's image hash</column>
</row>
</table>
</article>
@@ -0,0 +1,23 @@
<article
id="changelog"
authorId=""
categoryId="api"
title="Changelog"
created="-1"
snippet="Changelog for suzzy APIs and Services"
banner=""
noindex="false"
>
<header>Notices</header>
<text>Important and Breaking changes will be listed below:</text>
<list>
<item>Nothing to report!</item>
</list>
<header>History</header>
<subheader>Initial Release</subheader>
<quote>Date: May 12th 2024</quote>
<list>
<item>Applications are now generally available</item>
<item>suzzy Library now available</item>
</list>
</article>
@@ -0,0 +1,155 @@
<article
id="oauth2"
authorId=""
categoryId="api"
title="OAuth2"
created="400"
snippet="Developer documentation for OAuth2"
banner=""
noindex="false"
>
<header>Resources</header>
<text>
suzzy games provides support for OAuth2 allowing applications to
use the API for authorization and retrieval of user data.
</text>
<text>
It is recommended you
<a href="/library/api/tutorials#creating-an-application">Create an Application</a>
and store the given
<c>CLIENT_ID</c> and
<c>CLIENT_SECRET</c> for later use.
</text>
<subheader>OAuth2 URLs</subheader>
<table>
<row>
<column>URL</column>
<column>Description</column>
</row>
<row>
<column>https://suzzygames.com/oauth2</column>
<column>Base Authorization URL</column>
</row>
<row>
<column>https://apis.suzzygames.com/v1/oauth2/token</column>
<column>Token URL</column>
</row>
<row>
<column>https://apis.suzzygames.com/v1/oauth2/token/revoke</column>
<column>Token Revocation URL</column>
</row>
</table>
<quote>These endpoints will only accept the content type "application/x-www-form-urlencoded"</quote>
<subheader>OAuth2 Scopes</subheader>
<text>
A list of available scopes, some endpoints will return fields with a
<c>null</c> value or return an error if a required scope is unavailable.
</text>
<table>
<row>
<column>Name</column>
<column>Description</column>
</row>
<row>
<column>identify</column>
<column>Allows access to /users/@me endpoint</column>
</row>
<row>
<column>email</column>
<column>Allows /users/@me endpoint to return the user's email</column>
</row>
<row>
<column>connections</column>
<column>Allows access to /users/@me/applications endpoint</column>
</row>
</table>
<!-- OAuth2 User Flow -->
<header>Authorization Code Grant</header>
<text>
This is most commonly known as <b>standard OAuth2</b>, where the user
follows an Authorization Flow in return for an access code which the
application can exchange for the user's access token.
</text>
<quote>
All endpoints expect HTTP Basic Auth using your applications
CLIENT_ID as the username and CLIENT_SECRET as the password
</quote>
<subheader>Example Authorization URL</subheader>
<code syntax="skip">
https://suzzygames.com/oauth2?response_type=code&amp;redirect_uri=https%3A%2F%2Fexample.org&amp;client_id=1273&amp;redirect_uri=https%3A%2F%2Fexample.org&amp;scope=identify%20email&amp;state=1734831153572
</code>
<text>- <c>response_type</c> must always be code, as it is currently the only supported type.</text>
<text>- <c>redirect_uri</c> is one of the Redirect URIs set in your application settings, url encoded.</text>
<text>- <c>client_id</c> is your Application ID. </text>
<text>- <c>scope</c> is a list of <a href="#oauth2-scopes">OAuth2 Scopes</a> delimited by url encoded spaces <c>(%20)</c>.</text>
<text>- <c>state</c> is a unique string that will be returned if set.</text>
<subheader>Example Redirect URL</subheader>
<code syntax="skip">
https://example.org?code=9AED9A46641F3E3F489523C643C177BB.oboQpOkOSQJvdYYa4WuPq8J3TaYqNgylfq97sa99DRk&amp;state=1734831153572
</code>
<text>
The <c>code</c> can now be exchanged for a user's access token by
making a POST request to the <a href="#oauth2-urls">Token URL</a>
with the following parameters:
</text>
<text>- <c>code</c> - parameter taken from the query string</text>
<text>- <c>grant_type</c> - value must be <c>authorization_code</c></text>
<text>- <c>redirect_uri</c> - the <c>redirect_uri</c> from the authorization url</text>
<text>In response you will be bestowed an <a href="#access-token-response">Access Token Response</a></text>
<subheader>Access Token Response</subheader>
<table>
<row>
<column>Name</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>access_token</column>
<column>string</column>
<column>Token to include in authorization header</column>
</row>
<row>
<column>token_type</column>
<column>string</column>
<column>String to prepend to token in authorization header</column>
</row>
<row>
<column>expires_in</column>
<column>number</column>
<column>Seconds until access token expires</column>
</row>
<row>
<column>refresh_token</column>
<column>string</column>
<column>Token to submit when making a refresh request</column>
</row>
<row>
<column>scope</column>
<column>string</column>
<column>The relevant OAuth2 Scopes for this token (delimited with a space)</column>
</row>
</table>
<subheader>Refreshing Access Token</subheader>
<text>
Once an access token has expired you may refresh it by making a
POST request to the <a href="#oauth2-urls">Token URL</a> with
the following parameters:
</text>
<text>- <c>grant_type</c> - value must be <c>refresh_token</c></text>
<text>- <c>refresh_token</c> - the user's refresh token</text>
<text>In response you will be bestowed a fresh <a href="#access-token-response">Access Token Response</a></text>
<subheader>Revoking Access Token</subheader>
<text>
You can revoke an access token by making a POST request to the
<a href="#oauth2-urls">Token Revocation URL</a> with the following
parameters:
</text>
<text>- <c>token</c> - the user's access token or refresh token</text>
</article>
@@ -0,0 +1,108 @@
<article
id="reference"
authorId=""
categoryId="api"
title="Reference"
created="100"
snippet="References for suzzy APIs and Services"
banner=""
noindex="false"
>
<!-- Server URLs -->
<header>Sending Requests</header>
<text>
All requests to the api should be made to the following URL: <c>https://apis.suzzygames.com/v1/</c>
</text>
<!-- Server Ratelimiting -->
<header>Ratelimiting</header>
<text>
Each response will include headers related to ratelimiting, follow them otherwise
the server will automatically reject your request and respond with a
<c>429 Too Many Requests</c> error.
</text>
<table>
<row>
<column>Header</column>
<column>Description</column>
</row>
<row>
<column>RateLimit-Limit</column>
<column>Request Quota</column>
</row>
<row>
<column>RateLimit-Remaining</column>
<column>Requests Remaining</column>
</row>
<row>
<column>RateLimit-Reset</column>
<column>Float seconds until Quota Resets</column>
</row>
</table>
<quote>
Sending too many bad requests too quickly will result in your IP Address
being blocked temporarily.
</quote>
<quote>
Frequent abusers will get the ban hammer!
</quote>
<!-- User Assets -->
<header>Content URLs</header>
<text>
User Uploads <h>(Avatars, Banners, etc.)</h> are available at
<c>https://content.suzzygames.com/</c> using the following paths:
</text>
<table>
<row>
<column>Name</column>
<column>Path</column>
</row>
<row>
<column>User Avatars</column>
<column>/avatars/{user.id}/{user.avatar}/{extension}/{size}</column>
</row>
<row>
<column>User Banners</column>
<column>/banners/{user.id}/{user.banner}/{extension}/{size}</column>
</row>
</table>
<quote>Image Sizes: Large (192px), Medium (96px), Small (48px)</quote>
<quote>Image Extensions: webp, png, gif (if animated)</quote>
<quote>Animated images will have their hash begin with "a_"</quote>
<!-- User Default Assets -->
<header>Default Assets</header>
<text>
Ocasionally users won't have uploaded an avatar, you can use the provided
default ones by calculating the index with <c>index = {user.id} % 6</c> and using the follow URL:
</text>
<text>
<c>https://suzzygames.com/assets/images/default-user{index}.png</c>
</text>
<quote>Signed out users use "default-anon.png" instead</quote>
<subheader>Default Colors</subheader>
<text>
Default colors for user profiles are available below:
</text>
<table>
<row>
<column>Name</column>
<column>Color (Hex)</column>
</row>
<row>
<column>Profile Banner</column>
<column>#1D1E2E</column>
</row>
<row>
<column>Profile Background</column>
<column>#333550</column>
</row>
<row>
<column>Profile Border</column>
<column>#282A3F</column>
</row>
</table>
</article>
@@ -0,0 +1,28 @@
<article
id="tutorials"
authorId=""
categoryId="api"
title="Tutorials"
created="200"
snippet="Simple tutorials on certain things."
banner=""
noindex="false"
>
<header>Creating an Application</header>
<text>
You can create an application using the <a href="https://suzzygames.com/account/applications">My Applications</a>
page in the account management portal. Ensure your application has a
memorable name and that you keep its secret key secure and private.
</text>
<image
alt="Click on the Create Application Button"
caption="Click on the Create Application Button"
resource="file://library/tutorials/applications-step-1.png"
/>
<image
alt="You can then edit your Application by clicking on the Edit Button"
caption="You can then edit your Application by clicking on the Edit Button"
resource="file://library/tutorials/applications-step-2.png"
/>
<!-- TODO: IMPROVE OAUTH2 TUTORIAL -->
</article>
@@ -0,0 +1,212 @@
<article
id="users"
authorId=""
categoryId="api"
title="Users"
created="300"
snippet="Developer documentation for Users"
banner=""
noindex="false"
>
<!-- User Object Reference -->
<header>User Object</header>
<text>
Fields for a user object and the required oAuth2 Scope to view them.
Optional fields <h>(Type ending with ?)</h> or hidden fields <h>(Missing oAuth2 Scope)</h>
will have their value set as <c>null</c> instead.
</text>
<table>
<row>
<column>Field</column>
<column>Type</column>
<column>Description</column>
<column>Required oAuth2 Scope</column>
</row>
<row>
<column>id</column>
<column>integer</column>
<column>their unique account id</column>
<column>identify</column>
</row>
<row>
<column>created</column>
<column>timestamp</column>
<column>their account creation timestamp</column>
<column>identify</column>
</row>
<row>
<column>public_flags</column>
<column>integer</column>
<column>their public flags (badges)</column>
<column>identify</column>
</row>
<row>
<column>username</column>
<column>string</column>
<column>their username</column>
<column>identify</column>
</row>
<row>
<column>displayname</column>
<column>string</column>
<column>their displayname</column>
<column>identify</column>
</row>
<row>
<column>subtitle</column>
<column>string</column>
<column>their pronouns, status, etc.</column>
<column>identify</column>
</row>
<row>
<column>biography</column>
<column>string</column>
<column>their biography</column>
<column>identify</column>
</row>
<row>
<column>accent_background</column>
<column>integer</column>
<column>their profile background color</column>
<column>identify</column>
</row>
<row>
<column>accent_banner</column>
<column>integer</column>
<column>their profile banner color</column>
<column>identify</column>
</row>
<row>
<column>accent_border</column>
<column>integer</column>
<column>their profile border color</column>
<column>identify</column>
</row>
<row>
<column>avatar</column>
<column>string?</column>
<column>their avatar image hash</column>
<column>identify</column>
</row>
<row>
<column>banner</column>
<column>string?</column>
<column>their banner image hash</column>
<column>identify</column>
</row>
<row>
<column>verified</column>
<column>boolean</column>
<column>has email been verified?</column>
<column>identify</column>
</row>
<row>
<column>email</column>
<column>string?</column>
<column>their email address</column>
<column>email</column>
</row>
</table>
<quote>Usernames will always be alphanumeric with underscores and between 3-32 characters long</quote>
<quote>Usernames may change without warning, use account IDs instead!</quote>
<quote>Displaynames and Subtitles are up to 32 characters long</quote>
<quote>Biographies are up to 320 chracters long</quote>
<quote>Colors are in Decimal Representation</quote>
<!-- User Public Flags/Badges -->
<header>Public Flags</header>
<text>
Use a <b>Bitwise AND</b> operation in your language of choice to see if a flag is
set for a user. Flags are primarily used for badges which are special achievements
that were obtainable for a limited time or gifted to specific users.
</text>
<table>
<row>
<column>Value</column>
<column>Name</column>
<column>Description</column>
<column>Obtainable?</column>
</row>
<row>
<column>1 (1 &lt;&lt; 0)</column>
<column>BADGE_CROWN</column>
<column>Reserved for bakonpancakz</column>
<column>Never</column>
</row>
<row>
<column>2 (1 &lt;&lt; 1)</column>
<column>BADGE_VIP</column>
<column>Reserved for important users</column>
<column>No</column>
</row>
<row>
<column>4 (1 &lt;&lt; 2)</column>
<column>BADGE_PICROSS</column>
<column>Awarded by completing Challenge #1</column>
<column>Yes</column>
</row>
<row>
<column>8 (1 &lt;&lt; 3)</column>
<column>PUBLIC_UNAVAILABLE</column>
<column>Account is Unavailable</column>
<column>TOS Violation</column>
</row>
</table>
<quote>
Unavailable accounts, may be banned or deleted.
The reasoning will be sent to the account owners email.
</quote>
<!-- GET /users/@me -->
<header>Get Current User</header>
<text><c>GET /users/@me</c></text>
<text>
Returns the current user if you have the <c>identify</c> scope, also
returns email if you also have the <c>email</c> scope.
</text>
<!-- GET /users/@me/connections -->
<header>Get Connections for Current User</header>
<text><c>GET /users/@me/connections</c></text>
<text>
Requires the <c>connections</c> <a href='/library/api/oauth2#oauth2-scopes'>OAuth2 Scope</a>.
Returns an array of <a href="#connection-object">Connection</a> Objects the user is actively connected to.
</text>
<subheader>Connection Object</subheader>
<table>
<row>
<column>Field</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>id</column>
<column>integer</column>
<column>Unique Connection ID</column>
</row>
<row>
<column>created</column>
<column>Date</column>
<column>ISO Timestamp of when connection was created</column>
</row>
<row>
<column>scopes</column>
<column>integer</column>
<column>Associated OAuth2 scopes with connection</column>
</row>
<row>
<column>application</column>
<column>Application Object</column>
<column>An Application Object</column>
</row>
</table>
<text><i>Quick link to</i> <a href="/library/api/applications#application-object">Application Object</a>.</text>
<!-- GET /users/<user.username> -->
<header>Get User by Username </header>
<text><c>GET /users/&lt;user.username&gt;</c></text>
<text>
Returns a <a href="#user-object">user object</a> for a given username,
doesn't require any authorization.
</text>
</article>
@@ -0,0 +1,64 @@
<article
id="branding"
authorId=""
categoryId="home"
title="Branding"
created="200"
snippet="Branding Guidelines for suzzy games"
banner=""
noindex="true"
>
<!-- Guidelines -->
<header>Usage</header>
<text>
This page contains high quality versions of promotional assets from most
suzzy projects and games. Please use common sense and don't hesistate to
<a href="mailto:bakonpancakz@suzzygames.com">contact me</a> if you have any
questions.
</text>
<text>
<b>Do not use these assets commercially or for impersonation.</b>
</text>
<quote>Hint: You can click on any image to download it to your device.</quote>
<!-- Product: suzzy games -->
<header>suzzy games</header>
<beanie alt="suzzy games Logo" resource="file://library/branding/suzzy-logo.png?preset=branding" />
<beanie alt="suzzy games Icon" resource="file://library/branding/suzzy-icon.png?preset=branding" />
<quote>Trivia: The name for our ghost mascot is Boo! You can find them in the logo.</quote>
<quote>Trivia: The accent color for suzzy is #A1A8FF and is actually considered to be a light sky blue. Not purple!</quote>
<!-- Product: BECCA2D -->
<header>BECCA2D</header>
<text>
BECCA2D is a <b>2D Web Game Engine</b> developed by suzzy games and is
used in the upcoming <a href="/projects">super marimari_eno</a> fan-game.
</text>
<beanie alt="BECCA2D Logo" resource="file://library/branding/becca-logo.svg?preset=branding" />
<quote>Trivia: BECCA2D is named after my sister, since my nickname for her is Becca.</quote>
<!-- Product: Project Monke -->
<header>Project Monke</header>
<text>Project Monke is an upcoming platformer game developed by suzzy games.</text>
<beanie alt="PM Game Icon #1" resource="file://library/branding/monke-gameicon.png?preset=branding" />
<beanie alt="PM Game Icon #2" resource="file://library/branding/monke-gameicon-initials.png?preset=branding" />
<beanie alt="PM Game Logo #1" resource="file://library/branding/monke-logo-text.png?preset=branding" />
<beanie alt="PM Game Logo #2" resource="file://library/branding/monke-logo-initials.png?preset=branding" />
<beanie alt="PM Promo Art #1" resource="file://library/branding/monke-promo-1.png?preset=branding" />
<quote>Trivia: The Polka Dot pattern was inspired by DDLC+ promo art.</quote>
<!-- Product: Cart Ride around a Fumo -->
<header>Cart Ride Around a Fumo</header>
<text>
Cart Ride around a Fumo is a <a href="https://www.roblox.com/games/9987852646/Cart-Ride-Around-a-Fumo">ROBLOX experience</a> developed by suzzy games.
Promo and Game Icon Art drawn by <a href="https://twitter.com/MondaeAkari">@mondaeakari</a>.
</text>
<beanie alt="CRAF Game Icon #1" resource="file://library/branding/craf-gameicon-1.png?preset=branding" />
<beanie alt="CRAF Game Icon #2" resource="file://library/branding/craf-gameicon-2.png?preset=branding" />
<beanie alt="CRAF Promo Art #1" resource="file://library/branding/craf-promo-1.png?preset=branding" />
<beanie alt="CRAF Promo Art #2" resource="file://library/branding/craf-promo-2.png?preset=branding" />
<quote>
Trivia: The first icon didn't do so well to bring in new players.
Which sucks because I liked it ;-;
</quote>
</article>
@@ -0,0 +1,27 @@
<article
id="welcome"
authorId=""
categoryId="home"
title="Welcome"
created="100"
snippet="Welcome to the suzzy Library!"
banner=""
noindex="true"
>
<banner resource="file://library/introduction/banner-welcome.png" alt="Welcome Banner" />
<subheader>Welcome to the library!</subheader>
<text>
This site contains documentation and guides intended for developers.
You can navigate the website using the links on the side. <h>Or above if you're on mobile!</h>
</text>
<subheader>Heads up!</subheader>
<text>
Documentation and features are still early in development, you may occasionally
encounter an unexpected <c>500 Internal Server Error</c> or find a random typo.
</text>
<text>
Please send bug reports or feature requests to
<a href="mailto:bakonpancakz@gmail.com">bakonpancakz@gmail.com</a>
</text>
</article>
@@ -0,0 +1,24 @@
<article
id="privacy-policy"
authorId=""
categoryId="legal"
title="Privacy Policy"
created="999999"
snippet="Privacy Policy governing all suzzy services and products"
noindex="true"
banner=""
>
<header>Introduction</header>
<text>
This document entails the general privacy policy, covering all suzzy services.
Specific services may override it as necessary — please refer to their respective documents for more information.
</text>
<text>
<c>Last Updated on May 11th 2024</c>
</text>
<header>1. todo</header>
<text>
<h>here's where i would put my privacy policy. IF I HAD ONE!</h>
</text>
</article>
@@ -0,0 +1,24 @@
<article
id="terms-of-service"
authorId=""
categoryId="legal"
title="Terms of Service"
created="999998"
snippet="Terms of Service governing all suzzy services and products"
noindex="true"
banner=""
>
<header>Introduction</header>
<text>
This document entails the general terms of service, covering all suzzy services.
Specific services may override it as necessary — please refer to their respective documents for more information.
</text>
<text>
<c>Last Updated on May 11th 2024</c>
</text>
<header>1. todo</header>
<text>
<h>here's where i would put my terms of service. IF I HAD ONE!</h>
</text>
</article>
@@ -0,0 +1,135 @@
<article
id="endpoints"
authorId=""
categoryId="status"
title="Endpoints"
created="100"
snippet=""
banner=""
noindex="false"
>
<!-- GET /widget.json -->
<header>Get Widget</header>
<text><c>GET /widget.json</c></text>
<text>
Returns a unique <a href="#widget-object">Widget Object</a>. This
is the same endpoint use for the widget displayed in the top-right
corner of the suzzy games webpage.
</text>
<subheader>Widget Object</subheader>
<table>
<row>
<column>Field</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>status</column>
<column>string</column>
<column>Current status either "online", "degraded", or "down"</column>
</row>
<row>
<column>counter_players_online</column>
<column>number?</column>
<column>Estimated number of players online</column>
</row>
</table>
<quote>This endpoint returns the headers 'X-Refresh' and 'Etag'</quote>
<!-- GET /status.json -->
<header>Get Status</header>
<text><c>GET /status.json</c></text>
<text>
Returns a unique <a href="#status-object">Status Object</a>. Includes
the current status, an array of
<a href="/library/status/reference#tracker-object">tracker objects</a>,
and an array of
<a href="/library/status/reference#event-object">event objects</a>
which occurred today.
</text>
<subheader>Status Object</subheader>
<table>
<row>
<column>Field</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>status</column>
<column>string</column>
<column>Current status either "online", "degraded", or "down"</column>
</row>
<row>
<column>trackers</column>
<column>Array&lt;Trackers&gt;</column>
<column>List of Trackers</column>
</row>
<row>
<column>events</column>
<column>Array&lt;Events&gt;</column>
<column>List of Events for Today</column>
</row>
</table>
<quote>This endpoint returns the headers 'X-Refresh' and 'Etag'</quote>
<text>
<b>Notice:</b> This endpoint is compressed with <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/gzip_compression">gzip</a>.
Your client must support and notify the server that it accepts gzip
encoded content via the <c>Accept-Encoding</c> header. Otherwise
your request will be rejected with a <c>415 Unsupported Media Type</c>
status code.
</text>
<!-- GET /events -->
<header>Get Events</header>
<text><c>GET /events</c></text>
<text>
Returns an array of <a href="/library/status/reference#event-object">Event Objects</a>
for today. You can request a specific day using the query parameter <c>date</c> in
<c>MM/DD/YYYY</c> format <h>(ex. date=06/26/2003)</h>.
</text>
<!-- GET /status.json -->
<header>Get Events Summary</header>
<text><c>GET /events/summary</c></text>
<text>
Returns a Unique <a href="#summary-object">Summary Object</a> for this month.
You can request a specific month using the query parameter <c>date</c> in
<c>MM/YYYY</c> format. <h>(ex. date=06/2003)</h>
</text>
<subheader>Summary Object</subheader>
<table>
<row>
<column>Field</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>uptime</column>
<column>string</column>
<column>Uptime Percentage in Float</column>
</row>
<row>
<column>results</column>
<column>...</column>
<column>See Description Below</column>
</row>
</table>
<text>
The results field is a JSON object with the <b>keys</b> being the <i>day of the month</i>, and it's
<b>value</b> being an <i>Array with three integers</i>. Each integer represents how many seconds of
<b>Degredation</b>, <b>Downtime</b>, and <b>Maintenance</b> occurred
that day, respectively.
</text>
<code syntax="json">
{
"uptime": "99.99", // Uptime Percent as Float
"results": {
"0": [0, 0, 3600], // "Day": [Seconds Degraded, Downtime, Maintenance]
"12": [300, 0, 0], // Ex. 5 Minutes of Downtime on the 12th day of the month
}
}
</code>
</article>
@@ -0,0 +1,310 @@
<article
id="reference"
authorId=""
categoryId="status"
title="Reference"
created="0"
snippet=""
banner=""
noindex="false"
>
<!-- Server URLs -->
<header>Sending Requests</header>
<text>
All requests to the <b>Status Service</b> should be made to the following URL:
<c>https://status.suzzygames.com/api/</c>
</text>
<header>Automatic Refreshing</header>
<text>
This service updates the <a href="/library/status/endpoints#fetch-widget">widget</a>
and <a href="/library/status/endpoints#fetch-status">status</a> endpoints
every minute.
</text>
<text>
You can use the <c>X-Refresh</c> header to calculate how many float seconds
your client should wait before attempting to make another request.
</text>
<text>
This service also returns a <c>ETag</c> header which you can optionally include
in the next request. If nothing has changed the service will respond with a
<c>304 Not Modified</c> and an <b>empty body</b>.
</text>
<code syntax="yml">
X-Refresh: 12.73 # Seconds until next refresh
ETag: 1735594572 # Unique Cache Identifier
</code>
<quote>
Note: The 'X-Refresh' header is randomly offset up to 5 seconds to
help mitigate the sudden spike in traffic.
</quote>
<header>Service Unavailable</header>
<text>
On rare occasions the Service may return a <c>503 Service Unavailable</c>
error. This happens when the service has just restarted <h>(usually due to an update)</h>
and is doing some digital housekeeping before preparing the <a href="/library/status/endpoints#fetch-widget">widget</a>
and <a href="/library/status/endpoints#fetch-status">status</a> endpoints.
</text>
<text>
The error will however return the <c>X-Refresh</c> header and the
aforementioned endpoints should become available then.
</text>
<quote>
Note: The status service always returns an empty body when an error occurs.
Your client should not attempt to parse any JSON.
</quote>
<header>About Trackers</header>
<subheader>Tracker Object</subheader>
<text>
All trackers share the same base fields, but some fields are only
included for their respective types.
</text>
<text>
<b>Charts</b> take in values from multiple servers and average them
every specified interval compiling it into a <a href="#chart-data-point">Data Point</a>.
Charts return the <c>interval</c>, <c>history</c>, and <c>history_length</c> fields.
</text>
<text>
<b>Counters</b> take in individual numbers from multiple servers and accumulate
them together as one number. Counters return the <c>value</c> field.
</text>
<text>
<b>Services</b> keep track of multiple servers to ensure they're healthy and operational.
If the health check fails their status is changed to either <b>degraded</b> or <b>down</b>
generating an event and alerting an engineer.
Services return the <c>status</c> field.
</text>
<table>
<row>
<column>Field</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>id</column>
<column>string</column>
<column>Unique Tracker Identifier</column>
</row>
<row>
<column>public</column>
<column>boolean</column>
<column>Is this tracker public? (Internal)</column>
</row>
<row>
<column>ephemeral</column>
<column>boolean</column>
<column>Clear tracker data on startup? (Internal)</column>
</row>
<row>
<column>name</column>
<column>string</column>
<column>User-Friendly name for this tracker</column>
</row>
<row>
<column>description</column>
<column>string</column>
<column>Description that appears when mouse is hovered over</column>
</row>
<row>
<column>tags</column>
<column>Array&lt;string&gt;</column>
<column>Strings used to categorize this tracker</column>
</row>
<row>
<column>type</column>
<column>integer</column>
<column>The Type of Tracker</column>
</row>
<row>
<column>value</column>
<column>integer?</column>
<column>COUNTER: Current Tracker Value</column>
</row>
<row>
<column>status</column>
<column>integer?</column>
<column>SERVICE: Current Service Status</column>
</row>
<row>
<column>interval</column>
<column>integer?</column>
<column>CHART: Data Point Interval</column>
</row>
<row>
<column>history_length</column>
<column>integer?</column>
<column>CHART: Data Point History Length</column>
</row>
<row>
<column>history</column>
<column>Array&lt;DataPoint&gt;?</column>
<column>CHART: Data Point History</column>
</row>
</table>
<subheader>Tracker Types</subheader>
<table>
<row>
<column>Enum</column>
<column>Value</column>
</row>
<row>
<column>COUNTER</column>
<column>1</column>
</row>
<row>
<column>CHART</column>
<column>2</column>
</row>
<row>
<column>SERVICE</column>
<column>3</column>
</row>
</table>
<subheader>Service Status</subheader>
<table>
<row>
<column>Enum</column>
<column>Value</column>
</row>
<row>
<column>HEALTHY</column>
<column>0</column>
</row>
<row>
<column>DEGRADED</column>
<column>1</column>
</row>
<row>
<column>DOWN</column>
<column>2</column>
</row>
</table>
<subheader>Chart Data Point</subheader>
<text>
<b>Data Points</b> are Arrays that contain two integers.
The first integer is a timestamp for when the service compiled
the data into a point and can be converted into a UNIX Timestamp with
<c>TIMESTAMP * (INTERVAL * 60) * 1000</c>. The second integer is the
calculated average value.
</text>
<text>
You can then reverse iterate over the <a href="#tracker-object">trackers history field</a>
to plot the values on a line chart.
</text>
<table>
<row>
<column>Index</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>0</column>
<column>integer</column>
<column>Data Timestamp</column>
</row>
<row>
<column>1</column>
<column>integer</column>
<column>Data Value</column>
</row>
</table>
<header>About Events</header>
<subheader>Event Object</subheader>
<text>
Events are generated when downtime is detected or for when downtime is
scheduled. They include <b>"helpful"</b> messages either generated by
the service itself or personally written by an engineer.
</text>
<table>
<row>
<column>field</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>id</column>
<column>string</column>
<column>Unique Event Identifier</column>
</row>
<row>
<column>type</column>
<column>integer</column>
<column>Event Type</column>
</row>
<row>
<column>affects</column>
<column>string</column>
<column>The ID of Tracker this event pertains to</column>
</row>
<row>
<column>occurred</column>
<column>ISO Timestamp</column>
<column>When this event began</column>
</row>
<row>
<column>resolved</column>
<column>ISO Timestamp?</column>
<column>When this event was resolved</column>
</row>
<row>
<column>messages</column>
<column>Array&lt;Message&gt;</column>
<column>List of Message Objects (Unsorted)</column>
</row>
</table>
<subheader>Event Types</subheader>
<table>
<row>
<column>Enum</column>
<column>Value</column>
</row>
<row>
<column>DEGREDATION</column>
<column>0</column>
</row>
<row>
<column>DOWNTIME</column>
<column>1</column>
</row>
<row>
<column>MAINTENANCE</column>
<column>2</column>
</row>
</table>
<quote>Their respective Hex Colors: #f1c40f, #ff005b, #6cc0e0</quote>
<subheader>Message Object</subheader>
<text>
These message contain HTML Content. Which usually only use the
standard text formatting tags <c>&lt;b&gt; &lt;s&gt; &lt;i&gt;</c>.
</text>
<text>
<a target="_blank" href="">They may also occasionally include an image tag.</a>
</text>
<table>
<row>
<column>Field</column>
<column>Type</column>
<column>Description</column>
</row>
<row>
<column>created</column>
<column>ISO Timestamp</column>
<column>When this message was written</column>
</row>
<row>
<column>message</column>
<column>string</column>
<column>HTML Content</column>
</row>
</table>
</article>
@@ -0,0 +1 @@
extends template
@@ -0,0 +1,47 @@
{
"itemsPerPage": 8,
"noBrowser": true,
"authors": [
{
"id": "",
"name": "suzzy",
"icon": ""
}
],
"categories": [
{
"id": "home",
"name": "Home",
"icon": ""
},
{
"id": "legal",
"name": "Legal",
"icon": ""
},
{
"id": "api",
"name": "API",
"icon": ""
},
{
"id": "status",
"name": "Status",
"icon": ""
}
],
"articles": [
"home/Welcome.xml",
"home/Branding.xml",
"legal/Privacy Policy.xml",
"legal/Terms of Service.xml",
"api/Users.xml",
"api/OAuth2.xml",
"api/Applications.xml",
"api/Reference.xml",
"api/Tutorials.xml",
"api/Changelog.xml",
"status/Reference.xml",
"status/Endpoints.xml"
]
}
@@ -0,0 +1 @@
extends template
@@ -0,0 +1,110 @@
-
// Create Sidebar Categories
const t = {}
for (const a of articles) {
const k = categories.find(c => c.id === a.category.id).name
t[k] ? t[k].push(a) : t[k] = [a]
}
// Sort Sidebar Articles
for (const v of Object.values(t)) {
v.sort((a, b) => parseInt(a.info.created) - parseInt(b.info.created))
}
doctype html
html(lang='en')
head
meta(charset='UTF-8')
meta(name='viewport' content='width=device-width, initial-scale=1.0')
link(rel='icon' type='image/png' href='file://library/favicon.png')
link(rel='stylesheet' href='file://styles/template-global.css')
link(rel='stylesheet' href='file://styles/template-article.css')
link(rel='stylesheet' href='file://styles/browser-library.css')
link(rel='stylesheet' href='file://fonts/font-poppins.css')
meta(property='theme-color' content='#A1A8FF')
meta(property='og:image' content='file://library/logo-og.png?direct=true')
meta(property='og:title' content=`library | ${relevantArticle.category.name} > ${relevantArticle.info.title}`)
meta(property='og:description' content=relevantArticle.info.snippet)
meta(name='description' content=relevantArticle.info.snippet)
title library - #{relevantArticle.info.title} (#{relevantArticle.category.name})
body
div(class='wrapper-search')
div(class='container-search special-dropshadow')
div(class='search-actions')
input(class='search-input' tabindex='-1' type='text' name='Search Library' placeholder='Search Library')
button(class='search-close' tabindex='-1') &times;
div(class='search-body')
//- Loading Icon
div(class='pane' id='loading')
img(src='file://icons/diagram-loading.svg' style='width: 64px;')
//- Error Message
div(class='pane' id='error')
img(src='file://library/search/search-error.png')
span I ran into an issue...
p(class='text-tip text-muted') TypeError: Failed to Fetch
//- First Open/Empty Query
div(class='pane' id='help')
img(src='file://library/search/search-initial.png')
span Can't find something? Let me help you out!
p(class='text-tip text-muted') start typing to begin our search
//- Empty Results
div(class='pane' id='empty')
img(src='file://library/search/search-empty.png')
span I couldn't find anything!
p(class='text-tip text-muted') try again with some different keywords
//- Some Results
div(class='pane' id='results')
div(class='search-footer')
p(class='text-tip text-muted')
div(style='flex-basis: 100%;')
p Close #[span ESC]
p Navigate #[span ⬆⬇]
p Select #[span ↩]
div(class='wrapper-layout')
div(class='layout-navigation background-scroll special-shadow')
//- Logo and Search Box
a(class='navigation-button' id='skipper' href='#home')
| Skip to Content
a(href='/')
img(
class='navigation-logo'
alt='Logo for suzzy games'
src='file://library/logo-navigation.png'
)
button(class='navigation-button' id='search-open')
| Search Library
//- Render Sidebar
each [categoryName, categoryArticles] of Object.entries(t)
p(class='navigation-header') #{categoryName}
each someArticle of categoryArticles
- var thisLink = `${relevantArticle.category.id}/${relevantArticle.info.id}`
- var someLink = `${someArticle.category.id}/${someArticle.info.id}`
- var matchLink = (thisLink === someLink)
a(class='navigation-link' href=`/library/${someLink}` active=matchLink)
| #{someArticle.info.title}
if matchLink
each element of someArticle.elements
if element.type === 'header'
a(class='navigation-chapter' href=`#${formatString(element.value)}`)
| #{element.value}
div(class='layout-content' id='home')
//- Article Content
include ../_TemplateArticle
//- Mobile Users get a Return to Top Button
//- Also includes spacer so content doesn't cover button
div(class='layout-spacer')
a(class='default layout-return' href='#home') ▲
script(src='file://scripts/shared-article.ts')
script(src='file://scripts/browser-library.ts')
@@ -0,0 +1,102 @@
extends ../_TemplateProfile
block pageHeader
title suzzy - Manage Applications
block pageContent
- var loading_message = 'Applications'
- var empty_message = 'No Applications'
- var empty_source = 'file://icons/icon-app-none.svg'
- var empty_alt = 'No Apps'
p(class='text-hero') Applications
p(class='text-tip') Create applications that interface with suzzy games and it's users.
p(class='text-tip')
| Check out the #[a(href='/library' class='text-tip') library]
| for documentation on how to develop with suzzy games.
div(class='collection-divider')
button(class='default wide' id='action-create' hidden)
| Create New Application
include ../_TemplateProfilePage
//- //- Template Redirect URL
//- div(class='input-redirect' hidden)
//- input(
//- type='text'
//- class='text'
//- autocomplete='off'
//- maxlength=128
//- placeholder='https://example.org/oauth2-redirect'
//- required=true
//- pattern='^(http|https)://.*'
//- )
//- button(class='default destructive') Delete
//- //- Templat Application
//- div(class='collection-item special-shadow' id='application-template' hidden)
//- //- Application Metadata
//- div(class='application-header')
//- img(
//- id='app-icon'
//- aria-hidden='true'
//- class='application-icon'
//- onload='this.style.opacity=1'
//- )
//- div(class='application-info')
//- p(id='app-name' class='text-header') Name
//- p(id='app-desc' class='text-tip') Description
//- p(class='text-tip text-muted' id='verified-message' hidden) ★ Verified Application
//- button(id='input-edit' class='default') Edit
//- //- Application Options
//- div(class='application-options options-hidden')
//- div(class='divider-line')
//- //- Personalization
//- p(class='text-header') About
//- input(
//- id='input-name'
//- placeholder='Name'
//- autocomplete='off'
//- type='text'
//- class='text'
//- maxlength=32
//- )
//- textarea(
//- id='input-desc'
//- placeholder='Description'
//- autocomplete='off'
//- class='text'
//- maxlength=320
//- rows=5
//- style='max-height:200px;'
//- )
//- input(
//- type='file'
//- id='input-icon-file'
//- accept='image/jpeg, image/webp, image/gif, image/png'
//- hidden
//- )
//- button(class='default wide' id='input-icon')
//- | Upload Icon
//- button(class='small text-tip' id='input-icon-remove')
//- | Remove Icon
//- //- Redirects
//- div(class='divider-line')
//- p(class='text-header') Redirects
//- div(id='container-redirects')
//- button(class='default wide' id='input-redirect-new')
//- | New Redirect
//- p(class='text-tip' id='input-clientid') Client ID:
//- //- Actions
//- p(id='status-message')
//- button(class='default wide destructive' id='input-discard' disabled)
//- | Discard
//- button(class='default wide' id='input-save' disabled)
//- | Save Changes
//- //- Danger Zone
//- div(class='divider-line')
//- button(class='default destructive wide' id='input-reset')
//- | Reset Secret Key
//- button(class='default destructive wide' id='input-delete')
//- | Delete Application
script(src='file://scripts/profile-applications.ts')
@@ -0,0 +1,37 @@
extends ../_TemplateProfile
block pageHeader
title suzzy - Manage Connections
block pageContent
- var loading_message = 'Connections'
- var empty_message = 'No Connections'
- var empty_source = 'file://icons/icon-plug-socket.svg'
- var empty_alt = 'Disconnect'
p(class='text-hero') Connections
p(class='text-tip') Manage the connections your account has with applications.
p(class='text-tip')
| Official Applications are labelled with
| #[span(class='text-tip text-muted') (&#9733; Verified Application)]
| under their description.
div(class='collection-divider')
include ../_TemplateProfilePage
//- //- Template Scope
//- div(class='scope' id='scope-template' hidden)
//- div(class='dot-green')
//- span Scope Description
//- //- Template Item
//- div(class='collection-item special-shadow' id='application-template' hidden)
//- div(class='application-header')
//- img(aria-hidden='true' class='application-icon' onload='this.style.opacity=1')
//- div(class='application-info')
//- p(class='text-header') Name
//- p(class='text-tip') Description
//- p(class='text-tip text-muted' id='verified-message' hidden) ★ Verified Application
//- p(class='text-error' id='error-message')
//- button(class='default destructive') Disconnect
//- div(class='text-box')
//- p(class='text-tip') This application can:
//- div(class='scopes')
script(src='file://scripts/profile-connections.ts')
@@ -0,0 +1,30 @@
extends ../_TemplateProfile
block pageHeader
title suzzy - Manage Devices
block pageContent
- var loading_message = 'Devices'
- var empty_message = 'No Devices'
- var empty_source = 'file://icons/icon-app-none.svg'
- var empty_alt = 'Devices'
p(class='text-hero') Devices
p(class='text-tip') Here's a list of devices that are currently logged in with your account.
p(class='text-tip') If you see a device you don't recognize, log it out and change your password immediately.
div(class='collection-divider')
include ../_TemplateProfilePage
//- //- Template Item
//- div(class='item-device collection-item special-shadow' id='device-template' hidden)
//- div(class='device-icon')
//- img(aria-hidden='true' src='/assets/images/icons/icon-desktop.svg' alt='Icon for Desktop')
//- div(class='device-info')
//- div(class='info-alongside')
//- p(id='browser') Chrome on Windows 10
//- p(id='me' class='text-tip text-muted' hidden) (This Device)
//- div(class='info-alongside')
//- p(id='location') Modesto, United States
//- p(id='lastseen' class='text-tip text-muted') 3 hours ago
//- p(class='text-error' id='error-message')
//- button(class='device-action')
//- img(src='/assets/images/icons/icon-cross.svg' alt='Icon for Cross')
script(src='file://scripts/profile-devices.ts')
@@ -0,0 +1,96 @@
extends ../_TemplateProfile
block pageHeader
title suzzy - Manage Profile
block pageContent
- var loading_message = 'Profile'
p(class='text-hero') Profile
p(class='text-tip') Personalize your profile to make it uniquely yours!
p(class='text-tip') Animated Avatars and Banners are supported! Uploads up to 8MB are allowed.
div(class='collection-divider')
include ../_TemplateProfilePage
//- div(class='profile-options text-nopad')
//- //- About Section
//- p(class='text-header') About Me
//- input(
//- type='text'
//- id='input-displayname'
//- class='text'
//- placeholder='Displayname'
//- maxlength=32
//- )
//- input(
//- type='text'
//- id='input-subtitle'
//- class='text'
//- placeholder='Subtitle'
//- maxlength=32
//- )
//- textarea(
//- id='input-bio'
//- class='text'
//- placeholder='Biography'
//- rows=3
//- maxlength=320
//- style='max-height:200px;'
//- )
//- //- Images Section
//- p(class='text-header') Images
//- input(
//- type='file'
//- id='input-avatar-file'
//- accept='image/jpeg, image/webp, image/gif, image/png'
//- hidden
//- )
//- input(
//- type='file'
//- id='input-banner-file'
//- accept='image/jpeg, image/webp, image/gif, image/png'
//- hidden
//- )
//- button(class='default wide' id='button-avatar')
//- | Upload Avatar
//- button(class='small text-tip' id='button-avatar-remove')
//- | Remove Avatar
//- button(class='default wide' id='button-banner')
//- | Upload Banner
//- button(class='small text-tip' id='button-banner-remove')
//- | Remove Banner
//- //- Colors Section
//- p(class='text-header') Accents
//- div(class='widget-container')
//- div(class='widget-color')
//- label(class='text-tip' for='input-banner') Banner
//- input(id='input-banner' type='color' class='color-preview')
//- div(class='widget-color')
//- label(class='text-tip' for='input-border') Border
//- input(id='input-border' type='color' class='color-preview')
//- div(class='widget-color')
//- label(class='text-tip' for='input-background') Background
//- input(id='input-background' type='color' class='color-preview')
//- //- Actions Section
//- div(class='divider-line')
//- p(id='profile-status')
//- button(class='default wide destructive' id='button-cancel' disabled)
//- | Discard
//- button(class='default wide' id='button-submit' disabled)
//- | Save Changes
//- //- Preview
//- div(class='profile-container special-shadow')
//- div(id='profile-banner')
//- div(class='profile-header')
//- img(id='profile-avatar' alt='My Avatar')
//- div(id='profile-badges')
//- div(class='profile-about text-nopad')
//- div(class='info-alongside')
//- p(id='profile-displayname')
//- p(id='profile-username')
//- p(id='profile-subtitle' class='text-tip')
//- div(class='divider-line' id='profile-divider' hidden)
//- pre(id='profile-bio' class='text-tip')
script(src='file://scripts/profile-index.ts')
@@ -0,0 +1,57 @@
extends ../_TemplateProfile
block pageHeader
title suzzy - Manage Account
block pageContent
- var loading_message = 'Account'
p(class='text-hero') Account
p(class='text-tip') Welcome to your account's security settings.
p(class='text-tip') Here, you can manage various security options to help protect your account.
div(class='collection-divider')
include ../_TemplateProfilePage
//- div(id='security-wrapper' hidden)
//- //- Email Address
//- p(class='text-header') Email Address
//- p(class='text-tip' id='preview-email' aria-live='polite')
//- | Current Email Address:
//- p(class='text-error' id='error-email')
//- div(class='collection-wrapper')
//- button(class='default' id='email-reveal')
//- | Reveal Email Address
//- button(class='default' id='email-edit')
//- | Edit Email Address
//- button(class='default' id='email-resend' hidden)
//- | Resend Verification Email
//- div(class='divider-spacer')
//- //- Password
//- p(class='text-header') Password
//- p(class='text-tip') Change your account password, doing so will sign you out on all other devices.
//- button(class='default' id='password-change')
//- | Change Password
//- div(class='divider-spacer')
//- //- Multi-Factor
//- p(class='text-header') Multi-Factor Authentication (MFA)
//- p(class='text-tip') Use an Authenticator App to add an extra layer of security to your account.
//- p(class='text-error' id='error-mfa')
//- div(class='collection-wrapper')
//- button(class='default' id='mfa-setup-start' hidden)
//- | Setup Authenticator App
//- button(class='default destructive' id='mfa-setup-remove' hidden)
//- | Remove Authenticator App
//- button(class='default' id='mfa-recovery-view' hidden)
//- | View Recovery Codes
//- button(class='default destructive' id='mfa-recovery-regen' hidden)
//- | Generate New Codes
//- div(class='divider-spacer')
//- //- Account Deletion
//- div(class='divider-spacer')
//- p(class='text-header') Danger Zone
//- p(class='text-tip') Buttons that will cause major changes to your account.
//- p(class='text-error' id='error-danger')
//- button(class='default destructive' id='danger-deleteaccount')
//- | Request Account Deletion
script(src='file://scripts/profile-security.ts')
@@ -0,0 +1,187 @@
extends ../_TemplateSpecial
block pageHeader
meta(name='robots' content='noindex')
title Secret Challenge - Picross!
// Was bored and I love picross puzzles. Enjoy this little minigame.
// This is also a one-time thing! There are no more easter eggs that give out badges!
block pageEscaped
script(src='file://scripts/shared-utils.ts')
script(src='file://scripts/challenge-picross.ts')
block pageContent
// In case you're wondering I sampled my keyboard for the audio.
// Also no you cannot mute it, now suffer.
audio(hidden src='file://audio/picross_2.mp3' id='set')
audio(hidden src='file://audio/picross_1.mp3' id='release')
p(class='text-header') Secret Challenge: Picross!
p(class='text-tip') Solve this puzzle to earn the picross badge for your profile.
p(class='text-tip text-muted') Controls: Left click to fill, right click to mark.
// thank you gzip for savin bandwidth
div(class='special-picross')
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint') 1
p(class='picross-hint')
p(class='picross-hint') 1
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint') 1
p(class='picross-hint') 3
p(class='picross-hint') 1
p(class='picross-hint')
p(class='picross-hint') 1
p(class='picross-hint')
p(class='picross-hint')
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint') 1
p(class='picross-hint') 7
p(class='picross-hint') 5
p(class='picross-hint') 5
p(class='picross-hint') 4
p(class='picross-hint') 8
p(class='picross-hint') 3
p(class='picross-hint') 2
p(class='picross-hint') 2
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint') 3
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
div(class='picross-row')
p(class='picross-hint') 1
p(class='picross-hint') 1
p(class='picross-hint') 1
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint') 5
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint') 2
p(class='picross-hint') 2
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint') 3
p(class='picross-hint') 1
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint') 5
p(class='picross-hint') 1
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint') 8
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint') 7
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
div(class='picross-row')
p(class='picross-hint')
p(class='picross-hint')
p(class='picross-hint') 5
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
button(class='picross-button' dynamic)
//- User Actions
div(class='divider-line')
p(class='text-tip' id='time-spent') Time Spent: 00:00:00
div(class='special-alongside')
// NEVER GIVE UP!!!
a(dynamic class='default' target='_blank' href='https://www.youtube.com/watch?v=KxGRhd_iWuE&t=2s' onclick='this.onclick=()=>{ alert("quitter..."); window.location.assign("/") }') I Give Up...
a(dynamic class='default' id='done') Check it!
@@ -0,0 +1,28 @@
extends ../_TemplatePublic
block pageHeader
link(rel='stylesheet' href=`file://styles/public-homepage.css`)
meta(property='og:title' content='suzzy games')
meta(property='og:description' content='The official suzzy games website.')
meta(name='description' content='The official suzzy games website.')
meta(property='og:image' content='file://images/public-logo-og.png')
title suzzy games
block pageEscaped
//- Widget: Hero
div(class='widget widget-hero-wrapper')
div(class='widget-hero')
p(class='text-hero') Welcome!
p
| suzzy games is a personal project from the mind of
| #[a(target='_blank' href='https://twitter.com/bakonpancakz') bakonpancakz]
i
p(class='text-muted text-tip') ❝There is no theme, there is no incentive, just pure passion and efficient code❞
div(class='divider-spacer')
p All projects are stuck in development hell until they suddenly aren't
i
p(class='text-tip')
| Check out the #[a(class='text-tip' href='/blog') blog] to see what im up to
| or play some old #[a(class='text-tip' href='/projects') projects]
div(class='divider-spacer')
p(class='text-tip text-muted' id='birthday')
| Active since May 13th, 2021
script(src='file://scripts/public-homepage.ts')
@@ -0,0 +1,6 @@
extends ../_TemplateSpecial
block pageHeader
title suzzy games - Login
block pageEscaped
script(src='file://scripts/login-index.ts')
img(class='special-corner' src='file://images/login-globe.png')
@@ -0,0 +1,6 @@
extends ../_TemplateSpecial
block pageHeader
title suzzy games - Reset Password
block pageEscaped
script(src='file://scripts/login-passwordreset.ts')
img(class='special-corner' src='file://images/login-globe.png')
@@ -0,0 +1,6 @@
extends ../_TemplateSpecial
block pageHeader
title suzzy games - Update Password
block pageEscaped
script(src='file://scripts/login-passwordupdate.ts')
img(class='special-corner' src='file://images/login-globe.png')
@@ -0,0 +1,105 @@
extends ../_TemplatePublic
block pageHeader
link(rel='stylesheet' href='file://styles/public-projects.css')
meta(property='og:title' content='suzzy games - projects')
meta(property='og:description' content='Projects from over the years.')
meta(name='description' content='Projects from over the years.')
meta(property='og:image' content='https://suzzygames.com/assets/images/og-logo.png')
title suzzy games - projects
block pageEscaped
-
const PROJECT_MANIFEST = [
{
accent: '#ff9d2f',
brightness: 0.75,
background: 'file://projects/monke/background.jpeg',
name: 'Project Monke',
state: 'In Development',
platforms: ['desktop', 'mobile'],
images: [
{ label: 'Promo Banner', url: 'file://projects/monke/image-1.jpeg' },
],
description: [
'Can only be explained as Super Monkey Ball and Super Mario Maker in one.',
'Check the blog for news!',
],
actions: [
{ label: 'View Blog Posts', url: '/blog/project-monke', target: '' },
],
},
{
accent: '#91defd',
brightness: 0.75,
name: 'Cart Ride Around a Fumo',
background: 'file://projects/craf/background.jpeg',
platforms: ['desktop', 'mobile'],
state: '2021',
images: [
{ label: 'Promo Artwork #1', url: 'file://projects/craf/image-1.jpeg' },
{ label: 'Promo Artwork #2', url: 'file://projects/craf/image-2.jpeg' },
],
description: [
'My own spin on the classic ROBLOX cart ride genre.',
'Centered around fumos for no specific reason.',
'Warning: Contains explosions',
],
actions: [
{ label: 'Play on ROBLOX', url: 'https://www.roblox.com/games/9987852646/Cart-Ride-Around-a-Fumo', target: '_blank' },
],
}
]
//- Layout: Header
div(class='wrapper-widget-hero')
div(class='widget-hero')
p(class='text-hero') > Projects <
p All of my projects in reverse chronological order (a.k.a. new to old)
//- Layout: Projects
each p in PROJECT_MANIFEST
div(class='widget widget-project' style=`--accent: ${p.accent}; --brightness: ${p.brightness}; --background: url('${p.background}');`)
//- Stack div elements with background underneath the foreground
//- Image will be darkened using 'brightness' css variable
div(class='project-background')
div(class='project-foreground')
//- Project: Metadata
div(class='project-content')
//- Name, Platforms, Descriptions
div(class='project-info')
div(class='project-header')
p(class='text-header') #{p.name}
div(class='project-platforms')
if p.platforms.find(p => p === 'desktop')
img(class='platform-icon' alt='Icon for Desktop Devices' src='file://icons/icon-computer.svg')
if p.platforms.find(p => p === 'mobile')
img(class='platform-icon' alt='Icon for Mobile Devices' src='file://icons/icon-mobile.svg')
p &bull; #{p.state}
div(class='divider-line' style='background-color: var(--accent);')
div(class='project-description')
each s in p.description
p #{s}
//- Screenshots
div(class='project-gallery')
div(class='project-gallery-upper')
button(class='project-gallery-button' id='last') <
div(class='project-gallery-scroll')
each i in p.images
img(class='gallery-image' alt=`${p.name} - ${i.label}` src=i.url)
button(class='project-gallery-button' id='next') >
div(class='project-content project-lower')
div(class='project-actions')
each a in p.actions
a(class='default wide' target=a.target href=a.url) #{a.label}
//- Screenshot Gallery Controls
div(class='project-gallery-actions')
each _, i in p.images
div(class=`gallery-dot${i===0 ? ' gallery-dot-active' : ''}`)
script(src='file://scripts/public-projects.ts')
@@ -0,0 +1,6 @@
extends ../_TemplateSpecial
block pageHeader
title suzzy games - Signup
block pageEscaped
script(src='file://scripts/login-signup.ts')
img(class='special-corner' src='file://images/login-globe.png')
@@ -0,0 +1,6 @@
extends ../_TemplateSpecial
block pageHeader
title suzzy games - Verify Email
block pageEscaped
script(src='file://scripts/login-verifyemail.ts')
img(class='special-corner' src='file://images/login-globe.png')
@@ -0,0 +1,6 @@
extends ../_TemplateSpecial
block pageHeader
title suzzy games - Verify Login
block pageEscaped
script(src='file://scripts/login-verifylogin.ts')
img(class='special-corner' src='file://images/login-globe.png')