Merge remote-tracking branch 'origin/master' into jam-2022

This commit is contained in:
Ben Visness 2022-08-04 18:37:51 -05:00
commit 62972ae35a
235 changed files with 82781 additions and 1211 deletions

View File

@ -32,7 +32,7 @@ You will need the following software installed:
Depending on your installation of Postgres, you may need to modify the hostname and port in the Postgres section of the config.
- **Set up the database.** Run `go run src/main.go db seed` to initialize the database and fill it with sample data.
- **Set up the database.** Run `go run . db seed` to initialize the database and fill it with sample data.
- **Update your hosts file.** The website uses subdomains for official projects, so the site cannot simply be run off `localhost`. Add the following
line to your hosts file:
@ -50,12 +50,12 @@ line to your hosts file:
Running the site is easy:
```
go run src/main.go
go run .
```
You should now be able to visit http://handmade.local:9001 to see the website!
There are also several other commands built into the website executable. You can see documentation for each of them by running `go run src/main.go help` or adding the `-h` flag to any command.
There are also several other commands built into the website executable. You can see documentation for each of them by running `go run . help` or adding the `-h` flag to any command.
## Running tests

1
fishbowl-tools/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
!.vscode

View File

@ -0,0 +1,10 @@
{
"Resource Link": {
"prefix": [
"hmn-link"
],
"body": [
"<a class=\"inserted-after\" href=\"$1\" target=\"_blank\">$TM_SELECTED_TEXT</a>"
]
}
}

65
fishbowl-tools/steps.md Normal file
View File

@ -0,0 +1,65 @@
- [ ] Export with [DiscordChatExporter](https://github.com/Tyrrrz/DiscordChatExporter) CLI 2.34
```
DiscordChatExporter.Cli.exe export -c [thread-id] -t [token] -o [fishbowl].html --media
```
- [ ] Rename `[fishbowl].html_Files` to `files`, replace links in html
- [ ] Add target="_blank" to links
```
(a href="[^"]+")>
$1 target="_blank">
```
- [ ] Add JQuery to `<head>`
```html
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.1/jquery-ui.js"></script>
<script>
$( function() {
$( ".chatlog" ).sortable();
} );
</script>
```
- [ ] Drag/delete noise/edit in the browser
- [ ] Save as `[fishbowl]-dragged.html` (devtools -> `<html>` -> copy outerHTML)
- [ ] Remove JQuery, sortable classes
```
ui-sortable
ui-sortable-handle
```
- [ ] Check `#fishbowl-audience` for highlights
- [ ] Fix audience avatar paths if anything copied
- [ ] Fix bad pictures (composite emojis 404)
- [ ] Fix links with extra braces at the end
```
href="[^"]+\)"
```
- [ ] Fill in resource links with vscode snippet (select phrase, Ctrl+Shift+P -> Insert Snippet -> hmn-link, paste the url)
- [ ] Download twemojies
```
go run twemoji.go [fishbowl]-dragged.html files [fishbowl]-twemojied.html
```
- [ ] Fix timestamps
```
go run timestamps.go [fishbowl]-twemojied.html [fishbowl]-timestamped.html
```
- [ ] Create a branch off latest `hmn` master
- [ ] Create fishbowl folder under `hmn/src/templates/src/fishbowls/`
- [ ] Copy timestamped html and files, rename html
- [ ] Remove everything from html but chatlog
- [ ] Remove js, css and whitney from files
- [ ] Add content path to `fishbowl.go`
- [ ] Test locally
- [ ] Submit a pull request

View File

@ -0,0 +1,41 @@
package main
import (
"fmt"
"os"
"regexp"
)
func main() {
if len(os.Args) != 3 {
fmt.Println("Usage: go run timestamps.go [fishbowl].html [fishbowl]-timestamped.html")
os.Exit(1)
}
htmlPath := os.Args[1]
htmlOutPath := os.Args[2]
htmlBytes, err := os.ReadFile(htmlPath)
if err != nil {
panic(err)
}
html := string(htmlBytes)
regex, err := regexp.Compile(
"(<span class=\"chatlog__timestamp\">)(\\d+)-([A-Za-z]+)-(\\d+)( [^<]+</span>)",
)
if err != nil {
panic(err)
}
htmlOut := regex.ReplaceAllString(
html,
"$1$3 $2, 20$4$5",
)
err = os.WriteFile(htmlOutPath, []byte(htmlOut), 0666)
if err != nil {
panic(err)
}
}

77
fishbowl-tools/twemoji.go Normal file
View File

@ -0,0 +1,77 @@
package main
import (
"fmt"
"io"
"net/http"
"os"
"path"
"strings"
)
func main() {
if len(os.Args) != 4 {
fmt.Println("Usage: go run twemoji.go [fishbowl].html files [fishbowl]-twemojied.html")
os.Exit(1)
}
htmlPath := os.Args[1]
filesDir := os.Args[2]
htmlOutPath := os.Args[3]
htmlBytes, err := os.ReadFile(htmlPath)
if err != nil {
panic(err)
}
html := string(htmlBytes)
for {
linkStart := strings.Index(html, "https://twemoji.maxcdn.com/")
if linkStart == -1 {
break
}
linkEnd := strings.Index(html[linkStart:], "\"") + linkStart
link := html[linkStart:linkEnd]
emojiFilenameStart := strings.LastIndex(link, "/") + 1
emojiFilename := "twemoji_" + link[emojiFilenameStart:]
emojiPath := path.Join(filesDir, emojiFilename)
emojiResponse, err := http.Get(link)
if err != nil {
panic(err)
}
defer emojiResponse.Body.Close()
if emojiResponse.StatusCode > 299 {
panic("Non-200 status code: " + fmt.Sprint(emojiResponse.StatusCode))
}
emojiFile, err := os.Create(emojiPath)
if err != nil {
panic(err)
}
defer emojiFile.Close()
_, err = io.Copy(emojiFile, emojiResponse.Body)
if err != nil {
panic(err)
}
html = strings.ReplaceAll(html, link, emojiPath)
fmt.Println(emojiFilename)
}
html = strings.ReplaceAll(
html,
"<div class=\"chatlog\">",
"<div class=\"chatlog\">\n<!-- Emojis by Twitter's Twemoji https://twemoji.twitter.com/ -->",
)
err = os.WriteFile(htmlOutPath, []byte(html), 0666)
if err != nil {
panic(err)
}
}

15
main.go Normal file
View File

@ -0,0 +1,15 @@
package main
import (
_ "git.handmade.network/hmn/hmn/src/admintools"
_ "git.handmade.network/hmn/hmn/src/assets"
_ "git.handmade.network/hmn/hmn/src/buildscss"
_ "git.handmade.network/hmn/hmn/src/discord/cmd"
_ "git.handmade.network/hmn/hmn/src/initimage"
_ "git.handmade.network/hmn/hmn/src/migration"
"git.handmade.network/hmn/hmn/src/website"
)
func main() {
website.WebsiteCommand.Execute()
}

BIN
public/hms/banner_tall.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 KiB

BIN
public/hms/handmadecon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

76
public/js/carousel.js Normal file
View File

@ -0,0 +1,76 @@
function initCarousel(container, options = {}) {
const durationMS = options.durationMS ?? 0;
const onChange = options.onChange ?? (() => {});
const numCarouselItems = container.querySelectorAll('.carousel-item').length;
const buttonContainer = container.querySelector('.carousel-buttons');
let current = 0;
function activateCarousel(i) {
const items = document.querySelectorAll('.carousel-item');
for (const item of items) {
item.classList.remove('active');
}
items[i].classList.add('active');
const smallItems = document.querySelectorAll('.carousel-item-small');
if (smallItems.length > 0) {
for (const item of smallItems) {
item.classList.remove('active');
}
smallItems[i].classList.add('active');
}
const buttons = document.querySelectorAll('.carousel-button');
for (const button of buttons) {
button.classList.remove('active');
}
buttons[i].classList.add('active');
current = i;
onChange(current);
}
function activateNext() {
activateCarousel((current + numCarouselItems + 1) % numCarouselItems);
}
function activatePrev() {
activateCarousel((current + numCarouselItems - 1) % numCarouselItems);
}
const carouselTimer = durationMS > 0 && setInterval(() => {
if (numCarouselItems === 0) {
return;
}
activateNext();
}, durationMS);
function carouselButtonClick(i) {
activateCarousel(i);
if (carouselTimer) {
clearInterval(carouselTimer);
}
}
for (let i = 0; i < numCarouselItems; i++) {
const button = document.createElement('div');
button.classList.add('carousel-button', 'br-pill', 'w1', 'h1', 'mh2');
button.classList.toggle('active', i === 0);
const clickIndex = i;
button.addEventListener('click', () => {
carouselButtonClick(clickIndex);
});
buttonContainer.appendChild(button);
}
activateCarousel(0);
return {
next: activateNext,
prev: activatePrev,
};
}

View File

@ -0,0 +1,246 @@
// Requires base64.js
/**
* Sets up file / image uploading for Markdown content.
*
* @param eSubmits A list of elements of buttons to submit/save the page you're on. They
* will be disabled and tell users files are uploading while uploading is
* happening.
* @param eFileInput The `<input type="file">`
* @param eUploadBar Usually looks like
* ```
* <div class="upload_bar flex-grow-1">
* <div class="instructions">
* Upload files by dragging & dropping, pasting, or <label class="pointer link" for="file_input">selecting</label> them.
* </div>
* <div class="progress flex">
* <div class="progress_text mr3"></div>
* <div class="progress_bar flex-grow-1 flex-shrink-1 pa1"><div class=""></div></div>
* </div>
* </div>
* ```
* @param eText The text field that can be dropped into and is editing the markdown.
* @param doMarkdown The function returned by `initLiveMarkdown`.
* @param maxFileSize The max allowed file size in bytes.
* @param uploadUrl The URL to POST assets to (unique per project to avoid CORS issues).
*/
function setupMarkdownUpload(eSubmits, eFileInput, eUploadBar, eText, doMarkdown, maxFileSize, uploadUrl) {
const submitTexts = Array.from(eSubmits).map(e => e.value);
const uploadProgress = eUploadBar.querySelector('.progress');
const uploadProgressText = eUploadBar.querySelector('.progress_text');
const uploadProgressBar = eUploadBar.querySelector('.progress_bar');
const uploadProgressBarFill = eUploadBar.querySelector('.progress_bar > div');
let fileCounter = 0;
let enterCounter = 0;
let uploadQueue = [];
let currentUpload = null;
let currentXhr = null;
let currentBatchSize = 0;
let currentBatchDone = 0;
eFileInput.addEventListener("change", function(ev) {
if (eFileInput.files.length > 0) {
importUserFiles(eFileInput.files);
}
});
eText.addEventListener("dragover", function(ev) {
let effect = "none";
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
effect = "copy";
break;
}
}
ev.dataTransfer.dropEffect = effect;
ev.preventDefault();
});
eText.addEventListener("dragenter", function(ev) {
enterCounter++;
let droppable = false;
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
droppable = true;
break;
}
}
if (droppable) {
eText.classList.add("drop");
}
});
eText.addEventListener("dragleave", function(ev) {
enterCounter--;
if (enterCounter == 0) {
eText.classList.remove("drop");
}
});
function makeUploadString(uploadNumber, filename) {
return `Uploading file #${uploadNumber}: \`${filename}\`...`;
}
eText.addEventListener("drop", function(ev) {
enterCounter = 0;
eText.classList.remove("drop");
if (ev.dataTransfer && ev.dataTransfer.files) {
importUserFiles(ev.dataTransfer.files)
}
ev.preventDefault();
});
eText.addEventListener("paste", function(ev) {
const files = ev.clipboardData?.files ?? [];
if (files.length > 0) {
importUserFiles(files)
}
});
function importUserFiles(files) {
let items = [];
for (let i = 0; i < files.length; ++i) {
let f = files[i];
if (f.size < maxFileSize) {
items.push({ file: f, error: null });
} else {
items.push({ file: null, error: `\`${f.name}\` is too big! Max size is ${maxFileSize} but the file is ${f.size}.` });
}
}
let cursorStart = eText.selectionStart;
let cursorEnd = eText.selectionEnd;
let toInsert = "";
let linesToCursor = eText.value.substr(0, cursorStart).split("\n");
let cursorLine = linesToCursor[linesToCursor.length-1].trim();
if (cursorLine.length > 0) {
toInsert = "\n\n";
}
for (let i = 0; i < items.length; ++i) {
if (items[i].file) {
fileCounter++;
toInsert += makeUploadString(fileCounter, items[i].file.name) + "\n\n";
queueUpload(fileCounter, items[i].file);
} else {
toInsert += `${items[i].error}\n\n`;
}
}
eText.value = eText.value.substring(0, cursorStart) + toInsert + eText.value.substring(cursorEnd, eText.value.length);
doMarkdown();
uploadNext();
}
function replaceUploadString(upload, newString) {
let cursorStart = eText.selectionStart;
let cursorEnd = eText.selectionEnd;
let uploadString = makeUploadString(upload.uploadNumber, upload.file.name);
let insertIndex = eText.value.indexOf(uploadString)
eText.value = eText.value.replace(uploadString, newString);
if (cursorStart <= insertIndex + uploadString.length) {
eText.selectionStart = cursorStart;
} else {
eText.selectionStart = cursorStart - uploadString.length + newString.length;
}
if (cursorEnd <= insertIndex + uploadString.length) {
eText.selectionEnd = cursorEnd;
} else {
eText.selectionEnd = cursorEnd - uploadString.length + newString.length;
}
doMarkdown();
}
function replaceUploadStringError(upload) {
replaceUploadString(upload, `There was a problem uploading your file \`${upload.file.name}\`.`);
}
function queueUpload(uploadNumber, file) {
uploadQueue.push({
uploadNumber: uploadNumber,
file: file
});
currentBatchSize++;
uploadProgressText.textContent = `Uploading files ${currentBatchDone+1}/${currentBatchSize}`;
}
function uploadDone(ev) {
try {
if (currentXhr.status == 200 && currentXhr.response) {
if (currentXhr.response.url) {
let url = currentXhr.response.url;
let newString = `[${currentUpload.file.name}](${url})`;
if (currentXhr.response.mime.startsWith("image")) {
newString = "!" + newString;
}
replaceUploadString(currentUpload, newString);
} else if (currentXhr.response.error) {
replaceUploadString(currentUpload, `Upload failed for \`${currentUpload.file.name}\`: ${currentXhr.response.error}.`);
} else {
replaceUploadStringError(currentUpload);
}
} else {
replaceUploadStringError(currentUpload);
}
} catch (err) {
console.error(err);
replaceUploadStringError(currentUpload);
}
currentUpload = null;
currentXhr = null;
currentBatchDone++;
uploadNext();
}
function updateUploadProgress(ev) {
if (ev.lengthComputable) {
let progress = ev.loaded / ev.total;
uploadProgressBarFill.style.width = Math.floor(progress * 100) + "%";
}
}
function uploadNext() {
if (currentUpload == null) {
next = uploadQueue.shift();
if (next) {
uploadProgressText.textContent = `Uploading files ${currentBatchDone+1}/${currentBatchSize}`;
eUploadBar.classList.add("uploading");
uploadProgressBarFill.style.width = "0%";
for (const e of eSubmits) {
e.disabled = true;
e.value = "Uploading files...";
}
try {
let utf8Filename = strToUTF8Arr(next.file.name);
let base64Filename = base64EncArr(utf8Filename);
// NOTE(asaf): We use XHR because fetch can't do upload progress reports. Womp womp. https://youtu.be/Pubd-spHN-0?t=2
currentXhr = new XMLHttpRequest();
currentXhr.upload.addEventListener("progress", updateUploadProgress);
currentXhr.open("POST", uploadUrl, true);
currentXhr.setRequestHeader("Hmn-Upload-Filename", base64Filename);
currentXhr.responseType = "json";
currentXhr.addEventListener("loadend", uploadDone);
currentXhr.send(next.file);
currentUpload = next;
} catch (err) {
replaceUploadStringError(next);
console.error(err);
uploadNext();
}
} else {
for (const [i, e] of eSubmits.entries()) {
e.disabled = false;
e.value = submitTexts[i];
}
eUploadBar.classList.remove("uploading");
currentBatchSize = 0;
currentBatchDone = 0;
}
}
}
}

