Skip to content

Commit f899d4f

Browse files
committed
styles as file and lite-yt-embed.js
1 parent b2c68a3 commit f899d4f

File tree

9 files changed

+465
-30
lines changed

9 files changed

+465
-30
lines changed

www/compile.js

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const fs = require('fs');
22
const path = require('path');
33
const mdToHtml = require('./src/md2html');
4-
const { commonStyles } = require('./src/styles');
54
const { posts_source, siteUrl, ALLOWED_EXTENSIONS, menuItems, postsConfig } = require('./config');
65

76
const publicDir = path.join(__dirname, 'public');
@@ -366,8 +365,16 @@ function createBlogContent(posts, language) {
366365
const topTags = extractTags(recentPosts);
367366

368367
return `
368+
<div class="site-description-wrapper">
369+
<div class="site-description">
370+
${language === 'uk' ?
371+
'Тут ви знайдете корисні поради, приклади, інструкції та інші матеріали, які допоможуть вам у створенні коду з допомогою штучного інтелекту' :
372+
"Here you'll find useful tips, examples, instructions, and other materials to help you create code with the help of AI"}
373+
</div>
374+
</div>
369375
<div class="top-tags">
370-
${topTags.map(([tag, count]) => `<a href="javascript:void(0)" data-tag="${tag}" data-count=" • ${count}">${tag}</a>`).join(' ')}
376+
<a href="javascript:void(0)" class="up-button" title="${language === 'uk' ? 'Нагору' : 'Scroll to top'}">${language === 'uk' ? 'Початок' : 'Home'}</a>
377+
${topTags.map(([tag, count]) => `<a href="javascript:void(0)" data-tag="${tag}" data-count="${count} • ">${tag}</a>`).join(' ')}
371378
</div>
372379
<div class="container" style="grid-template-columns: 1fr">
373380
<div class="posts">
@@ -389,10 +396,19 @@ function createBlogContent(posts, language) {
389396
</div>
390397
<script>
391398
document.addEventListener('DOMContentLoaded', function() {
392-
const tagLinks = document.querySelectorAll('.top-tags a');
399+
const tagLinks = document.querySelectorAll('.top-tags a:not(.up-button)');
400+
const upButton = document.querySelector('.up-button');
393401
const posts = document.querySelectorAll('.post');
394402
const tagPositions = new Map();
395403
404+
upButton.addEventListener('click', function(e) {
405+
e.preventDefault();
406+
window.scrollTo({
407+
top: 0,
408+
behavior: 'smooth'
409+
});
410+
});
411+
396412
tagLinks.forEach(link => {
397413
link.addEventListener('click', function(e) {
398414
e.preventDefault();
@@ -540,7 +556,6 @@ function generateMenu(activeMenu, posts = [], currentMonth = '') {
540556
}
541557

542558
function createPage(title, content, activeMenu, posts = [], currentMonth = '', preGeneratedMenu = '') {
543-
// Если title это объект с date и time, используем date
544559
const pageTitle = typeof title === 'object' && title.date ?
545560
`${title.date} ${title.time} - CodeWithLLM` :
546561
title.includes(' - CodeWithLLM') ? title : `${title} - CodeWithLLM`;
@@ -572,12 +587,16 @@ function createPage(title, content, activeMenu, posts = [], currentMonth = '', p
572587
<meta property="twitter:description" content="${description}">
573588
574589
<title>${pageTitle}</title>
575-
<style>
576-
${commonStyles}
577-
</style>
590+
591+
<!-- Стили -->
592+
<link rel="stylesheet" href="/css/styles.css">
578593
579594
<!-- Добавляем favicon -->
580595
<link rel="icon" type="image/png" href="/img/favicon.png">
596+
597+
<!-- Подключаем lite-yt-embed -->
598+
<link rel="stylesheet" href="/css/lite-yt-embed.css" />
599+
<script src="/js/lite-yt-embed.js"></script>
581600
</head>
582601
<body>
583602
<div class="wrapper">
@@ -614,8 +633,8 @@ async function compile() {
614633
}
615634

616635
// Копируем favicon
617-
const faviconPngPath = path.join('.', 'favicon', 'favicon.png');
618-
const faviconIcoPath = path.join('.', 'favicon', 'favicon.ico');
636+
const faviconPngPath = path.join('.', 'template', 'favicon.png');
637+
const faviconIcoPath = path.join('.', 'template', 'favicon.ico');
619638

620639
if (fs.existsSync(faviconPngPath)) {
621640
fs.copyFileSync(faviconPngPath, path.join(imgDir, 'favicon.png'));
@@ -624,11 +643,39 @@ async function compile() {
624643
}
625644

626645
if (fs.existsSync(faviconIcoPath)) {
627-
fs.copyFileSync(faviconIcoPath, path.join(imgDir, 'favicon.ico'));
646+
fs.copyFileSync(faviconIcoPath, path.join(publicDir, 'favicon.ico'));
628647
} else {
629648
console.warn('⚠️ favicon.ico не найден в папке favicon');
630649
}
631650

651+
// Копируем файлы для lite-yt-embed и стили
652+
const cssDir = path.join(publicDir, 'css');
653+
const jsDir = path.join(publicDir, 'js');
654+
if (!fs.existsSync(cssDir)) fs.mkdirSync(cssDir);
655+
if (!fs.existsSync(jsDir)) fs.mkdirSync(jsDir);
656+
657+
const liteYtCssPath = path.join('.', 'template', 'lite-yt-embed.css');
658+
const liteYtJsPath = path.join('.', 'template', 'lite-yt-embed.js');
659+
const stylesCssPath = path.join('.', 'template', 'styles.css');
660+
661+
if (fs.existsSync(liteYtCssPath)) {
662+
fs.copyFileSync(liteYtCssPath, path.join(cssDir, 'lite-yt-embed.css'));
663+
} else {
664+
console.warn('⚠️ lite-yt-embed.css не найден в папке template');
665+
}
666+
667+
if (fs.existsSync(liteYtJsPath)) {
668+
fs.copyFileSync(liteYtJsPath, path.join(jsDir, 'lite-yt-embed.js'));
669+
} else {
670+
console.warn('⚠️ lite-yt-embed.js не найден в папке template');
671+
}
672+
673+
if (fs.existsSync(stylesCssPath)) {
674+
fs.copyFileSync(stylesCssPath, path.join(cssDir, 'styles.css'));
675+
} else {
676+
console.warn('⚠️ styles.css не найден в папке template');
677+
}
678+
632679
// Копируем изображения
633680
for (const lang of Object.values(posts_source)) {
634681
lang.forEach(({path: yearDir}) => {

www/favicon/favicon.ico

-15 KB
Binary file not shown.

www/favicon/favicon.png

-68 KB
Binary file not shown.

www/src/md2html.js

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,7 @@ marked.use({
1111
url.match(/(?:v=|\/v\/|^\/|embed\/)([^&\?\/]+)/i)?.[1];
1212
if (videoId) {
1313
return `
14-
<div class="youtube-wrapper">
15-
<iframe
16-
width="100%"
17-
height="400"
18-
src="https://www.youtube-nocookie.com/embed/${videoId}"
19-
frameborder="0"
20-
allowfullscreen>
21-
</iframe>
22-
</div>
14+
<lite-youtube videoid="${videoId}" playlabel="Play Video"></lite-youtube>
2315
`;
2416
}
2517
}

www/template/favicon.ico

15 KB
Binary file not shown.

www/template/favicon.png

42 KB
Loading

www/template/lite-yt-embed.css

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
lite-youtube {
2+
background-color: #000;
3+
position: relative;
4+
display: block;
5+
contain: content;
6+
background-position: center center;
7+
background-size: cover;
8+
cursor: pointer;
9+
max-width: 720px;
10+
}
11+
12+
/* gradient */
13+
lite-youtube::before {
14+
content: attr(data-title);
15+
display: block;
16+
position: absolute;
17+
top: 0;
18+
/* Pixel-perfect port of YT's gradient PNG, using https://github.com/bluesmoon/pngtocss plus optimizations */
19+
background-image: linear-gradient(180deg, rgb(0 0 0 / 67%) 0%, rgb(0 0 0 / 54%) 14%, rgb(0 0 0 / 15%) 54%, rgb(0 0 0 / 5%) 72%, rgb(0 0 0 / 0%) 94%);
20+
height: 99px;
21+
width: 100%;
22+
font-family: "YouTube Noto",Roboto,Arial,Helvetica,sans-serif;
23+
color: hsl(0deg 0% 93.33%);
24+
text-shadow: 0 0 2px rgba(0,0,0,.5);
25+
font-size: 18px;
26+
padding: 25px 20px;
27+
overflow: hidden;
28+
white-space: nowrap;
29+
text-overflow: ellipsis;
30+
box-sizing: border-box;
31+
}
32+
33+
lite-youtube:hover::before {
34+
color: white;
35+
}
36+
37+
/* responsive iframe with a 16:9 aspect ratio
38+
thanks https://css-tricks.com/responsive-iframes/
39+
*/
40+
lite-youtube::after {
41+
content: "";
42+
display: block;
43+
padding-bottom: calc(100% / (16 / 9));
44+
}
45+
lite-youtube > iframe {
46+
width: 100%;
47+
height: 100%;
48+
position: absolute;
49+
top: 0;
50+
left: 0;
51+
border: 0;
52+
}
53+
54+
/* play button */
55+
lite-youtube > .lyt-playbtn {
56+
display: block;
57+
/* Make the button element cover the whole area for a large hover/click target… */
58+
width: 100%;
59+
height: 100%;
60+
/* …but visually it's still the same size */
61+
background: no-repeat center/68px 48px;
62+
/* YT's actual play button svg */
63+
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 68 48"><path d="M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55c-2.93.78-4.63 3.26-5.42 6.19C.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24s-.06-10.95-1.48-16.26z" fill="red"/><path d="M45 24 27 14v20" fill="white"/></svg>');
64+
position: absolute;
65+
cursor: pointer;
66+
z-index: 1;
67+
filter: grayscale(100%);
68+
transition: filter .1s cubic-bezier(0, 0, 0.2, 1);
69+
border: 0;
70+
}
71+
72+
lite-youtube:hover > .lyt-playbtn,
73+
lite-youtube .lyt-playbtn:focus {
74+
filter: none;
75+
}
76+
77+
/* Post-click styles */
78+
lite-youtube.lyt-activated {
79+
cursor: unset;
80+
}
81+
lite-youtube.lyt-activated::before,
82+
lite-youtube.lyt-activated > .lyt-playbtn {
83+
opacity: 0;
84+
pointer-events: none;
85+
}
86+
87+
.lyt-visually-hidden {
88+
clip: rect(0 0 0 0);
89+
clip-path: inset(50%);
90+
height: 1px;
91+
overflow: hidden;
92+
position: absolute;
93+
white-space: nowrap;
94+
width: 1px;
95+
}

0 commit comments

Comments
 (0)