diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rwxr-xr-x | components/post-preview.sh | 26 | ||||
-rwxr-xr-x | config.sh | 8 | ||||
-rwxr-xr-x | footer.sh | 17 | ||||
-rw-r--r-- | header.sh | 100 | ||||
-rwxr-xr-x | index.sh | 39 | ||||
-rwxr-xr-x | nav.sh | 11 | ||||
-rwxr-xr-x | pages/article.sh | 21 | ||||
-rwxr-xr-x[-rw-r--r--] | pages/contato.sh | 23 | ||||
-rwxr-xr-x | pages/home.sh | 29 | ||||
-rw-r--r-- | static/header.js | 137 | ||||
-rw-r--r-- | static/styles.css (renamed from styles.css) | 86 | ||||
-rwxr-xr-x | utils/parse-article.sh | 18 |
13 files changed, 379 insertions, 138 deletions
@@ -1 +1,3 @@ /pages/articles/* +/artigos/* +/articles/* diff --git a/components/post-preview.sh b/components/post-preview.sh new file mode 100755 index 0000000..dbbf663 --- /dev/null +++ b/components/post-preview.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +source $SCRIPT_DIR/utils/parse-article.sh + +cat <<POST +<section class="post-preview"> + <div class="post-header-preview"> + <h5 class="post-title">$ stat -c "%w" \\<br/> > $ARTICLE_FILE</h5> + <small>$ARTICLE_BYTES</small> <br/> + <time>$(stat --format="Criado em: %w" $ARTICLE_FILE)</time> + + <ul class="tags"> + $( + for i in "${ARTICLE_TAGS[@]}"; do + echo "<li class="tag">$i</li>" + done + ) + </ul> + </div> + + <p>$(strip-html-tags "$ARTICLE_CONTENT" | head -c 120)...</p> + + <a href="/$ARTICLE_FILE">Ler artigo completo</a> +</section> +POST + diff --git a/config.sh b/config.sh new file mode 100755 index 0000000..4cfed05 --- /dev/null +++ b/config.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# All relatives paths must end with slash and must not be start by dot and/or slash +# Examplo +# VAR_PATH=dir/subdir/ + +# Folder where posts are stored, must be relative and suffed by a slash +ARTICLES_PATH=artigos/ @@ -5,6 +5,19 @@ cat <<FOOTER footer { text-align: center; } + + footer h1 { + text-align: left; + } + + #footer-command-prompt { + padding-top: 25px; + } + + #footer-command-prompt h1 { + padding: 0px; + margin: 0px; + } </style> <footer> @@ -14,5 +27,9 @@ cat <<FOOTER Gateway: $GATEWAY_INTERFACE <br /> Pré-processador HTML: GNU Bash $BASH_VERSION </p> + + <div id="footer-command-prompt"> + <h1 class="header-title">$ <span class="text"></span><span class="cursor after-prompt"> </span></h1> + </div> </footer> FOOTER @@ -2,105 +2,37 @@ cat <<HEADER <style> - header h1, header h2 { + .header-title { + font-size: 1.8rem; text-align: left; line-break: anywhere; } - header .separator-wrap { - margin-top: 15px; - } - - .cursor { - background-color: var(--primary-fg); - animation-name: blink-animation; - animation-duration: 500ms; - animation-iteration-count: infinite; - animation-timing-function: cubic-bezier(0.83,-0.22, 0.54, 0.55); - } - - @keyframes blink-animation { - to { - opacity: 0; - } - } - .mobile { display: none; } @media screen and (max-width: 769px) { .mobile { display: block; } .desktop { display: none; } + + .header-title { + font-size: 1.2rem; + } } </style> <script type="text/javascript"> let mobileText = '$HEADER_TITLE_MOBILE'; let desktopText = '$HEADER_TITLE'; - let isMobile = window.innerWidth < 769; - - function hideContent() { - let mainContainer = document.querySelector('main'); - let footer = document.querySelector('footer'); - let headerSeparator = document.querySelector('header > .separator-wrap'); - mainContainer.style.opacity = '0'; - footer.style.opacity = '0'; - headerSeparator.style.opacity = '0'; - } - - function showContent() { - let mainContainer = document.querySelector('main'); - let footer = document.querySelector('footer'); - let headerSeparator = document.querySelector('header > .separator-wrap'); - mainContainer.style.transition = '500ms'; - mainContainer.style.opacity = '1'; - footer.style.transition = '500ms'; - footer.style.opacity = '1'; - headerSeparator.style.transition = '500ms'; - headerSeparator.style.opacity = '1'; - - } - - function typingAnimation(el, text, index, cb) { - let char = text[index]; - if (char == ';') { - el.innerHTML += '<br />'; - } else if (char == ' ') { - el.innerHTML += ' '; - } else { - el.innerText += char; - } - - if (index < text.length - 1) { - setTimeout(() => typingAnimation(el, text, index + 1, cb), 30); - } else { - cb(); - } - } - - document.addEventListener('DOMContentLoaded', function(event) { - hideContent(); - let titleMobile = document.querySelector("#header-title-mobile > .text"); - let titleDesktop = document.querySelector('#header-title > .text'); - - if (isMobile) { - titleMobile.innerHTML = ''; - } else { - titleDesktop.innerHTML = ''; - } - - setTimeout(() => { - if (isMobile) { - typingAnimation(titleMobile, mobileText, 0, showContent); - } else { - typingAnimation(titleDesktop, desktopText, 0, showContent); - } - }, 200); - }); </script> - -<header> - <h1 id="header-title" class="desktop">$ <span class="text">echo "Jefferson Julio" >> ./programadores</span><span class="cursor"> </span></h1> - <h1 id="header-title-mobile" class="mobile">$ <span class="text">echo "Jefferson Julio" \\<br/> >> ./programadores</span><span class="cursor"> </span></h1> - <div class="separator-wrap"><div class="separator"></div></div> +<script type="text/javascript" src="/static/header.js"></script> +<script type="text/javascript" src="/static/navigator.js"></script> + +<header id="top-header"> + <h1 id="header-title" class="desktop header-title"> + $ <span class="text">${HEADER_TITLE/;/<br\/>$ }<br/></span> + </h1> + <h1 id="header-title-mobile" class="mobile header-title"> + $ <span class="text">${HEADER_TITLE_MOBILE/;/<br\/>$ }<br/></span> + </h1> </header> HEADER @@ -4,14 +4,25 @@ if [ "$REQUEST_URI" = '/favicon.ico' ]; then exit 0 fi +SCRIPT_DIR="$( cd "$( dirname "$(readlink -f "$0")" )" >/dev/null 2>&1 && pwd )" +source ./config.sh + RESPONSE_CONTENT_TYPE="text/html" STATUS=200 -HEADER_TITLE='source ./programadores/Jefferson Júlio/site/home.sh' -HEADER_TITLE_MOBILE='cd ./programadores/ && \\ ;> source Jefferson Júlio/\\ ;> site/home.sh' +HEADER_TITLE='cd programadores/;cd Jefferson\\ Julio/;source jefferson.sh' +HEADER_TITLE_MOBILE="$HEADER_TITLE" urldecode() { : "${*//+/ }"; echo -e "${_//%/\\x}"; } +status-ok () { + [ "$STATUS" -gt 199 ] && [ "$STATUS" -lt 300 ] && echo 1 +} + +strip-html-tags () { + echo "$1" | sed 's/<[^>]*>//g' +} + html () { cat <<HTML <html lang="pt-br"> @@ -19,15 +30,13 @@ html () { <meta charset="utf-8" /> <title>jefferson.sh</title> <meta name="viewport" content="width=device-width,initial-scale=1" /> - - <style> - $(cat ./styles.css) - </style> + <link rel="stylesheet" href="/static/styles.css" /> + <link rel="manifest" href="/static/manifest.json"> </head> <body> - $(source ./nav.sh) $(source ./header.sh) + $(source ./nav.sh) <main> $BODY @@ -47,14 +56,14 @@ router () { BODY=$(source ./pages/home.sh) ;; /contato) - HEADER_TITLE="source ./pages/contato.sh" + HEADER_TITLE="source pages/contato.sh" HEADER_TITLE_MOBILE="$HEADER_TITLE" BODY=$(source ./pages/contato.sh) ;; - /artigo/*.txt) - ARTICLE_FILE=./pages/articles/${REQUEST_URI/\/artigo\//} - HEADER_TITLE="cat $ARTICLE_FILE" - HEADER_TITLE_MOBILE="$HEADER_TITLE" + /$ARTICLES_PATH*.txt) + ARTICLE_FILE=.$REQUEST_URI + HEADER_TITLE="ARTICLE_FILE=$ARTICLE_FILE source pages/article.sh" + HEADER_TITLE_MOBILE="export ARTICLE_FILE=$ARTICLE_FILE;source pages/article.sh" BODY=$(source ./pages/article.sh) ;; /json) @@ -69,9 +78,9 @@ JSON ;; *) STATUS=404 - HEADER_TITLE="cat .$REQUEST_URI" - HEADER_TITLE_MOBILE="$HEADER_TITLE" - BODY="<p class='err-404'>cat: .$REQUEST_URI: Arquivo ou diretório inexistente</p>" + HEADER_TITLE="REQUEST_FILE=.$REQUEST_URI source page.sh" + HEADER_TITLE_MOBILE="export REQUEST_FILE=.$REQUEST_URI;source page.sh" + BODY="<p class='err-404'>404: .$REQUEST_URI: Arquivo ou diretório inexistente</p>" ;; esac } @@ -3,7 +3,8 @@ cat <<NAV <style> .top-nav { - position: relative; + position: sticky; + top: 0px; display: inline-flex; background-color: var(--primary-fg); height: 40px; @@ -31,8 +32,6 @@ cat <<NAV .nav-links li { position: relative; height: 100%; - padding-left: 5px; - padding-right: 5px; display: inline-flex; align-items: center; } @@ -59,8 +58,10 @@ cat <<NAV } .nav-header { - padding-top: 10px; + padding-top: 25px; padding-bottom: 10px; + display: block; + text-align: center; } .nav-header a { @@ -80,6 +81,7 @@ cat <<NAV </style> <nav class="top-nav"> + <div class="pad-left"></div> <span class="nav-header"> <a href="https://jefferson.sh">jefferson.sh</a> </span> @@ -87,5 +89,6 @@ cat <<NAV <li> <a href="/">Início</a> </li> <li> <a href="/contato">Contato</a> </li> </ul> + <div class="pad-right"></div> </nav> NAV diff --git a/pages/article.sh b/pages/article.sh index 9832a76..b50d922 100755 --- a/pages/article.sh +++ b/pages/article.sh @@ -1,26 +1,11 @@ #!/bin/bash -ARTICLE_TITLE=$(basename "$ARTICLE_FILE") - -# Pick all file content -ARTICLE_FILE_CONTENT=$(cat "$ARTICLE_FILE") - -# The first 3 lines of the file are metadata information -ARTICLE_METADATA=$(echo "$ARTICLE_FILE_CONTENT" | head -n 3) - -# Skip file metadata, first 3 lines -ARTICLE_CONTENT=$(echo "$ARTICLE_FILE_CONTENT" | tail -n +3) - -# Tags are placed on the first line of the file, delimited by commas -IFS=',' read -ra ARTICLE_TAGS <<< "$(echo "$ARTICLE_METADATA" | head -n 1)" - -ARTICLE_TIME=$(stat --format="Criado em: %w<br/>Última atualização: %z" "$ARTICLE_FILE") -ARTICLE_BYTES=$(stat --format="%o bytes" "$ARTICLE_FILE") +source $SCRIPT_DIR/utils/parse-article.sh if [ $? -gt 0 ]; then STATUS=404 cat <<ERR -<p class="err-404">cat: $ARTICLE_FILE: Arquivo ou diretório inexistente</p> +<p class="err-404">404: $ARTICLE_FILE: Arquivo ou diretório inexistente</p> ERR exit 0 fi @@ -29,7 +14,7 @@ cat <<ARTICLE <article class="container"> <section> <div class="post-header"> - <h4 class="post-title">stat -c "%o %w %z" $ARTICLE_FILE</h4> + <h4 class="post-title">$ stat -c "%s %w %z" \\ <br/> > $ARTICLE_FILE</h4> <small>$ARTICLE_BYTES</small> <br/> <time>$ARTICLE_TIME</time> <br/> diff --git a/pages/contato.sh b/pages/contato.sh index 74d1ad4..b54a64e 100644..100755 --- a/pages/contato.sh +++ b/pages/contato.sh @@ -1,4 +1,5 @@ #!/bin/bash + cat <<PAGE <style> @media screen and (max-width: 600px) { @@ -9,5 +10,27 @@ cat <<PAGE </style> <article class="contato-page container"> + <section> + <h1>Formas de contato</h1> + + <p> + Primeiramente, muito obrigado pelo interesse em me contatar! Você pode ver abaixo minhas + informações de contato, pode me chamar a qualquer hora, mas só respondo em horário + comercial. Se for algo urgente, escreva "URGENTE" sem aspas no título da mensagem + que tentarei fazer de tudo para atendê-lo o quanto antes. + </p> + </section> + + <section> + <address> + Email: <a href="mailto:[email protected]">[email protected]</a> <br /> + Linkedin: + <a href="https://www.linkedin.com/in/jefferson-j%C3%BAlio-56a30b149"> + https://www.linkedin.com/in/jefferson-j%C3%BAlio-56a30b149 + </a> + </address> + </section> </article> PAGE + +# Celular e Whatsapp: +55 (81) 97326-9793 <br /> diff --git a/pages/home.sh b/pages/home.sh index ebb399d..c9a9966 100755 --- a/pages/home.sh +++ b/pages/home.sh @@ -1,25 +1,26 @@ #!/bin/bash -print-all-articles () { - for i in $(find ./pages/articles -type f -name '*.txt'); do - ARTICLE_CONTENT=$(tail -n +3 $i) - cat <<POST -<section class="post-preview"> - <div class="post-header-preview"> - <h5 class="post-title">stat -c "%w %z" $i</h5> - <time>$(stat --format="Criado em: %w<br/>Última atualização: %z" $i)</time> - </div> - - <p>$(echo "$ARTICLE_CONTENT" | head -c 120)...</p> - - <a href="/artigo/$(basename $i)">Ler artigo completo</a> +welcome () { + cat <<WELCOME +<section class="conainer" id="welcome"> + <header> + <h1>Bem-vindo ao meu blog :3</h1> + <h4>Aqui mostro o meu trabalho e escrevo sobre programação.</h4> + </header> </section> -POST +WELCOME +} + +print-all-articles () { + # Find all articles and sort by date of creation + for i in $(find $ARTICLES_PATH -type f -name '*.txt' -printf "%T@ %p\n" | sort -rn | cut -b 23-); do + ARTICLE_FILE="${i/* /}" source $SCRIPT_DIR/components/post-preview.sh done } cat <<PAGE <article class="home-feed container"> + $(welcome) $(print-all-articles) </article> PAGE diff --git a/static/header.js b/static/header.js new file mode 100644 index 0000000..06d868a --- /dev/null +++ b/static/header.js @@ -0,0 +1,137 @@ +let isMobile = window.innerWidth < 769; +let keyPressAudio = new Audio('/static/key-press.mp3'); +let headerAnimationTerminated = false; +let cursorHTML = '<span class="cursor"> </span>'; + +window.addEventListener('resize', () => { + isMobile = window.innerWidth < 769; +}); + +function hideTopNav() { + let topNav = document.querySelector('.top-nav'); + topNav.style.opacity = '0'; + topNav.style.display = 'none'; +} + +function hideContent() { + hideTopNav(); + let mainContainer = document.querySelector('main'); + let footer = document.querySelector('footer'); + mainContainer.style.opacity = '0'; + mainContainer.style.display = 'none'; + footer.style.opacity = '0'; + footer.style.display = 'none'; +} + +function showContent(cb) { + let mainContainer = document.querySelector('main'); + let footer = document.querySelector('footer'); + let topNav = document.querySelector('.top-nav'); + + topNav.style.transition = '100ms'; + topNav.style.opacity = '1'; + topNav.style.display = 'block'; + + setTimeout(() => { + mainContainer.style.transition = '500ms'; + mainContainer.style.opacity = '1'; + mainContainer.style.display = 'block'; + setTimeout(() => { + footer.style.transition = '500ms'; + footer.style.opacity = '1'; + footer.style.display = 'block'; + if (cb) cb(); + }, 500); + }, 100); +} + +function clearHideContentTransition() { + let mainContainer = document.querySelector('main'); + let footer = document.querySelector('footer'); + let topNav = document.querySelector('.top-nav'); + topNav.style.transition = '0s'; + mainContainer.style.transition = '0s'; + footer.style.transition = '0s'; +} + +function typingAnimation(el, text, index, cb) { + let delay = 30; + + let char = text[index]; + if (char == ';') { + el.innerHTML += '<br />$ '; + !isMobile && keyPressAudio.play(); + delay += 400; + } else if (char == ' ') { + el.innerHTML += ' '; + } else { + el.innerText += char; + } + + if (index < text.length - 1) { + setTimeout(() => typingAnimation(el, text, index + 1, cb), delay); + } else { + !isMobile && keyPressAudio.play(); + el.innerHTML += '<br />'; + setTimeout(() => { + if (cb) cb(); + setTimeout(clearHideContentTransition, 1000); + }, 600); + } +} + +function clearHeaderTerminal(cb) { + let titleMobile = document.querySelector("#header-title-mobile > .text"); + let titleDesktop = document.querySelector('#header-title > .text'); + let footerCommandPrompt = document.querySelector('#footer-command-prompt'); + + if (isMobile) { + titleMobile.innerHTML = ''; + } else { + titleDesktop.innerHTML = ''; + } + + footerCommandPrompt.style.height = 'calc(100vh)'; + hideTopNav(); + footerCommandPrompt.querySelector('.after-prompt').scrollIntoView({ + behavior: 'smooth', + }); + + setTimeout(() => { + if (cb) cb(); + footerCommandPrompt.style.height = 'auto'; + }, 1000); +} + +function headerTerminalExecute(desktopText, mobileText, cb) { + window.scrollTo(0, 0); + headerAnimationTerminated = false; + let titleMobile = document.querySelector("#header-title-mobile > .text"); + let titleDesktop = document.querySelector('#header-title > .text'); + + if (!mobileText) mobileText = desktopText; + + setTimeout(() => { + if (isMobile) { + titleMobile.insertAdjacentHTML('afterend', cursorHTML); + typingAnimation(titleMobile, mobileText, 0, () => { + if (cb) cb(); + headerAnimationTerminated = true; + document.querySelector('#header-title-mobile .cursor').remove(); + }); + } else { + titleDesktop.insertAdjacentHTML('afterend', cursorHTML); + typingAnimation(titleDesktop, desktopText, 0, () => { + if (cb) cb(); + headerAnimationTerminated = true; + document.querySelector('#header-title .cursor').remove(); + }); + } + }, 200); +} + +document.addEventListener('DOMContentLoaded', function() { + clearHeaderTerminal(); + hideContent(); + headerTerminalExecute(desktopText, mobileText, () => showContent(bindAllRelativeAnchors)); +}); diff --git a/styles.css b/static/styles.css index b17d72f..e84dc56 100644 --- a/styles.css +++ b/static/styles.css @@ -8,6 +8,7 @@ body { } main { display: block; + margin-top: 25px; } h1 { font-size: 2em; @@ -152,13 +153,41 @@ template { html { --primary-bg: #2b2b2b; - --primary-fg: #65d84a; + /* --primary-fg: #65d84a; */ + --primary-fg: #0eff5d; background-color: var(--primary-bg); color: var(--primary-fg); font-family: 'VT323', monospace; - font-size: 14px; + font-size: 16px; + position: relative; + overflow: auto; + /* scroll-behavior: smooth; */ + min-height: 100vh; +} + +@media screen and (max-width: 600px) { + html { + font-size: 14px; + } } +html:before { + content: ' '; + display: block; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background: linear-gradient( + to bottom, + rgba(18, 16, 16, 0) 50%, + rgba(0, 0, 0, 0.25) 50% + ); + background-size: 100% 3px; + z-index: 99; + pointer-events: none; +} .separator-wrap { width: 100%; @@ -221,11 +250,33 @@ header > h2 { padding-right: 25px; } +.hidden { + display: none !important; +} + +@keyframes blink-animation { + to { + opacity: 0; + } +} + +.cursor { + background-color: var(--primary-fg); + animation-name: blink-animation; + animation-duration: 500ms; + animation-iteration-count: infinite; + animation-timing-function: cubic-bezier(0.83,-0.22, 0.54, 0.55); +} + @media screen and (min-width: 1000px) { - .container { + .container, [class^=err-] { padding-left: 215px; padding-right: 215px; } + + .pad-left, .pad-right { + width: 215px; + } } .post-header { @@ -269,6 +320,11 @@ header > h2 { margin-bottom: 15px; } +.post-header-preview .tags { + margin-top: 5px; + margin-bottom: 5px; +} + .tag { background-color: var(--primary-fg); color: var(--primary-bg); @@ -277,3 +333,27 @@ header > h2 { padding-top: 1.5px; padding-bottom: 1.5px; } + +.post-header-preview .tag { + font-size: 0.75rem; +} + +::-webkit-scrollbar { + width: 10px; + height: 10px; +} +::-webkit-scrollbar-thumb { + background: var(--primary-fg); + border-radius: 8px; +} +::-webkit-scrollbar-thumb:hover{ + background: var(--primary-bg); +} +::-webkit-scrollbar-track{ + background: var(--primary-bg); + border-radius: 7px; +} + +::-webkit-scrollbar-track:hover { + background: var(--primary-fg); +} diff --git a/utils/parse-article.sh b/utils/parse-article.sh new file mode 100755 index 0000000..c3e540a --- /dev/null +++ b/utils/parse-article.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +ARTICLE_TITLE=$(basename "$ARTICLE_FILE") + +# Pick all file content +ARTICLE_FILE_CONTENT=$(cat "$ARTICLE_FILE") + +# The first 3 lines of the file are metadata information +ARTICLE_METADATA=$(echo "$ARTICLE_FILE_CONTENT" | head -n 3) + +# Skip file metadata, first 3 lines +ARTICLE_CONTENT=$(echo "$ARTICLE_FILE_CONTENT" | tail -n +3) + +# Tags are placed on the first line of the file, delimited by commas +IFS=',' read -ra ARTICLE_TAGS <<< "$(echo "$ARTICLE_METADATA" | head -n 1)" + +ARTICLE_TIME=$(stat --format="Criado em: %w<br/>Última atualização: %z" "$ARTICLE_FILE") +ARTICLE_BYTES=$(stat --format="%s bytes" "$ARTICLE_FILE") |