diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..cc90457bb9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI + +on: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + + - name: "Build Bevy Assets" + run: cd generate-assets && ./generate_assets.sh + + - name: "Build website" + uses: shalzz/zola-deploy-action@master + env: + PAGES_BRANCH: gh-pages + BUILD_DIR: . + BUILD_ONLY: true + TOKEN: fake-secret \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000..a361829e20 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,24 @@ +name: Deploy + +on: + push: + branches: [master] + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +jobs: + build_and_deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + + - name: "Build Bevy Assets" + run: cd generate-assets && ./generate_assets.sh + + - name: "Build and deploy website" + uses: shalzz/zola-deploy-action@master + env: + PAGES_BRANCH: gh-pages + BUILD_DIR: . + TOKEN: ${{ secrets.CART_PAT }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 78b8144f2d..0000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,39 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: CI - -# Controls when the action will run. Triggers the workflow on pushes to master -# or any pull request or pull request -on: - push: - branches: [master] - pull_request: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - build: - runs-on: ubuntu-latest - if: github.ref != 'refs/heads/master' # Only on PRs, see also build_and_deploy below - steps: - - uses: actions/checkout@v2 - - - name: "Build website" - uses: shalzz/zola-deploy-action@master - env: - PAGES_BRANCH: gh-pages - BUILD_DIR: . - BUILD_ONLY: true - TOKEN: fake-secret - - build_and_deploy: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' # Only on a push to the master branch (like when PR's are merged) - steps: - - uses: actions/checkout@master - - - name: "Build and deploy website" - uses: shalzz/zola-deploy-action@master - env: - PAGES_BRANCH: gh-pages - BUILD_DIR: . - TOKEN: ${{ secrets.CART_PAT }} diff --git a/.gitignore b/.gitignore index 0cefd49c99..71da54c70d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ public **/.DS_Store .idea/ +content/assets diff --git a/generate-assets/.gitignore b/generate-assets/.gitignore new file mode 100644 index 0000000000..ebbe8e0e18 --- /dev/null +++ b/generate-assets/.gitignore @@ -0,0 +1,2 @@ +target +assets diff --git a/generate-assets/Cargo.lock b/generate-assets/Cargo.lock new file mode 100644 index 0000000000..65a430b962 --- /dev/null +++ b/generate-assets/Cargo.lock @@ -0,0 +1,149 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "generate-assets" +version = "0.1.0" +dependencies = [ + "rand", + "serde", + "toml", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +dependencies = [ + "rand_core", +] + +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" diff --git a/generate-assets/Cargo.toml b/generate-assets/Cargo.toml new file mode 100644 index 0000000000..ca0389cef5 --- /dev/null +++ b/generate-assets/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "generate-assets" +version = "0.1.0" +authors = [ + "Bevy Contributors ", + "Carter Anderson ", +] +license = "MIT" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +toml = "0.5" +serde = { version = "1", features = [ "derive" ] } +rand = "0.8" diff --git a/generate-assets/generate_assets.sh b/generate-assets/generate_assets.sh new file mode 100755 index 0000000000..6461f7cc69 --- /dev/null +++ b/generate-assets/generate_assets.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +git clone --branch bevy-asset https://github.com/bevyengine/awesome-bevy assets + +cargo run -- assets ../content diff --git a/generate-assets/parse_old_readme.py b/generate-assets/parse_old_readme.py new file mode 100644 index 0000000000..eb7e718099 --- /dev/null +++ b/generate-assets/parse_old_readme.py @@ -0,0 +1,42 @@ +import re +import os +import sys + +name_fix = { + 'bevy_nbody': 'thallada-bevy_nbody', + 'bevy-nbody': 'WhoisDavid-bevy-nbody', +} + +f = open(sys.argv[1] + "/README.md") +lines = f.readlines() + +root_folder = sys.argv[2] + "/" +os.mkdir(root_folder) + +category = None +subcategory = None +current_path = root_folder +for line in lines: + if line[0:3] == "## ": + category = line.split("# ")[1][0:-1] + current_path = root_folder + category + os.mkdir(current_path) + elif line[0:3] == "###": + subcategory = line.split("# ")[1][0:-1] + current_path = root_folder + category + "/" + subcategory + os.mkdir(current_path) + elif line[0:2] == "* ": + line = line[0:-1] + m = re.search('\* \[([^]]*)\]\(([^)]*)\)(: (.*))?', line) + name = m.group(1) + link = m.group(2) + desc = m.group(4) + if name in name_fix: + name = name_fix[name] + f = open(current_path + '/' + name.replace(' ', '-').replace('/', '-') + ".toml", "w") + f.write("name = \"" + name + "\"\n") + if desc is not None: + f.write("description = \"" + desc.replace('"', '\'') + "\"\n") + f.write("link = \"" + link + "\"\n") + f.close() + diff --git a/generate-assets/src/main.rs b/generate-assets/src/main.rs new file mode 100644 index 0000000000..725b47a633 --- /dev/null +++ b/generate-assets/src/main.rs @@ -0,0 +1,271 @@ +use rand::{prelude::SliceRandom, thread_rng}; +use serde::{Deserialize, Serialize}; +use std::{ + fs::{self, File}, + io::{self, prelude::*}, + path::{Path, PathBuf}, + str::FromStr, +}; + +#[derive(Deserialize, Debug, Clone)] +struct Asset { + name: String, + link: String, + description: Option, + order: Option, +} + +#[derive(Serialize)] +struct FrontMatterAsset { + title: String, + description: String, + weight: usize, + extra: FrontMatterAssetExtra, +} + +#[derive(Serialize)] +struct FrontMatterAssetExtra { + link: String, +} + +impl From<&Asset> for FrontMatterAsset { + fn from(asset: &Asset) -> Self { + FrontMatterAsset { + title: asset.name.clone(), + description: asset.description.clone().unwrap_or_default(), + weight: asset.order.unwrap_or(0), + extra: FrontMatterAssetExtra { + link: asset.link.clone(), + }, + } + } +} + +impl Asset { + fn write(&self, current_path: &str, weight: usize) -> io::Result<()> { + let path = Path::new(¤t_path); + + let mut frontmatter = FrontMatterAsset::from(self); + if self.order.is_none() { + frontmatter.weight = weight; + } + + let mut file = File::create(path.join(format!( + "{}.md", + self.name.to_ascii_lowercase().replace("/", "-") + )))?; + file.write_all( + format!( + r#"+++ +{} ++++ +"#, + toml::to_string(&frontmatter).unwrap(), + ) + .as_bytes(), + )?; + + Ok(()) + } +} + +#[derive(Debug, Clone)] +struct Section { + name: String, + content: Vec, + template: Option, + header: Option, + order: Option, + sort_order_reversed: bool, +} + +#[derive(Serialize)] +struct FrontMatterSection { + title: String, + sort_by: String, + template: Option, + weight: usize, + extra: FrontMatterSectionExtra, +} + +#[derive(Serialize)] +struct FrontMatterSectionExtra { + header_message: Option, + sort_order_reversed: bool, +} + +impl From<&Section> for FrontMatterSectionExtra { + fn from(section: &Section) -> Self { + FrontMatterSectionExtra { + header_message: section.header.clone(), + sort_order_reversed: section.sort_order_reversed, + } + } +} + +impl From<&Section> for FrontMatterSection { + fn from(section: &Section) -> Self { + FrontMatterSection { + title: section.name.clone(), + sort_by: "weight".to_string(), + template: section.template.clone(), + weight: section.order.unwrap_or(0), + extra: section.into(), + } + } +} + +impl Section { + fn write(&self, current_path: &str, weight: usize) -> io::Result<()> { + let path = Path::new(¤t_path).join(self.name.to_ascii_lowercase()); + fs::create_dir(path.clone())?; + + let mut frontmatter = FrontMatterSection::from(self); + if self.order.is_none() { + frontmatter.weight = weight; + } + + let mut file = File::create(path.join("_index.md"))?; + file.write_all( + format!( + r#"+++ +{} ++++ +"#, + toml::to_string(&frontmatter).unwrap(), + ) + .as_bytes(), + )?; + + let mut sorted_section = vec![]; + for content in self.content.iter() { + if let AssetNode::Section(section) = content { + sorted_section.push(AssetNode::Section(section.clone())); + } + } + sorted_section.sort_by_key(|section| format!("{}-{}", section.order(), section.name())); + + let mut randomized_assets = vec![]; + let mut manually_sorted_assets = vec![]; + for content in self.content.iter() { + if let AssetNode::Asset(asset) = content { + if asset.order.is_some() { + manually_sorted_assets.push(content.clone()); + } else { + randomized_assets.push(content.clone()); + } + } + } + manually_sorted_assets.sort_by_key(AssetNode::order); + randomized_assets.shuffle(&mut thread_rng()); + + for (i, content) in sorted_section + .iter() + .chain(manually_sorted_assets.iter()) + .chain(randomized_assets.iter()) + .enumerate() + { + content.write(path.to_str().unwrap(), i)? + } + Ok(()) + } +} + +#[derive(Debug, Clone)] +enum AssetNode { + Section(Section), + Asset(Asset), +} +impl AssetNode { + fn write(&self, current_path: &str, weight: usize) -> io::Result<()> { + match self { + AssetNode::Section(content) => content.write(current_path, weight), + AssetNode::Asset(content) => content.write(current_path, weight), + } + } + fn name(&self) -> String { + match self { + AssetNode::Section(content) => content.name.clone(), + AssetNode::Asset(content) => content.name.clone(), + } + } + fn order(&self) -> usize { + match self { + AssetNode::Section(content) => content.order.unwrap_or(99999), + AssetNode::Asset(content) => content.order.unwrap_or(99999), + } + } +} + +fn main() -> io::Result<()> { + let asset_dir = std::env::args().nth(1).unwrap(); + let content_dir = std::env::args().nth(2).unwrap(); + let _ = fs::create_dir(content_dir.clone()); + let mut asset_root_section = Section { + name: "Assets".to_string(), + content: vec![], + template: Some("assets.html".to_string()), + header: Some("Assets".to_string()), + order: None, + sort_order_reversed: false, + }; + visit_dirs( + PathBuf::from_str(&asset_dir).unwrap(), + &mut asset_root_section, + )?; + + asset_root_section.write(&content_dir, 0)?; + Ok(()) +} + +fn visit_dirs(dir: PathBuf, section: &mut Section) -> io::Result<()> { + if dir.is_dir() { + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + if path.file_name().unwrap() == ".git" { + continue; + } + if path.is_dir() { + let folder = path.file_name().unwrap(); + let (order, sort_order_reversed) = if path.join("_category.toml").exists() { + let from_file: toml::Value = toml::de::from_str( + &fs::read_to_string(path.join("_category.toml")).unwrap(), + ) + .unwrap(); + ( + from_file + .get("order") + .and_then(|v| v.as_integer()) + .map(|v| v as usize), + from_file + .get("sort_order_reversed") + .and_then(|v| v.as_bool()) + .unwrap_or(false), + ) + } else { + (None, false) + }; + let mut new_section = Section { + name: folder.to_str().unwrap().to_string(), + content: vec![], + template: None, + header: None, + order, + sort_order_reversed, + }; + visit_dirs(path.clone(), &mut new_section)?; + section.content.push(AssetNode::Section(new_section)); + } else { + if path.file_name().unwrap() == "_category.toml" + || path.extension().unwrap() != "toml" + { + continue; + } + let asset: Asset = toml::de::from_str(&fs::read_to_string(path).unwrap()).unwrap(); + section.content.push(AssetNode::Asset(asset)); + } + } + } + Ok(()) +} diff --git a/sass/_assets.scss b/sass/_assets.scss new file mode 100644 index 0000000000..64235076b4 --- /dev/null +++ b/sass/_assets.scss @@ -0,0 +1,42 @@ +.assets { + display: flex; + + .card-list { + display: flex; + flex-wrap: wrap; + } + + .card { + width: 30%; + margin-right: $card-list-gap; + height: 8rem; + } + + .card-title { + font-size: 1.3rem; + overflow-wrap: break-word; + } + + .card-description { + font-size: 1rem; + height: initial; + } + + .card-image > img { + display: block; + height: auto; + max-width: 100%; + } + + a { + text-decoration: none; + } + + .book-content { + h3 { + font-size: 1.9rem; + font-weight: 700; + text-decoration: none; + } + } +} \ No newline at end of file diff --git a/sass/_base.scss b/sass/_base.scss index 7da6f5b8a2..e0f5c937aa 100644 --- a/sass/_base.scss +++ b/sass/_base.scss @@ -9,7 +9,7 @@ html, body { font-family: 'Fira Sans', sans-serif; background-color: #232326; } -@media screen and (max-width: 712px) { +@media screen and (max-width: 790px) { html, body { font-size: 0.7rem; } diff --git a/sass/_book-nav.scss b/sass/_book-nav.scss index a385eb51a8..98de04abc9 100644 --- a/sass/_book-nav.scss +++ b/sass/_book-nav.scss @@ -16,7 +16,7 @@ $book-nav-width: 210px; } .book-nav { - margin-top: $content-padding; + margin-top: $default-padding; flex: 0 0 $book-nav-width; user-select: none; transition: transform 0.3s; diff --git a/sass/_book.scss b/sass/_book.scss index 7c1fcd05c1..c552f0fb74 100644 --- a/sass/_book.scss +++ b/sass/_book.scss @@ -12,16 +12,14 @@ vertical-align: middle; } -$content-padding: 15px; - .book-content { - padding-left: $content-padding; - padding-right: $content-padding; + padding-left: $default-padding; + padding-right: $default-padding; position: relative; width: 100%; min-width: 0; h1 { - margin-top: $content-padding; + margin-top: $default-padding; font-size: 2.4rem; margin-bottom: 15px; } @@ -95,12 +93,12 @@ $pager-bar-background-color-in: #29292c; .book-pager-image-left { float: left; - margin-left: $content-padding; + margin-left: $default-padding; } .book-pager-image-right { float: right; - margin-right: $content-padding; + margin-right: $default-padding; } $pager-bar-icon-width: 200px; diff --git a/sass/_card.scss b/sass/_card.scss index 34a761e714..2effc2cb09 100644 --- a/sass/_card.scss +++ b/sass/_card.scss @@ -1,5 +1,3 @@ -$card-list-gap: 15px; - .card { display: block; flex-direction: left; diff --git a/sass/_headerbar.scss b/sass/_headerbar.scss index 27da66a112..f04807e2d7 100644 --- a/sass/_headerbar.scss +++ b/sass/_headerbar.scss @@ -95,19 +95,19 @@ $header-active-color: #b1d9ff; } -@media screen and (max-width: 515px) { +@media screen and (max-width: 580px) { .header-button { display: none; } } -@media screen and (max-width: 1100px) { +@media screen and (max-width: 1120px) { .header-message { display: none; } } -@media screen and (max-width: 900px) { +@media screen and (max-width: 965px) { .header-logo-mobile, .toggle-nav-mobile { display: flex; } diff --git a/sass/site.scss b/sass/site.scss index 1643133e2c..7604a7bf12 100644 --- a/sass/site.scss +++ b/sass/site.scss @@ -15,9 +15,11 @@ $default-image-background-color: #1b1b1b; $card-hover-background: #2f3033; $media-max-width: 1000px; $border-radius: 10px; +$card-list-gap: 15px; @import "headerbar"; @import "community"; +@import "assets"; @import "book"; @import "book-nav"; @import "news"; diff --git a/templates/assets.html b/templates/assets.html new file mode 100644 index 0000000000..92f1bfa2a6 --- /dev/null +++ b/templates/assets.html @@ -0,0 +1,77 @@ +{% extends "base.html" %} + +{% block content %} +
+ + +
+ {% for subsection in section.subsections %} + {% set section = get_section(path=subsection) %} + +

+ {{ section.title }}# +

+ + {% set subsections = section.subsections %} + {% if section.extra.sort_order_reversed %} + {% set subsections = section.subsections | reverse %} + {% endif %} + {% for subsection in subsections %} + {% set section = get_section(path=subsection) %} + +

+ {{ section.title }}# +

+
+ {% set pages = section.pages %} + {% if section.extra.sort_order_reversed %} + {% set pages = section.pages | reverse %} + {% endif %} + {% for post in pages %} + + {% endfor %} +
+ + {% endfor %} + {% endfor %} +
+
+{% endblock content %} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 467018af25..0635e8145a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -15,6 +15,13 @@ {% set path = ""%} {% endif %} +{% if section %} + {% if section.path is starting_with("/learn/book/") %} + {% set show_nav_toggle = true %} + {% elif section.path is starting_with("/assets/") %} + {% set show_nav_toggle = true %} + {% endif %} +{% endif %} @@ -38,7 +45,7 @@ - {% if section and section.path is starting_with("/learn/book/") %} + {% if show_nav_toggle %}