rawfeed 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.txt +21 -0
- data/README.md +153 -1
- data/_data/resume.yml +184 -0
- data/_includes/alert +3 -0
- data/_includes/chart +34 -0
- data/_includes/details +57 -0
- data/_includes/enddetails +2 -0
- data/_includes/endtabs +2 -0
- data/_includes/image +61 -0
- data/_includes/layout/blog_search.html +18 -0
- data/_includes/layout/data.liquid +3 -0
- data/_includes/layout/disqus.html +30 -0
- data/_includes/layout/footer.html +34 -0
- data/_includes/layout/giscus.html +21 -0
- data/_includes/layout/google_analytics.html +11 -0
- data/_includes/layout/head.html +59 -0
- data/_includes/layout/header.html +143 -0
- data/_includes/layout/maintenance.html +30 -0
- data/_includes/layout/paginator.html +35 -0
- data/_includes/socials +22 -0
- data/_includes/tabs +94 -0
- data/_includes/toc +160 -0
- data/_includes/video +10 -0
- data/_layouts/blog.html +46 -0
- data/_layouts/contact.html +285 -0
- data/_layouts/default.html +248 -0
- data/_layouts/error.html +15 -0
- data/_layouts/home.html +58 -0
- data/_layouts/page.html +9 -0
- data/_layouts/post.html +103 -0
- data/_layouts/resume.html +260 -0
- data/_layouts/tag.html +22 -0
- data/_layouts/tag_posts.html +27 -0
- data/_sass/base/_index.scss +63 -0
- data/_sass/base/_reset.scss +10 -0
- data/_sass/base/_typography.scss +0 -0
- data/_sass/components/_badges.scss +24 -0
- data/_sass/components/_button.scss +17 -0
- data/_sass/components/_forms.scss +42 -0
- data/_sass/components/_gifs.scss +5 -0
- data/_sass/components/_index.scss +5 -0
- data/_sass/components/_markdown.scss +453 -0
- data/_sass/includes/_footer.scss +45 -0
- data/_sass/includes/_header.scss +240 -0
- data/_sass/includes/_highlight.scss +87 -0
- data/_sass/includes/_index.scss +9 -0
- data/_sass/includes/_maintenance.scss +16 -0
- data/_sass/includes/_paginator.scss +22 -0
- data/_sass/includes/_rouge-dark.scss +81 -0
- data/_sass/includes/_rouge-light.scss +121 -0
- data/_sass/includes/_terminal.scss +208 -0
- data/_sass/layouts/_blog.scss +96 -0
- data/_sass/layouts/_contact.scss +55 -0
- data/_sass/layouts/_default.scss +14 -0
- data/_sass/layouts/_error.scss +18 -0
- data/_sass/layouts/_home.scss +19 -0
- data/_sass/layouts/_index.scss +10 -0
- data/_sass/layouts/_page.scss +5 -0
- data/_sass/layouts/_post.scss +109 -0
- data/_sass/layouts/_resume.scss +330 -0
- data/_sass/layouts/_tag-posts.scss +48 -0
- data/_sass/layouts/_tag.scss +22 -0
- data/_sass/main.scss +128 -0
- data/_sass/theme/_dark.scss +79 -0
- data/_sass/theme/_index.scss +13 -0
- data/_sass/theme/_light.scss +56 -0
- data/assets/css/style.scss +5 -0
- data/assets/images/avatar_back.png +0 -0
- data/assets/images/avatar_dark.png +0 -0
- data/assets/images/avatar_light.png +0 -0
- data/assets/images/favicon.png +0 -0
- data/assets/js/avatar.js +50 -0
- data/assets/js/blog_search.js +102 -0
- data/assets/js/default.js +148 -0
- data/assets/js/terminal.js +15 -0
- data/assets/js/toc.js +20 -0
- data/assets/json/blog_search.json +16 -0
- data/assets/vendor/bootstrap/css/bootstrap-grid.css +4124 -0
- data/assets/vendor/bootstrap/css/bootstrap-grid.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-grid.min.css +7 -0
- data/assets/vendor/bootstrap/css/bootstrap-grid.min.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-grid.rtl.css +4123 -0
- data/assets/vendor/bootstrap/css/bootstrap-grid.rtl.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-grid.rtl.min.css +7 -0
- data/assets/vendor/bootstrap/css/bootstrap-grid.rtl.min.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-reboot.css +488 -0
- data/assets/vendor/bootstrap/css/bootstrap-reboot.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-reboot.min.css +7 -0
- data/assets/vendor/bootstrap/css/bootstrap-reboot.min.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-reboot.rtl.css +485 -0
- data/assets/vendor/bootstrap/css/bootstrap-reboot.rtl.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-reboot.rtl.min.css +7 -0
- data/assets/vendor/bootstrap/css/bootstrap-reboot.rtl.min.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-utilities.css +4266 -0
- data/assets/vendor/bootstrap/css/bootstrap-utilities.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-utilities.min.css +7 -0
- data/assets/vendor/bootstrap/css/bootstrap-utilities.min.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-utilities.rtl.css +4257 -0
- data/assets/vendor/bootstrap/css/bootstrap-utilities.rtl.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap-utilities.rtl.min.css +7 -0
- data/assets/vendor/bootstrap/css/bootstrap-utilities.rtl.min.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap.css +10878 -0
- data/assets/vendor/bootstrap/css/bootstrap.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap.min.css +7 -0
- data/assets/vendor/bootstrap/css/bootstrap.min.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap.rtl.css +10842 -0
- data/assets/vendor/bootstrap/css/bootstrap.rtl.css.map +1 -0
- data/assets/vendor/bootstrap/css/bootstrap.rtl.min.css +7 -0
- data/assets/vendor/bootstrap/css/bootstrap.rtl.min.css.map +1 -0
- data/assets/vendor/bootstrap/js/bootstrap.bundle.js +7075 -0
- data/assets/vendor/bootstrap/js/bootstrap.bundle.js.map +1 -0
- data/assets/vendor/bootstrap/js/bootstrap.bundle.min.js +7 -0
- data/assets/vendor/bootstrap/js/bootstrap.bundle.min.js.map +1 -0
- data/assets/vendor/bootstrap/js/bootstrap.esm.js +5202 -0
- data/assets/vendor/bootstrap/js/bootstrap.esm.js.map +1 -0
- data/assets/vendor/bootstrap/js/bootstrap.esm.min.js +7 -0
- data/assets/vendor/bootstrap/js/bootstrap.esm.min.js.map +1 -0
- data/assets/vendor/bootstrap/js/bootstrap.js +5249 -0
- data/assets/vendor/bootstrap/js/bootstrap.js.map +1 -0
- data/assets/vendor/bootstrap/js/bootstrap.min.js +7 -0
- data/assets/vendor/bootstrap/js/bootstrap.min.js.map +1 -0
- data/assets/vendor/simple-jekyll-search.js +433 -0
- data/assets/vendor/simple-jekyll-search.min.js +6 -0
- data/lib/rawfeed/draft.rb +31 -0
- data/lib/rawfeed/installer.rb +37 -0
- data/lib/rawfeed/layout.rb +138 -0
- data/lib/rawfeed/page.rb +33 -0
- data/lib/rawfeed/post.rb +60 -0
- data/lib/rawfeed/resume.rb +59 -0
- data/lib/rawfeed/utils.rb +74 -0
- data/lib/rawfeed/version.rb +1 -1
- data/lib/rawfeed.rb +5 -7
- metadata +145 -2
| @@ -0,0 +1,285 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            layout: default
         | 
| 3 | 
            +
            ---
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            {%- if site.google.recaptcha.pubkey and site.google.apps_script.url -%}
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            <script src="https://www.google.com/recaptcha/api.js" async defer></script>
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            <div class="container contact">
         | 
| 10 | 
            +
              <div class="modal fade"
         | 
| 11 | 
            +
              id="contactMessageModal"
         | 
| 12 | 
            +
              tabindex="-1"
         | 
| 13 | 
            +
              aria-labelledby="contactMessageModalLabel"
         | 
| 14 | 
            +
              aria-hidden="true"
         | 
| 15 | 
            +
              data-bs-backdrop="static"
         | 
| 16 | 
            +
              data-bs-keyboard="false">
         | 
| 17 | 
            +
                <div class="modal-dialog">
         | 
| 18 | 
            +
                  <div class="modal-content">
         | 
| 19 | 
            +
                    <div class="modal-header">
         | 
| 20 | 
            +
                      <h5 class="modal-title" id="contactMessageModalLabel"></h5>
         | 
| 21 | 
            +
                      <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
         | 
| 22 | 
            +
                    </div>
         | 
| 23 | 
            +
                    <div class="modal-body"></div>
         | 
| 24 | 
            +
                  </div>
         | 
| 25 | 
            +
                </div>
         | 
| 26 | 
            +
              </div>
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              <div class="row">
         | 
| 29 | 
            +
                <span class="contact-title"><strong>[ {{ page.title | downcase }} ]</strong></span>
         | 
| 30 | 
            +
                <form id="contactForm" class="mt-4 contact-form">
         | 
| 31 | 
            +
                  <div class="mb-3">
         | 
| 32 | 
            +
                    {% comment %} <label for="inputName" class="form-label">{{ site.text.contact.name | default: "Name" | downcase }}</label> {% endcomment %}
         | 
| 33 | 
            +
                    <input id="inputName"
         | 
| 34 | 
            +
                    name="name"
         | 
| 35 | 
            +
                    type="text"
         | 
| 36 | 
            +
                    placeholder="{{ site.text.contact.name.placeholder | default: 'First and last name' }}"
         | 
| 37 | 
            +
                    class="form-control contact-form__name"
         | 
| 38 | 
            +
                    required>
         | 
| 39 | 
            +
                  </div>
         | 
| 40 | 
            +
                  <div class="mb-3">
         | 
| 41 | 
            +
                    {% comment %} <label for="inputEmail" class="form-label">{{ site.text.contact.name | default: "Email address" | downcase }}</label> {% endcomment %}
         | 
| 42 | 
            +
                    <input id="inputEmail"
         | 
| 43 | 
            +
                    name="email"
         | 
| 44 | 
            +
                    type="email"
         | 
| 45 | 
            +
                    placeholder="{{ site.text.contact.email.placeholder | default: 'Your best email address' }}"
         | 
| 46 | 
            +
                    class="form-control contact-form__email"
         | 
| 47 | 
            +
                    aria-describedby="emailHelp"
         | 
| 48 | 
            +
                    required>
         | 
| 49 | 
            +
                    <div id="emailHelp" class="form-text contact-form__help">
         | 
| 50 | 
            +
                      {{ site.text.contact.email.help | default: "We'll never share your email with anyone else." }}
         | 
| 51 | 
            +
                    </div>
         | 
| 52 | 
            +
                  </div>
         | 
| 53 | 
            +
                  <textarea id="textMessage"
         | 
| 54 | 
            +
                  name="message"
         | 
| 55 | 
            +
                  class="form-control contact-form__message"
         | 
| 56 | 
            +
                  placeholder="{{ site.text.contact.message.placeholder | default: 'Write your message here' }}"
         | 
| 57 | 
            +
                  style="min-height: 150px"
         | 
| 58 | 
            +
                  required></textarea>
         | 
| 59 | 
            +
                  <small style="display: block; font-size: 8pt; opacity: .6">{{ site.text.contact.message.caracters.warning.content }}</small>
         | 
| 60 | 
            +
                  <!-- TODO: version: 0.2.0 Make reCaptcha change themes instantly -->
         | 
| 61 | 
            +
                  <div id="g-recaptcha" class="g-recaptcha mt-2" data-sitekey="{{ site.google.recaptcha.pubkey }}" style="display: inline-block; margin: 5px 0;"></div>
         | 
| 62 | 
            +
                  <div class="d-flex justify-content-end mb-5">
         | 
| 63 | 
            +
                    <button id="submitButton"
         | 
| 64 | 
            +
                    type="submit"
         | 
| 65 | 
            +
                    class="btn contact-form__submit">
         | 
| 66 | 
            +
                    {{ site.text.contact.button.text | default: 'Send!' }}
         | 
| 67 | 
            +
                    </button>
         | 
| 68 | 
            +
                  </div>
         | 
| 69 | 
            +
                </form>
         | 
| 70 | 
            +
              </div>
         | 
| 71 | 
            +
              <div class="row">
         | 
| 72 | 
            +
                <div class="contact-content">{{ content }}</div>
         | 
| 73 | 
            +
              </div>
         | 
| 74 | 
            +
            </div>
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            <script>
         | 
| 77 | 
            +
              const form = document.getElementById("contactForm");
         | 
| 78 | 
            +
              const submitButton = document.getElementById("submitButton");
         | 
| 79 | 
            +
              const endpoint = "{{ site.google.apps_script.url }}"; // URL Google Apps Script
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              // get modal
         | 
| 82 | 
            +
              function showModal(title, message, type = 'success') {
         | 
| 83 | 
            +
                const modalEl = document.getElementById('contactMessageModal');
         | 
| 84 | 
            +
                const modalTitle = modalEl.querySelector('.modal-title');
         | 
| 85 | 
            +
                const modalBody = modalEl.querySelector('.modal-body');
         | 
| 86 | 
            +
                const modalContent = modalEl.querySelector('.modal-content');
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                modalContent.classList.remove('contact-message-success', 'contact-message-error', 'contact-message-warning');
         | 
| 89 | 
            +
             | 
| 90 | 
            +
             | 
| 91 | 
            +
                // Apply the color according to the type
         | 
| 92 | 
            +
                if (type === 'success') {
         | 
| 93 | 
            +
                  modalContent.classList.add('contact-message-success');
         | 
| 94 | 
            +
                } else if (type === 'error') {
         | 
| 95 | 
            +
                  modalContent.classList.add('contact-message-error');
         | 
| 96 | 
            +
                } else if (type === 'warning') {
         | 
| 97 | 
            +
                  modalContent.classList.add('contact-message-warning');
         | 
| 98 | 
            +
                }
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                modalTitle.innerHTML = title;
         | 
| 101 | 
            +
                modalBody.innerHTML = message;
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                const bsModal = new bootstrap.Modal(modalEl);
         | 
| 104 | 
            +
                bsModal.show();
         | 
| 105 | 
            +
              }
         | 
| 106 | 
            +
             | 
| 107 | 
            +
              form.addEventListener("submit", async (e) => {
         | 
| 108 | 
            +
                e.preventDefault();
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                const recaptchaResponse = grecaptcha.getResponse();
         | 
| 111 | 
            +
                if (!recaptchaResponse) {
         | 
| 112 | 
            +
                  showModal(
         | 
| 113 | 
            +
                    "{{ site.text.contact.recaptcha.warning.title | default: 'Warning' }}",
         | 
| 114 | 
            +
                    "{{ site.text.contact.recaptcha.warning.content | default: "Please tick the 'I'm not a robot' box." }}",
         | 
| 115 | 
            +
                    "warning"
         | 
| 116 | 
            +
                  );
         | 
| 117 | 
            +
                  return;
         | 
| 118 | 
            +
                }
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                const textarea = document.getElementById('textMessage');
         | 
| 121 | 
            +
                const text = textarea.value.trim();
         | 
| 122 | 
            +
                if (text.length < {{ site.text.contact.message.caracters.min }}) {
         | 
| 123 | 
            +
                  showModal(
         | 
| 124 | 
            +
                    "{{ site.text.contact.message.caracters.warning.title | default: 'Warning' }}",
         | 
| 125 | 
            +
                    "{{ site.text.contact.message.caracters.warning.content | default: "The message must have at least 50 characters." }}",
         | 
| 126 | 
            +
                    "warning"
         | 
| 127 | 
            +
                  );
         | 
| 128 | 
            +
                  return;
         | 
| 129 | 
            +
                }
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                submitButton.disabled = true;
         | 
| 132 | 
            +
                submitButton.textContent = "{{ site.text.contact.message.status | default: "Sending...Wait" }}";
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                const formData = new FormData(form);
         | 
| 135 | 
            +
                const data = Object.fromEntries(formData.entries());
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                try {
         | 
| 138 | 
            +
                  const response = await fetch(endpoint, {
         | 
| 139 | 
            +
                    method: "POST",
         | 
| 140 | 
            +
                    redirect: "follow",
         | 
| 141 | 
            +
                    body: JSON.stringify(data)
         | 
| 142 | 
            +
                  });
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                  const result = await response.json();
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                  if (result.result === 'success') {
         | 
| 147 | 
            +
                    form.reset();
         | 
| 148 | 
            +
                    grecaptcha.reset();
         | 
| 149 | 
            +
                    showModal(
         | 
| 150 | 
            +
                      "{{ site.text.contact.message.success.title | default: 'Message Sent' }}",
         | 
| 151 | 
            +
                      "{{ site.text.contact.message.success.content | default: 'Your message has been sent successfully!' }}",
         | 
| 152 | 
            +
                      "success"
         | 
| 153 | 
            +
                    );
         | 
| 154 | 
            +
                  } else {
         | 
| 155 | 
            +
                    showModal(
         | 
| 156 | 
            +
                      "{{ site.text.contact.message.error.title | default: 'Error' }}",
         | 
| 157 | 
            +
                      "{{ site.text.contact.message.error.content | default: 'Something went wrong while sending your message.' }}",
         | 
| 158 | 
            +
                      "error"
         | 
| 159 | 
            +
                    );
         | 
| 160 | 
            +
                    throw new Error(result.message || "{{ site.text.contact.message.error.content | default: "An unknown error has occurred." }}");
         | 
| 161 | 
            +
                  }
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                } catch (error) {
         | 
| 164 | 
            +
                  console.error("Error sending:", error);
         | 
| 165 | 
            +
                  if (error.message.includes("reCAPTCHA")) {
         | 
| 166 | 
            +
                      showModal(
         | 
| 167 | 
            +
                        "{{ site.text.contact.message.error.title | default: 'Error' }}",
         | 
| 168 | 
            +
                        "{{ site.text.contact.recaptcha.fail | default: "Verification failed. Please reload the page and try again." }}",
         | 
| 169 | 
            +
                        "error"
         | 
| 170 | 
            +
                      );
         | 
| 171 | 
            +
                  } else {
         | 
| 172 | 
            +
                      showModal(
         | 
| 173 | 
            +
                        "{{ site.text.contact.message.error.title | default: 'Error' }}",
         | 
| 174 | 
            +
                        "{{ site.text.contact.recaptcha.error | default: "An error occurred while sending the message. Please try again." }}",
         | 
| 175 | 
            +
                        "error"
         | 
| 176 | 
            +
                      );
         | 
| 177 | 
            +
                  }
         | 
| 178 | 
            +
                  grecaptcha.reset();
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                } finally {
         | 
| 181 | 
            +
                  submitButton.disabled = false;
         | 
| 182 | 
            +
                  submitButton.textContent = "{{ site.text.contact.button.text | default: "Send!" }}";
         | 
| 183 | 
            +
                }
         | 
| 184 | 
            +
              });
         | 
