Refactoring
This commit is contained in:
parent
1e3833f7d0
commit
aa563e60ea
37 changed files with 472 additions and 2392 deletions
|
@ -1,23 +0,0 @@
|
||||||
doctype html
|
|
||||||
html(lang="en")
|
|
||||||
head
|
|
||||||
meta(charset="utf-8")
|
|
||||||
meta(name="viewport" content="width=device-width, initial-scale=1")
|
|
||||||
title Artur Gurgul - #{title}
|
|
||||||
meta(name="author" content="Artur Gurgul")
|
|
||||||
meta(name="description" content="This is my notepad")
|
|
||||||
|
|
||||||
link(rel="shortcut icon" href="/favicon.png")
|
|
||||||
link(rel="alternate" type="application/atom+xml" title="#{site.data.theme.name}" href="#{site.url}/atom.xml")
|
|
||||||
link(rel="stylesheet" href="/static/css/all.css")
|
|
||||||
link(rel="stylesheet" href="/static/css/hightlight.css")
|
|
||||||
body
|
|
||||||
.container
|
|
||||||
.sidebar
|
|
||||||
include sidebar.pug
|
|
||||||
.scroll
|
|
||||||
.content
|
|
||||||
h1.title= title
|
|
||||||
#post!= content
|
|
||||||
.footer
|
|
||||||
include footer.pug
|
|
|
@ -1,2 +0,0 @@
|
||||||
.disclaimer
|
|
||||||
p © Artur Gurgul, 2024 — Public Domain Licence
|
|
|
@ -1,10 +0,0 @@
|
||||||
nav
|
|
||||||
h2(style="font-size: 20px; margin: 0px;") Hi. I'm
|
|
||||||
a(href="/") Artur Gurgul
|
|
||||||
h2(style="font-size: 15px; margin-top: -0.5em;") and this is my notepad.
|
|
||||||
hr.hr-text(data-content="Contents")
|
|
||||||
ul#blog-posts.posts
|
|
||||||
each page in pages.filter(it => it.hidden != true && it.title != undefined )
|
|
||||||
li
|
|
||||||
span »
|
|
||||||
a(href=page.url, style=(false ? "font-weight: bold;" : ""))= page.title
|
|
|
@ -1,7 +0,0 @@
|
||||||
site:
|
|
||||||
title: Artur Gurgul - {{article.title}}
|
|
||||||
remote:
|
|
||||||
type: ssh
|
|
||||||
user: debian
|
|
||||||
host: artur.gurgul.pro
|
|
||||||
path: /var/www/artur.gurgul.pro
|
|
|
@ -1,672 +0,0 @@
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: OpenSans;
|
|
||||||
font-weight: italic;
|
|
||||||
src: url('/media/opensans/opensans-italic-variable_font_wdth,wght.ttf');
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: OpenSans;
|
|
||||||
font-weight: normal;
|
|
||||||
src: url('/media/opensans/opensans-regular-variable_font-wdth,wght.ttf');
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
position: sticky; top:0;
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
position: relative;
|
|
||||||
/* margin: 0 auto; */
|
|
||||||
padding: 0;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
width: 390px;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
body,
|
|
||||||
.content,
|
|
||||||
.container {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body,
|
|
||||||
.container {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
width: 800px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-right: 1px solid #DDD;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
}
|
|
||||||
.scroll {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
overflow-y: scroll
|
|
||||||
}
|
|
||||||
|
|
||||||
article,
|
|
||||||
aside,
|
|
||||||
details,
|
|
||||||
figcaption,
|
|
||||||
figure,
|
|
||||||
footer,
|
|
||||||
header,
|
|
||||||
hgroup,
|
|
||||||
menu,
|
|
||||||
nav,
|
|
||||||
section {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol,
|
|
||||||
ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote,
|
|
||||||
q {
|
|
||||||
quotes: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote:before,
|
|
||||||
blockquote:after,
|
|
||||||
q:before,
|
|
||||||
q:after {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
background: #fff;
|
|
||||||
font: 14px/21px -apple-system, BlinkMacSystemFont, sans-serif;
|
|
||||||
color: #444;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
color: #181818;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 a,
|
|
||||||
h2 a,
|
|
||||||
h3 a,
|
|
||||||
h4 a,
|
|
||||||
h5 a,
|
|
||||||
h6 a {
|
|
||||||
font-weight: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 46px;
|
|
||||||
line-height: 50px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 35px;
|
|
||||||
line-height: 40px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 28px;
|
|
||||||
line-height: 34px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 21px;
|
|
||||||
line-height: 30px;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h5 {
|
|
||||||
font-size: 17px;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h6 {
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 21px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.subheader {
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0 0 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p img {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.lead {
|
|
||||||
font-size: 21px;
|
|
||||||
line-height: 27px;
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
em {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-size: 80%;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote,
|
|
||||||
blockquote p {
|
|
||||||
font-size: 17px;
|
|
||||||
line-height: 24px;
|
|
||||||
color: #777;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px 20px 0 15px;
|
|
||||||
border-left: 2px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote cite {
|
|
||||||
display: block;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote cite:before {
|
|
||||||
content: "\2014 \0020";
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote cite a,
|
|
||||||
blockquote cite a:visited,
|
|
||||||
blockquote cite a:visited {
|
|
||||||
color: #0060ad;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border: solid #ddd;
|
|
||||||
border-width: 1px 0 0;
|
|
||||||
clear: both;
|
|
||||||
margin: 10px 0 30px;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a,
|
|
||||||
a:visited {
|
|
||||||
color: #333;
|
|
||||||
text-decoration: underline;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover,
|
|
||||||
a:focus {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
p a,
|
|
||||||
p a:visited {
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style: none outside;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
list-style: decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol,
|
|
||||||
ul.square,
|
|
||||||
ul.circle,
|
|
||||||
ul.disc {
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.square {
|
|
||||||
list-style: square outside;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.circle {
|
|
||||||
list-style: circle outside;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.disc {
|
|
||||||
list-style: disc outside;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul ul,
|
|
||||||
ul ol,
|
|
||||||
ol ol,
|
|
||||||
ol ul {
|
|
||||||
margin: 4px 0 5px 30px;
|
|
||||||
font-size: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul ul li,
|
|
||||||
ul ol li,
|
|
||||||
ol ol li,
|
|
||||||
ol ul li {
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
line-height: 18px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.large li {
|
|
||||||
line-height: 21px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li p {
|
|
||||||
line-height: 21px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.scale-with-grid {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs {
|
|
||||||
display: block;
|
|
||||||
margin: 0 0 20px 0;
|
|
||||||
padding: 0;
|
|
||||||
border-bottom: solid 1px #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs li {
|
|
||||||
display: block;
|
|
||||||
width: auto;
|
|
||||||
height: 30px;
|
|
||||||
padding: 0;
|
|
||||||
float: left;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs li a {
|
|
||||||
display: block;
|
|
||||||
text-decoration: none;
|
|
||||||
width: auto;
|
|
||||||
height: 29px;
|
|
||||||
padding: 0px 20px;
|
|
||||||
line-height: 30px;
|
|
||||||
border: solid 1px #ddd;
|
|
||||||
border-width: 1px 1px 0 0;
|
|
||||||
margin: 0;
|
|
||||||
background: #f5f5f5;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs li a.active {
|
|
||||||
background: #fff;
|
|
||||||
height: 30px;
|
|
||||||
position: relative;
|
|
||||||
top: -4px;
|
|
||||||
padding-top: 4px;
|
|
||||||
border-left-width: 1px;
|
|
||||||
margin: 0 0 0 -1px;
|
|
||||||
color: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs-content {
|
|
||||||
margin: 0;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs-content>li {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs-content>li.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs:before,
|
|
||||||
ul.tabs:after {
|
|
||||||
content: '\0020';
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
visibility: hidden;
|
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs:after {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.tabs {
|
|
||||||
zoom: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
label span,
|
|
||||||
legend span {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: 13px;
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
thead {
|
|
||||||
border-bottom: solid #0060ad;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #0060ad;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead th {
|
|
||||||
padding-left: 20px;
|
|
||||||
padding-right: 20px;
|
|
||||||
padding-top: 5px;
|
|
||||||
padding-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
tbody td {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-size: 16px;
|
|
||||||
background-color: white;
|
|
||||||
color: #222222;
|
|
||||||
line-height: 24px;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
border-top: 7px solid #0060ad;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
color: #181818;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1.title {
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: 32px;
|
|
||||||
line-height: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 21px;
|
|
||||||
line-height: 24px;
|
|
||||||
margin: 1em 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: 1em 0;
|
|
||||||
list-style: disc;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #0060ad;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: #0060ad;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: #0060ad;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
font-size: inherit;
|
|
||||||
font: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.posts {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.posts {
|
|
||||||
margin-top: 0;
|
|
||||||
list-style-type: none;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.posts li {
|
|
||||||
line-height: 22px;
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul.posts span {
|
|
||||||
font-family: 'Lucida Console', 'Andale Mono', monospace;
|
|
||||||
color: #aaa;
|
|
||||||
padding-right: 5px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.site .footer {
|
|
||||||
font-size: 80%;
|
|
||||||
color: #666;
|
|
||||||
border-top: 4px solid #eee;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
nav h1,
|
|
||||||
nav h2 {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post pre {
|
|
||||||
border: 0px solid #ddd;
|
|
||||||
background-color: #005fad06 !important;
|
|
||||||
padding: 0 .4em;
|
|
||||||
margin-bottom: 20px !important;
|
|
||||||
border-color: #005fad43 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post ul,
|
|
||||||
#post ol {
|
|
||||||
margin-left: 1.35em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post code {
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
background-color: #eef;
|
|
||||||
font-size: 85%;
|
|
||||||
padding: 0 .2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#post pre code {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
padding-top: 25px;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar p {
|
|
||||||
font-weight: 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar a {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
text {
|
|
||||||
font: 500 12px/22px -apple-system, BlinkMacSystemFont, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* path, rect {
|
|
||||||
stroke: red;
|
|
||||||
} */
|
|
||||||
|
|
||||||
g {
|
|
||||||
font: 500 12px/22px -apple-system, BlinkMacSystemFont, sans-serif;
|
|
||||||
}
|
|
||||||
/* stroke-width="2" */
|
|
||||||
|
|
||||||
.content {
|
|
||||||
font: 400 16px/22px -apple-system, BlinkMacSystemFont, sans-serif;
|
|
||||||
|
|
||||||
padding-left: 40px;
|
|
||||||
padding-top: 25px;
|
|
||||||
min-height: 400px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#home h2 {
|
|
||||||
color: #0060ad;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post pre {
|
|
||||||
background-color: white;
|
|
||||||
border-left: 12px solid #eee;
|
|
||||||
padding: 0 .8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#post code {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#stalker {
|
|
||||||
float: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disclaimer {
|
|
||||||
color: #aaa;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: smaller;
|
|
||||||
text-align: center;
|
|
||||||
padding-top: 40px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.highlight {
|
|
||||||
background-color: white;
|
|
||||||
color: #586e75;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight .c {
|
|
||||||
color: #586e75 !important;
|
|
||||||
font-style: italic !important
|
|
||||||
}
|
|
||||||
|
|
||||||
p:has(img) {
|
|
||||||
background-color: #005fad06;
|
|
||||||
border-left: 12px solid #005fad43;
|
|
||||||
}
|
|
||||||
|
|
||||||
p > img {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding-top: 12px;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p:has(svg) {
|
|
||||||
background-color: #005fad06;
|
|
||||||
border-left: 12px solid #005fad43;
|
|
||||||
}
|
|
||||||
|
|
||||||
p > svg {
|
|
||||||
display: block;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding-top: 12px;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
white-space: pre;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
code,
|
|
||||||
pre {
|
|
||||||
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
/*
|
|
||||||
|
|
||||||
XCode style (c) Angel Garcia <angelgarcia.mail@gmail.com>
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
.hljs {
|
|
||||||
background: #fff;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gray DOCTYPE selectors like WebKit */
|
|
||||||
.xml .hljs-meta {
|
|
||||||
color: #c0c0c0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-comment,
|
|
||||||
.hljs-quote {
|
|
||||||
color: #007400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-tag,
|
|
||||||
.hljs-attribute,
|
|
||||||
.hljs-keyword,
|
|
||||||
.hljs-selector-tag,
|
|
||||||
.hljs-literal,
|
|
||||||
.hljs-name {
|
|
||||||
color: #aa0d91;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-variable,
|
|
||||||
.hljs-template-variable {
|
|
||||||
color: #3F6E74;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-code,
|
|
||||||
.hljs-string,
|
|
||||||
.hljs-meta .hljs-string {
|
|
||||||
color: #c41a16;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-regexp,
|
|
||||||
.hljs-link {
|
|
||||||
color: #0E0EFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-title,
|
|
||||||
.hljs-symbol,
|
|
||||||
.hljs-bullet,
|
|
||||||
.hljs-number {
|
|
||||||
color: #1c00cf;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-section,
|
|
||||||
.hljs-meta {
|
|
||||||
color: #643820;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.hljs-title.class_,
|
|
||||||
.hljs-class .hljs-title,
|
|
||||||
.hljs-type,
|
|
||||||
.hljs-built_in,
|
|
||||||
.hljs-params {
|
|
||||||
color: #5c2699;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-attr {
|
|
||||||
color: #836C28;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-subst {
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-formula {
|
|
||||||
background-color: #eee;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-addition {
|
|
||||||
background-color: #baeeba;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-deletion {
|
|
||||||
background-color: #ffc8bd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-selector-id,
|
|
||||||
.hljs-selector-class {
|
|
||||||
color: #9b703f;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-doctag,
|
|
||||||
.hljs-strong {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hljs-emphasis {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
|
@ -3,12 +3,12 @@ html(lang="en")
|
||||||
head
|
head
|
||||||
meta(charset="utf-8")
|
meta(charset="utf-8")
|
||||||
meta(name="viewport" content="width=device-width, initial-scale=1")
|
meta(name="viewport" content="width=device-width, initial-scale=1")
|
||||||
title Artur Gurgul - #{title}
|
title Artur Gurgul - #{page.title}
|
||||||
meta(name="author" content="Artur Gurgul")
|
meta(name="author" content="Artur Gurgul")
|
||||||
meta(name="description" content="This is my notepad")
|
meta(name="description" content="This is my notepad")
|
||||||
|
|
||||||
link(rel="shortcut icon" href="/favicon.png")
|
link(rel="shortcut icon" href="/favicon.png")
|
||||||
link(rel="alternate" type="application/atom+xml" title="#{site.data.theme.name}" href="#{site.url}/atom.xml")
|
link(rel="alternate" type="application/atom+xml" title=page.title href="#{site.url}/atom.xml")
|
||||||
link(rel="stylesheet" href="/static/css/all.css")
|
link(rel="stylesheet" href="/static/css/all.css")
|
||||||
link(rel="stylesheet" href="/static/css/hightlight.css")
|
link(rel="stylesheet" href="/static/css/hightlight.css")
|
||||||
body
|
body
|
||||||
|
@ -17,7 +17,7 @@ html(lang="en")
|
||||||
include sidebar.pug
|
include sidebar.pug
|
||||||
.scroll
|
.scroll
|
||||||
.content
|
.content
|
||||||
h1.title= title
|
h1.title= page.title
|
||||||
#post!= content
|
#post!= page.file.html
|
||||||
.footer
|
.footer
|
||||||
include footer.pug
|
include footer.pug
|
||||||
|
|
|
@ -4,7 +4,7 @@ nav
|
||||||
h2(style="font-size: 15px; margin-top: -0.5em;") and this is my notepad.
|
h2(style="font-size: 15px; margin-top: -0.5em;") and this is my notepad.
|
||||||
hr.hr-text(data-content="Contents")
|
hr.hr-text(data-content="Contents")
|
||||||
ul#blog-posts.posts
|
ul#blog-posts.posts
|
||||||
each page in pages.filter(it => it.hidden != true && it.title != undefined )
|
each page in context.pages
|
||||||
li
|
li
|
||||||
span »
|
span »
|
||||||
a(href=page.url, style=(false ? "font-weight: bold;" : ""))= page.title
|
a(href=page.url, style=(page.isCurrent ? "font-weight: bold;" : ""))= page.title
|
||||||
|
|
|
@ -1,28 +1,5 @@
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: Montserrat;
|
|
||||||
/* declare weights giving two values to specify a range */
|
|
||||||
font-weight: 400 800;
|
|
||||||
src: url(/static/fonts/Montserrat-VariableFont_wght.ttf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: Montserrat;
|
|
||||||
/* declare weights giving two values to specify a range */
|
|
||||||
font-weight: 400 800;
|
|
||||||
font-style: italic;
|
|
||||||
src: url(/static/fonts/Montserrat-Italic-VariableFont_wght.ttf);
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: Proto;
|
|
||||||
/* declare weights giving two values to specify a range */
|
|
||||||
font-weight: 400 800;
|
|
||||||
src: url(/static/fonts/0xProto-Regular.ttf);
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
* {
|
||||||
font-family: Montserrat, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
@ -136,7 +113,7 @@ h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
color: #181818;
|
color: #181818;
|
||||||
font-family: Montserrat, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +437,7 @@ h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
color: #181818;
|
color: #181818;
|
||||||
font-family: Montserrat, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,7 +508,7 @@ ul.posts li {
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.posts span {
|
ul.posts span {
|
||||||
font-family: 'Proto', monospace;
|
font-family: monospace;
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
@ -576,7 +553,7 @@ nav h2 {
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
padding-top: 25px;
|
padding-top: 25px;
|
||||||
font-family: Montserrat, sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar p {
|
.sidebar p {
|
||||||
|
@ -588,7 +565,7 @@ nav h2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
text {
|
text {
|
||||||
font: 500 12px/22px Montserrat, sans-serif;
|
font: 500 12px/22px -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* path, rect {
|
/* path, rect {
|
||||||
|
@ -596,12 +573,12 @@ text {
|
||||||
} */
|
} */
|
||||||
|
|
||||||
g {
|
g {
|
||||||
font: 500 12px/22px Montserrat, sans-serif;
|
font: 500 12px/22px -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
}
|
}
|
||||||
/* stroke-width="2" */
|
/* stroke-width="2" */
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
font: 400 16px/22px Montserrat, sans-serif;
|
font: 400 16px/22px -apple-system, BlinkMacSystemFont, sans-serif;
|
||||||
|
|
||||||
padding-left: 40px;
|
padding-left: 40px;
|
||||||
padding-top: 25px;
|
padding-top: 25px;
|
||||||
|
@ -678,7 +655,7 @@ pre {
|
||||||
|
|
||||||
code,
|
code,
|
||||||
pre {
|
pre {
|
||||||
font-family: 'Proto', monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hljs {
|
.hljs {
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,9 +0,0 @@
|
||||||
---
|
|
||||||
layout: default
|
|
||||||
title: Content
|
|
||||||
hidden: true
|
|
||||||
---
|
|
||||||
|
|
||||||
# Content
|
|
||||||
|
|
||||||
there is my awasome content
|
|
1417
package-lock.json
generated
1417
package-lock.json
generated
File diff suppressed because it is too large
Load diff
25
package.json
25
package.json
|
@ -33,25 +33,22 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/artur-gurgul-pro/sajt#readme",
|
"homepage": "https://github.com/artur-gurgul-pro/sajt#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.3.0",
|
"chalk": "^5.4.1",
|
||||||
"commander": "^12.1.0",
|
"commander": "^14.0.0",
|
||||||
"express": "^4.19.2",
|
"express": "^5.1.0",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.3.0",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"highlight.js": "^11.10.0",
|
"highlight.js": "^11.11.1",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"marked": "^14.0.0",
|
"marked": "^15.0.12",
|
||||||
"marked-highlight": "^2.1.4",
|
"marked-highlight": "^2.2.1",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3"
|
||||||
"ssh2": "^1.15.0",
|
|
||||||
"ssh2-sftp-client": "^11.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.2",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/node": "^22.13.5",
|
"@types/node": "^22.15.30",
|
||||||
"@types/pug": "^2.0.10",
|
"@types/pug": "^2.0.10",
|
||||||
"electron": "^33.0.1",
|
"typescript": "^5.8.3"
|
||||||
"typescript": "^5.7.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,32 @@
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import yaml from 'js-yaml'
|
import yaml from 'js-yaml'
|
||||||
|
import { fileURLToPath } from 'url'
|
||||||
|
|
||||||
export default class ProjectConfig {
|
export default class ProjectConfig {
|
||||||
public config: any = {}
|
public config: any
|
||||||
|
|
||||||
|
private __filename: string
|
||||||
|
private __dirname: string
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
this.config = {
|
||||||
}
|
|
||||||
|
|
||||||
public load() {
|
|
||||||
this.config = {
|
|
||||||
... this.read(),
|
... this.read(),
|
||||||
buildDir: './.build',
|
buildDir: './.build',
|
||||||
ignore: [".build", ".sajt"]
|
ignore: [".build", ".sajt"]
|
||||||
}
|
}
|
||||||
|
|
||||||
this.config.remote.port = 22
|
this.__filename = fileURLToPath(import.meta.url)
|
||||||
this.config.remote.privateKey = fs.readFileSync(path.resolve(process.env.HOME ?? "", '.ssh/id_rsa'))
|
this.__dirname = path.dirname(this.__filename)
|
||||||
|
this.config.defaultProjectPath = path.join(this.__dirname, '..', 'empty')
|
||||||
}
|
}
|
||||||
|
|
||||||
read(): any {
|
read(): any {
|
||||||
const __dirname = process.cwd()
|
const __dirname = process.cwd()
|
||||||
const configPath = path.join(__dirname, '.sajt/config.yaml')
|
const configPath = path.join(__dirname, '.sajt/config.yaml')
|
||||||
|
if (!fs.existsSync(configPath)) {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
const fileContents = fs.readFileSync(configPath, 'utf8')
|
const fileContents = fs.readFileSync(configPath, 'utf8')
|
||||||
return yaml.load(fileContents)
|
return yaml.load(fileContents)
|
||||||
}
|
}
|
|
@ -1,12 +1,8 @@
|
||||||
import { Command } from 'commander'
|
import { Command } from 'commander'
|
||||||
import chalk from 'chalk'
|
|
||||||
//import fs from 'fs-extra'
|
|
||||||
import path from 'path'
|
|
||||||
import Project from './project.js'
|
import Project from './project.js'
|
||||||
import { serve } from './serve.js'
|
import { serve } from './serve.js'
|
||||||
|
|
||||||
const program = new Command()
|
const program = new Command()
|
||||||
|
|
||||||
let project = new Project()
|
let project = new Project()
|
||||||
|
|
||||||
program
|
program
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import Parser , {File} from "./renderer.js"
|
||||||
|
|
||||||
import hljs from 'highlight.js'
|
import hljs from 'highlight.js'
|
||||||
import { marked } from 'marked'
|
import { marked } from 'marked'
|
||||||
import { markedHighlight } from 'marked-highlight'
|
import { markedHighlight } from 'marked-highlight'
|
||||||
|
@ -13,15 +15,19 @@ marked.use(markedHighlight({
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export function parseMD(file: string) {
|
export default class Markdown implements Parser {
|
||||||
const fileContents = fs.readFileSync(path.join("./", file), 'utf8')
|
name = "md"
|
||||||
|
|
||||||
|
loadAsHTML(filePath: string): File {
|
||||||
|
const fileContents = fs.readFileSync(path.join("./", filePath), 'utf8')
|
||||||
const { data: metadata, content: markdownContent } = matter(fileContents)
|
const { data: metadata, content: markdownContent } = matter(fileContents)
|
||||||
const htmlContent = marked(markdownContent)
|
const htmlContent = marked(markdownContent, {async: false})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
meta: metadata,
|
data: metadata,
|
||||||
content: htmlContent
|
html: htmlContent
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderer = new marked.Renderer()
|
const renderer = new marked.Renderer()
|
||||||
|
|
50
src/page.ts
Normal file
50
src/page.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
import Renderer , {File} from "./renderer.js"
|
||||||
|
import path from 'path'
|
||||||
|
import utils from './utils.js'
|
||||||
|
|
||||||
|
export default class Page {
|
||||||
|
file: File
|
||||||
|
|
||||||
|
/// /example/path
|
||||||
|
dir: string[]
|
||||||
|
|
||||||
|
/// /example/path/file
|
||||||
|
path: string
|
||||||
|
|
||||||
|
/// /example/path/file.html
|
||||||
|
finalPath: string
|
||||||
|
|
||||||
|
/// file.html
|
||||||
|
fileName: string
|
||||||
|
|
||||||
|
hidden: boolean
|
||||||
|
title: string
|
||||||
|
layout: string
|
||||||
|
|
||||||
|
constructor(filePath: string, renderer: Renderer) {
|
||||||
|
this.file = renderer.loadAsHTML(filePath)
|
||||||
|
|
||||||
|
if (this.file.data?.path != null && this.file.data?.path != undefined) {
|
||||||
|
this.path = path.join("/", this.file.data?.path)
|
||||||
|
} else {
|
||||||
|
const parsedPath = path.parse(filePath)
|
||||||
|
const basePath = path.join("/", parsedPath.dir, parsedPath.name)
|
||||||
|
this.path = basePath
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedPath = path.parse(this.path)
|
||||||
|
parsedPath.ext = '.html'
|
||||||
|
parsedPath.base = `${parsedPath.name}${parsedPath.ext}`
|
||||||
|
this.finalPath = path.format(parsedPath)
|
||||||
|
|
||||||
|
const dirArray = utils.pathToArray(this.finalPath)
|
||||||
|
this.fileName = dirArray.pop() || ""
|
||||||
|
dirArray.shift()
|
||||||
|
this.dir = dirArray
|
||||||
|
this.hidden = this.file.data?.hidden || false
|
||||||
|
|
||||||
|
// TODO: if tilte do not exists search in markdown for # title
|
||||||
|
this.title = this.file.data.title
|
||||||
|
this.layout = this.file.data.layout || "default"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,41 +1,83 @@
|
||||||
import { fileURLToPath } from 'url'
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { cp } from './utils.js'
|
import { cp } from './utils.js'
|
||||||
import { build } from './site.js'
|
import ProjectConfig from './config.js'
|
||||||
import ProjectConfig from './project-config.js'
|
|
||||||
|
|
||||||
|
|
||||||
|
import fs from 'fs'
|
||||||
|
import pug from 'pug'
|
||||||
|
|
||||||
|
import utils from "./utils.js"
|
||||||
|
|
||||||
|
import Markdown from './markdown.js'
|
||||||
|
import Yaml from './yaml.js'
|
||||||
|
import Page from './page.js'
|
||||||
|
|
||||||
export default class Project {
|
export default class Project {
|
||||||
public config = new ProjectConfig() // should be injected
|
private config = new ProjectConfig()
|
||||||
private __filename: string
|
pages: Page[]
|
||||||
private __dirname: string
|
|
||||||
private DEFAULT_PROJECT_PATH: string
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// Get the directory of the current file
|
this.pages = this.loadPages()
|
||||||
this.__filename = fileURLToPath(import.meta.url)
|
|
||||||
this.__dirname = path.dirname(this.__filename)
|
|
||||||
|
|
||||||
// Path relative to the script file's directory
|
|
||||||
this.DEFAULT_PROJECT_PATH = path.join(this.__dirname, '..', 'empty')
|
|
||||||
|
|
||||||
|
console.log(this.pages.map(it=> {return {dir: it.dir, file: it.finalPath}}))
|
||||||
}
|
}
|
||||||
|
|
||||||
new() {
|
new() {
|
||||||
console.log("Initialize a new project")
|
cp(this.config.config.defaultProjectPath, ".")
|
||||||
console.log(this.DEFAULT_PROJECT_PATH)
|
|
||||||
cp(this.DEFAULT_PROJECT_PATH, ".")
|
|
||||||
|
|
||||||
console.log(this.config)
|
|
||||||
this.config.load()
|
|
||||||
}
|
|
||||||
|
|
||||||
existing() {
|
|
||||||
this.config.load()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
this.config.load()
|
|
||||||
build(this.config.config)
|
utils.removeDirectorySync(this.config.config.buildDir)
|
||||||
|
utils.cp("./.sajt/static", path.join(this.config.config.buildDir, "static"))
|
||||||
|
|
||||||
|
for(const page of this.pages) {
|
||||||
|
this.compile(page)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPages(): Page[] {
|
||||||
|
const mdParser = new Markdown()
|
||||||
|
const yamlParser = new Yaml()
|
||||||
|
|
||||||
|
let listMD = utils.getAllFilesWithExtension('.',".md", this.config.config.ignore)
|
||||||
|
.map(path => new Page(path, mdParser))
|
||||||
|
|
||||||
|
let listYML = utils.getAllFilesWithExtension('.',".yml", this.config.config.ignore)
|
||||||
|
.map(path => new Page(path, yamlParser))
|
||||||
|
|
||||||
|
return listMD.concat(listYML)
|
||||||
|
}
|
||||||
|
|
||||||
|
compile(page: Page) {
|
||||||
|
const output = path.join(this.config.config.buildDir, page.finalPath)
|
||||||
|
|
||||||
|
const compiledFunction = pug.compileFile(`.sajt/layouts/${page.layout}.pug`)
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
page,
|
||||||
|
context: this.context(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirname = path.dirname(output)
|
||||||
|
if (!fs.existsSync(dirname)) {
|
||||||
|
fs.mkdirSync(dirname, { recursive: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = compiledFunction(data)
|
||||||
|
fs.writeFileSync(output, html)
|
||||||
|
console.log(`> ${output}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
context(page: Page): any {
|
||||||
|
return {
|
||||||
|
pages: this.pages.map(it => { return {
|
||||||
|
title: it.title,
|
||||||
|
isCurrent: page == it,
|
||||||
|
url: it.finalPath,
|
||||||
|
isDir: false
|
||||||
|
}})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
10
src/renderer.ts
Normal file
10
src/renderer.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
export interface File {
|
||||||
|
get html(): string
|
||||||
|
get data(): { [key: string]: any }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default interface Renderer {
|
||||||
|
get name(): string
|
||||||
|
loadAsHTML(path: string): File
|
||||||
|
}
|
|
@ -1,15 +1,11 @@
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
import { setWatcher } from './watch.js'
|
import { setWatcher } from './watch.js'
|
||||||
//import { webdavMiddleware } from "./webdav.js"
|
|
||||||
|
|
||||||
const app = express()
|
const app = express()
|
||||||
const PORT = process.env.PORT || 3000
|
const PORT = process.env.PORT || 3000
|
||||||
|
|
||||||
export function serve() {
|
export function serve() {
|
||||||
app.use(express.static('./.build'))
|
app.use(express.static('./.build'))
|
||||||
|
|
||||||
//app.use(webdavMiddleware)
|
|
||||||
|
|
||||||
setWatcher(url => {
|
setWatcher(url => {
|
||||||
console.log(url)
|
console.log(url)
|
||||||
})
|
})
|
||||||
|
|
132
src/site.ts
132
src/site.ts
|
@ -1,132 +0,0 @@
|
||||||
import fs from 'fs'
|
|
||||||
import pug from 'pug'
|
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
import { cp } from "./utils.js"
|
|
||||||
import { parseMarkdown } from './markdown.js'
|
|
||||||
import { getAllFilesWithExtension, pathToArray, parseYML } from './utils.js'
|
|
||||||
import { parseMD } from './markdown.js'
|
|
||||||
|
|
||||||
function removeDirectorySync(directory: string) {
|
|
||||||
try {
|
|
||||||
fs.rmSync(directory, { recursive: true, force: true })
|
|
||||||
console.log("Directory and its contents removed.")
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error(`Error removing directory: ${err.message}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function compile(template: string, content: any, output: string) {
|
|
||||||
if (template == null) {
|
|
||||||
console.error("Template is not defined, loading the default")
|
|
||||||
template = "default"
|
|
||||||
}
|
|
||||||
const compiledFunction = pug.compileFile(`.sajt/layouts/${template}.pug`);
|
|
||||||
const data = {
|
|
||||||
...content,
|
|
||||||
site: {posts: []}
|
|
||||||
}
|
|
||||||
|
|
||||||
const dirname = path.dirname(output)
|
|
||||||
if (!fs.existsSync(dirname)) {
|
|
||||||
fs.mkdirSync(dirname, { recursive: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
const html = compiledFunction(data)
|
|
||||||
fs.writeFileSync(output, html)
|
|
||||||
console.log(`HTML has been rendered and saved to ${output}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function compileData(template: string, content: object, output: string) {
|
|
||||||
const compiledFunction = pug.compileFile(`.sajt/layouts/${template}.pug`)
|
|
||||||
|
|
||||||
const dirname = path.dirname(output)
|
|
||||||
if (!fs.existsSync(dirname)) {
|
|
||||||
fs.mkdirSync(dirname, { recursive: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
const html = compiledFunction(content)
|
|
||||||
fs.writeFileSync(output, html)
|
|
||||||
console.log(`HTML has been rendered and saved to ${output}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function readMetadata(ignore: string[]) {
|
|
||||||
let htmlExtension = "html"
|
|
||||||
|
|
||||||
let listMD = getAllFilesWithExtension('.',".md", ignore)
|
|
||||||
.map(f => { return {
|
|
||||||
pathMD: f,
|
|
||||||
type: "md",
|
|
||||||
data: {} as any,
|
|
||||||
md: parseMD(f)
|
|
||||||
} as any })
|
|
||||||
|
|
||||||
let listYML = getAllFilesWithExtension('.',".yml", ignore)
|
|
||||||
.map(f => { return {
|
|
||||||
pathMD: f,
|
|
||||||
type: "yml",
|
|
||||||
data: parseYML(f),
|
|
||||||
md: {meta: {}}
|
|
||||||
} as any })
|
|
||||||
|
|
||||||
let list = listMD.concat(listYML)
|
|
||||||
|
|
||||||
for(const site of list) {
|
|
||||||
if (site.md.meta?.path != null && site.md.meta?.path != undefined) {
|
|
||||||
site.path = path.join("/", site.md.meta.path)
|
|
||||||
} else {
|
|
||||||
const parsedPath = path.parse(site.pathMD)
|
|
||||||
const basePath = path.join("/", parsedPath.dir, parsedPath.name)
|
|
||||||
site.path = basePath
|
|
||||||
}
|
|
||||||
|
|
||||||
// add proper extension
|
|
||||||
const parsedPath = path.parse(site.path)
|
|
||||||
parsedPath.ext = htmlExtension.startsWith('.') ? htmlExtension : `.${htmlExtension}`
|
|
||||||
parsedPath.base = `${parsedPath.name}${parsedPath.ext}`
|
|
||||||
site.path = path.format(parsedPath)
|
|
||||||
|
|
||||||
// add dirs metadata
|
|
||||||
const dirArray = pathToArray(site.path)
|
|
||||||
site.fileName = dirArray.pop()
|
|
||||||
dirArray.shift()
|
|
||||||
site.dir = dirArray
|
|
||||||
site.meta = site.md.meta
|
|
||||||
site.hidden = site.data?.hidden || false
|
|
||||||
}
|
|
||||||
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
export function build(config: any) {
|
|
||||||
removeDirectorySync(config.buildDir)
|
|
||||||
cp("./.sajt/static", path.join(config.buildDir, "static"))
|
|
||||||
|
|
||||||
let data = readMetadata(config.ignore)
|
|
||||||
let pages = data.map(site => {
|
|
||||||
return {
|
|
||||||
title: site.meta.title,
|
|
||||||
url: site.path
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
for(const site of data) {
|
|
||||||
if (site.type == "md") {
|
|
||||||
compile(site.meta.layout,
|
|
||||||
{
|
|
||||||
content: site.md.content,
|
|
||||||
title: site.meta.title,
|
|
||||||
hidden: false,
|
|
||||||
pages
|
|
||||||
},
|
|
||||||
path.join(config.buildDir, site.path))
|
|
||||||
} else if (site.type == "yml") {
|
|
||||||
let data = {...site.data}
|
|
||||||
delete data.layout
|
|
||||||
parseMarkdown(data)
|
|
||||||
compileData(site.data.layout,
|
|
||||||
{data, pages, hidden: data.hidden},
|
|
||||||
path.join(config.buildDir, site.path))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
36
src/ssh.ts
36
src/ssh.ts
|
@ -1,36 +0,0 @@
|
||||||
/*const Client = require('ssh2-sftp-client')
|
|
||||||
|
|
||||||
async function uploadDirectory(serverConfig, localDirPath) {
|
|
||||||
const sftp = new Client()
|
|
||||||
await sftp.connect(serverConfig)
|
|
||||||
try {
|
|
||||||
await upload(sftp, config, localDirPath, serverConfig.path)
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error: ${err.message}`)
|
|
||||||
} finally {
|
|
||||||
await sftp.end()
|
|
||||||
console.log('Connection closed')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function upload(sftp, config, localPath, remotePath) {
|
|
||||||
|
|
||||||
console.log('Connected to the server')
|
|
||||||
|
|
||||||
const files = fs.readdirSync(localPath)
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
const localFilePath = path.join(localPath, file)
|
|
||||||
const remoteFilePath = `${remotePath}/${file}`
|
|
||||||
|
|
||||||
if (fs.statSync(localFilePath).isDirectory()) {
|
|
||||||
await sftp.mkdir(remoteFilePath, true)
|
|
||||||
await upload(sftp, config, localFilePath, remoteFilePath)
|
|
||||||
} else {
|
|
||||||
const fileContent = fs.readFileSync(localFilePath)
|
|
||||||
await sftp.put(Buffer.from(fileContent), remoteFilePath)
|
|
||||||
console.log(`File transferred successfully: ${localFilePath}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
28
src/utils.ts
28
src/utils.ts
|
@ -1,12 +1,7 @@
|
||||||
import yaml from 'js-yaml'
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
export function parseYML(file: string) {
|
|
||||||
const fileContents = fs.readFileSync(file, 'utf8')
|
|
||||||
return yaml.load(fileContents)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAllFilesWithExtension(directory: string, extension: string, excludes: string[]): string[] {
|
export function getAllFilesWithExtension(directory: string, extension: string, excludes: string[]): string[] {
|
||||||
let results: string[] = []
|
let results: string[] = []
|
||||||
function readDirectory(directory: string) {
|
function readDirectory(directory: string) {
|
||||||
|
@ -29,7 +24,6 @@ export function getAllFilesWithExtension(directory: string, extension: string, e
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyDirectory
|
|
||||||
export function cp(source: string, destination: string) {
|
export function cp(source: string, destination: string) {
|
||||||
fs.mkdirSync(destination, { recursive: true })
|
fs.mkdirSync(destination, { recursive: true })
|
||||||
const items = fs.readdirSync(source)
|
const items = fs.readdirSync(source)
|
||||||
|
@ -45,9 +39,23 @@ export function cp(source: string, destination: string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pathToArray(filePath: string) {
|
function removeDirectorySync(directory: string) {
|
||||||
// Normalize the file path to handle different OS path separators
|
try {
|
||||||
|
fs.rmSync(directory, { recursive: true, force: true })
|
||||||
|
console.log("Directory and its contents removed.")
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(`Error removing directory: ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pathToArray(filePath: string):string[] {
|
||||||
const normalizedPath = path.normalize(filePath)
|
const normalizedPath = path.normalize(filePath)
|
||||||
// Split the path into an array of directories
|
|
||||||
return normalizedPath.split(path.sep)
|
return normalizedPath.split(path.sep)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
pathToArray,
|
||||||
|
cp,
|
||||||
|
getAllFilesWithExtension,
|
||||||
|
removeDirectorySync
|
||||||
|
}
|
27
src/watch.ts
27
src/watch.ts
|
@ -2,34 +2,31 @@ import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
export function setWatcher(callback: (url: string) => void) {
|
export function setWatcher(callback: (url: string) => void) {
|
||||||
// Directory to watch
|
const directoryPath = './'
|
||||||
const directoryPath = './';
|
|
||||||
// Ensure the directory exists
|
|
||||||
if (!fs.existsSync(directoryPath)) {
|
if (!fs.existsSync(directoryPath)) {
|
||||||
fs.mkdirSync(directoryPath, { recursive: true });
|
fs.mkdirSync(directoryPath, { recursive: true })
|
||||||
}
|
}
|
||||||
console.log(`Watching for changes in: ${directoryPath}`);
|
console.log(`Watching for changes in: ${directoryPath}`)
|
||||||
// Watch the directory for changes
|
|
||||||
fs.watch(directoryPath, (eventType, filename) => {
|
fs.watch(directoryPath, (eventType, filename) => {
|
||||||
if (filename) {
|
if (filename) {
|
||||||
const fullPath = path.join(directoryPath, filename);
|
const fullPath = path.join(directoryPath, filename)
|
||||||
console.log(`File ${filename} has been ${eventType}`);
|
console.log(`File ${filename} has been ${eventType}`)
|
||||||
// Check if the file was added, changed, or deleted
|
|
||||||
fs.stat(fullPath, (err, stats) => {
|
fs.stat(fullPath, (err, stats) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err.code === 'ENOENT') {
|
if (err.code === 'ENOENT') {
|
||||||
console.log(`File ${filename} was deleted.`);
|
console.log(`File ${filename} was deleted.`)
|
||||||
} else {
|
} else {
|
||||||
console.error(`Error checking file status: ${err.message}`);
|
console.error(`Error checking file status: ${err.message}`)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (stats.isFile()) {
|
if (stats.isFile()) {
|
||||||
console.log(`File ${filename} exists with size: ${stats.size} bytes`);
|
console.log(`File ${filename} exists with size: ${stats.size} bytes`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
console.log('Filename not provided, but change detected.');
|
console.log('Filename not provided, but change detected.')
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
import { v2 as webdav } from 'webdav-server';
|
|
||||||
|
|
||||||
// 1. Create a user manager and add a user
|
|
||||||
const userManager = new webdav.SimpleUserManager()
|
|
||||||
const user = userManager.addUser('user', 'password', false)
|
|
||||||
|
|
||||||
// 2. Create a privilege manager
|
|
||||||
const privilegeManager = new webdav.SimplePathPrivilegeManager()
|
|
||||||
|
|
||||||
// 3. Configure the WebDAV server
|
|
||||||
const server = new webdav.WebDAVServer({
|
|
||||||
// HTTP Digest authentication for better security
|
|
||||||
httpAuthentication: new webdav.HTTPDigestAuthentication(userManager, 'default-realm'),
|
|
||||||
privilegeManager
|
|
||||||
})
|
|
||||||
|
|
||||||
// 4. Set up a physical file system folder (e.g., `./data`) as the root
|
|
||||||
const publicFileSystem = new webdav.PhysicalFileSystem('./')
|
|
||||||
|
|
||||||
server.setFileSystem('/', publicFileSystem, (success) => {
|
|
||||||
if (!success) {
|
|
||||||
console.error('Failed to set file system')
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
// Give the user all permissions on the root
|
|
||||||
privilegeManager.setRights(user, '/', ['all'])
|
|
||||||
});
|
|
||||||
|
|
||||||
export const webdavMiddleware = webdav.extensions.express("/", server)
|
|
||||||
*/
|
|
20
src/yaml.ts
Normal file
20
src/yaml.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import yaml from 'js-yaml'
|
||||||
|
import Renderer , {File} from "./renderer.js"
|
||||||
|
import fs from 'fs'
|
||||||
|
|
||||||
|
export default class Yaml implements Renderer {
|
||||||
|
name = "yml"
|
||||||
|
|
||||||
|
loadAsHTML(file: string): File {
|
||||||
|
const fileContents = fs.readFileSync(file, 'utf8')
|
||||||
|
|
||||||
|
// let data = {...site.data}
|
||||||
|
// delete data.layout
|
||||||
|
// parseMarkdown(data)
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: yaml.load(fileContents) as { [key: string]: any },
|
||||||
|
html: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
types/config.d.ts
vendored
4
types/config.d.ts
vendored
|
@ -1,7 +1,7 @@
|
||||||
export default class ProjectConfig {
|
export default class ProjectConfig {
|
||||||
config: any;
|
config: any;
|
||||||
|
private __filename;
|
||||||
|
private __dirname;
|
||||||
constructor();
|
constructor();
|
||||||
load(): void;
|
|
||||||
getConfig(): any;
|
|
||||||
read(): any;
|
read(): any;
|
||||||
}
|
}
|
||||||
|
|
2
types/context.d.ts
vendored
Normal file
2
types/context.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export default interface Context {
|
||||||
|
}
|
11
types/markdown.d.ts
vendored
11
types/markdown.d.ts
vendored
|
@ -1,7 +1,6 @@
|
||||||
export declare function parseMD(file: string): {
|
import Parser, { File } from "./renderer.js";
|
||||||
meta: {
|
export default class Markdown implements Parser {
|
||||||
[key: string]: any;
|
name: string;
|
||||||
};
|
loadAsHTML(filePath: string): File;
|
||||||
content: string | Promise<string>;
|
}
|
||||||
};
|
|
||||||
export declare function parseMarkdown(obj: any): void;
|
export declare function parseMarkdown(obj: any): void;
|
||||||
|
|
12
types/page.d.ts
vendored
Normal file
12
types/page.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import Renderer, { File } from "./renderer.js";
|
||||||
|
export default class Page {
|
||||||
|
file: File;
|
||||||
|
dir: string[];
|
||||||
|
path: string;
|
||||||
|
finalPath: string;
|
||||||
|
fileName: string;
|
||||||
|
hidden: boolean;
|
||||||
|
title: string;
|
||||||
|
layout: string;
|
||||||
|
constructor(filePath: string, renderer: Renderer);
|
||||||
|
}
|
10
types/parser.d.ts
vendored
Normal file
10
types/parser.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export interface File {
|
||||||
|
get html(): string;
|
||||||
|
get data(): {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default interface Parser {
|
||||||
|
get name(): string;
|
||||||
|
parse(path: string): File;
|
||||||
|
}
|
12
types/project.d.ts
vendored
12
types/project.d.ts
vendored
|
@ -1,11 +1,11 @@
|
||||||
import ProjectConfig from './project-config.js';
|
import Page from './page.js';
|
||||||
export default class Project {
|
export default class Project {
|
||||||
config: ProjectConfig;
|
private config;
|
||||||
private __filename;
|
pages: Page[];
|
||||||
private __dirname;
|
|
||||||
private DEFAULT_PROJECT_PATH;
|
|
||||||
constructor();
|
constructor();
|
||||||
new(): void;
|
new(): void;
|
||||||
existing(): void;
|
|
||||||
build(): void;
|
build(): void;
|
||||||
|
loadPages(): Page[];
|
||||||
|
compile(page: Page): void;
|
||||||
|
context(page: Page): any;
|
||||||
}
|
}
|
||||||
|
|
10
types/renderer.d.ts
vendored
Normal file
10
types/renderer.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
export interface File {
|
||||||
|
get html(): string;
|
||||||
|
get data(): {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default interface Renderer {
|
||||||
|
get name(): string;
|
||||||
|
loadAsHTML(path: string): File;
|
||||||
|
}
|
9
types/utils.d.ts
vendored
9
types/utils.d.ts
vendored
|
@ -1,4 +1,11 @@
|
||||||
export declare function parseYML(file: string): unknown;
|
|
||||||
export declare function getAllFilesWithExtension(directory: string, extension: string, excludes: string[]): string[];
|
export declare function getAllFilesWithExtension(directory: string, extension: string, excludes: string[]): string[];
|
||||||
export declare function cp(source: string, destination: string): void;
|
export declare function cp(source: string, destination: string): void;
|
||||||
|
declare function removeDirectorySync(directory: string): void;
|
||||||
export declare function pathToArray(filePath: string): string[];
|
export declare function pathToArray(filePath: string): string[];
|
||||||
|
declare const _default: {
|
||||||
|
pathToArray: typeof pathToArray;
|
||||||
|
cp: typeof cp;
|
||||||
|
getAllFilesWithExtension: typeof getAllFilesWithExtension;
|
||||||
|
removeDirectorySync: typeof removeDirectorySync;
|
||||||
|
};
|
||||||
|
export default _default;
|
||||||
|
|
5
types/yaml.d.ts
vendored
Normal file
5
types/yaml.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import Renderer, { File } from "./renderer.js";
|
||||||
|
export default class Yaml implements Renderer {
|
||||||
|
name: string;
|
||||||
|
loadAsHTML(file: string): File;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue