diff options
Diffstat (limited to 'src')
30 files changed, 1238 insertions, 350 deletions
diff --git a/src/build/article.mjs b/src/build/article.mjs new file mode 100644 index 0000000..1a7b09b --- /dev/null +++ b/src/build/article.mjs @@ -0,0 +1,31 @@ +// Set of functions to handle articles + +function getArticleYear(article) { + if (article.metaData.pubDate.getFullYear) { + return article.metaData.pubDate.getFullYear(); + } + + if (article.metaData.pubDate.getUTCFullYear) { + return article.metaData.pubDate.getUTCFullYear(); + } + + return 0; +} + +function getArticleDate(article) { + if (article.metaData.pubDate.getDate) { + return article.metaData.pubDate.getFullYear() * 100 + article.metaData.pubDate.getDate(); + } + + if (article.metaData.pubDate.getUTCDate) { + return article.metaData.pubDate.getUTCFullYear() * 100 + article.metaData.pubDate.getDate(); + } + + return 0; +} + +function cmpArticles(a, b) { + return getArticleDate(b) - getArticleDate(a); +} + +export {getArticleDate, getArticleYear, cmpArticles}; diff --git a/src/build/i18n.mjs b/src/build/i18n.mjs new file mode 100644 index 0000000..d1cebe6 --- /dev/null +++ b/src/build/i18n.mjs @@ -0,0 +1,85 @@ + +const i18n = { + fr: { + intro: { + 'description': 'Éternel étudiant en Math-Info.', + 'about': 'Autodidacte passionné,<br><span class="type_wrap"><span class="type">désormais ingénieur.</span></span>', + 'about_tags': 'GNU\\Linux, C, C++, Python, Math, autohébergement, décentralisation, P2P, commun, ... <br> ', + }, + title: { + 'main': 'ache: Blog personnel', + 'home': 'L\'accueil', + 'git': 'Dépôt git personnel', + 'mastodon': 'Mon mastodon', + }, + articles: [ + 'framasoft-et-les-mascottes-du-libre.md', + 'les-trains-et-la-publicité.md', + 'formats-images-web.md', + 'bizarreries-du-langage-c.md', + 'retour-sur-laoc-2021-semaine-1.md', + '2FA-discord-sur-pc.md', + 'duckduckgo-google-en-mieux.md', + ], + rss: { + 'title': 'ache: Blog personnel', + 'quick_description': 'Programmation, Algorithmique, Système, *pick you poison*', + 'description': `<b>Ceci est un flux RSS</b> à destination des agrégateurs de contenu.<br>\nEn tant qu'être humain vous cherchez certainement <a href="https://ache.one/fr/">mon site web</a>.` + }, + toc_keyword: "Sommaire", + tag_desc: "Liste des articles ayant le tag", + index_desc: "Blog personnel à propos de programmation, logiciel libre et autohébergement. Essayons de rendre le monde meilleur.", + like_title: "Si vous avez aimez cet article cliquez sur le cœur !", + like_text: "Vous pouvez même envoyez plusieurs cœurs ! <br><span class=\"likesNotes\">Le délais d'attente entre deux cœurs double à chaque fois.</span>", + }, + en: { + intro: { + 'description': 'Eternal student in computer science.', + 'quick_description': 'Programmation, Algorithmique, Système, *pick you poison*', + 'about': 'Self-taught developper,<br><span class="type_wrap"><span class="type">now engineer.</span></span>', + 'about_tags': 'GNU\\Linux, C, C++, Python, Math, self-hosted, dececntralisation, P2P, ... <br>', + }, + title: { + 'main': 'ache: Personal blog', + 'home': 'Home', + 'git': 'Personnel git repository', + 'mastodon': 'Mastodon account', + }, + articles: [ + 'rail-and-advertising.md', + 'web-image-formats.md', + 'c-language-quirks.md', + ], + rss: { + 'title': 'ache: Personal blog', + 'description': `<b>This is a RSS feed</b> for content agreagator.<br>\nAs a human being, you are certainly looking for <a href="https://ache.one/en/">my website</a>.` + }, + toc_keyword: "Table of contents", + tag_desc: "List of articles with the tag", + index_desc: "Personal blog about programming, free software and self-hosting. Let's try to make the world a better place.", + like_title: "If you liked this article click on the heart!", + like_text: "You can even send multiple hearts! <br><span class=\"likesNotes\">The waiting time between two hearts doubles each time.</span>", + } +} + +const lang_desc = { + fr: "Version Française", + en: "English version", +} + +export function addDescription(alt_lang) { + if (alt_lang) { + alt_lang.forEach(e => { + e.description = lang_desc[e.lang] + }); + } + + return alt_lang; +} + +export function getTocHeading() { + return Object.entries(i18n).map(([_, v]) => v.toc_keyword).join('|'); +} + + +export default i18n; diff --git a/src/build/index.mjs b/src/build/index.mjs index ce490cf..a14cdc7 100644 --- a/src/build/index.mjs +++ b/src/build/index.mjs @@ -1,96 +1,20 @@ +import process from 'node:process'; import fs from 'node:fs'; import mustache from 'mustache'; -import {u} from 'unist-builder'; -import {h} from 'hastscript'; -import {select} from 'hast-util-select'; -import {toString as hastToString} from 'mdast-util-to-string'; -import cssesc from 'cssesc'; - -import {toHtmlRaw, toString, toMdRaw, mdToHtmlRaw} from './to-html.mjs'; import loadSVG from './load-svg.mjs'; -import listArticles from './list-articles.mjs'; import getRSS from './rss.mjs'; -import toml from '@ltd/j-toml'; - -function getArticleYear(article) { - if(article.metaData.pubDate.getFullYear) { - return article.metaData.pubDate.getFullYear(); - } else if(article.metaData.pubDate.getUTCFullYear) { - return article.metaData.pubDate.getUTCFullYear(); - } - - return 0; -} -function getArticleDate(article) { - if(article.metaData.pubDate.getDate) { - return article.metaData.pubDate.getFullYear() * 100 + article.metaData.pubDate.getDate(); - } else if(article.metaData.pubDate.getUTCDate) { - return article.metaData.pubDate.getUTCFullYear() * 100 + article.metaData.pubDate.getDate(); - } - - return 0; -} -function cmpArticles(a, b) { - return getArticleDate(b) - getArticleDate(a); -} - -const loadMD = (listFile, suffix) => { - const listContent = []; - for (const file of listFile) { - console.log(`Working on ${file}`); - const content = fs.readFileSync(`${suffix}/${file}`, 'utf8'); - const mdRaw = toMdRaw(content); - const tomlStringValue = mdRaw.children[0].value; - const metaData = toml.parse(tomlStringValue); - const newHTML = mdToHtmlRaw(mdRaw); - - const htmlContent = newHTML; - const htmlRender = toString(htmlContent); - - const titleHtml = select('h1', htmlContent); - const intro = select('p', htmlContent); - intro.children = intro.children.filter(child => child.tagName !== 'br'); - - const logo = select('img', intro); - logo.properties.src = `${suffix}/${logo.properties.src}`; - logo.properties.height = '150'; - logo.properties.width = '150'; - - const logoP = select('source', intro); - if (logoP !== null) { - console.log(logoP) - logoP.properties.srcSet= `${suffix}/${logoP.properties.srcSet}`; - console.log(logoP) - } - - titleHtml.children[0].properties.href = `${suffix}/${file.slice(0, -3)}`; - - const title = hastToString(titleHtml); - const domTitle = cssesc(title.replace(/\s+/g, '-').replace(/['"#@]/, '').toLowerCase()); // Maybe encodeURI +import loadMD from './loadMD.mjs'; +import {cmpArticles} from './article.mjs'; +import i18n from './i18n.mjs'; +import {addDescription} from './i18n.mjs'; +import {minifyHTML} from './utils.mjs'; - const readMore = h('a', 'Lire plus...'); +import { SitemapStream, streamToPromise } from 'sitemap' +import { Readable } from 'stream' - readMore.properties.href = `${suffix}/${file.slice(0, -3)}`; - const pubYear = getArticleYear({metaData}); - - listContent.push({ - name: file.slice(0, -3), - content: htmlRender, - intro: toString(u('root', [titleHtml, intro, readMore])), - introDesc: hastToString(intro), - imageUrl: logo.properties.src, - metaData, - pubYear, - title, - domTitle, - url: `/${suffix}/${file.slice(0, -3)}`, - }); - } - - return listContent; -}; const leftPanelTmpl = fs.readFileSync('src/templates/left.tmpl', 'utf8'); +const likesTmpl = fs.readFileSync('src/templates/likes.tmpl', 'utf8'); const headerTmpl = fs.readFileSync('src/templates/header.tmpl', 'utf8'); const articleTmpl = fs.readFileSync('src/templates/article.tmpl', 'utf8'); const indexTmpl = fs.readFileSync('src/templates/index.tmpl', 'utf8'); @@ -101,73 +25,126 @@ const baseUrl = 'https://ache.one/'; const partials = { header: headerTmpl, leftPanel: leftPanelTmpl, + likesButton: likesTmpl, hid: hidTmpl, }; +// Load global variables const svg = loadSVG(); -const articles = loadMD(listArticles, 'articles'); - -const tagsArticle = new Map(); - -for (const article of articles) { - const context = { - svg, - title: `${article.title} - ache`, - canonical: `${baseUrl}${article.url.slice(1)}`, - content: article.content, - domTitle: article.domTitle, - metaData: article.metaData, - }; - const output = mustache.render(articleTmpl, context, partials); - - console.log(`Create : ${article.title}`); - fs.writeFileSync(`articles/${article.name}.html`, output); - - for (const tag of article.metaData.tags) { - if (tagsArticle.has(tag)) { - tagsArticle.get(tag).push(article); - } else { - tagsArticle.set(tag, [article]); +let links = []; + +for (const lang in i18n) { + const tagsArticle = new Map(); + const filter = process.argv.slice(2); + const listArticle = (filter.length > 0) ? i18n[lang].articles.filter((article) => filter.includes(article.name)) : i18n[lang].articles; + const articles = loadMD(listArticle, 'articles', lang); + + for (const article of articles) { + const context = { + svg, + page_title: `${article.title} - ache`, + title: i18n[lang].title, + intro: i18n[lang].intro, + lang, + canonical: `${baseUrl}${article.url.slice(1)}`, + content: article.content, + title: i18n[lang].title, + metaData: article.metaData, + alt_lang: addDescription(article.metaData.alt_lang), + description: article.intro, + + like_title: i18n[lang].like_title, + like_text: i18n[lang].like_text, + }; + const output = mustache.render(articleTmpl, context, partials); + + console.log(`Create : ${article.title}`); + fs.writeFileSync(`articles/${article.name}.html`, minifyHTML(output)); + links.push({url: context.canonical, changefreq: 'yearly', priority: 0.6, img: [{url: article.imageUrl}]}) + + for (const tag of article.metaData.tags) { + if (tagsArticle.has(tag)) { + tagsArticle.get(tag).push(article); + } else { + tagsArticle.set(tag, [article]); + } } } -} -try { - fs.mkdirSync('tag'); -} catch { - fs.rmSync('tag', {force: true, recursive: true}); - fs.mkdirSync('tag'); +// Set of pages language dependant + try { + fs.mkdirSync(`${lang}/tag`, {recursive: true}); + } catch { + fs.rmSync(`${lang}/tag`, {force: true, recursive: true}); + fs.mkdirSync(`${lang}/tag`, {recursive: true}); + } + + for (const [tag, articles] of tagsArticle.entries()) { + console.log(`Create tag page : ${lang}/${tag}.html`); + articles.sort(cmpArticles); + + const context = { + svg, + page_title: `ache - Tag: ${tag}`, + title: i18n[lang].title, + intro: i18n[lang].intro, + lang, + tag, + articles, + description: `${i18n[lang]['tag_desc']} ${tag}.`, + }; + + const output = mustache.render(tagTmpl, context, partials); + fs.writeFileSync(`${lang}/tag/${tag}.html`, minifyHTML(output)); + links.push({url: `${baseUrl}${lang}/tag/${tag}`, changefreq: 'monthly', priority: 0.3}) + } + + console.log(`Create RSS Flux: ${lang}/rss.xml`); + const xmlFeed = getRSS(articles, baseUrl, lang); + fs.writeFileSync(`${lang}/rss.xml`, xmlFeed); + links.push({url: `${baseUrl}${lang}/rss.xml`, changefreq: 'monthly', priority: 0.3}) + + { + const alt_lang = { + fr: { + lang: 'en', + url: '/en/', + }, + en: { + lang: 'fr', + url: '/fr/', + }, + }; + const context = { + page_title: i18n[lang].title.main, + title: i18n[lang].title, + intro: i18n[lang].intro, + lang, + canonical: `${baseUrl}${lang}`, + svg, + articles, + alt_lang: [alt_lang[lang]], + description: i18n[lang].index_desc, + }; + + console.log(`Create : Home page ${lang}`); + const output = mustache.render(indexTmpl, context, partials); + fs.writeFileSync(`${lang}/index.html`, minifyHTML(output)); + links.push({url: `${baseUrl}${lang}`, changefreq: 'monthly', priority: 0.7, img: [{url: "https://ache.one/res/ache.svg"}]}) + } } -for (const [tag, articles] of tagsArticle.entries()) { - console.log(`Create tag page : ${tag}.xml`); - articles.sort(cmpArticles); +console.log(`Create: sitemap.xml`); - const context = { - svg, - title: `ache - Tag: ${tag}`, - tag, - articles, - }; +// Create a stream to write to +const stream = new SitemapStream({hostname: 'https://ache.one'}); - const output = mustache.render(tagTmpl, context, partials); - fs.writeFileSync(`tag/${tag}.html`, output); -} +// Return a promise that resolves with your XML string +streamToPromise( + Readable.from(links) + .pipe(stream)) + .then(data => data.toString()) + .then(sitemapXML => fs.writeFileSync("sitemap.xml", sitemapXML) +); -console.log('Create RSS Flux: rss.xml'); -const xmlFeed = getRSS(articles, baseUrl); -fs.writeFileSync('rss.xml', xmlFeed); - -{ - const context = { - title: 'ache: Blog personnel', - canonical: baseUrl, - svg, - articles, - }; - - console.log('Create : Home page'); - const output = mustache.render(indexTmpl, context, partials); - fs.writeFileSync('index.html', output); -} diff --git a/src/build/list-articles.mjs b/src/build/list-articles.mjs deleted file mode 100644 index d327573..0000000 --- a/src/build/list-articles.mjs +++ /dev/null @@ -1,7 +0,0 @@ -const listArticles = [ - 'bizarreries-du-langage-c.md', - 'retour-sur-laoc-2021-semaine-1.md', - '2FA-discord-sur-pc.md', - 'duckduckgo-google-en-mieux.md', -]; -export default listArticles; diff --git a/src/build/loadMD.mjs b/src/build/loadMD.mjs new file mode 100644 index 0000000..6ba4f76 --- /dev/null +++ b/src/build/loadMD.mjs @@ -0,0 +1,79 @@ +import fs from 'node:fs'; +import {u} from 'unist-builder'; +import {h} from 'hastscript'; +import {select} from 'hast-util-select'; +import {toString as hastToString} from 'mdast-util-to-string'; +import cssesc from 'cssesc'; +import toml from '@ltd/j-toml'; + +import {toString, toMdRaw, mdToHtmlRaw} from './to-html.mjs'; +import {getArticleYear} from './article.mjs'; +import i18n from './i18n.mjs'; + +const loadMD = (listFile, suffix, lang) => { + const listContent = []; + for (const file of listFile) { + console.log(`Working on ${file}`); + const content = fs.readFileSync(`${suffix}/${file}`, 'utf8'); + const mdRaw = toMdRaw(content); + const tomlStringValue = mdRaw.children[0].value; + const metaData = toml.parse(tomlStringValue); + const newHTML = mdToHtmlRaw(mdRaw); + + const htmlContent = newHTML; + const htmlRender = toString(htmlContent); + + const titleHtml = select('h1', htmlContent); + const intro = select('p', htmlContent); + intro.children = intro.children.filter(child => child.tagName !== 'br'); + + const logo = select('img', intro); + if (logo && logo?.properties) { + if (logo.properties.src[0] != '/') { + logo.properties.src = `/${suffix}/${logo.properties.src}`; + } + logo.properties.height = '150'; + logo.properties.width = '150'; + } + + const logoP = select('source', intro); + if (logoP !== null) { + logoP.properties.srcSet = `/${suffix}/${logoP.properties.srcSet}`; + } + + titleHtml.children[0].properties.href = `/${suffix}/${file.slice(0, -3)}`; + + const title = hastToString(titleHtml); + const domTitle = cssesc(title.replace(/\s+/g, '-').replace(/['"#@]/, '').toLowerCase()); // Maybe encodeURI + + const readMore = h('a', i18n[lang]['read_more']); + + readMore.properties.href = `/${suffix}/${file.slice(0, -3)}`; + const pubYear = getArticleYear({metaData}); + + if (metaData.pubDate) { + try { + metaData.pubDateISO = metaData.pubDate.toISOString(); + } catch (error) { + console.error(`Error on file ${file} with pubDate (${metaData.pubDate}): ${error}`); + } + } + + listContent.push({ + name: file.slice(0, -3), + content: htmlRender, + intro: toString(u('root', [titleHtml, intro, readMore])), + introDesc: hastToString(intro), + imageUrl: logo?.properties?.src || '', + metaData, + pubYear, + title, + domTitle, + url: `/${suffix}/${file.slice(0, -3)}`, + }); + } + + return listContent; +}; + +export default loadMD; diff --git a/src/build/rss.mjs b/src/build/rss.mjs index e890152..6394af5 100644 --- a/src/build/rss.mjs +++ b/src/build/rss.mjs @@ -1,37 +1,43 @@ import RSS from 'rss'; +import i18n from './i18n.mjs'; -const getRSS = (articles, baseUrl) => { +const getRSS = (articles, baseUrl, lang) => { + console.log(i18n[lang]['rss']['title']); const rssFeed = new RSS({ - title: 'ache: Blog personnel', - description: 'Programmation, Algorithmique, Système, *pick you poison*', + title: i18n[lang]['rss']['title'], + description: i18n[lang]['rss']['description'], // eslint-disable-next-line camelcase + custom_namespaces: { + 'visible_description': i18n[lang]['rss']['description'] + }, + site_url: "https://ache.one", feed_url: `${baseUrl}rss.xml`, canonical: `${baseUrl}rss.xml`, // eslint-disable-next-line camelcase site_url: baseUrl, // eslint-disable-next-line camelcase image_url: `${baseUrl}ache.svg`, - language: 'fr', - pubDate: (new Date().toLocaleString()), + language: lang, + pubDate: Date(), ttl: '1440', - // eslint-disable-next-line camelcase - custom_elements: ['<?xml-stylesheet type="text/css" href="http://localhost:8080/s/css/style.css" ?>'], }); for (const article of articles.slice(0, 10)) { + let image_url = (article.imageUrl[0] != '/') ? `/${article.imageUrl}` : article.imageUrl; + rssFeed.item({ title: article.title, description: '<article>' + article.content + '</article>', // eslint-disable-next-line camelcase - image_url: article.imageUrl, + image_url, url: `${baseUrl}articles/${article.name}`, guid: article.domTitle, author: 'ache', - date: article.metaData.pubDate.toISOString(), + date: article.metaData.pubDateISO, categories: article.metaData.tags, // eslint-disable-next-line camelcase custom_elements: [ - {logo: article.imageUrl}, + {logo: image_url}, {intro: article.introDesc}, ], }); diff --git a/src/build/special_box.mjs b/src/build/special_box.mjs index 0b284ec..1457a98 100644 --- a/src/build/special_box.mjs +++ b/src/build/special_box.mjs @@ -10,6 +10,7 @@ export default function specialBox() { if (node.type === 'containerDirective' && ( node.name === 'attention' || node.name === 'question' + || node.name === 'note' || node.name === 'information')) { const data = node.data || (node.data = {}); @@ -18,6 +19,20 @@ export default function specialBox() { className: 'special-box ' + node.name, }; } + if (node.type === 'containerDirective' && node.name === 'details') { + if(node.children.length > 0 && node.children[0].type == "paragraph") { + node.children[0] = { + type: "containerDirective", + data: { + hName: 'summary' + }, + children: node.children[0].children + }; + + const data = node.data || (node.data = {}); + data.hName = 'details'; + } + } }); }; } diff --git a/src/build/to-html.mjs b/src/build/to-html.mjs index adfcd7a..786f91d 100644 --- a/src/build/to-html.mjs +++ b/src/build/to-html.mjs @@ -15,6 +15,7 @@ import rehypeStringify from 'rehype-stringify'; import rehypeHighlight from 'rehype-highlight'; import remarkSpecialBox from './special_box.mjs'; import remarkRemoveFootnoteHeader from './remove-footnote-header.mjs'; +import {getTocHeading} from './i18n.mjs'; const autoLinkOption = { behavior: 'wrap', @@ -31,7 +32,7 @@ const pictureOptions = { const generator = unified() .use(remarkParse) .use(remarkGfm) - .use(remarkToc, {heading: 'Sommaire', tight: true, ordered: true}) + .use(remarkToc, {heading: getTocHeading(), tight: true, ordered: true}) .use(remarkMath) .use(remarkDirective) .use(remarkSpecialBox) @@ -49,7 +50,7 @@ const generator = unified() const generatorMd = unified() .use(remarkParse) .use(remarkGfm) - .use(remarkToc, {heading: 'Sommaire', tight: true, ordered: true}) + .use(remarkToc, {heading: getTocHeading(), tight: true, ordered: true}) .use(remarkMath) .use(remarkDirective) .use(remarkSpecialBox) diff --git a/src/build/utils.mjs b/src/build/utils.mjs new file mode 100644 index 0000000..fa85ee6 --- /dev/null +++ b/src/build/utils.mjs @@ -0,0 +1,10 @@ +import { minify } from 'html-minifier'; + +export const minifyHTML = (html) => minify(html, { + removeAttributeQuotes: true, + removeComments: true, + minifyJS: true, + minifyCSS: true, + collapseWhitespace: true, + collapseBooleanAttributes: true, +}) diff --git a/src/css/_contenu.scss b/src/css/_contenu.scss index 3aa56d1..ee809ca 100755 --- a/src/css/_contenu.scss +++ b/src/css/_contenu.scss @@ -1,53 +1,48 @@ article { -/* - margin-left: #{"max(0px, (100vw - var(--width_panel) - 10px - 950px - 6vw - 2px - 15px)*11/24)"}; - margin-right: #{"max(0px, (100vw - var(--width_panel) - 10px - 950px - 6vw - 2px - 15px)*13/24)"}; -*/ margin: auto 0 0 0; - a { - color: #7C54CB; - text-decoration: none; - &:hover, &:focus { - text-decoration: underline; - text-decoration-style: dashed; - } - } + font-family: Nimbus sans, Fira Code, Helvetica, get a font bro, Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif; + line-height: 1.5rem; + font-size: 1rem; - font-family: "noto serif", 'Georgia', serif; position: relative; - max-width: 950px; ; - line-height: 1.6rem; + max-width: 950px; .pubdate { - position: absolute; - top: 0; - right: 0; - margin: 55px 10px; - transform: rotate(20deg); - text-shadow: 1px 1px 0.2px green; - overflow: clip; - color: pink; text-align: right; + line-height: 1.65; } ul { line-height: 1.6; } + h1, h2, h3 { + font-family: Palatino Linotype, Bookman Old Style, Fira Code, get a font bro, Times; + } + h1, h2, h3, h4 { + margin: 2.5rem 0 1rem 0; + } + p { + margin: 0.5rem 0; + } h1 { - font-size: 3rem; + font-size: 2rem; line-height: 3rem; + margin: 0.5rem 0; } h2 { - font-size: 2rem; + font-size: 1.6rem; line-height: 3rem; } h3 { - font-size: 1.6rem; + font-size: 1.3rem; + line-height: 3rem; + } + h4 { + font-size: 1.1rem; line-height: 3rem; - color: #660; } .anchor { @@ -69,11 +64,20 @@ article { > p:first-of-type { > img { height: 150px; + width: 150px; float: right; + position: relative; + top: -25px; + } + > img:not([src$="-inv.svg"]) { + filter: revert; } > picture { height: 150px; + width: 150px; float: right; + position: relative; + top: -25px; } @media #{$gt-gsm} { margin-top: 2.5% 0; @@ -82,7 +86,7 @@ article { @media #{$gt-gsm} { ul { - line-height: 1.4; + line-height: 1.5; } float: none; @@ -104,10 +108,22 @@ article { transition: margin-left 0.5s ease-out 0.25s; } } + li.task-list-item { + list-style-type: "- "; + input[type='checkbox'] { + width: 20px; + height: 20px; + } + } li p { display: inline; } - ol { + #sommaire + ol, #table-of-contents + ol { + line-height: 1.6rem; + @media #{$gt-gsm} { + line-height: 1.4em; + } + li { list-style-type: upper-roman; } @@ -116,7 +132,6 @@ article { } } .footnotes { - color: #504d4d; font-size: 0.8em; li { padding-bottom: 15px; @@ -125,9 +140,27 @@ article { ol p { display: inline; } + + @mixin lightMode { + color: #504d4d; + } + @mixin darkMode { + color: #E0DDDD; + } + + @media (prefers-color-scheme: dark) { @include darkMode; } + @media (prefers-color-scheme: light) { @include lightMode; } + &.light{ @include lightMode; } + &.dark { @include darkMode; } + + } + + blockquote { + padding: 1px 2%; + + border-left: 5px solid #ccc; } - font-size: 1.3rem; h1 h2 h3 { line-height: initial; } @@ -144,16 +177,20 @@ article { font-size: 0.9rem; } } - @mixin lightMode { - h1 { - color: #605; + @media #{$big-laptop} { + pre code { + width: 110%; + margin: 0 -10%; } - h2 { - color: #551; + } + @mixin lightMode { + .pubDate { + color: #606060; } - h3 { - color: #660; + blockquote { + color: #504d4d; } + h4, h5, h6 { color: black; } @@ -163,53 +200,65 @@ article { color: initial; > p:first-of-type { - color: #944040; - > img { + > img[src$=".jpg"], > img[src$=".png"], > img[src$=".avif"] { filter: none; } } } @mixin darkMode { - h1 { - color: #FEF; + .pubDate { + color: #606060; } - h2 { - color: #EFE; - } - h3 { - color: #DED; + blockquote { + color: #E0DDDD; } + h4, h5, h6 { color: #EFEFEF; } - img { + img:not(.no-dark) { filter: invert(1); } color: #EFEFEF; > p:first-of-type { - color: #6bbfbf; - > img { + > img[src$=".jpg"], > img[src$=".png"], > img[src$=".avif"] { filter: invert(0); } } } - @media (prefers-color-scheme: dark) { @include darkMode; } @media (prefers-color-scheme: light) { @include lightMode; } + @media (prefers-color-scheme: dark) { @include darkMode; } + @media (prefers-color-scheme: light) { @include lightMode; } &.light{ @include lightMode; } &.dark { @include darkMode; } + + iframe, img, div, video, svg, image { + border: none; + margin: 0 auto; + + @media #{$big-laptop} { + &.big { + width: 118%; + max-width: 118%; + margin: 0 -10%; + } + } + } } .keybs { - border: 1px solid gray; font-size: 1em; - box-shadow: 1px 0 1px 0 #eee,0 2px 0 2px #ccc,0 2px 0 3px #444; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; margin: 2px 3px; padding: 1px 5px; white-space: nowrap; + + // Theme style + border: 1px solid gray; + box-shadow: 1px 0 1px 0 #eee,0 2px 0 2px #ccc,0 2px 0 3px #444; } .marge { @@ -221,7 +270,7 @@ article { } } .sidenotes { - margin: 10px 10px 0 0; + margin: 10px 10px 0 10%; text-align: justify; @media #{$tab} { @@ -230,10 +279,16 @@ article { @mixin lightMode { color: #333333; + sup { + color: black; + } } @mixin darkMode { color: #eee; + sup { + color: white; + } } @media (prefers-color-scheme: dark) { @include darkMode; } @media (prefers-color-scheme: light) { @include lightMode; } @@ -249,23 +304,202 @@ article { .sidenote { position: absolute; font-style: oblique; - font-size: 1.05rem; + font-size: 0.8rem; + } +} + +section.likes { + display: flex; + justify-content: center; + margin: 3rem 0 1rem 0; + + @mixin lightMode { + .likesBox { + background-color: #CDE3EC; + .likesNotes { + color: #555; + } + .icon { + .nbLikes { + color: #00324D; + } + } + } + } + @mixin darkMode { + .likesBox { + background-color: #3C3C7C; + .likesNotes { + color: #CCC; + } + .icon { + .nbLikes { + color: #F0D5AD; + } + } + } + } + @media (prefers-color-scheme: dark) { @include darkMode; } + @media (prefers-color-scheme: light) { @include lightMode; } + &.light{ @include lightMode; } + &.dark { @include darkMode; } + + .likesBox { + padding: 20px; + border-radius: 8px; + display: flex; + + + .likesTitle { + font: monospace; + font-weight: 800; + } + .likesText { + font-size: 0.8em; + } + .likesNotes { + font-size: 0.8em; + &.err { + color: red; + } + } + + + .layout { + display: flex; + align-items: center; + .icon { + fill: transparent; + stroke: pink; + stroke-width: 20; + cursor: pointer; + position: relative; + svg { + overflow: visible; + width: 10rem; + } + + path { + stroke-dashoffset: 0; + stroke-dasharray: 1550; + transform-origin: center; + } + + .hear-main { + z-index: 2; + } + .heart-background { + position: absolute; + left: 0; + right: 0; + z-index: 1; + stroke: none; + } + .heart-main path.anim { + animation: stroke-animation 1.5s ease-in-out forwards; + } + + .heart-main ~ .heart-background path.anim-click { + animation: fade-animation 0.35s ease-in-out forwards; + } + .nbLikes { + font-family: Source Sans Pro,Segoe UI,Trebuchet MS,Helvetica,Helvetica Neue,Arial,sans-serif; + text-align: center; + position: relative; + top: -10px; + height: 0; + } + } + } + + @keyframes stroke-animation { + 0% { + fill: transparent; + transform: scale(1); + } + 30% { + fill: pink; + transform: scale(1.25); + } + 50% { + transform: scale(1); + } + 100% { + stroke-dashoffset: 0; + fill: pink; + } + } + + @keyframes fade-animation { + 0% { + fill: transparent; + transform: scale(1); + } + 13% { + fill: lightpink; + transform: scale(1.2); + opacity: 1; + } + 66% { + opacity: 0.8; + } + 100% { + transform: scale(2); + opacity: 0; + } + } } } -code { - color: #a00; - background: #e2e2e2; - border: 0px; - border-radius: 8px; - padding: 0 5px; +.alt-lang { + a { + padding-right: 20px; + } + font-size: 1.15rem; + text-transform: lowercase; + font-weight: bolder; + position: relative; + top: -3px; + font-family: Source Sans Pro,Segoe UI,Trebuchet MS,Helvetica,Helvetica Neue,Arial,sans-serif; +} + +.tags { + display: flex; + gap: 10px; + flex-flow: row-reverse; + flex-wrap: wrap; + font-size: 1rem; + line-height: 1.65; + + .tag { + padding: 0px 8px; + border-radius: 3px; + + // Theme style + + @mixin lightMode { + background-color: #DDD; + color: #333; + } + @mixin darkMode { + background-color: #444; + color: #eee; + } + + @media (prefers-color-scheme: dark) { @include darkMode; } + @media (prefers-color-scheme: light) { @include lightMode; } + &.light{ @include lightMode; } + &.dark { @include darkMode; } + } } +/* Used only on tag's pages */ .articleList { list-style-type: none; font-size: 1.2em; - font-family: palatino, Times, serif; text-indent: -65px; + font-family: palatino, Times, serif; + position: relative; top: -3px; left: -60px; @@ -276,26 +510,48 @@ code { } } -.tags { - display: flex; - gap: 10px; - flex-flow: row-reverse; - font-size: 0.8em; - .tag { - background-color: #E0D0C9; - color: #553; - padding: 0px 8px; - border-radius: 3px; - } -} +/* Used only on tag's pages */ .inline-tag { position: relative; top: -3px; font-size: 0.7em; - background-color: #E0D0C9; - color: #553; + background-color: #DDD; padding: 3px 8px; border-radius: 3px; margin: 0 5px; + + @mixin lightMode { + background-color: #DDD; + color: #333; + } + @mixin darkMode { + background-color: #444; + color: #eee; + } + + @media (prefers-color-scheme: dark) { @include darkMode; } + @media (prefers-color-scheme: light) { @include lightMode; } + &.light{ @include lightMode; } + &.dark { @include darkMode; } } + + +video::cue { + background-position: top; + background-origin: border-box; + position: 90%; +} + +video::cue(.back) { + font-style: oblique; + opacity: 0.85; + font-size: 0.7em; + line-height: 0.9; + padding-bottom: 5em; +} + +hr { + margin: 2.5rem 0; +} + diff --git a/src/css/_sommaire.scss b/src/css/_sommaire.scss index 44a3b1e..0eadd0b 100755 --- a/src/css/_sommaire.scss +++ b/src/css/_sommaire.scss @@ -1,9 +1,11 @@ .sommaire_blien { line-height: 1.45rem; - color: #121311; font-size: 0.8em; font-weight: bold; - font-family: monospace, serif; + + /* Theme style */ + color: #121311; + &:before { content: "/"; } @@ -12,12 +14,14 @@ text-decoration: none; } a:hover, a:focus { + /* Theme style */ color: #BBAABB; } } #side-bar { text-align: center; + font-family: ui-monospace, Menlo, "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", "Cousine", Courier New, monospace, sans-serif; ul { columns: 2; @@ -29,8 +33,21 @@ color: inherit; #logo-ache { + &:hover { + filter: drop-shadow(0px 0px 6px rgba(0, 0, 0, 0.4)); + } filter: none; } + #ache a { + text-decoration: none; + color: inherit; + &:hover { + text-shadow: 0px 2px 8px rgba(0, 0, 0, 0.4); + text-decoration: underline royalblue 10%; + text-undefline-offset: 5px; + } + text-shadow: none; + } #desc_intro { text-shadow: 0 1px 0 #DDD; } @@ -50,9 +67,23 @@ @mixin darkMode { background: #101110; color: #FFF; + #logo-ache { + &:hover { + filter: drop-shadow(0px 0px 6px rgba(0, 0, 0, 0.4)) invert(1); + } filter: invert(1); } + #ache a { + text-decoration: none; + color: inherit; + &:hover { + text-shadow: 0px 2px 8px rgba(255, 255, 255, 0.9); + text-decoration: underline goldenrod 10%; + text-underline-offset: 5px; + } + text-shadow: none; + } #desc_intro { text-shadow: 2px 4px 0 #333; } @@ -74,10 +105,6 @@ &.dark { @include darkMode; } - - h1 { - font-family: monospace, "Helvetica Neue", Arial, sans-serif; - } svg { margin: 0 auto; display: block; @@ -90,6 +117,7 @@ height: 100%; transition: left 0.5s ease-in 0.25s; + /* Theme style */ border-right: 3px solid rgba(51, 51, 51, 0.1); &.hidden { @@ -101,6 +129,8 @@ #desc_intro { padding-left: 10px; padding-right: 10px; + + /* Theme style */ text-shadow: 2px 4px 0 #DDD; } #desc { @@ -109,11 +139,9 @@ padding-left: 15px; padding-right: 15px; font-size: 1.05em; - font-family: monospace, "Helvetica Neue", Arial, sans-serif; } .about { font-size: 0.8em; - font-family: monospace; align-items: center; justify-content: center; } @@ -135,12 +163,13 @@ display: inline-block; width: 20%; text-align: left; - color: #121311; font-size: 0.8em; font-weight: bold; - font-family: monospace, serif; - text-shadow: 0 1px 0 #DDD; margin: auto; + + /* Theme style */ + color: #121311; + text-shadow: 0 1px 0 #DDD; } #ontheweb { visibility: hidden; @@ -164,30 +193,3 @@ } } } - -.lab { - height: 200px; - width: 100%; - - @media #{$gt-gsm} { - position: absolute; - left: 0; - bottom: 0px; - } - @media screen and (max-height: 749px) { - visibility: hidden; - } -} -.lab a svg { - opacity: 0.45; -} -.lab a svg:hover { - opacity: 1; -} -/* -<nav> -<div id="sommaire_blien">Accueil</div> -<div id="sommaire_blien">Blog</div> -<div id="sommaire_blien">Liens</div> -</nav> - */ diff --git a/src/css/_style.scss b/src/css/_style.scss index ac47ea6..060fe8f 100644 --- a/src/css/_style.scss +++ b/src/css/_style.scss @@ -13,18 +13,19 @@ .qcm_item p {margin: 0; padding: 0; display: inline;} .qcm_check, .qcm_radio {padding-left: 32px;} -blockquote { - color: #504d4d; - padding: 1px 2%; - border-left: 5px solid #ccc; -} + +details { + color: #424242; +} .special-box { - background: #eee7da; margin: 12.5px 12.5px; - padding: 10px 0 10px 95px; - color: #424242; + padding: 10px 0 10px 95px; min-height: 60px; + + /* Theme style */ + background: #eee7da; + color: #424242; } .special-box:before{ content: ""; @@ -35,28 +36,44 @@ blockquote { margin: 0 0 -48px -70px; } @mixin box($name, $bg) { + /* Theme style */ .#{$name} { background: $bg; padding-right: 4%; padding-top: 2%; font-family: monospace, serif; - font-size: 14px; - &:before { - background-image: url("/s/imgM/ic_" + $name + "_black_48px.svg"); - } + font-size: 1rem; + + background-image: url("/s/imgM/ic_" + $name + "_black_48px.svg"); + background-position: 25px center; + background-repeat: no-repeat; } } + @include box('information', #daeaee); -@include box('comment', #eea); +@include box('note', #bed2ff); @include box('attention', #eee7da); @include box('question', #e2daee); @include box('good', #aea); @include box('bad', #eaa); +details { + background-color: lightyellow; + border-radius: 20px; + margin: 40px; + padding: 25px; + + summary { + cursor: pointer; + } +} + .secret { - background: #eee; padding: 10px 0 10px 15px; min-height: 20px; + + /* Theme style */ + background: #eee; } .hiden_block_quote { display: none; @@ -65,12 +82,14 @@ blockquote { margin-top: 5px; } kbd { - background-color: #f8f6ea; padding: 2px 6px; border-radius: 3px; - border: 1px solid #e0dab6; border-bottom-width: 3px; + + /* Theme style */ text-shadow: 0 1px 0 #fff; + background-color: #f8f6ea; + border: 1px solid #e0dab6; color: #5e551f; } .view { @@ -88,19 +107,22 @@ kbd { font-family : inherit; font-size : 1em; cursor : pointer; + > div { width : 40px; height : 24px; - background : #c0c0c0 url('image.png') no-repeat center center; line-height : 24px; - border : 1px solid rgba(27, 31, 35, 0.2); border-radius : 3px; font-size: 12px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + /* Theme style */ + border : 1px solid rgba(27, 31, 35, 0.2); + background : #c0c0c0 url('image.png') no-repeat center center; color: #24292e; background-color: #eff3f6; background-image: linear-gradient(to top, #fafbfc 0%, #eff3f6 90%); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + } > div > div { font-weight: bold; @@ -111,12 +133,16 @@ kbd { } @mixin state($border, $border-c) { - border: 2px solid $border; border-radius: 2px; outline: none; border-color: $border-c; + + /* Theme style */ box-shadow: 0 0 5px $border-c; + border: 2px solid $border; } + +/* Theme style */ .valid { @include state(#c0392b, #9F9); } diff --git a/src/css/design.scss b/src/css/design.scss index c5f1e86..d98db42 100755 --- a/src/css/design.scss +++ b/src/css/design.scss @@ -2,7 +2,7 @@ $gsm: "screen and (max-width: 768px)"; $gt-gsm: "screen and (min-width: 768px)"; $tab: "screen and (max-width: 1024px)"; $laptop: "screen and (min-width: 1024px)"; -$big-laptop: "screen and (min-width: 1400px)"; +$big-laptop: "screen and (min-width: 1520px)"; $big-screen: "screen and (min-width: 1720px)"; @import 'contenu'; @@ -12,37 +12,64 @@ $big-screen: "screen and (min-width: 1720px)"; html { - @mixin lightMode { background: #f1f1f1; } @mixin darkMode { background: #222; } - @media (prefers-color-scheme: dark) { @include darkMode; } + + @media (prefers-color-scheme: dark) { @include darkMode; } @media (prefers-color-scheme: light) { @include lightMode; } + &.light { @include lightMode; } &.dark { @include darkMode; } } -/* - Disabled because of firefx bugs : <https://bugzilla.mozilla.org/show_bug.cgi?id=1712656> - -@media (prefers-color-scheme: dark) { - html{background:#282328;filter:invert(0.98);} - img,svg,.cp_embed_wrapper,pre{filter:invert(0.8);} - #logo-ache {filter:invert(0);} -} -*/ - body { margin: 0; line-height: 1.35; - color: #333333; word-wrap: break-word; font-size: 16px; + a { + text-decoration: underline; + text-decoration-thickness: 10%; + text-decoration-style: dotted; + + @mixin lightMode { + &:hover, &:focus { + text-shadow: 0 0 2px #999; + text-decoration-color: royalblue; + } + + color: #458; + &:visited { + color: #444; + } + } + @mixin darkMode { + &:hover, &:focus { + text-shadow: 0 0 2px #FFF; + text-decoration-color: goldenrod; + } + + + color: #B7B7A7; + &:visited { + color: #C7C7C7; + } + } + + @media (prefers-color-scheme: dark) { @include darkMode; } + @media (prefers-color-scheme: light) { @include lightMode; } + + &.light { @include lightMode; } + &.dark { @include darkMode; } + } + --width_panel: 100%; + --width_panel_bis: 0px; @media #{$gt-gsm} { --width_panel: 290px; --width_panel_bis: 290px; @@ -50,10 +77,8 @@ body { #side-bar { box-sizing: border-box; - padding-top: 10px; - text-rendering: optimizelegibility; width: var(--width_panel); left: calc(var(--width_panel_bis) - var(--width_panel); @@ -114,9 +139,9 @@ ache { margin-left: auto; margin-right: auto; } -img { +img, video { display: block; - margin: 0 auto; + margin: 15px 15px; max-width: 100%; } table { @@ -126,22 +151,65 @@ table { p { display: inline; } - th { - background-color: lightblue; + + @mixin lightMode { + th { + background-color: lightblue; + } + tr:nth-child(even) { + background-color: #DEDEFF99; + } + } + @mixin darkMode { + th { + background-color: darkslategrey; + } + + tr:nth-child(even) { + background-color: darkslateblue; + } + } + + + @media (prefers-color-scheme: dark) { @include darkMode; } + @media (prefers-color-scheme: light) { @include lightMode; } + + &.light { @include lightMode; } + &.dark { @include darkMode; } + +} +p code { + display: inline-block; + white-space: nowrap; +} +code { + border: 0px; + border-radius: 8px; + padding: 0 5px; + font-weight: 500; + font-family: Consolas, Liberation Mono, Monaco, Lucida Console, monospace; + + @mixin lightMode { + color: #c22222; + background: #FFF; } + @mixin darkMode { + color: red; + background: black; + } + + @media (prefers-color-scheme: dark) { @include darkMode; } + @media (prefers-color-scheme: light) { @include lightMode; } + + &.light { @include lightMode; } + &.dark { @include darkMode; } + + } table, th, td { - border: 1px solid #dfe2e5; padding: 7px 13px; -} -.suite { - color: #686868; - outline: none; - text-decoration: none; - font-size: 0.6em; - font-weight: bold; - font-family: monospace, serif; - text-shadow: 0 1px 0 #DDD; + + border: 1px solid #dfe2e5; } .decal_panel { diff --git a/src/img/computer.svg b/src/img/computer.svg index 8ed3927..cbefd6c 100644 --- a/src/img/computer.svg +++ b/src/img/computer.svg @@ -3,7 +3,7 @@ id="svg2" width="555" height="435" - viewBox="0 0 555 435"> + viewBox="150 25 350 330"> <defs id="defs6"> <clipPath diff --git a/src/img/ic_note_black_48px.svg b/src/img/ic_note_black_48px.svg new file mode 100644 index 0000000..a96b381 --- /dev/null +++ b/src/img/ic_note_black_48px.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="48"> + <path d="M5 2h16v20H3V2h2zm14 18V4H5v16h14zM7 6h10v2H7V6zm10 4H7v2h10v-2zM7 14h7v2H7v-2z" fill="currentColor"/> +</svg> diff --git a/src/img/image-formats.svg b/src/img/image-formats.svg new file mode 100644 index 0000000..7248f25 --- /dev/null +++ b/src/img/image-formats.svg @@ -0,0 +1,118 @@ +<svg height="800" width="800" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ viewBox="0 0 512 512" xml:space="preserve">
+ <style>
+text {
+ font-family: monospace;
+ font-weight: 800;
+ font-size: 80px;
+}
+.webp2 {
+ animation: webp2 7.5s infinite;
+}
+.jxl {
+ animation: jxl 7.5s infinite;
+}
+.avif {
+ animation: avif 7.5s infinite;
+}
+@keyframes webp2 {
+ 0% {
+ fill: #FFF0;
+ stroke: #FFFF;
+ stroke-dashoffset: 25%; stroke-dasharray: 0 50%; stroke-width: 4;
+ }
+ 23.33% {fill: rgba(72,138,20,0); stroke: #FFFF; }
+ 26.666% {fill: rgba(72,138,20,0); stroke: #FFFF; stroke-width: 5; }
+ 33.33% {
+ fill: #FFFF; stroke: rgba(54,95,160,0);
+ stroke-dashoffset: -25%; stroke-dasharray: 50% 0; stroke-width: 5;
+ }
+ 35% {
+ fill: #FFF0; stroke: rgba(54,95,160,0);
+ stroke-dashoffset: -25%; stroke-dasharray: 50% 0; stroke-width: 0;
+ }
+ 100% {
+ fill: #FFF0; stroke: rgba(54,95,160,0);
+ stroke-dashoffset: -25%; stroke-dasharray: 50% 0; stroke-width: 0;
+ }
+}
+@keyframes avif {
+ 0% {
+ fill: #FFF0;
+ stroke: #FFFF;
+ stroke-dashoffset: 25%; stroke-dasharray: 0 50%; stroke-width: 0;
+ }
+ 33.33% {
+ fill: #FFF0;
+ stroke: #FFFF;
+ stroke-dashoffset: 25%; stroke-dasharray: 0 50%; stroke-width: 4;
+ }
+ 56.66% {fill: rgba(72,138,20,0); stroke: #FFFF; }
+ 54% {fill: rgba(72,138,20,0); stroke: #FFFF; stroke-width: 5; }
+ 66.66% {
+ fill: #FFFF; stroke: rgba(54,95,160,0);
+ stroke-dashoffset: -25%; stroke-dasharray: 50% 0; stroke-width: 5;
+ }
+ 68% {
+ fill: #FFF0; stroke: rgba(54,95,160,0);
+ stroke-dashoffset: -25%; stroke-dasharray: 50% 0; stroke-width: 0;
+ }
+ 100% {
+ fill: #FFF0; stroke: rgba(54,95,160,0);
+ stroke-dashoffset: -25%; stroke-dasharray: 50% 0; stroke-width: 0;
+ }
+}
+@keyframes jxl {
+ 0% {
+ fill: #FFF0;
+ stroke: #FFFF;
+ stroke-dashoffset: 25%; stroke-dasharray: 0 50%; stroke-width: 0;
+ }
+ 66.66% {
+ fill: #FFF0;
+ stroke: #FFFF;
+ stroke-dashoffset: 25%; stroke-dasharray: 0 50%; stroke-width: 4;
+ }
+ 81% {fill: rgba(72,138,20,0); stroke: #FFFF; }
+ 87% {fill: rgba(72,138,20,0); stroke: #FFFF; stroke-width: 5; }
+ 100% {
+ fill: #FFFF; stroke: rgba(54,95,160,0);
+ stroke-dashoffset: -25%; stroke-dasharray: 50% 0; stroke-width: 5;
+ }
+}
+ </style>
+<path style="fill:#ECEDEF;" d="M100.641,0c-14.139,0-25.6,11.461-25.6,25.6v460.8c0,14.139,11.461,25.6,25.6,25.6h375.467
+ c14.139,0,25.6-11.461,25.6-25.6V85.333L416.375,0H100.641z"/>
+<path style="fill:#D9DCDF;" d="M441.975,85.333h59.733L416.375,0v59.733C416.375,73.872,427.836,85.333,441.975,85.333z"/>
+<path style="fill:#C6CACF;" d="M399.308,42.667H75.041v153.6h324.267c4.713,0,8.533-3.821,8.533-8.533V51.2
+ C407.841,46.487,404.02,42.667,399.308,42.667z"/>
+<path style="fill:#FF8C78;" d="M382.241,179.2H18.843c-7.602,0-11.41-9.191-6.034-14.567L75.041,102.4L12.809,40.167
+ C7.433,34.791,11.241,25.6,18.843,25.6h363.398c4.713,0,8.533,3.821,8.533,8.533v136.533
+ C390.775,175.379,386.954,179.2,382.241,179.2z"/>
+<g>
+ <text x="22%" y="27%" class="webp2">WebP2</text>
+ <text x="23%" y="27%" class="avif">AVIF</text>
+ <text x="27%" y="27%" class="jxl">JXL</text>
+</g>
+<path style="fill:#3C9FE8;" d="M399.308,477.867H177.441c-9.426,0-17.067-7.641-17.067-17.067V238.933
+ c0-9.426,7.641-17.067,17.067-17.067h221.867c9.426,0,17.067,7.641,17.067,17.067V460.8
+ C416.375,470.226,408.734,477.867,399.308,477.867z"/>
+<path style="fill:#8AC451;" d="M160.375,460.8c0,9.426,7.641,17.067,17.067,17.067h221.867c9.426,0,17.067-7.641,17.067-17.067
+ v-25.6h-256L160.375,460.8L160.375,460.8z"/>
+<circle style="fill:#FFD791;" cx="279.84" cy="281.6" r="42.667"/>
+<circle style="fill:#FFC44F;" cx="279.84" cy="281.6" r="25.6"/>
+<g>
+ <path style="fill:#C5E8FA;" d="M211.575,256h-17.067c-4.713,0-8.533-3.821-8.533-8.533c0-4.713,3.821-8.533,8.533-8.533h17.067
+ c4.713,0,8.533,3.821,8.533,8.533C220.108,252.179,216.288,256,211.575,256z"/>
+ <path style="fill:#C5E8FA;" d="M365.175,290.133h-17.067c-4.713,0-8.533-3.821-8.533-8.533c0-4.713,3.821-8.533,8.533-8.533h17.067
+ c4.713,0,8.533,3.821,8.533,8.533C373.708,286.313,369.888,290.133,365.175,290.133z"/>
+ <path style="fill:#C5E8FA;" d="M382.241,315.733h-17.067c-4.713,0-8.533-3.821-8.533-8.533s3.821-8.533,8.533-8.533h17.067
+ c4.713,0,8.533,3.821,8.533,8.533S386.955,315.733,382.241,315.733z"/>
+</g>
+<polygon style="fill:#C4DF64;" points="160.375,358.4 220.108,290.133 322.508,409.6 365.175,358.4 416.375,418.133 416.375,435.2
+ 160.375,435.2 "/>
+<g>
+ <polygon style="fill:#ECEDEF;" points="365.175,358.4 329.619,401.067 401.746,401.067 "/>
+ <polygon style="fill:#ECEDEF;" points="220.108,290.133 167.841,349.867 271.308,349.867 "/>
+</g>
+</svg>
\ No newline at end of file diff --git a/src/img/lt.svg b/src/img/lt.svg index bf84f40..682e527 100644 --- a/src/img/lt.svg +++ b/src/img/lt.svg @@ -7,17 +7,16 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - viewBox="0 -256 1792 1792" + viewBox="-100 10 800 1100" version="1.1" inkscape:version="1.0.2 (e86c870879, 2021-01-15)" - width="70px" - height="70px" + width="20px" aria-label="hidden"> <g id="glob"> <path - fill="orange" + fill="royalblue" opacity="0.4" - stroke="orange" + stroke="royalblue" stroke-width="5" d="m 627,992 q 0,-13 -10,-23 L 224,576 617,183 q 10,-10 10,-23 0,-13 -10,-23 L 567,87 Q 557,77 544,77 531,77 521,87 L 55,553 q -10,10 -10,23 0,13 10,23 l 466,466 q 10,10 23,10 13,0 23,-10 l 50,-50 q 10,-10 10,-23 z" id="lt" diff --git a/src/img/masto.svg b/src/img/masto.svg deleted file mode 100644 index 1e057ad..0000000 --- a/src/img/masto.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><desc>Mastodon Logo</desc><g transform="scale(0.08)"> <path fill="#777" d="M211.80734 139.0875c-3.18125 16.36625-28.4925 34.2775-57.5625 37.74875-15.15875 1.80875-30.08375 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.39125 27.9425 21.11625.7225 39.91875-5.20625 39.91875-5.20625l.8675 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234 213.82 1.40609 165.31125.20859 116.09125c-.365-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67234 3.45375 78.20359.2425 107.86484 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.975 14.7525 32.975 65.0825 0 0 .41375 37.13375-4.59875 62.915" /> <path fill="#fff" d="M177.50984 80.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025 0-17.4175 7.5075-17.4175 22.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375 0-15.74 6.32875-15.74 18.7975v59.15H38.90484V80.077c0-12.455 3.17125-22.3525 9.54125-29.675 6.56875-7.3225 15.17125-11.07625 25.85-11.07625 12.355 0 21.71125 4.74875 27.8975 14.2475l6.01375 10.08125 6.015-10.08125c6.185-9.49875 15.54125-14.2475 27.8975-14.2475 10.6775 0 19.28 3.75375 25.85 11.07625 6.36875 7.3225 9.54 17.22 9.54 29.675"/> </g> </svg> diff --git a/src/img/mastodon.svg b/src/img/mastodon.svg new file mode 100644 index 0000000..c4f910f --- /dev/null +++ b/src/img/mastodon.svg @@ -0,0 +1,4 @@ +<svg xmlns="http://www.w3.org/2000/svg" +aria-label="Mastodon" role="img" +viewBox="80 85 350 340" width="20" height="20" +fill="#fff"><linearGradient id="b" y2="1"><stop offset="0" stop-color="#6364ff"/><stop offset="1" stop-color="#563acc"/></linearGradient><path fill="url(#b)" d="M317 381q-124 28-123-39 69 15 149 2 67-13 72-80 3-101-3-116-19-49-72-58-98-10-162 0-56 10-75 58-12 31-3 147 3 32 9 53 13 46 70 69 83 23 138-9"/><path d="M360 293h-36v-93q-1-26-29-23-20 3-20 34v47h-36v-47q0-31-20-34-30-3-30 28v88h-36v-91q1-51 44-60 33-5 51 21l9 15 9-15q16-26 51-21 43 9 43 60"/></svg>
\ No newline at end of file diff --git a/src/js/love.js b/src/js/love.js new file mode 100644 index 0000000..66f47d4 --- /dev/null +++ b/src/js/love.js @@ -0,0 +1,125 @@ +window.addEventListener('DOMContentLoaded', () => { + // This script insert a love button at the end of an article page + + const articles = document.querySelectorAll('article'); + + function getCurrentPageLang() { + return document.documentElement.lang || "en"; + } + function getLikeEndPoint() { + let currentArticleName = window.location.pathname.split('/')[2]; + if (currentArticleName.indexOf('.') > 0) { + currentArticleName = currentArticleName.slice(0, currentArticleName.lastIndexOf('.')) + } + + return `${window.location.origin}/like/${currentArticleName}`; + } + + const isAnArticle = articles.length === 1; // The front page have multiple articles. + + if (isAnArticle) { + const article = articles[0]; + const likes = article.querySelector('.likes'); + const nbLikes = article.querySelector('.nbLikes'); + const footnotes = article.querySelector('.footnotes'); + + likes.style="display: 'block';"; + const updateNbLikes = () => { + fetch(getLikeEndPoint(), { + method: 'GET', + headers: { + 'i-love-what-you-do': '<3', + }, + }).then((res) => { + if (res.ok) { + res.text().then((text) => nbLikes.textContent = text); + } else { + nbLikes.textContent = ""; + } + }); + }; + + updateNbLikes(); + + if (footnotes) { + // We want: article.insertBefore(likes, footnotes); + footnotes.before(likes); + } + + const icon = likes.querySelector('.icon'); + const messagesLike = likes.querySelector('.likesNotes'); + + icon.addEventListener('mouseover', () => { + for (const c of icon.children) { + const path = c.querySelector('path'); + path?.classList.add('anim'); + } + }); + + icon.addEventListener('mouseout', () => { + for (const c of icon.children) { + const path = c.querySelector('path'); + path?.classList.remove('anim'); + } + }); + + icon.addEventListener('click', () => { + const c = icon.children[1]; + const lang = getCurrentPageLang(); + + const path = c.querySelector('path'); + + path.classList.add('anim-click'); + path.getAnimations().forEach(anim => { + anim.cancel(); + anim.play(); + }); + + fetch(getLikeEndPoint(), { + method: 'POST', + headers: { + 'i-love-what-you-do': '<3', + 'lang': lang, + }, + }).then(response => { + const t = response.text(); + + if (response.ok) { + messagesLike.classList.remove('err'); + t.then(t => messagesLike.textContent = t); + updateNbLikes(); + } else { + t.then(t => { + // In case of error, we want to be able to set the initial message back + const previousText = messagesLike.textContent; + const timeoutErr = 3000; + const wasAnError = messagesLike.classList.contains('err'); + + // Set the error + messagesLike.classList.add('err'); + if (Math.floor(response.status / 100) == 4) { + messagesLike.textContent = t; + } else { + messagesLike.textContent = lang == 'fr' ? 'Désolé, le service est indisponible.' : 'Sorry, service unavailable.'; + } + + // Set the initial message back + if(!wasAnError) { + setTimeout(() => { + if(messagesLike.classList.contains('err')) { + messagesLike.classList.remove('err'); + messagesLike.textContent = previousText; + } + }, timeoutErr); + } + }); + } + }).catch(() => { + // In case of exception error. We don't want to set back the initial message. + messagesLike.classList.add('err'); + messagesLike.textContent = lang == 'fr' ? 'Désolé, le service est indisponible.' : 'Sorry, service unavailable.'; + messageText = ''; + }); + }); + } +}); diff --git a/src/js/sidenotes.js b/src/js/sidenotes.js index 8ab99f9..851e11b 100644 --- a/src/js/sidenotes.js +++ b/src/js/sidenotes.js @@ -28,7 +28,7 @@ const resize = () => { const newSidenotes = notes.map(sidenoteLi => { const div = document.createElement('div'); const refName = sidenoteLi.querySelector('.data-footnote-backref').attributes.href.value; - const refSideNode = article.querySelector(refName); + const refSideNode = article.querySelector(`#${CSS.escape(refName.slice(1))}`); const sup = document.createElement('sup'); sup.textContent = refSideNode.textContent + ' '; diff --git a/src/js/theme.js b/src/js/theme.js index fd0b755..8239c69 100644 --- a/src/js/theme.js +++ b/src/js/theme.js @@ -8,7 +8,12 @@ window.addEventListener('DOMContentLoaded', () => { const html = document.querySelector('html'); const aside = document.querySelector('#side-bar'); const sidenotes = document.querySelector('.sidenotes'); + const footnotes = document.querySelector('.footnotes'); const harr = document.querySelector('#harr'); + const links = document.querySelectorAll('a'); + const likes = document.querySelectorAll('.likes'); + const tables = document.querySelectorAll('table'); + const codes = document.querySelectorAll('p code'); if (storageTheme === 'dark' || storageTheme === 'light') { const theme = storageTheme; @@ -19,6 +24,27 @@ window.addEventListener('DOMContentLoaded', () => { sidenotes.classList.add(theme); harr.classList.add(theme); + if(footnotes) { + footnotes.classList.add(theme); + } + + for (let link of links) { + link.classList.add(theme); + } + for (let likeBox of likes) { + likeBox.classList.add(theme); + } + for (let table of tables) { + table.classList.add(theme); + } + for (let code of codes) { + code.classList.add(theme); + } + + for (const article of document.querySelectorAll('article')) { + article.classList.add(theme); + } + for (const article of document.querySelectorAll('article')) { article.classList.add(theme); } @@ -43,11 +69,37 @@ window.addEventListener('DOMContentLoaded', () => { if (!sidenotes.classList.replace(...arg)) { sidenotes.classList.add(theme); } + if (footnotes && !footnotes.classList.replace(...arg)) { + footnotes.classList.add(theme); + } + + if (!harr.classList.replace(...arg)) { harr.classList.add(theme); } + for (let link of links) { + if (!link.classList.replace(...arg)) { + link.classList.add(theme); + } + } + for (let table of tables) { + if (!table.classList.replace(...arg)) { + table.classList.add(theme); + } + } + for (let code of codes) { + if (!code.classList.replace(...arg)) { + code.classList.add(theme); + } + } + for (let like of likes) { + if (!like.classList.replace(...arg)) { + like.classList.add(theme); + } + } + for (const article of document.querySelectorAll('article')) { if (!article.classList.replace(...arg)) { article.classList.add(theme); diff --git a/src/js/zen.js b/src/js/zen.js index 80476e6..be5f7d7 100644 --- a/src/js/zen.js +++ b/src/js/zen.js @@ -1,18 +1,25 @@ window.addEventListener('DOMContentLoaded', () => { let firstTime = window.location.pathname != '/' && window.pageYOffset < 800; const toggleArrow = document.querySelector('#harr'); + const hid = document.querySelector('#hid'); const initValue = document.body.style.getPropertyValue('--width_panel_bis'); toggleArrow.addEventListener('click', () => { if (toggleArrow.classList.contains('hide_arrow_off')) { firstTime = false; showAbout(); + showHID(); setTimeout(() => { toggleArrow.classList.remove('hide_arrow_off'); }, 1000); } else { toggleArrow.classList.add('hide_arrow_off'); hideAbout(); + + // If the screen is smaller than 500px, hide the HID + if (window.screen.availWidth <= 500) { + hideHID(); + } } }); @@ -24,6 +31,14 @@ window.addEventListener('DOMContentLoaded', () => { document.body.style.setProperty('--width_panel_bis', initValue); } + function hideHID() { + hid.style.setProperty('display', 'none'); + } + + function showHID() { + hid.style.setProperty('display', ''); + } + window.addEventListener('scroll', () => { if(!toggleArrow) return; if (window.pageYOffset >= 800) { diff --git a/src/templates/article.tmpl b/src/templates/article.tmpl index e5267ff..cd87f39 100644 --- a/src/templates/article.tmpl +++ b/src/templates/article.tmpl @@ -1,12 +1,14 @@ <!DOCTYPE html> -<html lang="fr"> +<html lang="{{lang}}"> {{> header }} <body> <div class="decal_panel"> <div class="marge"></div> <article class="post" id="{{domTitle}}_article"> - <div class="tags">{{# metaData.tags }}<a href="/tag/{{{ . }}}" class="tag">{{{ . }}}</a>{{/ metaData.tags }}</div> + <div class="tags">{{# metaData.tags }}<a href="/{{ lang }}/tag/{{{ . }}}" class="tag">{{{ . }}}</a>{{/ metaData.tags }}</div> + <div class="pubdate">{{ metaData.pubDateISO }}</div> {{{ content }}} + {{> likesButton }} </article> <div class="sidenotes"></div> </div> diff --git a/src/templates/header.tmpl b/src/templates/header.tmpl index 433b130..e92da4a 100644 --- a/src/templates/header.tmpl +++ b/src/templates/header.tmpl @@ -1,6 +1,6 @@ <head> <meta charset="utf-8" /> - <title>{{ title }}</title> + <title>{{ page_title }}</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="canonical" href="{{{ canonical }}}"/> <link href="/s/css/style.css" rel="stylesheet"/> @@ -8,6 +8,6 @@ <link href="/s/css/katex.css" rel="stylesheet"/> </noscript> <script src="/s/js/main.js" defer></script> - <meta name= "description" content="ache: {{ description }} " /> + <meta name= "description" content="{{ description }} " /> <link rel="alternate" type="application/rss+xml" href="/rss.xml"> </head> diff --git a/src/templates/hid.tmpl b/src/templates/hid.tmpl index 242ed5f..7f0ab78 100644 --- a/src/templates/hid.tmpl +++ b/src/templates/hid.tmpl @@ -1 +1 @@ -<div id="hid"><div role=button tabindex=0 class=sun aria-label="light mode">{{{svg.sun}}}</div><div role=button tabindex=0 class=moon aria-label="dark mode">{{{svg.moon}}}</div></div> +<div id="hid">{{# alt_lang }}<span role=button tabindex=0 class="alt-lang" aria-label="{{ description }}" title="{{ description }}"><a href="{{ url }}">{{ lang }}</a></span>{{/ alt_lang }}<div role=button tabindex=0 class=sun aria-label="light mode">{{{svg.sun}}}</div><div role=button tabindex=0 class=moon aria-label="dark mode">{{{svg.moon}}}</div></div> diff --git a/src/templates/index.tmpl b/src/templates/index.tmpl index 61d1221..5ed2ed4 100644 --- a/src/templates/index.tmpl +++ b/src/templates/index.tmpl @@ -1,5 +1,5 @@ <!DOCTYPE html> -<html lang="fr"> +<html lang="{{lang}}"> {{>header }} <body> {{#articles}} diff --git a/src/templates/left.tmpl b/src/templates/left.tmpl index d227522..c82c692 100644 --- a/src/templates/left.tmpl +++ b/src/templates/left.tmpl @@ -2,25 +2,27 @@ {{{ svg.lt }}} </nav> <aside id="side-bar"> + <a href="/{{ lang }}/" class="button"> {{{ svg.ache }}} - <h2> Ache </h2> - <div id="desc"><div id="desc_intro">Éternel étudiant en Math-Info.<br><span class="about">Autodidacte passionné,<br><span class="type_wrap"><span class="type">désormais ingénieur.</span></span></span></div><br> <span class="about">GNU\Linux, C, C++, Python, Math, autohébergement, décentralisation, P2P, commun, ... <br> </span><br></div> + </a> + <h2 id="ache"><a href="/{{ lang}}/">Ache</a></h2> + <div id="desc"><div id="desc_intro">{{{ intro.description }}}<br><span class="about">{{{ intro.about }}}</span></div><br><span class="about">{{{ intro.about_tags }}}</span><br></div> <nav> <ul> - <li class="sommaire_blien"><a href="/" title="L'accueil">home</a> - </li><li class="sommaire_blien"><a href="http://git.ache.one/" title="Dépôt git personnel">git</a> - </li> + <li class="sommaire_blien"><a href="/{{ lang }}" title="{{ title.home }}">home</a> + </li><li class="sommaire_blien"><a href="http://git.ache.one/" title="{{ title.git }}">git</a> + </li> </ul> </nav> <nav id="ontheweb"> <ul> - <li class="about_bar"><a href="https://twitter.com/arobase_che" title="Mon twitter abandonné"> - {{{ svg.twitter }}} + <li class="about_bar"><a rel="me" href="https://mastodon.xyz/@ache" title="{{ title.mosto }}"> + {{{ svg.mastodon }}} </a></li> - <li class="about_bar"><a href="http://git.ache.one" title="Dépôt git personnel"> + <li class="about_bar"><a href="https://git.ache.one" title="{{ title.git }}"> {{{ svg.git }}} </a></li> - <li class="about_bar"><a href="/rss.xml" title="Flux RSS"> + <li class="about_bar"><a href="/{{ lang }}/rss.xml" title="Flux RSS"> {{{ svg.rss }}} </a></li> </ul> diff --git a/src/templates/likes.tmpl b/src/templates/likes.tmpl new file mode 100644 index 0000000..c81d936 --- /dev/null +++ b/src/templates/likes.tmpl @@ -0,0 +1,19 @@ +<section class="likes" style="display: none"> + <span class="likesBox"> + <div class="layout"> + <div class="icon"> + <svg class="heart-main" viewBox="0 0 512 512" title="heart" z-index="1" height="100"> + <path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" class=""></path> + </svg> + <svg class="heart-background" viewBox="0 0 512 512" title="heart" z-index="1" height="100"> + <path d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z" class=""></path> + </svg> + <div class="nbLikes"><div></div></div> + </div> + </div> + <div> + <span class="likesTitle">{{ like_title }}</span> + <p class="likesText">{{{ like_text }}}</p> + </div></span> + + </section> diff --git a/src/templates/tag.tmpl b/src/templates/tag.tmpl index 7d2d152..f73365d 100644 --- a/src/templates/tag.tmpl +++ b/src/templates/tag.tmpl @@ -1,5 +1,5 @@ <!DOCTYPE html> -<html lang="fr"> +<html lang="{{lang}}"> {{>header }} <body> <div class="decal_panel"> @@ -11,7 +11,7 @@ {{# articles}} <li><a href="{{ url }}"><span class="pubYear">{{ pubYear }}</span>{{ title }}</a> {{# metaData.tags }} - <a href="/tag/{{{ . }}}" class="inline-tag">{{{ . }}}</a> + <a href="/{{ lang }}/tag/{{{ . }}}" class="inline-tag">{{{ . }}}</a> {{/ metaData.tags }} </li> {{/ articles}} |