| 185 | 
            +
            </script>
         | 
| 186 | 
            +
            {%- else -%}
         | 
| 187 | 
            +
             | 
| 188 | 
            +
            <div class="contat-disabled">
         | 
| 189 | 
            +
              <h1 style="background-color: yellow;color: black;padding: 10px">Warning: Email form disabled</h1>
         | 
| 190 | 
            +
              <p>To use the email submission form, you need to:</p>
         | 
| 191 | 
            +
              <p>1 - Copy the script below and implement it in <a href="https://script.google.com" target="_blank">Google Apps Script</a>:</p>
         | 
| 192 | 
            +
              <blockquote>
         | 
| 193 | 
            +
                <p>Note1: Don't forget to put your gmail in the script.</p>
         | 
| 194 | 
            +
                <p>Note2: Without editing the script in Google Apps Script, you need to deploy it again.</p>
         | 
| 195 | 
            +
              </blockquote>
         | 
| 196 | 
            +
             | 
| 197 | 
            +
            {% highlight javascript linenos %}
         | 
| 198 | 
            +
            // IMPORTANT! You must put your gmail email here.
         | 
| 199 | 
            +
            const TO_ADDRESS = "YOUR EMAIL GMAIL";
         | 
| 200 | 
            +
             | 
| 201 | 
            +
            // Get the secret key from the Script Properties
         | 
| 202 | 
            +
            const RECAPTCHA_SECRET_KEY = PropertiesService.getScriptProperties().getProperty('RECAPTCHA_SECRET_KEY');
         | 
| 203 | 
            +
             | 
| 204 | 
            +
            // Function to validate the reCAPTCHA token
         | 
| 205 | 
            +
            function validateRecaptcha(token) {
         | 
| 206 | 
            +
              if (!token) {
         | 
| 207 | 
            +
                throw new Error("Missing reCAPTCHA token.");
         | 
| 208 | 
            +
              }
         | 
| 209 | 
            +
              const url = "https://www.google.com/recaptcha/api/siteverify";
         | 
| 210 | 
            +
              const payload = {
         | 
| 211 | 
            +
                secret: RECAPTCHA_SECRET_KEY,
         | 
| 212 | 
            +
                response: token
         | 
| 213 | 
            +
              };
         | 
| 214 | 
            +
             | 
| 215 | 
            +
              const response = UrlFetchApp.fetch(url, {
         | 
| 216 | 
            +
                method: "post",
         | 
| 217 | 
            +
                payload: payload
         | 
| 218 | 
            +
              });
         | 
| 219 | 
            +
             | 
| 220 | 
            +
              const result = JSON.parse(response.getContentText());
         | 
| 221 | 
            +
             | 
| 222 | 
            +
              if (!result.success) {
         | 
| 223 | 
            +
                throw new Error("reCAPTCHA verification failed: " + (result['error-codes'] || 'Unknown error.'));
         | 
| 224 | 
            +
              }
         | 
| 225 | 
            +
             | 
| 226 | 
            +
              return true;
         | 
| 227 | 
            +
            }
         | 
| 228 | 
            +
             | 
| 229 | 
            +
             | 
