Compare commits

...

7 commits

14 changed files with 1226 additions and 5451 deletions

6455
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -10,8 +10,8 @@
"astro": "astro" "astro": "astro"
}, },
"dependencies": { "dependencies": {
"astro": "^2.9.0", "astro": "^2.10.5",
"astro-compress": "^1.1.50", "astro-compress": "^2.0.5",
"intl": "^1.2.5", "intl": "^1.2.5",
"less": "^4.1.3", "less": "^4.1.3",
"reading-time": "^1.5.0", "reading-time": "^1.5.0",

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 KiB

View file

@ -19,6 +19,7 @@ const {
<div class="card card-elevated" id={post.slug}> <div class="card card-elevated" id={post.slug}>
<Article <Article
PostContent={PostContent} PostContent={PostContent}
slug={post.slug}
title={title} title={title}
description={description} description={description}
readingTime={readingTime} readingTime={readingTime}

View file

@ -35,6 +35,8 @@ const {
@icon-size: 1.5rem; @icon-size: 1.5rem;
li { li {
height: fit-content;
a { a {
display: flex; display: flex;
flex-direction: row; flex-direction: row;

View file

@ -0,0 +1,52 @@
---
import Icon from './Icon.astro';
export interface Props {
btnKind: 'prev' | 'pagenum' | 'next';
url?: string | undefined;
num?: number;
}
const { btnKind, url, num = 1 } = Astro.props;
---
<a href={url ?? 'javascript:void(0)'} class={btnKind}>
{
btnKind === 'pagenum' ? <span class="pagenum">{num}</span> :
btnKind === 'prev' ? <Icon code="&#xf104;" /> :
btnKind === 'next' ? <Icon code="&#xf105;" /> :
""
}
</a>
<style lang="less">
a {
@size: 1.1rem;
@color: var(--fg);
width: @size;
height: @size;
box-sizing: content-box;
font-size: @size;
padding: 0.625rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: @color;
&:hover {
color: @color;
background: var(--accent-bg);
border-radius: var(--bdrs);
}
}
.pagenum {
font-weight: 600;
}
</style>

View file

@ -0,0 +1,82 @@
---
import Icon from "./Icon.astro";
---
<div id="scroll-to-top" data-show="0">
<a href="#">
<Icon code="&#xf106;" />
</a>
</div>
<style lang="less">
@size: 2.25rem;
@font: 1.50rem;
div {
position: absolute;
&[data-show="0"] a {
height: 0;
opacity: 0;
transition: opacity 0.2s ease, height 0.2s step-end;
}
&[data-show="1"] a {
height: @size;
opacity: 1;
transition: opacity 0.2s ease, height 0.2s step-start;
}
}
a {
position: fixed;
bottom: 0.5rem;
right: 0.625rem;
display: inline-flex;
justify-content: center;
align-items: center;
padding: 0.25rem;
width: @size;
height: @size;
border-radius: @size;
font-size: @font;
background: var(--accent-bg);
color: var(--fg);
&:hover {
background: var(--bg-sec);
color: var(--fg);
}
}
</style>
<script>
const stt = document.getElementById('scroll-to-top')
const doc = document.documentElement
let scrollPos = 0
let ticking = false
const handler = (pos: number) => {
if (pos > doc.clientHeight / 2) {
stt.dataset.show = '1'
}
else {
stt.dataset.show = '0'
}
}
addEventListener('scroll', () => {
scrollPos = window.scrollY
if (!ticking) {
window.requestAnimationFrame(() => {
handler(scrollPos)
ticking = false
})
ticking = true
}
})
</script>

View file

@ -0,0 +1,19 @@
---
comments: dcat09/18
---
# Начинающему фронтендеру: margin vs padding
Раньше я не понимал разницы между margin и padding, и ставил их в CSS наугад, проверяя результат.
На StackOverflow объяснение было так себе, да и инглиш я тогда знал тоже так себе.
Помог мне Chrome, а точнее инструменты разработчика. Внизу вкладки "Стили" есть наглядная визуализация отступов цветными прямоугольниками.
![](/assets/20220629-mvsp/devtools.jpg)
- Вокруг содержимого элемента -- **padding**, ограничивающий текст от рамки
- После него -- **border**, т.е. сама рамка
- После border -- **margin**, ограничивающий элемент от других элементов
А посмотреть всё это в интерактивном виде можно на [странице на Codeberg Pages](https://darkcat09.codeberg.page/margin-vs-padding).
Исходники: [Codeberg](https://codeberg.org/DarkCat09/margin-vs-padding) | [Gitea](https://git.dc09.ru/DarkCat09/margin-vs-padding).

View file

@ -0,0 +1,28 @@
---
comments: dcat09/37
---
# Технологии XDT, или YouTube всё знает...
Смотрел я на СТСе Гарри Поттера.
На следующий день в рекомендациях ютуба появились видео на тему вышеуказанного фильма, которые я не искал примерно год.
Как? Ведь тогда установлены были не официальные сервисы, а MicroG.
Где-то на хабре в комментах под статьёй то ли про телеметрию, то ли про рекламу, был такой вариант: генерация высокочастотных звуков при трансляции телепередач, чтобы наши девайсы отслеживали их и понимали, что мы смотрим. И на основе этого предлагать таргетированную рекламу.
<s>Лишь бы этим не воспользовались. Или уже?</s>
**Upd:**
Технология называется [Cross-device tracking (CDT, XDT)](https://en.m.wikipedia.org/wiki/Cross-device_tracking).
К ней относится аналитика, обычно с целью показа рекламы, которая, вместо сохранения идентификатора в одной сессии браузера или его привязки к одному IP-адресу, может передавать рекламный ID между устройствами.
Внедряют почти все, кому не лень.
- Самый простой способ -- отслеживание **залогиненных пользователей**.
На сайте у человека есть аккаунт, в который он входит с разных устройств.
Таким образом можно сохранять список устройств, далее - делать что угодно.
"Рекламный идентификатор" у Google является ярким примером такого XDT.
- Способ сложнее, о котором я и рассказывал -- **использование ультразвука** для передачи информации между устройствами.
Лидирует в этом варианте XDT комания SilverPush, у которой ещё и патент на эту технологию.
Для защиты можно использовать приложение [PilferShushJammer](https://github.com/kaputnikGo/PilferShushJammer).
За информацию большое спасибо [@nvrm17](https://t.me/nvrm17)

View file

@ -1,4 +1,5 @@
--- ---
draft: true
comments: dcat09/1 comments: dcat09/1
--- ---
@ -92,5 +93,5 @@ echo('Your ID: ' . $uid);
Скомбинированный вариант: временный секретный ключ, на основе которого генерируется одноразовый код, отправляемый по E-Mail/SMS. Ключ не хранится в БД, плюс он временный, взлом почти невозможен. Скомбинированный вариант: временный секретный ключ, на основе которого генерируется одноразовый код, отправляемый по E-Mail/SMS. Ключ не хранится в БД, плюс он временный, взлом почти невозможен.
## 2FA ## 2FA
Двухфакторная аутентификация, она же 2FA, -- мощный механизм защиты учётной записи. Двухфакторная аутентификация, она же 2FA, -- мощный механизм защиты учётной записи.
// TODO: Дописать // TODO: Дописать

View file

@ -4,11 +4,6 @@ comments: dcat09/338
# Коротко о разработке ПО сейчас # Коротко о разработке ПО сейчас
_[Оригинальный текст](https://factoryfactoryfactory.net)_
_Перевод [DarkCat09](https://dc09.ru)_
***
Представим, что я решил сделать подставку для специй. Представим, что я решил сделать подставку для специй.
Я уже имел дело с небольшими проектами по работе с деревом, и хорошо представляю, что мне необходимо: древесный материал и несколько основных инструментов: рулетка, уровень, пила и молоток. Я уже имел дело с небольшими проектами по работе с деревом, и хорошо представляю, что мне необходимо: древесный материал и несколько основных инструментов: рулетка, уровень, пила и молоток.
@ -65,7 +60,7 @@ _Перевод [DarkCat09](https://dc09.ru)_
— Внимательно слушаю. — Внимательно слушаю.
— Когда мы шагнули назад и посмотрели на всю картину инфраструктуры строительных инструментов, мы определили, что люди разочарованы в необходимости управлять фабрикой фабрик, как и производимой ею фабрикой. Что довольно обременительно, когда вы оперируете по такому же сценарию ещё и фабрикой фабрик рулеток, фабрикой фабрик уровней, фабрикой фабрик пил, не говоря уже о холдинге компаний по производству древесного материала. Когда мы здраво оценили ситуацию, мы поняли, что вышеописанное слишком сложно для тех, кому просто хочется сделать подставку для специй. — Когда мы шагнули назад и посмотрели на всю картину инфраструктуры строительных инструментов, мы определили, что люди разочарованы в необходимости управлять фабрикой фабрик, как и производимой ею фабрикой. Это довольно обременительно, когда вы оперируете по такому же сценарию ещё и фабрикой фабрик рулеток, фабрикой фабрик уровней, фабрикой фабрик пил, не говоря уже о холдинге компаний по производству древесного материала. Когда мы здраво оценили ситуацию, мы поняли, что вышеописанное слишком сложно для тех, кому просто хочется сделать подставку для специй.
— Да, без шуток, абсолютно верно. — Да, без шуток, абсолютно верно.
@ -84,3 +79,8 @@ _Перевод [DarkCat09](https://dc09.ru)_
— Что будет очень полезно для вас! — Что будет очень полезно для вас!
— У этой штуки же есть документация, да? — У этой штуки же есть документация, да?
***
_[Оригинальный текст](https://factoryfactoryfactory.net) написан Benji Smith,_
_переведён [DarkCat09](https://dc09.ru)_

View file

@ -2,6 +2,7 @@
import Layout from "./Layout.astro"; import Layout from "./Layout.astro";
import NavMenu from "./NavMenu.astro"; import NavMenu from "./NavMenu.astro";
import MenuItem from "../components/MenuItem.astro"; import MenuItem from "../components/MenuItem.astro";
import ScrollToTop from "../components/ScrollToTop.astro";
export interface Props { export interface Props {
title: string; title: string;
@ -14,6 +15,7 @@ const { title, description, mainpage = false } = Astro.props;
<Layout title={"Blog | " + title} description={description}> <Layout title={"Blog | " + title} description={description}>
<slot name="metadata" slot="metadata" /> <slot name="metadata" slot="metadata" />
<ScrollToTop />
<NavMenu> <NavMenu>
{ {
mainpage ? mainpage ?

View file

@ -1,8 +1,10 @@
--- ---
import BlogLayout from "../../layouts/BlogLayout.astro"; import BlogLayout from "../../layouts/BlogLayout.astro";
import ArticleCard from "../../components/ArticleCard.astro"; import ArticleCard from "../../components/ArticleCard.astro";
import PageCtrlBtn from "../../components/PageCtrlBtn.astro";
import { getCollection } from "astro:content"; import { getCollection } from "astro:content";
import { Page } from "astro";
export async function getStaticPaths({ paginate }) { export async function getStaticPaths({ paginate }) {
const POSTS_ON_PAGE = 15; const POSTS_ON_PAGE = 15;
@ -19,7 +21,7 @@ export async function getStaticPaths({ paginate }) {
return paginate(posts, { pageSize: POSTS_ON_PAGE }); return paginate(posts, { pageSize: POSTS_ON_PAGE });
} }
const { page } = Astro.props; const page: Page = Astro.props.page;
const description = const description =
"DarkCat09's blog. " + "DarkCat09's blog. " +
"Reading a QR code without a phone, " + "Reading a QR code without a phone, " +
@ -35,6 +37,11 @@ const description =
<div id="articles"> <div id="articles">
{page.data.map(({ post }) => <ArticleCard post={post} />)} {page.data.map(({ post }) => <ArticleCard post={post} />)}
</div> </div>
<div id="page-ctrl">
<PageCtrlBtn btnKind="prev" url={page.url.prev} />
<PageCtrlBtn btnKind="pagenum" num={page.currentPage} />
<PageCtrlBtn btnKind="next" url={page.url.next} />
</div>
</BlogLayout> </BlogLayout>
<style lang="less"> <style lang="less">
@ -46,4 +53,12 @@ const description =
align-items: center; align-items: center;
gap: @body-padding + @main-padding; gap: @body-padding + @main-padding;
} }
#page-ctrl {
margin-top: 0.5rem;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
</style> </style>