Merge branch 'main' into cours
Some checks are pending
ci/woodpecker/push/publish Pipeline is pending
Some checks are pending
ci/woodpecker/push/publish Pipeline is pending
This commit is contained in:
commit
775c5bb7bc
16 changed files with 150 additions and 66 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -560,9 +560,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cached"
|
||||
version = "0.47.0"
|
||||
version = "0.48.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69b0116662497bc24e4b177c90eaf8870e39e2714c3fcfa296327a93f593fc21"
|
||||
checksum = "355face540df58778b96814c48abb3c2ed67c4878a8087ab1819c1fedeec505f"
|
||||
dependencies = [
|
||||
"ahash 0.8.3",
|
||||
"async-trait",
|
||||
|
@ -578,9 +578,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cached_proc_macro"
|
||||
version = "0.18.1"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f"
|
||||
checksum = "9d52f526f7cbc875b296856ca8c964a9f6290556922c303a8a3883e3c676e6a1"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
|
@ -590,9 +590,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cached_proc_macro_types"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
|
||||
checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
|
|
|
@ -12,7 +12,7 @@ license = "AGPL-3.0-or-later"
|
|||
[dependencies]
|
||||
actix-web = { version = "4.4", default-features = false, features = ["macros", "compress-brotli"] }
|
||||
actix-files = "0.6"
|
||||
cached = { version = "0.47", features = ["async"] }
|
||||
cached = { version = "0.48", features = ["async"] }
|
||||
ramhorns = "0.14"
|
||||
toml = "0.8"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
- [Global configuration](#global-configuration)
|
||||
- [Link shortener for contacts](#link-shortener-for-contacts)
|
||||
- [Add content](#add-content)
|
||||
- [Index](#index)
|
||||
- [Blog](#blog)
|
||||
- [Projects](#projects)
|
||||
- [Contacts](#contacts)
|
||||
|
@ -139,9 +140,27 @@ option: value
|
|||
Markdown file
|
||||
```
|
||||
|
||||
## Index
|
||||
|
||||
Markdown file is stored in `/app/data/index.md`
|
||||
|
||||
```
|
||||
---
|
||||
name: Option<String>
|
||||
pronouns: Option<String>
|
||||
avatar: Option<String>
|
||||
avatar_caption: Option<String>
|
||||
---
|
||||
|
||||
Index content
|
||||
```
|
||||
|
||||
- If no `name`, the `fullname` used in the configuration will be used
|
||||
- `avatar` is the link of the avatar
|
||||
|
||||
## Blog
|
||||
|
||||
Markdown files are stored in `/app/data/blog/`
|
||||
Markdown files are stored in `/app/data/blog/posts/`
|
||||
|
||||
```
|
||||
---
|
||||
|
@ -158,11 +177,15 @@ Post content
|
|||
- If no `title`, the filename will be used
|
||||
- `date` format is `day-month-year`
|
||||
- `publish` is default to false. When false, posts are hidden from index
|
||||
but accessible, see #30
|
||||
but accessible, see [#30](https://git.mylloon.fr/Anri/mylloon.fr/issues/30)
|
||||
|
||||
### About <!-- omit in toc -->
|
||||
|
||||
The file is stored at `/app/data/blog/about.md`.
|
||||
|
||||
## Projects
|
||||
|
||||
Markdown files are stored in `/app/data/projects/`
|
||||
Markdown files are stored in `/app/data/projects/apps/`
|
||||
|
||||
```
|
||||
---
|
||||
|
@ -177,7 +200,14 @@ Project description
|
|||
|
||||
- If no `link` : the div won't be clickable and will be reported as is to the user
|
||||
(no corner-arrow)
|
||||
- Note that only a handful of [`language`s are supported](./static/css/languages.css).
|
||||
- Note that only a handful of [`language`s are supported](./static/css/languages.css)
|
||||
|
||||
You can also put apps in an "Archived" category, in this case, store markdown
|
||||
files in `archive` subdirectory of `apps`.
|
||||
|
||||
### About <!-- omit in toc -->
|
||||
|
||||
The file is stored at `/app/data/projects/about.md`.
|
||||
|
||||
## Contacts
|
||||
|
||||
|
@ -206,6 +236,18 @@ Custom project description
|
|||
- `description` will be rendered as HTML "title" (text will appear when cursor
|
||||
is hover the link)
|
||||
|
||||
Also, contacts are categorized, here is the list of the available categories:
|
||||
|
||||
- `socials`
|
||||
- `forges`
|
||||
- `others`
|
||||
|
||||
For example, `socials` contact files are stored in `/app/data/contacts/socials/`.
|
||||
|
||||
### About <!-- omit in toc -->
|
||||
|
||||
The file is stored at `/app/data/contacts/about.md`.
|
||||
|
||||
## Courses
|
||||
|
||||
Markdown files are stored in `/app/data/cours/`
|
||||
|
|
|
@ -47,7 +47,7 @@ async fn main() -> Result<()> {
|
|||
.service(agreements::security)
|
||||
.service(agreements::humans)
|
||||
.service(agreements::robots)
|
||||
.service(agreements::sitemap)
|
||||
.service(agreements::webmanifest)
|
||||
.service(blog::index)
|
||||
.service(blog::rss)
|
||||
.service(blog::page)
|
||||
|
|
|
@ -71,8 +71,29 @@ fn build_robotstxt() -> String {
|
|||
"User-agent: * Allow: /".into()
|
||||
}
|
||||
|
||||
#[get("/sitemap.xml")]
|
||||
async fn sitemap() -> impl Responder {
|
||||
// TODO
|
||||
actix_web::web::Redirect::to("/")
|
||||
#[get("/app.webmanifest")]
|
||||
async fn webmanifest(config: web::Data<Config>) -> impl Responder {
|
||||
HttpResponse::Ok()
|
||||
.content_type(ContentType("application/manifest+json".parse().unwrap()))
|
||||
.body(build_webmanifest(config.get_ref().to_owned()))
|
||||
}
|
||||
|
||||
#[derive(Content, Debug)]
|
||||
struct WebManifestTemplate {
|
||||
name: String,
|
||||
description: String,
|
||||
url: String,
|
||||
}
|
||||
|
||||
#[once(time = 60)]
|
||||
fn build_webmanifest(config: Config) -> String {
|
||||
config.tmpl.render(
|
||||
"app.webmanifest",
|
||||
WebManifestTemplate {
|
||||
name: config.fc.clone().app_name.unwrap(),
|
||||
description: "Easy WebPage generator".to_owned(),
|
||||
url: get_url(config.fc),
|
||||
},
|
||||
Infos::default(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use ::rss::{
|
|||
extension::atom::{AtomExtension, Link},
|
||||
Category, Channel, Guid, Image, Item,
|
||||
};
|
||||
use actix_web::{get, web, HttpResponse, Responder};
|
||||
use actix_web::{get, http::header::ContentType, web, HttpResponse, Responder};
|
||||
use cached::proc_macro::once;
|
||||
use chrono::{DateTime, Datelike, Local, NaiveDateTime, Utc};
|
||||
use chrono_tz::Europe;
|
||||
|
@ -27,6 +27,8 @@ use crate::{
|
|||
};
|
||||
|
||||
const MIME_TYPE_RSS: &str = "application/rss+xml";
|
||||
const BLOG_DIR: &str = "blog";
|
||||
const POST_DIR: &str = "posts";
|
||||
|
||||
#[get("/blog")]
|
||||
async fn index(config: web::Data<Config>) -> impl Responder {
|
||||
|
@ -36,13 +38,19 @@ async fn index(config: web::Data<Config>) -> impl Responder {
|
|||
#[derive(Content, Debug)]
|
||||
struct BlogIndexTemplate {
|
||||
navbar: NavBar,
|
||||
about: Option<File>,
|
||||
posts: Vec<Post>,
|
||||
no_posts: bool,
|
||||
}
|
||||
|
||||
#[once(time = 60)]
|
||||
fn build_index(config: Config) -> String {
|
||||
let mut posts = get_posts(format!("{}/blog", config.locations.data_dir));
|
||||
let blog_dir = format!("{}/{}", config.locations.data_dir, BLOG_DIR);
|
||||
let mut posts = get_posts(format!("{}/{}", blog_dir, POST_DIR));
|
||||
|
||||
// Get about
|
||||
let about: Option<File> =
|
||||
read_file(&format!("{}/about.md", blog_dir), TypeFileMetadata::Generic);
|
||||
|
||||
// Sort from newest to oldest
|
||||
posts.sort_by_cached_key(|p| (p.date.year, p.date.month, p.date.day));
|
||||
|
@ -55,6 +63,7 @@ fn build_index(config: Config) -> String {
|
|||
blog: true,
|
||||
..NavBar::default()
|
||||
},
|
||||
about,
|
||||
no_posts: posts.is_empty(),
|
||||
posts,
|
||||
},
|
||||
|
@ -82,7 +91,7 @@ struct Post {
|
|||
impl Post {
|
||||
// Fetch the file content
|
||||
fn fetch_content(&mut self, data_dir: &str) {
|
||||
let blog_dir = format!("{}/blog", data_dir);
|
||||
let blog_dir = format!("{}/{}/{}", data_dir, BLOG_DIR, POST_DIR);
|
||||
let ext = ".md";
|
||||
|
||||
if let Some(file) = read_file(
|
||||
|
@ -217,7 +226,7 @@ fn get_post(
|
|||
name: String,
|
||||
data_dir: String,
|
||||
) -> (Infos, String) {
|
||||
let blog_dir = format!("{}/blog", data_dir);
|
||||
let blog_dir = format!("{}/{}/{}", data_dir, BLOG_DIR, POST_DIR);
|
||||
let ext = ".md";
|
||||
|
||||
*post = read_file(
|
||||
|
@ -272,13 +281,16 @@ fn get_post(
|
|||
#[get("/blog/rss")]
|
||||
async fn rss(config: web::Data<Config>) -> impl Responder {
|
||||
HttpResponse::Ok()
|
||||
.append_header(("content-type", MIME_TYPE_RSS))
|
||||
.content_type(ContentType(MIME_TYPE_RSS.parse().unwrap()))
|
||||
.body(build_rss(config.get_ref().to_owned()))
|
||||
}
|
||||
|
||||
#[once(time = 10800)] // 3h
|
||||
fn build_rss(config: Config) -> String {
|
||||
let mut posts = get_posts(format!("{}/blog", config.locations.data_dir));
|
||||
let mut posts = get_posts(format!(
|
||||
"{}/{}/{}",
|
||||
config.locations.data_dir, BLOG_DIR, POST_DIR
|
||||
));
|
||||
|
||||
// Sort from newest to oldest
|
||||
posts.sort_by_cached_key(|p| (p.date.year, p.date.month, p.date.day));
|
||||
|
|
|
@ -13,6 +13,8 @@ use crate::{
|
|||
template::{Infos, NavBar},
|
||||
};
|
||||
|
||||
const CONTACT_DIR: &str = "contacts";
|
||||
|
||||
pub fn pages(cfg: &mut web::ServiceConfig) {
|
||||
// Here define the services used
|
||||
let routes = |route_path| {
|
||||
|
@ -76,7 +78,7 @@ fn find_links(directory: String) -> Vec<ContactLink> {
|
|||
#[get("/{service}/{scope}")]
|
||||
async fn service_redirection(config: web::Data<Config>, req: HttpRequest) -> impl Responder {
|
||||
let info = req.match_info();
|
||||
let link = find_links(format!("{}/contacts", config.locations.data_dir))
|
||||
let link = find_links(format!("{}/{}", config.locations.data_dir, CONTACT_DIR))
|
||||
.iter()
|
||||
// Find requested service
|
||||
.filter(|&x| x.service == *info.query("service"))
|
||||
|
@ -105,6 +107,7 @@ async fn service_redirection(config: web::Data<Config>, req: HttpRequest) -> imp
|
|||
#[derive(Content, Debug)]
|
||||
struct NetworksTemplate {
|
||||
navbar: NavBar,
|
||||
about: Option<File>,
|
||||
|
||||
socials_exists: bool,
|
||||
socials: Vec<File>,
|
||||
|
@ -123,9 +126,15 @@ fn remove_paragraphs(list: &mut [File]) {
|
|||
|
||||
#[once(time = 60)]
|
||||
fn build_page(config: Config) -> String {
|
||||
let contacts_dir = format!("{}/contacts", config.locations.data_dir);
|
||||
let contacts_dir = format!("{}/{}", config.locations.data_dir, CONTACT_DIR);
|
||||
let ext = ".md";
|
||||
|
||||
// Get about
|
||||
let about = read_file(
|
||||
&format!("{}/about.md", contacts_dir),
|
||||
TypeFileMetadata::Generic,
|
||||
);
|
||||
|
||||
let socials_dir = "socials";
|
||||
let mut socials = glob(&format!("{contacts_dir}/{socials_dir}/*{ext}"))
|
||||
.unwrap()
|
||||
|
@ -156,6 +165,8 @@ fn build_page(config: Config) -> String {
|
|||
contact: true,
|
||||
..NavBar::default()
|
||||
},
|
||||
about,
|
||||
|
||||
socials_exists: !socials.is_empty(),
|
||||
socials,
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ async fn page(config: web::Data<Config>) -> impl Responder {
|
|||
#[derive(Content, Debug)]
|
||||
struct PortfolioTemplate<'a> {
|
||||
navbar: NavBar,
|
||||
about: Option<File>,
|
||||
location_apps: Option<&'a str>,
|
||||
apps: Option<Vec<File>>,
|
||||
archived_apps: Option<Vec<File>>,
|
||||
|
@ -30,22 +31,29 @@ struct PortfolioTemplate<'a> {
|
|||
#[once(time = 60)]
|
||||
fn build_page(config: Config) -> String {
|
||||
let projects_dir = format!("{}/projects", config.locations.data_dir);
|
||||
let apps_dir = format!("{}/apps", projects_dir);
|
||||
let ext = ".md";
|
||||
|
||||
// Get about
|
||||
let about = read_file(
|
||||
&format!("{}/about.md", projects_dir),
|
||||
TypeFileMetadata::Generic,
|
||||
);
|
||||
|
||||
// Get apps
|
||||
let apps = glob(&format!("{projects_dir}/*{ext}"))
|
||||
let apps = glob(&format!("{apps_dir}/*{ext}"))
|
||||
.unwrap()
|
||||
.map(|e| read_file(&e.unwrap().to_string_lossy(), TypeFileMetadata::Portfolio).unwrap())
|
||||
.collect::<Vec<File>>();
|
||||
|
||||
let appdata = if apps.is_empty() {
|
||||
(None, Some(projects_dir.as_str()))
|
||||
(None, Some(apps_dir.as_str()))
|
||||
} else {
|
||||
(Some(apps), None)
|
||||
};
|
||||
|
||||
// Get archived apps
|
||||
let archived_apps = glob(&format!("{projects_dir}/archive/*{ext}"))
|
||||
let archived_apps = glob(&format!("{apps_dir}/archive/*{ext}"))
|
||||
.unwrap()
|
||||
.map(|e| read_file(&e.unwrap().to_string_lossy(), TypeFileMetadata::Portfolio).unwrap())
|
||||
.collect::<Vec<File>>();
|
||||
|
@ -63,6 +71,7 @@ fn build_page(config: Config) -> String {
|
|||
portfolio: true,
|
||||
..NavBar::default()
|
||||
},
|
||||
about,
|
||||
apps: appdata.0,
|
||||
location_apps: appdata.1,
|
||||
archived_apps: archived_appdata.0,
|
||||
|
|
|
@ -1,15 +1,3 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="180.000000pt" height="180.000000pt" viewBox="0 0 180.000000 180.000000" preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0.000000,180.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none">
|
||||
<path d="M380 1780 c0 -47 56 -117 85 -107 19 8 19 47 1 92 -13 31 -19 35 -50 35 -30 0 -36 -4 -36 -20z"/>
|
||||
<path d="M1220 1740 c-34 -34 -17 -127 26 -144 27 -10 39 10 36 65 -3 89 -25 116 -62 79z"/>
|
||||
<path d="M789 1729 c-22 -41 12 -119 51 -119 25 0 32 26 19 74 -18 68 -47 87 -70 45z"/>
|
||||
<path d="M0 1683 c0 -55 3 -64 26 -82 33 -26 41 -26 54 0 15 28 -1 66 -46 110 l-34 33 0 -61z"/>
|
||||
<path d="M1563 1686 c-20 -43 -11 -86 18 -86 25 0 53 62 45 96 -9 36 -43 31 -63 -10z"/>
|
||||
<path d="M343 1395 c-47 -33 -37 -116 19 -158 36 -27 74 -16 95 27 41 87 -42 182 -114 131z"/>
|
||||
<path d="M1313 1382 c-12 -9 -27 -32 -34 -49 -23 -69 52 -153 138 -153 84 0 119 70 76 153 -31 60 -131 87 -180 49z"/>
|
||||
<path d="M862 998 c-19 -19 -14 -46 16 -91 16 -23 32 -50 35 -60 4 -9 18 -22 32 -29 22 -10 30 -8 53 9 15 11 28 32 30 47 9 73 -120 170 -166 124z"/>
|
||||
<path d="M1428 779 c-29 -16 -21 -63 27 -159 82 -164 65 -189 -173 -247 -192 -47 -434 -29 -646 48 -108 39 -160 83 -181 152 -16 54 -27 67 -61 67 -32 0 -54 -31 -54 -76 0 -63 50 -138 122 -184 178 -116 546 -178 784 -134 30 6 34 2 88 -75 61 -86 85 -106 116 -96 43 14 33 73 -28 152 -13 18 -22 39 -19 46 3 8 33 24 68 37 111 42 159 111 146 211 -7 54 -91 223 -123 248 -30 23 -41 25 -66 10z"/>
|
||||
</g>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240" version="1.0" viewBox="0 0 180 180">
|
||||
<path d="M38 2c0 4.7 5.6 11.7 8.5 10.7 1.9-.8 1.9-4.7.1-9.2C45.3.4 44.7 0 41.6 0c-3 0-3.6.4-3.6 2zM122 6c-3.4 3.4-1.7 12.7 2.6 14.4 2.7 1 3.9-1 3.6-6.5-.3-8.9-2.5-11.6-6.2-7.9zM78.9 7.1C76.7 11.2 80.1 19 84 19c2.5 0 3.2-2.6 1.9-7.4-1.8-6.8-4.7-8.7-7-4.5zM0 11.7c0 5.5.3 6.4 2.6 8.2 3.3 2.6 4.1 2.6 5.4 0 1.5-2.8-.1-6.6-4.6-11L0 5.6v6.1zM156.3 11.4c-2 4.3-1.1 8.6 1.8 8.6 2.5 0 5.3-6.2 4.5-9.6-.9-3.6-4.3-3.1-6.3 1zM34.3 40.5c-4.7 3.3-3.7 11.6 1.9 15.8 3.6 2.7 7.4 1.6 9.5-2.7 4.1-8.7-4.2-18.2-11.4-13.1zM131.3 41.8c-1.2.9-2.7 3.2-3.4 4.9-2.3 6.9 5.2 15.3 13.8 15.3 8.4 0 11.9-7 7.6-15.3-3.1-6-13.1-8.7-18-4.9zM86.2 80.2c-1.9 1.9-1.4 4.6 1.6 9.1 1.6 2.3 3.2 5 3.5 6 .4.9 1.8 2.2 3.2 2.9 2.2 1 3 .8 5.3-.9 1.5-1.1 2.8-3.2 3-4.7.9-7.3-12-17-16.6-12.4zM142.8 102.1c-2.9 1.6-2.1 6.3 2.7 15.9 8.2 16.4 6.5 18.9-17.3 24.7-19.2 4.7-43.4 2.9-64.6-4.8-10.8-3.9-16-8.3-18.1-15.2-1.6-5.4-2.7-6.7-6.1-6.7-3.2 0-5.4 3.1-5.4 7.6 0 6.3 5 13.8 12.2 18.4 17.8 11.6 54.6 17.8 78.4 13.4 3-.6 3.4-.2 8.8 7.5 6.1 8.6 8.5 10.6 11.6 9.6 4.3-1.4 3.3-7.3-2.8-15.2-1.3-1.8-2.2-3.9-1.9-4.6.3-.8 3.3-2.4 6.8-3.7 11.1-4.2 15.9-11.1 14.6-21.1-.7-5.4-9.1-22.3-12.3-24.8-3-2.3-4.1-2.5-6.6-1z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"name": "Site Anri K.",
|
||||
"short_name": "Site Anri K.",
|
||||
"icons": [
|
||||
{
|
||||
"src": "android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#2a2424",
|
||||
"background_color": "#2a2424",
|
||||
"start_url": "https://www.mylloon.fr/",
|
||||
"display": "standalone"
|
||||
}
|
21
templates/app.webmanifest
Normal file
21
templates/app.webmanifest
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "{{#data}}{{name}}{{/data}}",
|
||||
"start_url": "{{#data}}{{url}}{{/data}}",
|
||||
"display": "standalone",
|
||||
"background_color": "#2a2424",
|
||||
"description": "{{#data}}{{description}}{{/data}}",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#2a2424",
|
||||
"related_applications": [
|
||||
{
|
||||
"platform": "source",
|
||||
"url": "https://git.mylloon.fr/Anri/mylloon.fr"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
{{#data}}
|
||||
|
||||
<h1>Blog</h1>
|
||||
<p>Blog perso, je dis peut-être n'importe quoi 🫶</p>
|
||||
{{#about}} {{&content}} {{/about}}
|
||||
<a id="rss" href="/blog/rss">Lien vers le flux RSS</a>
|
||||
|
||||
{{#no_posts}}
|
||||
|
|
|
@ -8,9 +8,8 @@
|
|||
<header>{{>navbar.html}}</header>
|
||||
<main>
|
||||
<h1>Contact</h1>
|
||||
<p>Je suis présent relativement partout sur internet 😸</p>
|
||||
{{#data}}{{#about}} {{&content}} {{/about}} {{#socials_exists}}
|
||||
|
||||
{{#data}} {{#socials_exists}}
|
||||
<h2>Réseaux sociaux</h2>
|
||||
<ul>
|
||||
{{#socials}} {{>contact/element.html}} {{/socials}}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="author" href="/humans.txt" />
|
||||
<link rel="manifest" href="/app.webmanifest" />
|
||||
|
||||
{{>icons.html}} {{>metadata.html}}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
sizes="16x16"
|
||||
href="/icons/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="/icons/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/icons/safari-pinned-tab.svg" color="#5bbad5" />
|
||||
<link rel="shortcut icon" href="/icons/favicon.ico" />
|
||||
<meta name="msapplication-TileColor" content="#ffffff" />
|
||||
|
|
|
@ -10,14 +10,10 @@
|
|||
<main>
|
||||
{{#data}}
|
||||
<h1>Portfolio</h1>
|
||||
<p>
|
||||
Je programme depuis 2018 et j'ai appris une multitude de langages
|
||||
depuis. Étant passionné de logiciels libres depuis que je m'y intéresse,
|
||||
je publie tout sur des forges publiques.
|
||||
</p>
|
||||
{{#about}} {{&content}} {{/about}}
|
||||
|
||||
{{#location_apps}}
|
||||
<!-- Error message -->
|
||||
{{#location_apps}}
|
||||
<p>{{location_apps}} {{err_msg}}</p>
|
||||
{{/location_apps}} {{^location_apps}}
|
||||
|
||||
|
|
Loading…
Reference in a new issue