| 230 | 
            +
            function doPost(e) {
         | 
| 231 | 
            +
              try {
         | 
| 232 | 
            +
                const data = JSON.parse(e.postData.contents);
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                // 1. Validate the reCAPTCHA token first!
         | 
| 235 | 
            +
                validateRecaptcha(data['g-recaptcha-response']);
         | 
| 236 | 
            +
             | 
| 237 | 
            +
                // 2. If validation passed, continue with the rest of the code
         | 
| 238 | 
            +
                const { name, email, message } = data;
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                if (!name || !email || !message) {
         | 
| 241 | 
            +
                  throw new Error("Missing form data.");
         | 
| 242 | 
            +
                }
         | 
| 243 | 
            +
             | 
| 244 | 
            +
                const subject = "New website message " + name;
         | 
| 245 | 
            +
                const htmlBody = `
         | 
| 246 | 
            +
                  <p>You have received a new message from your website.:</p><hr>
         | 
| 247 | 
            +
                  <p><b>Name:</b> ${name}</p>
         | 
| 248 | 
            +
                  <p><b>Email:</b> <a href="mailto:${email}">${email}</a></p>
         | 
| 249 | 
            +
                  <p><b>Message:</b></p>
         | 
| 250 | 
            +
                  <p style="white-space: pre-wrap;">${message}</p><hr>
         | 
| 251 | 
            +
                `;
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                MailApp.sendEmail({
         | 
| 254 | 
            +
                  to: TO_ADDRESS,
         | 
| 255 | 
            +
                  subject: subject,
         | 
| 256 | 
            +
                  htmlBody: htmlBody,
         | 
| 257 | 
            +
                  replyTo: email
         | 
| 258 | 
            +
                });
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                return ContentService
         | 
| 261 | 
            +
                  .createTextOutput(JSON.stringify({ 'result': 'success', 'message': 'Message sent!' }))
         | 
| 262 | 
            +
                  .setMimeType(ContentService.MimeType.JSON);
         | 
| 263 | 
            +
             | 
| 264 | 
            +
              } catch (err) {
         | 
| 265 | 
            +
                Logger.log(err.toString());
         | 
| 266 | 
            +
                return ContentService
         | 
| 267 | 
            +
                  .createTextOutput(JSON.stringify({ 'result': 'error', 'message': err.toString() }))
         | 
| 268 | 
            +
                  .setMimeType(ContentService.MimeType.JSON);
         | 
| 269 | 
            +
              }
         | 
| 270 | 
            +
            }
         | 
| 271 | 
            +
            {% endhighlight %}
         | 
| 272 | 
            +
              <p>2 - Create a <a href="https://console.cloud.google.com/security/recaptcha" target="_blank">reCaptcha</a>
         | 
| 273 | 
            +
              on Google and add the reCaptcha <strong>PRIVATE</strong> key to the <strong>Google Apps Script</strong> script property.</p>
         | 
| 274 | 
            +
              <p>3 - Copy the reCaptcha <strong>PUBLIC</strong> key and the <strong>Google Apps Script</strong> URL and place them in <strong>_config.yml:</strong></p>
         | 
| 275 | 
            +
              {% highlight yml linenos %}
         | 
| 276 | 
            +
            google:
         | 
| 277 | 
            +
              ###
         | 
| 278 | 
            +
              ###
         | 
| 279 | 
            +
              apps_script:
         | 
| 280 | 
            +
                url: "https://script.google.com/macros/s/BuD..."
         | 
| 281 | 
            +
              recaptcha:
         | 
| 282 | 
            +
                pubkey: "8Lci194rAAAAA70Sv..."
         | 
| 283 | 
            +
              {% endhighlight %}
         | 
| 284 | 
            +
            </div>
         | 
| 285 | 
            +
            {%- endif -%}
         | 
| @@ -0,0 +1,248 @@ | |
| 1 | 
            +
            <!-- Theme for Jekyll.rb by: © William C. Canin -->
         | 
| 2 | 
            +
            {%- if site.maintenance.enable -%}
         | 
| 3 | 
            +
              {%- include layout/maintenance.html -%}
         | 
| 4 | 
            +
            {%- else -%}
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            {%- assign index = site.pages | where: "path", "index.md" | first -%}
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            <!DOCTYPE html>
         | 
| 9 | 
            +
            <html id="top" lang="{{ site.lang | default: 'en-US' }}" data-theme="light">
         | 
| 10 | 
            +
              {%- include layout/head.html -%}
         | 
| 11 | 
            +
              <body data-layout="{{ page.layout | default: '' }}" data-terminal-enabled="{{ site.home.terminal.enable | default: false }}">
         | 
| 12 | 
            +
                {%- if site.home.terminal.enable and page.url == "/" and index.layout == "home" -%}
         | 
| 13 | 
            +
                  <div class="default default-terminal" style="max-width: {{ site.layout.width | default: '780px' }} !important;">
         | 
| 14 | 
            +
                    {%- include layout/header.html -%}
         | 
| 15 | 
            +
                  </div>
         | 
| 16 | 
            +
                  <main class="content">{{ content }}</main>
         | 
| 17 | 
            +
                  {%- include layout/footer.html -%}
         | 
| 18 | 
            +
                {%- else -%}
         | 
| 19 | 
            +
                  <div class="default" style="max-width: {{ site.layout.width | default: '780px' }} !important;">
         | 
| 20 | 
            +
                    {%- include layout/header.html -%}
         | 
| 21 | 
            +
                    <main class="content">{{ content }}</main>
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    {%- include layout/footer.html -%}
         | 
| 24 | 
            +
                  </div>
         | 
| 25 | 
            +
                {%- endif -%}
         | 
| 26 | 
            +
              </body>
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              <!-- Scripts -->
         | 
| 29 | 
            +
              <script src="{{ '/assets/vendor/bootstrap/js/bootstrap.bundle.js' | relative_url }}"></script>
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              {%- if page.comments != false and site.blog.post.comments.provider == 'giscus' -%}
         | 
| 32 | 
            +
              <script>
         | 
| 33 | 
            +
                window.giscusThemes = {
         | 
| 34 | 
            +
                  light: "{{ site.blog.post.comments.giscus.theme_light | default: 'light' }}",
         | 
| 35 | 
            +
                  dark: "{{ site.blog.post.comments.giscus.theme_dark | default: 'dark' }}"
         | 
| 36 | 
            +
                };
         | 
| 37 | 
            +
              </script>
         | 
| 38 | 
            +
              {%- endif -%}
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              <script src="{{ '/assets/js/default.js' | relative_url }}"></script>
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              {%- if page.url == '/' and site.home.terminal.enable -%}
         | 
| 43 | 
            +
                <script src="{{ '/assets/js/terminal.js' | relative_url }}"></script>
         | 
| 44 | 
            +
              {%- endif -%}
         | 
| 45 | 
            +
             | 