View File

@ -7497,6 +7497,24 @@ article code {
.cg5 {
column-gap: 4rem; }
.g0 {
gap: 0; }
.g1 {
gap: 0.25rem; }
.g2 {
gap: 0.5rem; }
.g3 {
gap: 1rem; }
.g4 {
gap: 2rem; }
.g5 {
gap: 4rem; }
@media screen and (min-width: 30em) {
.bi-avoid-ns {
break-inside: avoid; }
@ -8497,6 +8515,7 @@ input[type=submit] {
flex-shrink: 0;
border-radius: 100%;
overflow: hidden;
object-fit: cover;
background-color: #bbb;
background-color: var(--dimmest-color); }
@ -9116,6 +9135,33 @@ span.icon-rss::before {
padding: 0px;
min-height: 0em; }
.project-carousel .carousel-item {
position: absolute;
top: 0;
left: 0; }
.project-carousel .carousel-item br {
line-height: 0.6em; }
.project-carousel .carousel-item-small {
position: absolute;
top: 0;
left: 0; }
.project-carousel .carousel-item-small:not(.active) {
display: none; }
.project-carousel .carousel-description {
max-height: 14rem;
overflow: hidden; }
.project-carousel .carousel-fade {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 30px;
background: linear-gradient( rgba(240, 240, 240, 0) , #f0f0f0 );
background: linear-gradient( var(--dim-background-transparent) , var(--dim-background) ); }
.project .pair {
display: flex;
align-items: flex-start; }
@ -9346,37 +9392,14 @@ span.icon-rss::before {
max-height: calc(100vh - 2rem); } }
.carousel-container .carousel {
box-sizing: content-box;
position: relative; }
.carousel-container .carousel-item {
position: absolute;
top: 0;
left: 0; }
.carousel-container .carousel-item:not(.active) {
display: none; }
.carousel-container .carousel-item br {
line-height: 0.6em; }
.carousel-container .carousel-item:not(.active) {
display: none; }
.carousel-container .carousel-description {
max-height: 14rem;
overflow: hidden; }
.carousel-container .carousel-fade {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 30px;
background: linear-gradient( rgba(240, 240, 240, 0) , #f0f0f0 );
background: linear-gradient( var(--dim-background-transparent) , var(--dim-background) ); }
.carousel-container .carousel-item-small {
position: absolute;
top: 0;
left: 0; }
.carousel-container .carousel-item-small:not(.active) {
display: none; }
.carousel-container .carousel-buttons {
display: flex;
justify-content: center; }
.carousel-container .carousel-button {
border: 1px solid;

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

View File

@ -23,7 +23,7 @@ set -euo pipefail
cd /home/hmn/hmn
git fetch --all
git reset --hard origin/$branch
go build -o /home/hmn/bin/hmn src/main.go
go build -o /home/hmn/bin/hmn .
SCRIPT
echo "Running migrations..."

View File

@ -15,7 +15,7 @@ deploy: ## Manually build and deploy a branch of the website.
/home/hmn/hmn/server/deploy.sh
build: ## Rebuild the website binary
sudo -u hmn --preserve-env=PATH bash -c "cd ~/hmn && go build -o /home/hmn/bin/hmn src/main.go"
sudo -u hmn --preserve-env=PATH bash -c "cd ~/hmn && go build -o /home/hmn/bin/hmn ."
edit-config: ## Edit the website config
vim /home/hmn/hmn/src/config/config.go

View File

@ -289,7 +289,7 @@ if [ $checkpoint -lt 110 ]; then
cd /home/hmn/hmn
echo "Building the site for the first time. This may take a while..."
go build -v -o /home/hmn/bin/hmn src/main.go
go build -v -o /home/hmn/bin/hmn .
SCRIPT
echo 'PATH=$PATH:/home/hmn/bin' >> ~/.bashrc

View File

@ -84,6 +84,8 @@ func UrlWithFragment(path string, query []Q, fragment string) string {
return HMNProjectContext.UrlWithFragment(path, query, fragment)
}
// Takes a project URL and rewrites it using the current URL context. This can be used
// to convert a personal project URL to official and vice versa.
func (c *UrlContext) RewriteProjectUrl(u *url.URL) string {
// we need to strip anything matching the personal project regex to get the base path
match := RegexPersonalProject.FindString(u.Path)

View File

@ -368,6 +368,10 @@ func TestDiscordShowcaseBacklog(t *testing.T) {
AssertRegexMatch(t, BuildDiscordShowcaseBacklog(), RegexDiscordShowcaseBacklog, nil)
}
func TestConferences(t *testing.T) {
AssertRegexMatch(t, BuildConferences(), RegexConferences, nil)
}
func AssertSubdomain(t *testing.T, fullUrl string, expectedSubdomain string) {
t.Helper()

View File

@ -192,6 +192,13 @@ func BuildProjectSubmissionGuidelines() string {
return Url("/project-guidelines", nil)
}
var RegexConferences = regexp.MustCompile("^/conferences$")
func BuildConferences() string {
defer CatchPanic()
return Url("/conferences", nil)
}
/*
* User
*/

View File

@ -1,15 +1,8 @@
package main
import (
_ "git.handmade.network/hmn/hmn/src/admintools"
_ "git.handmade.network/hmn/hmn/src/assets"
_ "git.handmade.network/hmn/hmn/src/buildscss"
_ "git.handmade.network/hmn/hmn/src/discord/cmd"
_ "git.handmade.network/hmn/hmn/src/initimage"
_ "git.handmade.network/hmn/hmn/src/migration"
"git.handmade.network/hmn/hmn/src/website"
)
import "fmt"
func main() {
website.WebsiteCommand.Execute()
fmt.Println("main.go has moved! You should now run:")
fmt.Println("go run .")
}

View File

@ -163,16 +163,8 @@ func SampleSeed() {
}
}
// admin := CreateAdminUser("admin", "12345678")
// user := CreateUser("regular_user", "12345678")
// hmnProject := CreateProject("hmn", "Handmade Network")
// Create category
// Create thread
// Create accepted user project
// Create pending user project
// Create showcase items
// Create codelanguages
// Create library and library resources
// Finally, set sequence numbers to things that won't conflict
utils.Must1(tx.Exec(ctx, "SELECT setval('project_id_seq', 100, true);"))
utils.Must0(tx.Commit(ctx))
}

View File

@ -1,51 +1,15 @@
.carousel-container {
.carousel {
box-sizing: content-box;
position: relative;
// height: 12rem;
@media #{$breakpoint-large} {
// height: $height-5;
}
}
.carousel-item {
position: absolute;
top: 0;
left: 0;
&:not(.active) {
display: none;
}
br {
line-height: 0.6em;
}
.carousel-item:not(.active) {
display: none;
}
.carousel-description {
max-height: 14rem;
overflow: hidden;
}
.carousel-fade {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 30px;
@include usevar(background, "linear-gradient(" dim-background-transparent "," dim-background ")")
}
.carousel-item-small {
position: absolute;
top: 0;
left: 0;
&:not(.active) {
display: none;
}
.carousel-buttons {
display: flex;
justify-content: center;
}
.carousel-button {

View File

@ -349,6 +349,13 @@ article code {
.cg4 { column-gap: $spacing-large; }
.cg5 { column-gap: $spacing-extra-large; }
.g0 { gap: $spacing-none; }
.g1 { gap: $spacing-extra-small; }
.g2 { gap: $spacing-small; }
.g3 { gap: $spacing-medium; }
.g4 { gap: $spacing-large; }
.g5 { gap: $spacing-extra-large; }
@media #{$breakpoint-not-small} {
.bi-avoid-ns { break-inside: avoid; }
.cc-auto-ns { column-count: auto; }

View File

@ -56,6 +56,7 @@
flex-shrink: 0;
border-radius: 100%;
overflow: hidden;
object-fit: cover;
@include usevar(background-color, dimmest-color);
}

View File

@ -1,3 +1,40 @@
.project-carousel {
.carousel-item {
position: absolute;
top: 0;
left: 0;
br {
line-height: 0.6em;
}
}
.carousel-item-small {
position: absolute;
top: 0;
left: 0;
&:not(.active) {
display: none;
}
}
.carousel-description {
max-height: 14rem;
overflow: hidden;
}
.carousel-fade {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 30px;
@include usevar(background, "linear-gradient(" dim-background-transparent "," dim-background ")")
}
}
.project {
.pair {
display: flex;

View File

@ -297,6 +297,8 @@ func LinkToTemplate(link *models.Link) Link {
return tlink
}
var controlCharRegex = regexp.MustCompile(`\p{Cc}`)
func TimelineItemsToJSON(items []TimelineItem) string {
// NOTE(asaf): As of 2021-06-22: This only serializes the data necessary for snippet showcase.
builder := strings.Builder{}
@ -314,11 +316,13 @@ func TimelineItemsToJSON(items []TimelineItem) string {
builder.WriteString(`"description":"`)
jsonString := string(item.Description)
jsonString = strings.ToValidUTF8(jsonString, "")
jsonString = strings.ReplaceAll(jsonString, `\`, `\\`)
jsonString = strings.ReplaceAll(jsonString, `"`, `\"`)
jsonString = strings.ReplaceAll(jsonString, "\n", "\\n")
jsonString = strings.ReplaceAll(jsonString, "\r", "\\r")
jsonString = strings.ReplaceAll(jsonString, "\t", "\\t")
jsonString = controlCharRegex.ReplaceAllString(jsonString, "")
builder.WriteString(jsonString)
builder.WriteString(`",`)

View File

@ -0,0 +1,25 @@
{{ template "base.html" . }}
{{ define "content" }}
{{ $bannerclass := "overflow-hidden br2 mt2 h4 h5-ns cover bg-center" }}
<div class="center-layout mw7 ph3 ph0-ns post-content flex flex-column g3">
<div class="fronke">
<a href="https://handmadehero.org/conference">
<h2>HandmadeCon</h2>
</a>
<div class="{{ $bannerclass }}" style="background-image: url('{{ static "hms/handmadecon.png" }}')"></div>
<p>In 2015 and 2016, Casey Muratori ran HandmadeCon, inviting many amazing programmers to come talk about what they do. You probably already know that Casey is a fantastic interviewer, and the talks are loaded with amazing insights.</p>
<p>The annotated talks can all be viewed via the <a href="https://guide.handmadehero.org/hmcon/">Handmade Hero episode guide</a>. Alternatively, the talks can be found in <a href="https://www.youtube.com/playlist?list=PLEMXAbCVnmY6HE0bxUHeLLP5QbBE2zhJi">this playlist</a> on the <a href="https://mollyrocket.com/">Molly Rocket</a> YouTube channel.</p>
</div>
<div>
<a href="https://handmade-seattle.com/">
<h2>Handmade Seattle</h2>
</a>
<div class="{{ $bannerclass }}" style="background-image: url('{{ static "hms/banner_tall.jpg" }}')"></div>
<p>Handmade Seattle, the spiritual successor to HandmadeCon, was started in 2019 by Abner Coimbre, the founder of Handmade Network. From the start, Handmade Seattle has been an independent conference, free from corporate sponsorships. The conferences are hybrid online/physical, so you can participate no matter where in the world you live.</p>
<p>Tickets can be purchased at <a href="https://handmade-seattle.com/">the conference website</a>.</p>
<p><a href="https://media.handmade-seattle.com/tag/talks/">Talks</a> and <a href="https://media.handmade-seattle.com/tag/demos/">demos</a> can be viewed on the conference's media site.</p>
</div>
</div>
{{ end }}

View File

@ -4,6 +4,7 @@
{{ template "markdown_previews.html" . }}
<script src="{{ static "js/base64.js" }}"></script>
<script src="{{ static "js/markdown_upload.js" }}"></script>
<style>
#editor {
@ -121,222 +122,14 @@
/*
/ Asset upload
*/
const submitButton = document.querySelector("#form input[type=submit]");
const submitText = submitButton.value;
const fileInput = document.querySelector('#file_input');
const uploadBar = document.querySelector('.upload_bar');
const uploadProgress = document.querySelector('.upload_bar .progress');
const uploadProgressText = document.querySelector('.upload_bar .progress_text');
const uploadProgressBar = document.querySelector('.upload_bar .progress_bar');
const uploadProgressBarFill = document.querySelector('.upload_bar .progress_bar > div');
let fileCounter = 0;
let enterCounter = 0;
let uploadQueue = [];
let currentUpload = null;
let currentXhr = null;
let currentBatchSize = 0;
let currentBatchDone = 0;
fileInput.addEventListener("change", function(ev) {
if (fileInput.files.length > 0) {
importUserFiles(fileInput.files);
}
});
textField.addEventListener("dragover", function(ev) {
let effect = "none";
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
effect = "copy";
break;
}
}
ev.dataTransfer.dropEffect = effect;
ev.preventDefault();
});
textField.addEventListener("dragenter", function(ev) {
enterCounter++;
let droppable = false;
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
droppable = true;
break;
}
}
if (droppable) {
textField.classList.add("drop");
}
});
textField.addEventListener("dragleave", function(ev) {
enterCounter--;
if (enterCounter == 0) {
textField.classList.remove("drop");
}
});
function makeUploadString(uploadNumber, filename) {
return `Uploading file #${uploadNumber}: \`${filename}\`...`;
}
textField.addEventListener("drop", function(ev) {
enterCounter = 0;
textField.classList.remove("drop");
if (ev.dataTransfer && ev.dataTransfer.files) {
importUserFiles(ev.dataTransfer.files)
}
ev.preventDefault();
});
textField.addEventListener("paste", function(ev) {
const files = ev.clipboardData?.files ?? [];
if (files.length > 0) {
importUserFiles(files)
}
});
function importUserFiles(files) {
let items = [];
for (let i = 0; i < files.length; ++i) {
let f = files[i];
if (f.size < maxFileSize) {
items.push({ file: f, error: null });
} else {
items.push({ file: null, error: `\`${f.name}\` is too big! Max size is ${maxFileSize} but the file is ${f.size}.` });
}
}
let cursorStart = textField.selectionStart;
let cursorEnd = textField.selectionEnd;
let toInsert = "";
let linesToCursor = textField.value.substr(0, cursorStart).split("\n");
let cursorLine = linesToCursor[linesToCursor.length-1].trim();
if (cursorLine.length > 0) {
toInsert = "\n\n";
}
for (let i = 0; i < items.length; ++i) {
if (items[i].file) {
fileCounter++;
toInsert += makeUploadString(fileCounter, items[i].file.name) + "\n\n";
queueUpload(fileCounter, items[i].file);
} else {
toInsert += `${items[i].error}\n\n`;
}
}
textField.value = textField.value.substring(0, cursorStart) + toInsert + textField.value.substring(cursorEnd, textField.value.length);
doMarkdown();
uploadNext();
}
function replaceUploadString(upload, newString) {
let cursorStart = textField.selectionStart;
let cursorEnd = textField.selectionEnd;
let uploadString = makeUploadString(upload.uploadNumber, upload.file.name);
let insertIndex = textField.value.indexOf(uploadString)
textField.value = textField.value.replace(uploadString, newString);
if (cursorStart <= insertIndex + uploadString.length) {
textField.selectionStart = cursorStart;
} else {
textField.selectionStart = cursorStart - uploadString.length + newString.length;
}
if (cursorEnd <= insertIndex + uploadString.length) {
textField.selectionEnd = cursorEnd;
} else {
textField.selectionEnd = cursorEnd - uploadString.length + newString.length;
}
doMarkdown();
}
function replaceUploadStringError(upload) {
replaceUploadString(upload, `There was a problem uploading your file \`${upload.file.name}\`.`);
}
function queueUpload(uploadNumber, file) {
uploadQueue.push({
uploadNumber: uploadNumber,
file: file
});
currentBatchSize++;
uploadProgressText.textContent = `Uploading files ${currentBatchDone+1}/${currentBatchSize}`;
}
function uploadDone(ev) {
try {
if (currentXhr.status == 200 && currentXhr.response) {
if (currentXhr.response.url) {
let url = currentXhr.response.url;
let newString = `[${currentUpload.file.name}](${url})`;
if (currentXhr.response.mime.startsWith("image")) {
newString = "!" + newString;
}
replaceUploadString(currentUpload, newString);
} else if (currentXhr.response.error) {
replaceUploadString(currentUpload, `Upload failed for \`${currentUpload.file.name}\`: ${currentXhr.response.error}.`);
} else {
replaceUploadStringError(currentUpload);
}
} else {
replaceUploadStringError(currentUpload);
}
} catch (err) {
console.error(err);
replaceUploadStringError(currentUpload);
}
currentUpload = null;
currentXhr = null;
currentBatchDone++;
uploadNext();
}
function updateUploadProgress(ev) {
if (ev.lengthComputable) {
let progress = ev.loaded / ev.total;
uploadProgressBarFill.style.width = Math.floor(progress * 100) + "%";
}
}
function uploadNext() {
if (currentUpload == null) {
next = uploadQueue.shift();
if (next) {
uploadProgressText.textContent = `Uploading files ${currentBatchDone+1}/${currentBatchSize}`;
uploadBar.classList.add("uploading");
uploadProgressBarFill.style.width = "0%";
submitButton.disabled = true;
submitButton.value = "Uploading files...";
try {
let utf8Filename = strToUTF8Arr(next.file.name);
let base64Filename = base64EncArr(utf8Filename);
// NOTE(asaf): We use XHR because fetch can't do upload progress reports. Womp womp. https://youtu.be/Pubd-spHN-0?t=2
currentXhr = new XMLHttpRequest();
currentXhr.upload.addEventListener("progress", updateUploadProgress);
currentXhr.open("POST", uploadUrl, true);
currentXhr.setRequestHeader("Hmn-Upload-Filename", base64Filename);
currentXhr.responseType = "json";
currentXhr.addEventListener("loadend", uploadDone);
currentXhr.send(next.file);
currentUpload = next;
} catch (err) {
replaceUploadStringError(next);
console.error(err);
uploadNext();
}
} else {
submitButton.disabled = false;
submitButton.value = submitText;
uploadBar.classList.remove("uploading");
currentBatchSize = 0;
currentBatchDone = 0;
}
}
}
setupMarkdownUpload(
document.querySelectorAll("#form input[type=submit]"),
document.querySelector('#file_input'),
document.querySelector('.upload_bar'),
textField,
doMarkdown,
maxFileSize,
uploadUrl
);
</script>
{{ end }}

View File

@ -8,8 +8,6 @@
<p>This is an archive of those conversations. If you would like to catch one live, <a href="https://discord.gg/hmn" target="_blank">join the Discord!</a></p>
<p class="c--dim i">We're still working through our backlog of fishbowls, so not all of these are available yet. We'll publish them as we finish them.</p>
<div class="mt3 mw7">
{{ range .Fishbowls }}
<div class="br2 bg--dim pa3 mb2">

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFDB5E" d="M34.956 17.916c0-.503-.12-.975-.321-1.404-1.341-4.326-7.619-4.01-16.549-4.221-1.493-.035-.639-1.798-.115-5.668.341-2.517-1.282-6.382-4.01-6.382-4.498 0-.171 3.548-4.148 12.322-2.125 4.688-6.875 2.062-6.875 6.771v10.719c0 1.833.18 3.595 2.758 3.885C8.195 34.219 7.633 36 11.238 36h18.044c1.838 0 3.333-1.496 3.333-3.334 0-.762-.267-1.456-.698-2.018 1.02-.571 1.72-1.649 1.72-2.899 0-.76-.266-1.454-.696-2.015 1.023-.57 1.725-1.649 1.725-2.901 0-.909-.368-1.733-.961-2.336.757-.611 1.251-1.535 1.251-2.581z"/><path fill="#EE9547" d="M23.02 21.249h8.604c1.17 0 2.268-.626 2.866-1.633.246-.415.109-.952-.307-1.199-.415-.247-.952-.108-1.199.307-.283.479-.806.775-1.361.775h-8.81c-.873 0-1.583-.71-1.583-1.583s.71-1.583 1.583-1.583H28.7c.483 0 .875-.392.875-.875s-.392-.875-.875-.875h-5.888c-1.838 0-3.333 1.495-3.333 3.333 0 1.025.475 1.932 1.205 2.544-.615.605-.998 1.445-.998 2.373 0 1.028.478 1.938 1.212 2.549-.611.604-.99 1.441-.99 2.367 0 1.12.559 2.108 1.409 2.713-.524.589-.852 1.356-.852 2.204 0 1.838 1.495 3.333 3.333 3.333h5.484c1.17 0 2.269-.625 2.867-1.632.247-.415.11-.952-.305-1.199-.416-.245-.953-.11-1.199.305-.285.479-.808.776-1.363.776h-5.484c-.873 0-1.583-.71-1.583-1.583s.71-1.583 1.583-1.583h6.506c1.17 0 2.27-.626 2.867-1.633.247-.416.11-.953-.305-1.199-.419-.251-.954-.11-1.199.305-.289.487-.799.777-1.363.777h-7.063c-.873 0-1.583-.711-1.583-1.584s.71-1.583 1.583-1.583h8.091c1.17 0 2.269-.625 2.867-1.632.247-.415.11-.952-.305-1.199-.417-.246-.953-.11-1.199.305-.289.486-.799.776-1.363.776H23.02c-.873 0-1.583-.71-1.583-1.583s.709-1.584 1.583-1.584z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#BB1A34" d="M1.728 21c-.617 0-.953-.256-1.127-.471-.171-.211-.348-.585-.225-1.165L3.104 6.658l-1.714.097h-.013c-.517 0-.892-.168-1.127-.459-.22-.272-.299-.621-.221-.98.15-.702.883-1.286 1.667-1.329l4.008-.227c.078-.005.15-.008.217-.008.147 0 .536 0 .783.306.252.312.167.709.139.839L3.719 19.454c-.187.884-.919 1.489-1.866 1.542L1.728 21zm10.743-2c-1.439 0-2.635-.539-3.459-1.559-1.163-1.439-1.467-3.651-.878-6.397 1.032-4.812 4.208-8.186 7.902-8.395 1.59-.089 2.906.452 3.793 1.549 1.163 1.439 1.467 3.651.878 6.397-1.032 4.81-4.208 8.184-7.904 8.394-.112.008-.223.011-.332.011zm3.414-13.746l-.137.004c-1.94.111-3.555 2.304-4.32 5.866-.478 2.228-.381 3.899.272 4.707.297.368.717.555 1.249.555l.14-.004c1.94-.109 3.554-2.301 4.318-5.864.478-2.228.382-3.9-.27-4.708-.296-.369-.718-.556-1.252-.556zm11.591 12.107c-1.439 0-2.637-.539-3.462-1.56-1.163-1.439-1.467-3.651-.878-6.397 1.033-4.813 4.209-8.186 7.903-8.394 1.603-.09 2.903.453 3.79 1.549 1.163 1.439 1.467 3.651.878 6.396-1.031 4.809-4.206 8.183-7.902 8.396-.112.008-.221.01-.329.01zm3.411-13.747l-.136.004c-1.941.111-3.556 2.304-4.32 5.865-.478 2.229-.381 3.901.272 4.708.297.368.719.555 1.251.555l.14-.004c1.939-.109 3.554-2.302 4.318-5.864.479-2.227.383-3.899-.27-4.707-.298-.37-.72-.557-1.255-.557zM11 35.001c-.81 0-1.572-.496-1.873-1.299-.388-1.034.136-2.187 1.17-2.575.337-.126 8.399-3.108 20.536-4.12 1.101-.096 2.067.727 2.159 1.827.092 1.101-.727 2.067-1.827 2.159-11.59.966-19.386 3.851-19.464 3.88-.23.086-.468.128-.701.128zM2.001 29c-.804 0-1.563-.488-1.868-1.283-.396-1.031.118-2.188 1.149-2.583.542-.209 13.516-5.126 32.612-6.131 1.113-.069 2.045.789 2.103 1.892.059 1.103-.789 2.045-1.892 2.103-18.423.97-31.261 5.821-31.389 5.87-.235.089-.477.132-.715.132z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#EF9645" d="M26.992 19.016c-.255-.255-.799-.611-1.44-.962l-1.911-2-2.113 2h-.58l-2.509-3.634c-1.379.01-2.497 1.136-2.487 2.515l-3.556-2.112c-.817.364-1.389 1.18-1.389 2.133v.96l-4 4.168.016 2.185 9.984 10.729S27.525 19.71 27.55 19.74c-.129-.223-.513-.702-.558-.724z"/><g fill="#FFDC5D"><path d="M25.552 5.81c0-1.107-.906-2.013-2.013-2.013-1.107 0-2.013.906-2.013 2.013v12.245h4.025V5.81zm-4.605 12.244V16.01c-.008-1.103-.909-1.991-2.012-1.983-1.103.008-1.991.909-1.983 2.012l.012 2.016h3.983zM8.916 16h.168c1.059 0 1.916.858 1.916 1.917v4.166C11 23.142 10.143 24 9.084 24h-.168C7.857 24 7 23.142 7 22.083v-4.166C7 16.858 7.857 16 8.916 16zm6.918 2.96l-.056.062C15.304 19.551 15 20.233 15 21c0 .063.013.123.018.185.044.678.308 1.292.728 1.774-.071.129-.163.243-.259.353-.366.417-.89.688-1.487.688-1.104 0-2-.896-2-2v-6c0-.441.147-.845.389-1.176.364-.497.947-.824 1.611-.824 1.104 0 2 .896 2 2v2.778c-.061.055-.109.123-.166.182z"/><path d="M9.062 25c1.024 0 1.925-.526 2.45-1.322.123.183.271.346.431.497 1.185 1.115 3.034 1.044 4.167-.086.152-.152.303-.305.419-.488l-.003-.003C16.727 23.713 17 24 18 24h2.537c-.37.279-.708.623-1.024 1-1.228 1.467-2.013 3.606-2.013 6 0 .276.224.5.5.5s.5-.224.5-.5c0-2.548.956-4.775 2.377-6 .732-.631 1.584-1 2.498-1 .713.079.847-1 .125-1H18c-1.104 0-2-.896-2-2s.896-2 2-2h8c.858 0 1.66.596 1.913 1.415L29 24c.103.335.479 1.871.411 2.191C29.411 31 24.715 36 19 36c-6.537 0-11.844-5.231-11.986-11.734l.014.01c.515.445 1.176.724 1.91.724h.124z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><ellipse fill="#66471B" cx="11.5" cy="14.5" rx="2.5" ry="3.5"/><ellipse fill="#66471B" cx="24.5" cy="14.5" rx="2.5" ry="3.5"/><path fill="#66471B" d="M7 21.262c0 3.964 4.596 9 11 9s11-5 11-9c0 0-10.333 2.756-22 0z"/><path fill="#E8596E" d="M18.545 23.604l-1.091-.005c-3.216-.074-5.454-.596-5.454-.596v6.961c0 3 2 6 6 6s6-3 6-6v-6.92c-1.922.394-3.787.542-5.455.56z"/><path fill="#DD2F45" d="M18 31.843c.301 0 .545-.244.545-.545v-7.694l-1.091-.005v7.699c.001.301.245.545.546.545z"/></svg>

After

Width:  |  Height:  |  Size: 665 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><path fill="#664500" d="M16.664 10.543c-.207-.09-.451-.03-.592.148-.009.012-.979 1.202-3.335 1.844-.775.212-1.531.319-2.245.319-.992 0-1.559-.212-1.562-.213-.21-.085-.453-.014-.587.169-.135.184-.128.435.016.612.071.087 1.78 2.147 4.125 2.147.351 0 .702-.047 1.043-.14 2.854-.778 3.41-4.208 3.432-4.354.034-.223-.087-.441-.295-.532zm10.975 2.155c-.138-.181-.38-.245-.59-.159-.006.003-.633.256-1.737.256-.661 0-1.358-.089-2.073-.265-2.369-.583-3.369-1.749-3.377-1.758-.146-.176-.388-.233-.596-.137-.207.095-.323.318-.283.542.026.145.668 3.56 3.539 4.267.311.077.629.116.946.116 2.396 0 4.099-2.16 4.17-2.252.138-.181.137-.43.001-.61zM9.998 10c-.448 0-.855-.303-.969-.757-.133-.535.191-1.077.724-1.212.036-.009 3.593-.961 5.352-4.478.247-.494.846-.695 1.342-.447.494.247.694.848.447 1.342-2.181 4.361-6.471 5.477-6.652 5.523-.081.02-.163.029-.244.029zM26 10c-.08 0-.161-.01-.242-.03-.182-.045-4.472-1.162-6.652-5.523-.247-.494-.047-1.095.447-1.342.493-.247 1.094-.047 1.342.447 1.758 3.517 5.315 4.469 5.352 4.478.533.137.857.681.722 1.215-.116.453-.522.755-.969.755zm-8 18.965c-3.623 0-6.027.422-9 1-.679.131-2 0-2-2 0-4 4.595-9 11-9 6.404 0 11 5 11 9 0 2-1.321 2.132-2 2-2.973-.578-5.377-1-9-1z"/><path fill="#FFF" d="M10 25s3-1 8-1 8 1 8 1-2-4-8-4-8 4-8 4z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 KiB

View File

@ -31,7 +31,7 @@
</div>
<div class="chatlog__content chatlog__markdown">
<span class="chatlog__markdown-preserve">We all want software to be <em>flexible</em>. We want it to work for lots of different users, we want it to adapt to different workflows. We had a fishbowl about how to design to avoid configuration, and you should read it. (See the pinned messages in <span class="chatlog__markdown-mention">#fishbowl</span>!) But when you can't avoid configuration, how do you do it well?
<span class="chatlog__markdown-preserve">We all want software to be <em>flexible</em>. We want it to work for lots of different users, we want it to adapt to different workflows. We had a fishbowl about <a class="inserted-after" href="../config/" target="_blank">how to design to avoid configuration</a>, and you should read it. But when you can't avoid configuration, how do you do it well?
And furthermore, if we want our software to be flexible, for users to be able to adapt it to their needs, what options do we have besides configuration? What other ways are there to make software flexible?
@ -155,7 +155,7 @@ And what about <em>extending</em> software? Flexibility is one thing, but some s
<div class="chatlog__message-primary">
<div class="chatlog__content chatlog__markdown">
<span class="chatlog__markdown-preserve">To give a quick summary of what we discussed at the config fishbowl (<a href="https://discord.com/channels/239737791225790464/707742863076622358/820370649313968130" target="_blank">https://discord.com/channels/239737791225790464/707742863076622358/820370649313968130</a>), we had a couple key insights:</span>
<span class="chatlog__markdown-preserve">To give a quick summary of what we discussed at the <a class="inserted-after" href="../config/" target="_blank">config fishbowl</a>, we had a couple key insights:</span>
</div>
@ -1298,7 +1298,7 @@ And what about <em>extending</em> software? Flexibility is one thing, but some s
</div>
<div class="chatlog__content chatlog__markdown">
<span class="chatlog__markdown-preserve">Sure, that makes sense - and in fact I think that falls in line with what we discussed in our config fishbowl</span>
<span class="chatlog__markdown-preserve">Sure, that makes sense - and in fact I think that falls in line with what we discussed in our <a class="inserted-after" href="../config/" target="_blank">config fishbowl</a></span>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#3B88C3" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M19.723 6.552c2.264 0 6.666.744 6.666 3.473 0 1.116-.775 2.077-1.922 2.077-1.272 0-2.139-1.085-4.744-1.085-3.844 0-5.829 3.256-5.829 7.038 0 3.689 2.015 6.852 5.829 6.852 2.605 0 3.658-1.302 4.93-1.302 1.396 0 2.047 1.395 2.047 2.107 0 2.977-4.682 3.659-6.976 3.659-6.294 0-10.666-4.992-10.666-11.41-.001-6.448 4.34-11.409 10.665-11.409z"/></svg>

After

Width:  |  Height:  |  Size: 552 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#3B88C3" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M15.675 9.156c0-1.55.992-2.418 2.326-2.418 1.333 0 2.325.868 2.325 2.418v17.611c0 1.551-.992 2.418-2.325 2.418-1.333 0-2.326-.867-2.326-2.418V9.156z"/></svg>

After

Width:  |  Height:  |  Size: 363 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#3B88C3" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M7.336 17.962c0-6.449 4.341-11.41 10.666-11.41 6.232 0 10.666 5.116 10.666 11.41 0 6.418-4.31 11.41-10.666 11.41-6.294 0-10.666-4.992-10.666-11.41zm16.496 0c0-3.752-1.954-7.131-5.83-7.131-3.876 0-5.829 3.379-5.829 7.131 0 3.782 1.891 7.132 5.829 7.132 3.938 0 5.83-3.35 5.83-7.132z"/></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#3B88C3" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M10.777 9.342c0-1.458.868-2.418 2.419-2.418h5.488c4.559 0 7.938 2.977 7.938 7.41 0 4.527-3.504 7.349-7.751 7.349H15.43v5.085c0 1.551-.992 2.418-2.326 2.418s-2.326-.867-2.326-2.418V9.342zm4.651 8.248h3.162c1.954 0 3.194-1.426 3.194-3.287 0-1.86-1.24-3.287-3.194-3.287h-3.162v6.574z"/></svg>

After

Width:  |  Height:  |  Size: 495 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#3B88C3" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M10.777 9.342c0-1.488.837-2.418 2.388-2.418h5.457c4.775 0 7.72 2.14 7.72 7.007 0 3.411-2.573 5.364-5.736 5.86l5.272 5.644c.433.465.619.93.619 1.364 0 1.209-.961 2.387-2.324 2.387-.559 0-1.303-.217-1.799-.806l-6.883-8.341h-.062v6.728c0 1.551-.992 2.418-2.326 2.418s-2.326-.867-2.326-2.418V9.342zm4.651 7.348h3.193c1.705 0 2.884-1.023 2.884-2.759 0-1.767-1.18-2.729-2.884-2.729h-3.193v5.488z"/></svg>

After

Width:  |  Height:  |  Size: 604 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#3B88C3" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M24.125 9.652c0 1.209-.806 2.294-2.076 2.294-1.271 0-2.264-.93-4.125-.93-1.333 0-2.542.713-2.542 2.016 0 3.193 10.357 1.147 10.357 9.146 0 4.434-3.659 7.193-7.938 7.193-2.388 0-7.534-.558-7.534-3.473 0-1.209.806-2.201 2.077-2.201 1.457 0 3.193 1.209 5.209 1.209 2.046 0 3.163-1.147 3.163-2.667 0-3.658-10.356-1.457-10.356-8.65 0-4.341 3.565-7.038 7.689-7.038 1.736.001 6.076.652 6.076 3.101z"/></svg>

After

Width:  |  Height:  |  Size: 606 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#3B88C3" d="M36 32c0 2.209-1.791 4-4 4H4c-2.209 0-4-1.791-4-4V4c0-2.209 1.791-4 4-4h28c2.209 0 4 1.791 4 4v28z"/><path fill="#FFF" d="M26.621 9.001c0-1.24-.93-2.263-2.232-2.263-.807 0-1.52.341-1.922.93l-4.465 6.48-4.465-6.48c-.403-.589-1.116-.93-1.922-.93-1.302 0-2.232 1.023-2.232 2.263 0 .496.155.992.434 1.396L15 17.692v8.417C15 27.706 16.294 29 17.891 29h.219c1.596 0 2.89-1.294 2.89-2.891v-8.414l5.188-7.299c.279-.403.433-.899.433-1.395z"/></svg>

After

Width:  |  Height:  |  Size: 523 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><circle fill="#31373D" cx="18" cy="18" r="18"/><circle fill="#E1E8ED" cx="18" cy="18" r="9"/><path fill="#31373D" d="M13.703 20.203c0-1.406.773-2.443 1.881-3.041-.826-.598-1.336-1.406-1.336-2.514 0-2.057 1.705-3.375 3.797-3.375 2.039 0 3.814 1.301 3.814 3.375 0 .984-.492 1.969-1.354 2.514 1.195.598 1.881 1.688 1.881 3.041 0 2.443-1.986 4.008-4.342 4.008-2.425 0-4.341-1.652-4.341-4.008zm2.742-.176c0 .896.527 1.758 1.6 1.758 1.002 0 1.6-.861 1.6-1.758 0-1.107-.633-1.758-1.6-1.758-1.02.001-1.6.774-1.6 1.758zm.334-5.097c0 .791.457 1.336 1.266 1.336.809 0 1.283-.545 1.283-1.336 0-.756-.457-1.336-1.283-1.336-.826 0-1.266.58-1.266 1.336z"/></svg>

After

Width:  |  Height:  |  Size: 707 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFDC5D" d="M30 20.145s.094-2.362-1.791-3.068c-1.667-.625-2.309.622-2.309.622s.059-1.913-1.941-2.622c-1.885-.667-2.75.959-2.75.959s-.307-1.872-2.292-2.417C17.246 13.159 16 14.785 16 14.785V2.576C16 1.618 15.458.001 13.458 0S11 1.66 11 2.576v20.5c0 1-1 1-1 0V20.41c0-3.792-2.037-6.142-2.75-6.792-.713-.65-1.667-.98-2.82-.734-1.956.416-1.529 1.92-.974 3.197 1.336 3.078 2.253 7.464 2.533 9.538.79 5.858 5.808 10.375 11.883 10.381 6.626.004 12.123-5.298 12.128-11.924v-3.931z"/></svg>

After

Width:  |  Height:  |  Size: 553 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#EF9645" d="M4.861 9.147c.94-.657 2.357-.531 3.201.166l-.968-1.407c-.779-1.111-.5-2.313.612-3.093 1.112-.777 4.263 1.312 4.263 1.312-.786-1.122-.639-2.544.483-3.331 1.122-.784 2.67-.513 3.456.611l10.42 14.72L25 31l-11.083-4.042L4.25 12.625c-.793-1.129-.519-2.686.611-3.478z"/><path fill="#FFDC5D" d="M2.695 17.336s-1.132-1.65.519-2.781c1.649-1.131 2.78.518 2.78.518l5.251 7.658c.181-.302.379-.6.6-.894L4.557 11.21s-1.131-1.649.519-2.78c1.649-1.131 2.78.518 2.78.518l6.855 9.997c.255-.208.516-.417.785-.622L7.549 6.732s-1.131-1.649.519-2.78c1.649-1.131 2.78.518 2.78.518l7.947 11.589c.292-.179.581-.334.871-.498L12.238 4.729s-1.131-1.649.518-2.78c1.649-1.131 2.78.518 2.78.518l7.854 11.454 1.194 1.742c-4.948 3.394-5.419 9.779-2.592 13.902.565.825 1.39.26 1.39.26-3.393-4.949-2.357-10.51 2.592-13.903L24.515 8.62s-.545-1.924 1.378-2.47c1.924-.545 2.47 1.379 2.47 1.379l1.685 5.004c.668 1.984 1.379 3.961 2.32 5.831 2.657 5.28 1.07 11.842-3.94 15.279-5.465 3.747-12.936 2.354-16.684-3.11L2.695 17.336z"/><g fill="#5DADEC"><path d="M12 32.042C8 32.042 3.958 28 3.958 24c0-.553-.405-1-.958-1s-1.042.447-1.042 1C1.958 30 6 34.042 12 34.042c.553 0 1-.489 1-1.042s-.447-.958-1-.958z"/><path d="M7 34c-3 0-5-2-5-5 0-.553-.447-1-1-1s-1 .447-1 1c0 4 3 7 7 7 .553 0 1-.447 1-1s-.447-1-1-1zM24 2c-.552 0-1 .448-1 1s.448 1 1 1c4 0 8 3.589 8 8 0 .552.448 1 1 1s1-.448 1-1c0-5.514-4-10-10-10z"/><path d="M29 .042c-.552 0-1 .406-1 .958s.448 1.042 1 1.042c3 0 4.958 2.225 4.958 4.958 0 .552.489 1 1.042 1s.958-.448.958-1C35.958 3.163 33 .042 29 .042z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFDB5E" d="M34.956 17.916c0-.503-.12-.975-.321-1.404-1.341-4.326-7.619-4.01-16.549-4.221-1.493-.035-.639-1.798-.115-5.668.341-2.517-1.282-6.382-4.01-6.382-4.498 0-.171 3.548-4.148 12.322-2.125 4.688-6.875 2.062-6.875 6.771v10.719c0 1.833.18 3.595 2.758 3.885C8.195 34.219 7.633 36 11.238 36h18.044c1.838 0 3.333-1.496 3.333-3.334 0-.762-.267-1.456-.698-2.018 1.02-.571 1.72-1.649 1.72-2.899 0-.76-.266-1.454-.696-2.015 1.023-.57 1.725-1.649 1.725-2.901 0-.909-.368-1.733-.961-2.336.757-.611 1.251-1.535 1.251-2.581z"/><path fill="#EE9547" d="M23.02 21.249h8.604c1.17 0 2.268-.626 2.866-1.633.246-.415.109-.952-.307-1.199-.415-.247-.952-.108-1.199.307-.283.479-.806.775-1.361.775h-8.81c-.873 0-1.583-.71-1.583-1.583s.71-1.583 1.583-1.583H28.7c.483 0 .875-.392.875-.875s-.392-.875-.875-.875h-5.888c-1.838 0-3.333 1.495-3.333 3.333 0 1.025.475 1.932 1.205 2.544-.615.605-.998 1.445-.998 2.373 0 1.028.478 1.938 1.212 2.549-.611.604-.99 1.441-.99 2.367 0 1.12.559 2.108 1.409 2.713-.524.589-.852 1.356-.852 2.204 0 1.838 1.495 3.333 3.333 3.333h5.484c1.17 0 2.269-.625 2.867-1.632.247-.415.11-.952-.305-1.199-.416-.245-.953-.11-1.199.305-.285.479-.808.776-1.363.776h-5.484c-.873 0-1.583-.71-1.583-1.583s.71-1.583 1.583-1.583h6.506c1.17 0 2.27-.626 2.867-1.633.247-.416.11-.953-.305-1.199-.419-.251-.954-.11-1.199.305-.289.487-.799.777-1.363.777h-7.063c-.873 0-1.583-.711-1.583-1.584s.71-1.583 1.583-1.583h8.091c1.17 0 2.269-.625 2.867-1.632.247-.415.11-.952-.305-1.199-.417-.246-.953-.11-1.199.305-.289.486-.799.776-1.363.776H23.02c-.873 0-1.583-.71-1.583-1.583s.709-1.584 1.583-1.584z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><ellipse fill="#66471B" cx="11.5" cy="14.5" rx="2.5" ry="3.5"/><ellipse fill="#66471B" cx="24.5" cy="14.5" rx="2.5" ry="3.5"/><path fill="#66471B" d="M7 21.262c0 3.964 4.596 9 11 9s11-5 11-9c0 0-10.333 2.756-22 0z"/><path fill="#E8596E" d="M18.545 23.604l-1.091-.005c-3.216-.074-5.454-.596-5.454-.596v6.961c0 3 2 6 6 6s6-3 6-6v-6.92c-1.922.394-3.787.542-5.455.56z"/><path fill="#DD2F45" d="M18 31.843c.301 0 .545-.244.545-.545v-7.694l-1.091-.005v7.699c.001.301.245.545.546.545z"/></svg>

After

Width:  |  Height:  |  Size: 665 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18"/><ellipse fill="#664500" cx="18" cy="25" rx="4" ry="5"/><ellipse fill="#664500" cx="12" cy="13.5" rx="2.5" ry="3.5"/><ellipse fill="#664500" cx="24" cy="13.5" rx="2.5" ry="3.5"/></svg>

After

Width:  |  Height:  |  Size: 341 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#50A5E6" d="M30 22c-3 0-6.688 7.094-7 10-.421 3.915 2 4 2 4h11V26s-3.438-4-6-4z"/><ellipse transform="rotate(-60 27.574 28.49)" fill="#1C6399" cx="27.574" cy="28.489" rx="5.848" ry="1.638"/><path fill="#F9CA55" d="M20.086 0c1.181 0 2.138.957 2.138 2.138 0 .789.668 10.824.668 10.824L17.948 18V2.138C17.948.957 18.905 0 20.086 0z"/><path fill="#FFDC5D" d="M18.875 4.323c0-1.099.852-1.989 1.903-1.989 1.051 0 1.903.891 1.903 1.989 0 0 .535 5.942 1.192 9.37.878 1.866 1.369 4.682 1.261 6.248.054.398 5.625 5.006 5.625 5.006-.281 1.813-2.259 6.155-4.759 8.159l-3.521-2.924c-2.885-.404-4.458-3.331-4.458-4.264 0-2.984.854-21.595.854-21.595z"/><path fill="#50A5E6" d="M6 22c3 0 6.688 7.094 7 10 .421 3.915-2 4-2 4H0V26s3.438-4 6-4z"/><ellipse transform="rotate(-30 8.424 28.489)" fill="#1C6399" cx="8.426" cy="28.489" rx="1.638" ry="5.848"/><path fill="#F9CA55" d="M16.061.011c-1.266-.127-2.333.864-2.333 2.103 0 .78-.184 10.319-.184 10.319L17.895 18l.062-15.765c0-1.106-.795-2.114-1.896-2.224z"/><path fill="#FFDC5D" d="M17.125 4.323c0-1.099-.852-1.989-1.903-1.989-1.051 0-1.903.891-1.903 1.989 0 0-.535 5.942-1.192 9.37-.878 1.866-1.369 4.682-1.261 6.248-.054.398-5.625 5.006-5.625 5.006C5.522 26.76 7.5 31.102 10 33.106l3.521-2.924c2.885-.404 4.458-3.331 4.458-4.264 0-2.984-.854-21.595-.854-21.595z"/><path fill="#F9CA55" d="M17.958 25.823c-.414 0-.75-.336-.75-.75V2.792c0-.414.336-.75.75-.75s.75.336.75.75v22.282c.001.413-.335.749-.75.749z"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCB4C" d="M30.728 5.272c7.029 7.029 7.029 18.427 0 25.456s-18.426 7.029-25.456 0c-7.029-7.029-7.029-18.427 0-25.456s18.426-7.029 25.456 0"/><path fill="#65471B" d="M19.279 19.28c-3.494 3.494-6.219 5.404-9.645 7.714-.781.529-1.928 1.929.001 3.859 3.857 3.857 13.111 4.247 19.287-1.929 6.177-6.177 5.788-15.43 1.929-19.288-1.929-1.93-3.33-.782-3.858 0-2.308 3.423-4.219 6.149-7.714 9.644z"/><path fill="#FFF" d="M11.564 28.923s3.858-1.929 9.644-7.716c5.787-5.786 7.715-9.643 7.715-9.643s1.928 5.787-4.822 12.537c-6.75 6.751-12.537 4.822-12.537 4.822z"/><path fill="#65471B" d="M8.265 25.234c-.215.215-.525.33-.849.283-.547-.078-.927-.584-.848-1.131.025-.176.543-3.63 2.774-6.922-2.627.873-4.589 2.796-4.614 2.821-.391.389-1.024.388-1.414-.003-.39-.39-.39-1.022 0-1.412.156-.156 3.882-3.828 8.485-3.828.405 0 .769.243.924.617.155.374.069.804-.217 1.09-3.262 3.262-3.953 7.874-3.96 7.919-.031.224-.134.419-.281.566zm16.968-16.97c-.147.147-.341.25-.563.282-.047.007-4.676.715-7.921 3.96-.286.286-.716.372-1.09.217-.374-.155-.617-.519-.617-.924 0-4.604 3.672-8.329 3.828-8.485.39-.39 1.022-.39 1.412 0 .391.389.392 1.023.003 1.413-.026.026-1.949 1.987-2.822 4.614 3.292-2.231 6.745-2.749 6.921-2.774.546-.078 1.052.3 1.131.847.049.324-.066.635-.282.85z"/><path fill="#64AADD" d="M30.101 12.055c2.741.334 5.234-1.618 5.567-4.36.334-2.741-1.617-5.234-4.359-5.568-2.741-.333-10.409 2.763-10.53 3.756-.121.992 6.581 5.837 9.322 6.172zM12.157 29.999c.334 2.741-1.618 5.234-4.36 5.567-2.741.334-5.234-1.617-5.568-4.359-.334-2.742 2.763-10.41 3.756-10.531.992-.12 5.837 6.582 6.172 9.323z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#EF9645" d="M26.992 19.016c-.255-.255-.799-.611-1.44-.962l-1.911-2-2.113 2h-.58l-2.509-3.634c-1.379.01-2.497 1.136-2.487 2.515l-3.556-2.112c-.817.364-1.389 1.18-1.389 2.133v.96l-4 4.168.016 2.185 9.984 10.729S27.525 19.71 27.55 19.74c-.129-.223-.513-.702-.558-.724z"/><g fill="#FFDC5D"><path d="M25.552 5.81c0-1.107-.906-2.013-2.013-2.013-1.107 0-2.013.906-2.013 2.013v12.245h4.025V5.81zm-4.605 12.244V16.01c-.008-1.103-.909-1.991-2.012-1.983-1.103.008-1.991.909-1.983 2.012l.012 2.016h3.983zM8.916 16h.168c1.059 0 1.916.858 1.916 1.917v4.166C11 23.142 10.143 24 9.084 24h-.168C7.857 24 7 23.142 7 22.083v-4.166C7 16.858 7.857 16 8.916 16zm6.918 2.96l-.056.062C15.304 19.551 15 20.233 15 21c0 .063.013.123.018.185.044.678.308 1.292.728 1.774-.071.129-.163.243-.259.353-.366.417-.89.688-1.487.688-1.104 0-2-.896-2-2v-6c0-.441.147-.845.389-1.176.364-.497.947-.824 1.611-.824 1.104 0 2 .896 2 2v2.778c-.061.055-.109.123-.166.182z"/><path d="M9.062 25c1.024 0 1.925-.526 2.45-1.322.123.183.271.346.431.497 1.185 1.115 3.034 1.044 4.167-.086.152-.152.303-.305.419-.488l-.003-.003C16.727 23.713 17 24 18 24h2.537c-.37.279-.708.623-1.024 1-1.228 1.467-2.013 3.606-2.013 6 0 .276.224.5.5.5s.5-.224.5-.5c0-2.548.956-4.775 2.377-6 .732-.631 1.584-1 2.498-1 .713.079.847-1 .125-1H18c-1.104 0-2-.896-2-2s.896-2 2-2h8c.858 0 1.66.596 1.913 1.415L29 24c.103.335.479 1.871.411 2.191C29.411 31 24.715 36 19 36c-6.537 0-11.844-5.231-11.986-11.734l.014.01c.515.445 1.176.724 1.91.724h.124z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#269" d="M32 0H4C1.791 0 0 1.791 0 4v22h36V4c0-2.209-1.791-4-4-4z"/><path fill="#3F484C" d="M10 36V7l4-4h2l4 4v29zm23-25c0-1-1-1-1-1h-7s-1 0-1 1v25h9V11z"/><path fill="#292F33" d="M28 19c0-1-1-1-1-1h-8c-1 0-1 1-1 1v17h10V19zm-17 2H6v-7s0-1-1-1H0v19c0 2.209 1.791 4 4 4h8V22s0-1-1-1zm21 4c-1 0-1 1-1 1v10h1c2.209 0 4-1.791 4-4v-7h-4z"/><path d="M8 31h2v2H8zm0-8h2v2H8zm-2 4h2v2H6zM16 9h2v2h-2zm0 4h2v2h-2zm-2 4h2v2h-2zm10 3h2v2h-2zm-2 4h2v2h-2zm-2 6h2v2h-2zm9-18h2v2h-2zm0 4h2v2h-2z" fill="#FFCC4D"/><g fill="#FFF"><circle cx="10.5" cy="2.5" r=".5"/><circle cx="7.5" cy="11.5" r=".5"/><circle cx="22" cy="5" r="1"/><circle cx="26.5" cy="5.5" r=".5"/><circle cx="31" cy="3" r="1"/><circle cx="31.5" cy="7.5" r=".5"/><path d="M6.5 7C5.119 7 4 5.881 4 4.5c0-1.13.755-2.074 1.784-2.383C5.533 2.048 5.273 2 5 2 3.343 2 2 3.343 2 5s1.343 3 3 3c.959 0 1.803-.458 2.353-1.159C7.085 6.938 6.801 7 6.5 7z"/></g></svg>

After

Width:  |  Height:  |  Size: 978 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><path fill="#664500" d="M28.457 17.797c-.06-.135-1.499-3.297-4.457-3.297-2.957 0-4.397 3.162-4.457 3.297-.092.207-.032.449.145.591.175.142.426.147.61.014.012-.009 1.262-.902 3.702-.902 2.426 0 3.674.881 3.702.901.088.066.194.099.298.099.11 0 .221-.037.312-.109.177-.142.238-.386.145-.594zm-12 0c-.06-.135-1.499-3.297-4.457-3.297-2.957 0-4.397 3.162-4.457 3.297-.092.207-.032.449.144.591.176.142.427.147.61.014.013-.009 1.262-.902 3.703-.902 2.426 0 3.674.881 3.702.901.088.066.194.099.298.099.11 0 .221-.037.312-.109.178-.142.237-.386.145-.594zM18 22c-3.623 0-6.027-.422-9-1-.679-.131-2 0-2 2 0 4 4.595 9 11 9 6.404 0 11-5 11-9 0-2-1.321-2.132-2-2-2.973.578-5.377 1-9 1z"/><path fill="#FFF" d="M9 23s3 1 9 1 9-1 9-1-2 4-9 4-9-4-9-4z"/></svg>

After

Width:  |  Height:  |  Size: 920 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18-9.94 0-18-8.059-18-18C0 8.06 8.06 0 18 0c9.941 0 18 8.06 18 18"/><ellipse fill="#66471B" cx="11.5" cy="14.5" rx="2.5" ry="3.5"/><ellipse fill="#66471B" cx="24.5" cy="14.5" rx="2.5" ry="3.5"/><path fill="#66471B" d="M7 21.262c0 3.964 4.596 9 11 9s11-5 11-9c0 0-10.333 2.756-22 0z"/><path fill="#E8596E" d="M18.545 23.604l-1.091-.005c-3.216-.074-5.454-.596-5.454-.596v6.961c0 3 2 6 6 6s6-3 6-6v-6.92c-1.922.394-3.787.542-5.455.56z"/><path fill="#DD2F45" d="M18 31.843c.301 0 .545-.244.545-.545v-7.694l-1.091-.005v7.699c.001.301.245.545.546.545z"/></svg>

After

Width:  |  Height:  |  Size: 665 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><circle fill="#FFCC4D" cx="18" cy="18" r="18"/><path fill="#664500" d="M10.515 23.621C10.56 23.8 11.683 28 18 28c6.318 0 7.44-4.2 7.485-4.379.055-.217-.043-.442-.237-.554-.195-.111-.439-.078-.6.077C24.629 23.163 22.694 25 18 25s-6.63-1.837-6.648-1.855C11.256 23.05 11.128 23 11 23c-.084 0-.169.021-.246.064-.196.112-.294.339-.239.557z"/><ellipse fill="#664500" cx="12" cy="13.5" rx="2.5" ry="3.5"/><ellipse fill="#664500" cx="24" cy="13.5" rx="2.5" ry="3.5"/></svg>

After

Width:  |  Height:  |  Size: 525 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFDB5E" d="M34.956 17.916c0-.503-.12-.975-.321-1.404-1.341-4.326-7.619-4.01-16.549-4.221-1.493-.035-.639-1.798-.115-5.668.341-2.517-1.282-6.382-4.01-6.382-4.498 0-.171 3.548-4.148 12.322-2.125 4.688-6.875 2.062-6.875 6.771v10.719c0 1.833.18 3.595 2.758 3.885C8.195 34.219 7.633 36 11.238 36h18.044c1.838 0 3.333-1.496 3.333-3.334 0-.762-.267-1.456-.698-2.018 1.02-.571 1.72-1.649 1.72-2.899 0-.76-.266-1.454-.696-2.015 1.023-.57 1.725-1.649 1.725-2.901 0-.909-.368-1.733-.961-2.336.757-.611 1.251-1.535 1.251-2.581z"/><path fill="#EE9547" d="M23.02 21.249h8.604c1.17 0 2.268-.626 2.866-1.633.246-.415.109-.952-.307-1.199-.415-.247-.952-.108-1.199.307-.283.479-.806.775-1.361.775h-8.81c-.873 0-1.583-.71-1.583-1.583s.71-1.583 1.583-1.583H28.7c.483 0 .875-.392.875-.875s-.392-.875-.875-.875h-5.888c-1.838 0-3.333 1.495-3.333 3.333 0 1.025.475 1.932 1.205 2.544-.615.605-.998 1.445-.998 2.373 0 1.028.478 1.938 1.212 2.549-.611.604-.99 1.441-.99 2.367 0 1.12.559 2.108 1.409 2.713-.524.589-.852 1.356-.852 2.204 0 1.838 1.495 3.333 3.333 3.333h5.484c1.17 0 2.269-.625 2.867-1.632.247-.415.11-.952-.305-1.199-.416-.245-.953-.11-1.199.305-.285.479-.808.776-1.363.776h-5.484c-.873 0-1.583-.71-1.583-1.583s.71-1.583 1.583-1.583h6.506c1.17 0 2.27-.626 2.867-1.633.247-.416.11-.953-.305-1.199-.419-.251-.954-.11-1.199.305-.289.487-.799.777-1.363.777h-7.063c-.873 0-1.583-.711-1.583-1.584s.71-1.583 1.583-1.583h8.091c1.17 0 2.269-.625 2.867-1.632.247-.415.11-.952-.305-1.199-.417-.246-.953-.11-1.199.305-.289.486-.799.776-1.363.776H23.02c-.873 0-1.583-.71-1.583-1.583s.709-1.584 1.583-1.584z"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#662113" d="M34.539 33.488s1.18.215 1.18 1.256c0 1.043-1.042 1.256-2.084 1.256h-17.72C13.831 36 10 32 8 32l-.592 4H1v-2.936c0-2.084-.881-2.285-.881-3.211 0-2.084.672-4.395.672-4.395L17 33l17.539.488z"/><path fill="#C1694F" d="M25.449 27.111C23.146 26.054 19.194 21.618 19 21.4 14.149 13.139 19 1 19 1s-1.916-1-7-1c-.674 0-1.343.018-2 .049L9 1 8 .182C4.616.47 2 1 2 1s1 15 1 17-.723 3.401-.723 3.401S.84 23.829.583 24.959c-.287 1.26.22 2.113 1.084 2.322C4.704 28.013 8 29 15.637 32.66c0 0 2 1.34 4.363 1.34h14.539s1.18-.257 1.18-2.424c0-1.084-7.907-3.381-10.27-4.465z"/><path fill="#D99E82" d="M10 20V.049c-.687.032-1.356.078-2 .133V20h2z"/></svg>

After

Width:  |  Height:  |  Size: 718 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#BB1A34" d="M1.728 21c-.617 0-.953-.256-1.127-.471-.171-.211-.348-.585-.225-1.165L3.104 6.658l-1.714.097h-.013c-.517 0-.892-.168-1.127-.459-.22-.272-.299-.621-.221-.98.15-.702.883-1.286 1.667-1.329l4.008-.227c.078-.005.15-.008.217-.008.147 0 .536 0 .783.306.252.312.167.709.139.839L3.719 19.454c-.187.884-.919 1.489-1.866 1.542L1.728 21zm10.743-2c-1.439 0-2.635-.539-3.459-1.559-1.163-1.439-1.467-3.651-.878-6.397 1.032-4.812 4.208-8.186 7.902-8.395 1.59-.089 2.906.452 3.793 1.549 1.163 1.439 1.467 3.651.878 6.397-1.032 4.81-4.208 8.184-7.904 8.394-.112.008-.223.011-.332.011zm3.414-13.746l-.137.004c-1.94.111-3.555 2.304-4.32 5.866-.478 2.228-.381 3.899.272 4.707.297.368.717.555 1.249.555l.14-.004c1.94-.109 3.554-2.301 4.318-5.864.478-2.228.382-3.9-.27-4.708-.296-.369-.718-.556-1.252-.556zm11.591 12.107c-1.439 0-2.637-.539-3.462-1.56-1.163-1.439-1.467-3.651-.878-6.397 1.033-4.813 4.209-8.186 7.903-8.394 1.603-.09 2.903.453 3.79 1.549 1.163 1.439 1.467 3.651.878 6.396-1.031 4.809-4.206 8.183-7.902 8.396-.112.008-.221.01-.329.01zm3.411-13.747l-.136.004c-1.941.111-3.556 2.304-4.32 5.865-.478 2.229-.381 3.901.272 4.708.297.368.719.555 1.251.555l.14-.004c1.939-.109 3.554-2.302 4.318-5.864.479-2.227.383-3.899-.27-4.707-.298-.37-.72-.557-1.255-.557zM11 35.001c-.81 0-1.572-.496-1.873-1.299-.388-1.034.136-2.187 1.17-2.575.337-.126 8.399-3.108 20.536-4.12 1.101-.096 2.067.727 2.159 1.827.092 1.101-.727 2.067-1.827 2.159-11.59.966-19.386 3.851-19.464 3.88-.23.086-.468.128-.701.128zM2.001 29c-.804 0-1.563-.488-1.868-1.283-.396-1.031.118-2.188 1.149-2.583.542-.209 13.516-5.126 32.612-6.131 1.113-.069 2.045.789 2.103 1.892.059 1.103-.789 2.045-1.892 2.103-18.423.97-31.261 5.821-31.389 5.87-.235.089-.477.132-.715.132z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#EF9645" d="M26.992 19.016c-.255-.255-.799-.611-1.44-.962l-1.911-2-2.113 2h-.58l-2.509-3.634c-1.379.01-2.497 1.136-2.487 2.515l-3.556-2.112c-.817.364-1.389 1.18-1.389 2.133v.96l-4 4.168.016 2.185 9.984 10.729S27.525 19.71 27.55 19.74c-.129-.223-.513-.702-.558-.724z"/><g fill="#FFDC5D"><path d="M25.552 5.81c0-1.107-.906-2.013-2.013-2.013-1.107 0-2.013.906-2.013 2.013v12.245h4.025V5.81zm-4.605 12.244V16.01c-.008-1.103-.909-1.991-2.012-1.983-1.103.008-1.991.909-1.983 2.012l.012 2.016h3.983zM8.916 16h.168c1.059 0 1.916.858 1.916 1.917v4.166C11 23.142 10.143 24 9.084 24h-.168C7.857 24 7 23.142 7 22.083v-4.166C7 16.858 7.857 16 8.916 16zm6.918 2.96l-.056.062C15.304 19.551 15 20.233 15 21c0 .063.013.123.018.185.044.678.308 1.292.728 1.774-.071.129-.163.243-.259.353-.366.417-.89.688-1.487.688-1.104 0-2-.896-2-2v-6c0-.441.147-.845.389-1.176.364-.497.947-.824 1.611-.824 1.104 0 2 .896 2 2v2.778c-.061.055-.109.123-.166.182z"/><path d="M9.062 25c1.024 0 1.925-.526 2.45-1.322.123.183.271.346.431.497 1.185 1.115 3.034 1.044 4.167-.086.152-.152.303-.305.419-.488l-.003-.003C16.727 23.713 17 24 18 24h2.537c-.37.279-.708.623-1.024 1-1.228 1.467-2.013 3.606-2.013 6 0 .276.224.5.5.5s.5-.224.5-.5c0-2.548.956-4.775 2.377-6 .732-.631 1.584-1 2.498-1 .713.079.847-1 .125-1H18c-1.104 0-2-.896-2-2s.896-2 2-2h8c.858 0 1.66.596 1.913 1.415L29 24c.103.335.479 1.871.411 2.191C29.411 31 24.715 36 19 36c-6.537 0-11.844-5.231-11.986-11.734l.014.01c.515.445 1.176.724 1.91.724h.124z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#DD2E44" d="M35.885 11.833c0-5.45-4.418-9.868-9.867-9.868-3.308 0-6.227 1.633-8.018 4.129-1.791-2.496-4.71-4.129-8.017-4.129-5.45 0-9.868 4.417-9.868 9.868 0 .772.098 1.52.266 2.241C1.751 22.587 11.216 31.568 18 34.034c6.783-2.466 16.249-11.447 17.617-19.959.17-.721.268-1.469.268-2.242z"/></svg>

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

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