| 46 | 
            +
              {%- if site.avatar.open -%}
         | 
| 47 | 
            +
                <script src="{{ '/assets/js/avatar.js' | relative_url }}"></script>
         | 
| 48 | 
            +
              {%- endif -%}
         | 
| 49 | 
            +
             | 
| 50 | 
            +
              {%- if site.blog.search.enable -%}
         | 
| 51 | 
            +
                {%- if page.url == '/blog/' or page.url == '/blog/index.html' -%}
         | 
| 52 | 
            +
                  <script src="{{ '/assets/vendor/simple-jekyll-search.min.js' | relative_url }}"></script>
         | 
| 53 | 
            +
                  <script src="{{ '/assets/js/blog_search.js' | relative_url }}"></script>
         | 
| 54 | 
            +
                  <script>
         | 
| 55 | 
            +
                    var sjs = SimpleJekyllSearch({
         | 
| 56 | 
            +
                      searchInput: document.getElementById('blog-search__input'),
         | 
| 57 | 
            +
                      resultsContainer: document.getElementById('blog-search__results'),
         | 
| 58 | 
            +
                      searchResultTemplate: '<li><span class="blog-list__meta"><time datetime="{date}">{date}</time></span> »  <a class="blog-list__link" href="{{ site.url }}{url}">{title}</a></li>',
         | 
| 59 | 
            +
                      noResultsText: '<p>{{ site.text.blog.no_results | default: "No results found" }}</p>',
         | 
| 60 | 
            +
                      json: '/assets/json/blog_search.json'
         | 
| 61 | 
            +
                    })
         | 
| 62 | 
            +
                  </script>
         | 
| 63 | 
            +
                {%- endif -%}
         | 
| 64 | 
            +
              {%- endif -%}
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              {%- if site.home.terminal.enable and page.url == "/" -%}
         | 
| 67 | 
            +
              <script>
         | 
| 68 | 
            +
                document.addEventListener("DOMContentLoaded", () => {
         | 
| 69 | 
            +
                  const screen = document.getElementById('screen');
         | 
| 70 | 
            +
                  const terminal = document.getElementById("terminal");
         | 
| 71 | 
            +
                  const socialsEl = document.getElementById("terminal-screen--socials");
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                  const commands = {
         | 
| 74 | 
            +
                    help: `{{ site.text.home.terminal.commands }}`,
         | 
| 75 | 
            +
                    about: document.getElementById("home-content").innerHTML,
         | 
| 76 | 
            +
                    socials: socialsEl ? socialsEl.innerHTML : `{{ site.text.home.terminal.no_socials }}`,
         | 
| 77 | 
            +
                  };
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  function createInputLine() {
         | 
| 80 | 
            +
                    const line = document.createElement('div');
         | 
| 81 | 
            +
                    line.className = 'line';
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    const prompt = document.createElement('span');
         | 
| 84 | 
            +
                    prompt.className = 'prompt';
         | 
| 85 | 
            +
                    prompt.textContent = `[{{ site.text.home.terminal.user }}@{{ site.text.home.terminal.hostname }}:~]$`;
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    // wrapper para conter input, cursor e measure
         | 
| 88 | 
            +
                    const wrapper = document.createElement('span');
         | 
| 89 | 
            +
                    wrapper.className = 'input-wrapper';
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    const input = document.createElement('input');
         | 
| 92 | 
            +
                    input.type = 'text';
         | 
| 93 | 
            +
                    input.className = 'input';
         | 
| 94 | 
            +
                    input.placeholder = `{{ site.text.home.terminal.welcome }}`;
         | 
| 95 | 
            +
                    input.spellcheck = false;
         | 
| 96 | 
            +
                    input.autocomplete = 'off';
         | 
| 97 | 
            +
                    input.autocorrect = 'off';
         | 
| 98 | 
            +
                    input.autocapitalize = 'off';
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    const cursor = document.createElement('span');
         | 
| 101 | 
            +
                    cursor.className = 'cursor';
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    const measure = document.createElement('span');
         | 
| 104 | 
            +
                    measure.className = 'measure';
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    wrapper.appendChild(input);
         | 
| 107 | 
            +
                    wrapper.appendChild(cursor);
         | 
| 108 | 
            +
                    wrapper.appendChild(measure);
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                    line.appendChild(prompt);
         | 
| 111 | 
            +
                    line.appendChild(wrapper);
         | 
| 112 | 
            +
                    screen.appendChild(line);
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                    input.focus();
         | 
| 115 | 
            +
                    screen.scrollTop = screen.scrollHeight;
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    // Updates the fake cursor position based on the input's selectionStart
         | 
| 118 | 
            +
                    function updateCursor() {
         | 
| 119 | 
            +
                      const sel = input.selectionStart || 0;
         | 
| 120 | 
            +
                      // measure the text to the position of the caret
         | 
| 121 | 
            +
                      measure.textContent = input.value.slice(0, sel);
         | 
| 122 | 
            +
                      const textWidth = measure.offsetWidth; // largura do texto sem scroll
         | 
| 123 | 
            +
                      const visibleLeft = textWidth - input.scrollLeft;
         | 
| 124 | 
            +
                      cursor.style.left = visibleLeft + 'px';
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                      // ensure the caret is visible (for long texts): adjust input's scrollLeft
         | 
| 127 | 
            +
                      const paddingRight = 10;
         | 
| 128 | 
            +
                      if (textWidth - input.scrollLeft > input.clientWidth - paddingRight) {
         | 
| 129 | 
            +
                        input.scrollLeft = textWidth - input.clientWidth + paddingRight;
         | 
| 130 | 
            +
                        cursor.style.left = (textWidth - input.scrollLeft) + 'px';
         | 
| 131 | 
            +
                      } else if (textWidth < input.scrollLeft) {
         | 
| 132 | 
            +
                        input.scrollLeft = textWidth;
         | 
| 133 | 
            +
                        cursor.style.left = (textWidth - input.scrollLeft) + 'px';
         | 
| 134 | 
            +
                      }
         | 
| 135 | 
            +
                    }
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    // show/hide cursor animation as focus changes
         | 
| 138 | 
            +
                    function onFocus() { cursor.style.opacity = '1'; updateCursor(); }
         | 
| 139 | 
            +
                    function onBlur()  { cursor.style.opacity = '0'; }
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    input.addEventListener('input', updateCursor);
         | 
| 142 | 
            +
                    input.addEventListener('keydown', (e) => {
         | 
| 143 | 
            +
                      // Update position on keys that do not trigger input immediately (arrows, delete, etc.)
         | 
| 144 | 
            +
                      setTimeout(updateCursor, 0);
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                      if (e.key === 'Enter') {
         | 
| 147 | 
            +
                        e.preventDefault();
         | 
| 148 | 
            +
                        const cmd = input.value.trim().toLowerCase();
         | 
| 149 | 
            +
                        if (cmd) {
         | 
| 150 | 
            +
                          // remove input/cursor/measure and place fixed text
         | 
| 151 | 
            +
                          wrapper.removeChild(input);
         | 
| 152 | 
            +
                          wrapper.removeChild(cursor);
         | 
| 153 | 
            +
                          wrapper.removeChild(measure);
         | 
| 154 | 
            +
                          const cmdText = document.createElement('span');
         | 
| 155 | 
            +
                          cmdText.textContent = cmd;
         | 
| 156 | 
            +
                          wrapper.appendChild(cmdText);
         | 
| 157 | 
            +
                          processCommand(cmd);
         | 
| 158 | 
            +
                        } else {
         | 
| 159 | 
            +
                          // if you enter without command, it just creates a new empty line (with prompt)
         | 
| 160 | 
            +
                          wrapper.removeChild(input);
         | 
| 161 | 
            +
                          wrapper.removeChild(cursor);
         | 
| 162 | 
            +
                          wrapper.removeChild(measure);
         | 
| 163 | 
            +
                          const blank = document.createElement('span');
         | 
| 164 | 
            +
                          blank.textContent = '';
         | 
| 165 | 
            +
                          wrapper.appendChild(blank);
         | 
| 166 | 
            +
                        }
         | 
| 167 | 
            +
                        // New linr input
         | 
| 168 | 
            +
                        createInputLine();
         | 
| 169 | 
            +
                      } else if (e.key === 'Escape') {
         | 
| 170 | 
            +
                        e.preventDefault();
         | 
| 171 | 
            +
                        screen.innerHTML = '';
         | 
| 172 | 
            +
                        createInputLine();
         | 
| 173 | 
            +
                      }
         | 
| 174 | 
            +
                    });
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                    // arrows, mouse click, mouseup (position caret), etc.
         | 
| 177 | 
            +
                    input.addEventListener('keyup', updateCursor);
         | 
| 178 | 
            +
                    input.addEventListener('click', () => {
         | 
| 179 | 
            +
                      // updates after click (selectionStart already set)
         | 
| 180 | 
            +
                      setTimeout(updateCursor, 0);
         | 
| 181 | 
            +
                    });
         | 
| 182 | 
            +
                    input.addEventListener('mouseup', () => setTimeout(updateCursor, 0));
         | 
| 183 | 
            +
                    input.addEventListener('focus', onFocus);
         | 
| 184 | 
            +
                    input.addEventListener('blur', onBlur);
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                    updateCursor();
         | 
| 187 | 
            +
                  }
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                  // processes commands
         | 
| 190 | 
            +
                  function processCommand(cmd) {
         | 
| 191 | 
            +
                    switch(true){
         | 
| 192 | 
            +
                      case cmd === 'help': commandsPrint(commands.help, mode='html'); break;
         | 
| 193 | 
            +
                      case cmd === 'date': commandsPrint(new Date().toString(), mode='text'); break;
         | 
| 194 | 
            +
                      // case cmd.startsWith('echo '): commandsPrint(cmd.split(' ').slice(1).join(' ')); break;
         | 
| 195 | 
            +
                      case cmd === 'about': writeLineHTML(commands.about); break;
         | 
| 196 | 
            +
                      case cmd === 'socials': writeLineHTML(commands.socials); break;
         | 
| 197 | 
            +
                      case cmd === 'clear': screen.innerHTML=''; break;
         | 
| 198 | 
            +
                      default: if(cmd) commandsPrint(cmd + `{{ site.text.home.terminal.error }}`, mode='text');
         | 
| 199 | 
            +
                    }
         | 
| 200 | 
            +
                  }
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                  function writeLineHTML(content, mode = 'html') {
         | 
| 203 | 
            +
                    const wrapper = document.createElement('div');
         | 
| 204 | 
            +
                    wrapper.className = 'line-wrapper';
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                    if (mode === 'html') {
         | 
| 207 | 
            +
                      wrapper.innerHTML = content;
         | 
| 208 | 
            +
                    } else {
         | 
| 209 | 
            +
                      wrapper.textContent = content;
         | 
| 210 | 
            +
                    }
         | 
| 211 | 
            +
                    screen.appendChild(wrapper);
         | 
| 212 | 
            +
                    screen.scrollTop = screen.scrollHeight;
         | 
| 213 | 
            +
                  }
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                  function commandsPrint(text, mode = 'html') {
         | 
| 216 | 
            +
                    // creates the wrapper to group all the lined
         | 
| 217 | 
            +
                    const wrapper = document.createElement('div');
         | 
| 218 | 
            +
                    wrapper.className = 'line-wrapper';
         | 
| 219 | 
            +
             | 
| 220 | 
            +
                    text.split('\n').forEach((t) => {
         | 
| 221 | 
            +
                      const line = document.createElement('div');
         | 
| 222 | 
            +
                      line.className = 'line';
         | 
| 223 | 
            +
                      if (mode === 'html') {
         | 
| 224 | 
            +
                        line.innerHTML = t;
         | 
| 225 | 
            +
                      } else {
         | 
| 226 | 
            +
                        line.textContent = t;
         | 
| 227 | 
            +
                      }
         | 
| 228 | 
            +
                      wrapper.appendChild(line);
         | 
| 229 | 
            +
                    });
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                    screen.appendChild(wrapper);
         | 
| 232 | 
            +
                    screen.scrollTop = screen.scrollHeight;
         | 
| 233 | 
            +
                  }
         | 
| 234 | 
            +
             | 
| 235 | 
            +
                  // start terminal
         | 
| 236 | 
            +
                  createInputLine();
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                  // when clicking on the terminal, it always focuses on the last existing input
         | 
| 239 | 
            +
                  terminal.addEventListener("click", (e) => {
         | 
| 240 | 
            +
                    // avoids focusing when clicking a header button, etc.
         | 
| 241 | 
            +
                    const lastInput = screen.querySelector('.input:last-of-type');
         | 
| 242 | 
            +
                    if (lastInput) lastInput.focus();
         | 
| 243 | 
            +
                  });
         | 
| 244 | 
            +
                });
         | 
| 245 | 
            +
              </script>
         | 
| 246 | 
            +
              {%- endif -%}
         | 
| 247 | 
            +
            </html>
         | 
| 248 | 
            +
            {%- endif -%}
         | 
    
        data/_layouts/error.html
    ADDED
    
    | @@ -0,0 +1,15 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            layout: default
         | 
| 3 | 
            +
            ---
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            <div class="container error">
         | 
| 6 | 
            +
              <div class="row">
         | 
| 7 | 
            +
                <div class="col-sm text-center">
         | 
| 8 | 
            +
                   {%- if site.text.error.image -%}
         | 
| 9 | 
            +
                      <img class="error-image" src="{{ site.text.error.image }}" alt="error 404">
         | 
| 10 | 
            +
                   {%- endif -%}
         | 
| 11 | 
            +
                  <h1 class="error-title">{{ site.text.error.title | escape }}</h1>
         | 
| 12 | 
            +
                  <h2 class="error-description">{{ site.text.error.message | markdownify }}</h2>
         | 
| 13 | 
            +
                </div>
         | 
| 14 | 
            +
              </div>
         | 
| 15 | 
            +
            </div>
         | 
    
        data/_layouts/home.html
    ADDED
    
    | @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            layout: default
         | 
| 3 | 
            +
            ---
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            {%- if site.home.terminal.enable and page.url == "/" -%}
         | 
| 6 | 
            +
            <div class="d-flex justify-content-center align-items-center mb-5 home home-terminal">
         | 
| 7 | 
            +
              <div id="terminal" class="terminal">
         | 
| 8 | 
            +
                <div class="terminal-header">
         | 
| 9 | 
            +
                  <div class="terminal-header__btn terminal-header__close" title="close"></div>
         | 
| 10 | 
            +
                  <div class="terminal-header__btn terminal-header__min" title="minimize"></div>
         | 
| 11 | 
            +
                  <div class="terminal-header__btn terminal-header__max" title="maximize"></div>
         | 
| 12 | 
            +
                  <div class="terminal-header__title">{{ site.text.home.terminal.user }}@{{ site.text.home.terminal.hostname }}</div>
         | 
| 13 | 
            +
                </div>
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                <div id="screen" class="terminal-screen">
         | 
| 16 | 
            +
                  <div id="home-content" style="display:none !important;">
         | 
| 17 | 
            +
                    {{ content }}
         | 
| 18 | 
            +
                  </div>
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  <div id="terminal-screen--socials" style="display:none">
         | 
| 21 | 
            +
                    {% assign links = site.socials.links %}<strong>. </strong>
         | 
| 22 | 
            +
                    {% for item in links %}
         | 
| 23 | 
            +
                      <a class="socials-link" title="{{ item.title }}" href="{{ item.url }}" target="_blank">
         | 
| 24 | 
            +
                        {{ item.title }}
         | 
| 25 | 
            +
                      </a><strong> . </strong>
         | 
| 26 | 
            +
                    {% endfor %}
         | 
| 27 | 
            +
                  </div>
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                </div>
         | 
| 30 | 
            +
              </div>
         | 
| 31 | 
            +
            </div>
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            <div class="home-text">
         | 
| 34 | 
            +
              <div class="container home">
         | 
| 35 | 
            +
                <div class="row">
         | 
| 36 | 
            +
                  <div class="content" style="{%- if page.text_center -%} text-align: center; {%- endif -%}">
         | 
| 37 | 
            +
                    {{ content }}
         | 
| 38 | 
            +
                  </div>
         | 
| 39 | 
            +
                </div>
         | 
| 40 | 
            +
              </div>
         | 
| 41 | 
            +
              {%- include socials pos="center" -%}
         | 
| 42 | 
            +
            </div>
         | 
| 43 | 
            +
             | 
| 44 | 
            +
            {%- else -%}
         | 
| 45 | 
            +
            <div class="container home">
         | 
| 46 | 
            +
              <div class="row">
         | 
| 47 | 
            +
                <div class="content" style="{%- if page.text_center -%} text-align: center; {%- endif -%}">
         | 
| 48 | 
            +
                  {{ content }}
         | 
| 49 | 
            +
                </div>
         | 
| 50 | 
            +
              </div>
         | 
| 51 | 
            +
            </div>
         | 
| 52 | 
            +
            {%- endif -%}
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            {%- if site.home.terminal.enable == false -%}
         | 
| 55 | 
            +
              {%- if page.url == "/" and site.socials.enable -%}
         | 
| 56 | 
            +
                {%- include socials pos="center" -%}
         | 
| 57 | 
            +
              {%- endif -%}
         | 
| 58 | 
            +
            {%- endif -%}
         |