Compare commits

...

10 Commits

Author SHA1 Message Date
Карпич Дмитрий Александрович
97977d44d8 chore(*): add all 2025-05-28 03:48:01 +03:00
Henrique Ramos
8f90133e1e chore: use space instead of tab for formatting 2025-02-26 21:05:19 -03:00
Henrique Ramos
1c281f220d chore: use arktype infer to create Slide type 2025-02-26 21:03:48 -03:00
Henrique Ramos
967ad59394 chore: add Svelte island to flexbox slide 2025-02-03 18:26:02 -03:00
Henrique Ramos
32af9bde3d chore: add draft flag 2025-02-03 11:32:13 -03:00
Henrique Ramos
be2a96ac22 chore: use arktype for schema validation 2025-02-03 11:15:47 -03:00
Henrique Ramos
03cd0f35bc chore: update background color 2025-02-01 18:48:22 -03:00
Henrique Ramos
0614e2b7ef feat: use astro files for slides 2025-02-01 18:26:43 -03:00
Henrique Ramos
8ca43b3785 chore: use biomejs 2025-02-01 18:26:19 -03:00
Henrique Ramos
9955f9ed7b chore: use css in favor of scss 2025-02-01 18:26:00 -03:00
20 changed files with 3552 additions and 3334 deletions

14
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,14 @@
{
"editor.defaultFormatter": "biomejs.biome",
// allow autocomplete for ArkType expressions like "string | num"
"editor.quickSuggestions": {
"strings": "on"
},
// prioritize ArkType's "type" for autoimports
"typescript.preferences.autoImportSpecifierExcludeRegexes": [
"^(node:)?os$"
],
"[astro]": {
"editor.defaultFormatter": "astro-build.astro-vscode"
}
}

View File

@ -1,5 +1,9 @@
// @ts-check
import { defineConfig } from 'astro/config';
import { defineConfig } from "astro/config";
import svelte from "@astrojs/svelte";
// https://astro.build/config
export default defineConfig({});
export default defineConfig({
integrations: [svelte()],
});

30
biome.json Normal file
View File

@ -0,0 +1,30 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "space"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double"
}
}
}

60
example/server.cjs Executable file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env node
const http = require('http');
// Конфигурация приложения
let debugMode = false;
// Создаем HTTP-сервер
const server = http.createServer((req, res) => {
res.end(`Debug mode: ${debugMode ? 'ON' : 'OFF'}`);
});
// Graceful shutdown
const gracefulShutdown = (signal) => {
console.log(`\nReceived ${signal}. Closing server...`);
server.close(() => {
console.log('All connections closed. Exiting.');
process.exit(0);
});
setTimeout(() => {
console.error('Forcing shutdown after 5 seconds!');
process.exit(1);
}, 5000);
};
// Обработчики сигналов
process.on('SIGINT', () => gracefulShutdown('SIGINT (Ctrl+C)')); // Terminal interrupt
process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); // Default kill signal
process.on('SIGHUP', () => { // Hangup
console.log('\nReceived SIGHUP. Reloading configuration...');
// Здесь можно перечитать конфиги
});
// Пользовательские сигналы
// Выключен для демонстрации поведения встроенного дебаггера
// теперь при перехвате SIGUSR1, дебаггер не будет активирован
/*
process.on('SIGUSR1', () => { // User-defined 1
debugMode = true;
console.log('\nDebug mode activated (SIGUSR1)');
});
*/
process.on('SIGUSR2', () => { // User-defined 2
debugMode = false;
console.log('\nDebug mode deactivated (SIGUSR2)');
});
// Запуск сервера
server.listen(3003, () => {
const pid = process.pid;
console.log(`Server started. PID: ${pid}`);
console.log('Test commands:');
console.log(` Kill with SIGTERM: kill -TERM ${pid}`);
console.log(` Toggle debug mode: kill -USR1 ${pid} / kill -USR2 ${pid}`);
console.log(` Force kill: kill -9 ${pid}`);
console.log('Endpoint: curl http://localhost:3003');
});

View File

@ -1,5 +1,5 @@
{
"name": "presentations",
"name": "slides",
"type": "module",
"version": "0.0.1",
"scripts": {
@ -9,11 +9,15 @@
"astro": "astro"
},
"dependencies": {
"astro": "^5.0.5",
"@astrojs/svelte": "^7.0.4",
"astro": "^5.2.3",
"reveal.js": "^5.1.0",
"sass": "^1.83.0"
"svelte": "^5.19.7",
"typescript": "^5.7.3"
},
"devDependencies": {
"@types/reveal.js": "^5.0.4"
"@biomejs/biome": "^1.9.4",
"@types/reveal.js": "^5.0.5",
"arktype": "^2.0.4"
}
}

3081
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +0,0 @@
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders"; // Not available with legacy API
const slides = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/slides" }),
schema: z.object({
title: z.string(),
description: z.string(),
authors: z.array(z.string()),
publishedAt: z.coerce.date(),
}),
});
export const collections = { slides };

View File

@ -5,7 +5,9 @@ export interface Props {
description?: string;
}
import Layout from "./BaseLayout.astro";
import "@themes/hnrq.scss";
import "reveal.js/dist/reveal.css";
import "reveal.js/plugin/highlight/monokai.css";
import "@theme";
const { title, authors, description } = Astro.props;
---
@ -14,20 +16,6 @@ const { title, authors, description } = Astro.props;
<Fragment slot="head">
{description && <meta name="description" content={description} />}
{authors.map((author) => <meta name="author" content={author} />)}
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.6.0/css/reveal.min.css"
integrity="sha512-V5fKCVKOFy36w8zJmLzPH5R6zU6KvuHOvxfMRczx2ZeqTjKRGSBO9yiZjCKEJS3n6EmENwrH/xvSwXqxje+VVA=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/reveal.js/3.6.0/plugin/highlight/monokai.css"
integrity="sha512-Ww3X8n7Y0V9xFWft1PYfLEESkLKZYjWHmaZgo6HZu4R0mX5D+sNK5YoLSgE10aS2SDXnppWRXndYiN7n/LxV3A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
</Fragment>
<div class="reveal">
<div class="slides">

View File

@ -1,21 +1,17 @@
---
import SlideLayout from "@layouts/SlideLayout.astro";
import { getCollection, render } from "astro:content";
import { getSlides } from "@utils/getSlides";
export const getStaticPaths = async () =>
(await getCollection("slides")).map((slide) => ({
params: { id: slide.id },
props: { slide },
}));
export const getStaticPaths = () =>
getSlides().map((slide) => ({ params: { id: slide.id }, props: { slide } }));
const { slide } = Astro.props;
const { Content } = await render(slide);
---
<SlideLayout
title={slide.data.title}
authors={slide.data.authors}
description={slide.data.description}
title={slide.title}
authors={slide.authors}
description={slide.description}
>
<Content />
<slide.default />
</SlideLayout>

View File

@ -1,33 +1,33 @@
---
import Layout from "@layouts/BaseLayout.astro";
import { getCollection } from "astro:content";
import { getSlides } from "@utils/getSlides";
const slides = (await getCollection("slides")).sort((c1, c2) =>
c1.data.title > c2.data.title ? -1 : 1
);
const slides = getSlides()
.filter(({ draft }) => !draft)
.sort((c1, c2) => (c1.title > c2.title ? -1 : 1));
---
<Layout title="Home">
<h1>Slides</h1>
<p>Here you can find a list of all available slides:</p>
<h1>Слайды</h1>
<p>Список всех доступных слайдов:</p>
{
slides.map((slide) => (
<a href={`/${slide.id}`}>
<h2>{slide.data.title}</h2>
<p>{slide.data.description}</p>
<small>{slide.data.publishedAt.toLocaleDateString()}</small>
<h2>{slide.title}</h2>
<p>{slide.description}</p>
<small>{new Date(slide.publishedAt).toLocaleDateString()}</small>
</a>
))
}
</Layout>
<style lang="scss">
@use "@themes/hnrq";
<style>
@import "@theme";
body {
background-color: hnrq.$backgroundColor;
color: hnrq.$mainColor;
font-family: hnrq.$mainFont;
background-color: var(--background);
color: var(--main-color);
font-family: var(--main-font);
box-sizing: border-box;
display: flex;
flex-direction: column;
@ -43,17 +43,17 @@ const slides = (await getCollection("slides")).sort((c1, c2) =>
h4,
h5,
h6 {
font-family: hnrq.$headingFont;
font-family: var(--heading-font);
}
a {
padding: 2rem;
color: hnrq.$mainColor;
color: var(--main-color);
text-decoration: none;
transition: all 0.2s ease-in-out;
transition: filter 0.2s ease-in-out;
background-color: var(--background);
&:hover {
background: hnrq.$mainColor;
color: hnrq.$backgroundColor;
filter: invert(1);
}
}
</style>

View File

@ -0,0 +1,182 @@
---
export const title = "Сигналы в Linux и их применение в Node.js";
export const authors = ["Карпич Дмитрий"];
export const publishedAt = "2025-05-28";
export const description =
"Cигналы, их типы и как они используются в Node.js для управления процессами и обработки событий.";
---
<section>
<section>
<h2>Что такое сигналы?</h2>
</section>
<section>
<strong>Определение:</strong> сигналы в Unix-подобных системах - это асинхронные
события, которые могут быть отправлены процессу операционной системы или
другому процессу.
</section>
<section>
<strong>Аналогия:</strong> сигналы можно сравнить с "уведомлениями" для процессов
(например, "завершись", "приостановись").
</section>
<section>
<strong>Примеры ситуаций:</strong> приостановка процесса с помощью команды
Ctrl+Z, команда Ctrl+C, завершение процесса, ошибка сегментации и т.п.
</section>
</section>
<section>
<section>
<h2>Типы сигналов</h2>
</section>
<section>
<strong>Сигналы по умолчанию:</strong> SIGINT, SIGTERM, SIGKILL, SIGSTOP
и т.д.
</section>
<section>
<strong>Сигналы пользовательских действий:</strong> SIGUSR1, SIGUSR2.
</section>
<section>
<strong>Сигналы от системных ошибок:</strong> SIGSEGV, SIGFPE, SIGPIPE и
т.д.
</section>
</section>
<section>
<section>
<h2>Отправка сигналов процессам</h2>
</section>
<section>
<h2>Команда kill</h2>
<p>используется для отправки сигналов процессу.</p>
<pre><code data-trim class="language-bash">kill -SIGINT 1234</code></pre>
</section>
<section>
<h2>Команда killall</h2>
<p>
используется для отправки сигналов всем процессам с определенным
именем.
</p>
<pre><code data-trim class="language-bash">killall -SIGTERM myprocess</code></pre>
</section>
<section>
<h2>Команда pkill</h2>
<p>
используется для отправки сигналов процессам, соответствующим
определенному шаблону.
</p>
<pre><code data-trim class="language-bash">pkill -SIGKILL myprocess</code></pre>
</section>
</section>
<section>
<section>
<h2>Некоторые сигналы в Linux</h2>
</section>
<section>
<ul>
<li>
<strong>SIGHUP (1)</strong> — потеря соединения с управляющим терминалом
</li>
<li>
<strong>SIGINT (2)</strong> — прерывание процесса с клавиатуры (Ctrl+C)
</li>
<li>
<strong>SIGQUIT (3)</strong> — аварийное завершение
</li>
<li>
<strong>SIGABRT (6)</strong> — аварийная остановка процесса
</li>
<li>
<strong>SIGKILL (9)</strong> — немедленное завершение (не блокируется)
</li>
<li>
<strong>SIGTERM (15)</strong> — стандартный сигнал для завершения
</li>
<li>
<strong>SIGSTOP (17,19,23)</strong> — приостановка процесса до SIGCONT
</li>
</ul>
</section>
</section>
<section>
<section>
<h2>Обработка сигналов в node.js</h2>
</section>
<section>
<p>
В Node.js можно обрабатывать сигналы с помощью модуля <code
>process</code
>.
</p>
<pre><code data-trim class="language-javascript">
{`process.on('SIGINT', () => {
console.log('Received SIGINT. Exiting...');
process.exit(0);
});`}
</code></pre>
</section>
</section>
<section>
<section>
<h2>Зачем перехватывать сигналы в Node.js?</h2>
</section>
<section>
<h3>Graceful Shutdown</h3>
<p>
Корректное завершение работы: закрытие соединений с БД, остановка
HTTP-сервера перед выходом.
</p>
</section>
<section>
<h3>Кастомизация поведения</h3>
<p>
Выполнение кастомной логики перед завершением: логирование, отправка
метрик, уведомления.
</p>
</section>
<section>
<h3>Игнорирование сигналов</h3>
<p>
Предотвращение завершения процесса по умолчанию (например, для
фоновых демонов).
</p>
</section>
<section>
<h3>Перезагрузка конфигурации</h3>
<p>
Обновление настроек приложения без перезапуска (по сигналу SIGHUP).
</p>
</section>
<section>
<h3>Обработка ошибок</h3>
<p>
Cleanup при аварийных ситуациях: освобождение ресурсов, сохранение
состояния.
</p>
</section>
<section>
<h3>Интеграция с оркестраторами</h3>
<p>
Корректная реакция на сигналы от Docker/Kubernetes (например,
SIGTERM при остановке контейнера).
</p>
</section>
</section>
<section>
<h2>Демонстрация тестового кода с перехватом основных сигналов</h2>
<p>
Переходим к коду.
</p>
</section>

View File

@ -1,148 +0,0 @@
---
title: "Finite State Machines with XState for Game Development using Three.js"
authors: ["Henrique Ramos"]
publishedAt: 2024-12-13
description: "Using XState Finite State Machines to coordinate character actions in a Three.js game"
---
<section>
<section>
<h2>What is a Finite State Machine (FSM)?</h2>
<ul>
<li>
A mathematical model of computation that consists of a set of states and
transitions between those states.
</li>
<li>Used to represent systems with a finite number of states.</li>
<li>Well-suited for modeling complex game behaviors.</li>
</ul>
</section>
<section>
<h2>Visualizing FSMs</h2>
<iframe
title="Stately.AI FSM visualization"
src="https://stately.ai/registry/editor/embed/f135a841-e83d-4bdc-8a86-9ebe0f8ff3cd?machineId=9e9d23aa-1c58-44a7-a20d-538fc7017e37"
width="1000"
height="600"
></iframe>
</section>
</section>
<section>
<h2>Why use FSMs in Game Development?</h2>
<ul>
<li>Improve code organization and readability.</li>
<li>Facilitate easier testing and debugging.</li>
<li>Enable more flexible and reactive game behaviors.</li>
<li>Promote code reusability.</li>
</ul>
</section>
<section>
<h2>Introducing XState</h2>
<ul>
<li>
A powerful and flexible state chart library for building complex state
machines.
</li>
<li>
Well-suited for game development due to its visual state charts and
hierarchical state machines.
</li>
<li>Provides a declarative approach to state machine design.</li>
</ul>
</section>
<section>
<h2>Key Features of XState</h2>
<ul>
<li>Visual state charts for intuitive design.</li>
<li>Hierarchical state machines for complex behaviors.</li>
<li>Context and actions for managing state data and side effects.</li>
<li>Guards and conditions for controlling transitions.</li>
<li>Parallel states for concurrent behaviors.</li>
</ul>
</section>
<section>
<h2>Integrating XState with Three.js</h2>
<ul>
<li>Create a Three.js scene and renderer.</li>
<li>Define game objects and their initial states.</li>
<li>Initialize an XState machine to manage the game's overall state.</li>
</ul>
</section>
<section>
<h2>Mapping XState States to Game Objects</h2>
<ul>
<li>Use XState's context to store references to game objects.</li>
<li>Define actions to update game object properties and behaviors.</li>
<li>Trigger state transitions based on game events or conditions.</li>
</ul>
</section>
<section>
<h2>Handling Game Events and Input</h2>
<ul>
<li>Listen for user input events (e.g., keyboard, mouse, gamepad).</li>
<li>
Use XState's
<code>send</code>
function to trigger state transitions based on input.
</li>
<li>Update game object states and behaviors accordingly.</li>
</ul>
</section>
<section>
<h2>Advanced Techniques</h2>
<ul>
<li>Parallel States</li>
<li>History States</li>
<li>Delay Transitions</li>
<li>Custom Guards and Conditions</li>
</ul>
</section>
<section>
<h2>Conclusion</h2>
By leveraging XState, you can create more robust, flexible, and maintainable
game behaviors. XState's powerful features and intuitive state chart design
make it an excellent choice for game development using Three.js.
</section>
<section>
<h2>Finite State Machine</h2>
<iframe
height="600"
width="1000"
title="HNRQ.dev character FSM"
src="https://stately.ai/registry/editor/embed/d947654c-2a61-4870-bc38-d34a5224ea00?machineId=b2a3771b-2530-4ce7-9f71-380eee380537"
></iframe>
</section>
<section>
<h2>FSM in action</h2>
<iframe src="https://hnrq.dev" title="HNRQ.dev" height="600" width="1000"></iframe>
<small>Available at <a href="https://hnrq.dev">hnrq.dev</a></small>
</section>
<section>
<h2>References</h2>
<ul>
<li>
XState Documentation:
<a href="https://xstate.js.org/">https://xstate.js.org/</a>
</li>
<li>
Three.js Documentation:
<a href="https://threejs.org/">https://threejs.org/</a>
</li>
<li>
Finite State Machines:
<a href="https://en.wikipedia.org/wiki/Finite-state_machine"
>https://en.wikipedia.org/wiki/Finite-state_machine</a
>
</li>
</ul>
</section>

318
src/theme/index.css Normal file
View File

@ -0,0 +1,318 @@
@import "./variables";
@import url("https://fonts.googleapis.com/css2?family=Anaheim:wght@400..800&family=Overpass+Mono:wght@300..700&display=swap");
body {
background: var(--background);
}
section.has-dark-background,
h1,
h2,
h3,
h4,
h5,
h6 {
color: var(--main-color);
}
.reveal-viewport {
background: var(--background);
background-color: var(--background-color);
}
.reveal {
font-family: var(--main-font);
font-size: var(--main-font-size);
font-weight: normal;
color: var(--main-color);
}
.reveal ::selection {
color: var(--selection-color);
background: var(--selection-background-color);
text-shadow: none;
}
.reveal ::-moz-selection {
color: var(--selection-color);
background: var(--selection-background-color);
text-shadow: none;
}
.reveal .slides section,
.reveal .slides section > section {
line-height: 1.3;
font-weight: inherit;
}
/*********************************************
* HEADERS
*********************************************/
.reveal :is(h1, h2, h3, h4, h5, h6) {
margin: var(--heading-margin);
color: var(--heading-color);
font-family: var(--heading-font);
font-weight: var(--heading-font-weight);
line-height: var(--heading-line-height);
letter-spacing: var(--heading-letter-spacing);
text-transform: var(--heading-text-transform);
text-shadow: var(--heading-text-shadow);
word-wrap: break-word;
}
.reveal h1 {
font-size: var(--heading1-size);
}
.reveal h2 {
font-size: var(--heading2-size);
}
.reveal h3 {
font-size: var(--heading3-size);
}
.reveal h4 {
font-size: var(--heading4-size);
}
.reveal h1 {
text-shadow: var(--heading1-text-shadow);
}
/*********************************************
* OTHER
*********************************************/
.reveal p {
margin: var(--block-margin) 0;
line-height: 1.3;
}
/* Remove trailing margins after titles */
.reveal :is(h1, h2, h3, h4, h5, h6):last-child {
margin-bottom: 0;
}
/* Ensure certain elements are never larger than the slide itself */
.reveal :is(img, video, iframe) {
max-width: 95%;
max-height: 95%;
}
.reveal :is(strong, b) {
font-weight: bold;
}
.reveal em {
font-style: italic;
}
.reveal :is(ol, dl, ul) {
display: inline-block;
text-align: left;
margin: 0 0 0 1em;
}
.reveal ol {
list-style-type: decimal;
}
.reveal ul {
list-style-type: disc;
}
.reveal ul ul {
list-style-type: square;
}
.reveal ul ul ul {
list-style-type: circle;
}
.reveal :is(ul ul, ul ol, ol ol, ol ul) {
display: block;
margin-left: 40px;
}
.reveal dt {
font-weight: bold;
}
.reveal dd {
margin-left: 40px;
}
.reveal blockquote {
display: block;
position: relative;
width: 70%;
margin: var(--block-margin) auto;
padding: 5px;
font-style: italic;
background: rgba(255, 255, 255, 0.05);
box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2);
}
.reveal blockquote :is(p:first-child, p:last-child) {
display: inline-block;
}
.reveal q {
font-style: italic;
}
.reveal pre {
display: block;
position: relative;
width: 90%;
margin: var(--block-margin) auto;
text-align: left;
font-size: 0.55em;
font-family: var(--code-font);
line-height: 1.2em;
word-wrap: break-word;
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15);
}
.reveal code {
font-family: var(--code-font);
text-transform: none;
tab-size: 2;
}
.reveal pre code {
display: block;
padding: 5px;
overflow: auto;
max-height: 400px;
word-wrap: normal;
}
.reveal .code-wrapper {
white-space: normal;
}
.reveal .code-wrapper code {
white-space: pre;
}
.reveal table {
margin: auto;
border-collapse: collapse;
border-spacing: 0;
}
.reveal table th {
font-weight: bold;
}
.reveal table :is(th, td) {
text-align: left;
padding: 0.2em 0.5em 0.2em 0.5em;
border-bottom: 1px solid;
}
.reveal table :is(th[align="center"], td[align="center"]) {
text-align: center;
}
.reveal table :is(th[align="right"], td[align="right"]) {
text-align: right;
}
.reveal table tbody tr:last-child :is(th, td) {
border-bottom: none;
}
.reveal sup {
vertical-align: super;
font-size: smaller;
}
.reveal sub {
vertical-align: sub;
font-size: smaller;
}
.reveal small {
display: inline-block;
font-size: 0.6em;
line-height: 1.2em;
vertical-align: top;
}
.reveal small * {
vertical-align: top;
}
.reveal img {
margin: var(--block-margin) 0;
}
/*********************************************
* LINKS
*********************************************/
.reveal a {
color: var(--link-color);
text-decoration: none;
transition: color 0.15s ease;
}
.reveal a:hover {
color: var(--link-colohover);
text-shadow: none;
border: none;
}
.reveal .roll span:after {
color: #fff;
background: var(--link-colodark);
}
/*********************************************
* Frame helper
*********************************************/
.reveal .frame {
border: 4px solid var(--main-color);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
}
.reveal a .frame {
transition: all 0.15s linear;
}
.reveal a:hover .frame {
border-color: var(--link-color);
box-shadow: 0 0 20px rgba(0, 0, 0, 0.55);
}
/*********************************************
* NAVIGATION CONTROLS
*********************************************/
.reveal .controls {
color: var(--link-color);
}
/*********************************************
* PROGRESS BAR
*********************************************/
.reveal .progress {
background: rgba(0, 0, 0, 0.2);
color: var(--link-color);
}
/*********************************************
* PRINT BACKGROUND
*********************************************/
@media print {
.backgrounds {
background-color: var(--background-color);
}
}

38
src/theme/variables.css Normal file
View File

@ -0,0 +1,38 @@
:root {
--background: #000;
--background-color: #000;
--base-spacing: 8px;
--main-font: "Anaheim", sans-serif;
--main-font-size: 40px;
--main-color: #eee;
--block-margin: 20px;
--heading-margin: 0 0 20px 0;
--heading-font: "Overpass Mono", monospace;
--heading-color: #fff;
--heading-line-height: 1.2;
--heading-letter-spacing: normal;
--heading-text-transform: none;
--heading-text-shadow: #aaa;
--heading-font-weight: 800;
--heading1-text-shadow: #aaa;
--heading1-size: 3.77em;
--heading2-size: 3.5rem;
--heading3-size: 1.55em;
--heading4-size: 1em;
--code-font: monospace;
--link-color: #aaaaff;
--link-color-dark: color-mix(in srgb, var(--link-color), #000 10%);
--link-color-hover: color-mix(in srgb, var(--link-color), #000 20%);
--selection-background-color: #888;
--selection-color: #fff;
--overlay-element-bg-color: 0, 0, 0;
--overlay-element-fg-color: 240, 240, 240;
}

View File

@ -1,36 +0,0 @@
// Default mixins and settings -----------------
@use "sass:color";
@import "reveal.js/css/theme/template/mixins";
@import "reveal.js/css/theme/template/settings";
// ---------------------------------------------
// Include theme-specific fonts
@import url("https://fonts.googleapis.com/css2?family=Anaheim:wght@400..800&family=Overpass+Mono:wght@300..700&display=swap");
$mainColor: #fff;
$headingColor: $mainColor;
$headingTextShadow: #aaa;
$backgroundColor: #000;
$linkColor: #aaaaff;
$linkColorHover: color.scale($linkColor, $lightness: 20%);
$selectionBackgroundColor: #888;
$overlayElementBgColor: 0, 0, 0;
$overlayElementFgColor: 240, 240, 240;
$headingFont: "Overpass Mono", monospace;
$heading2Size: 3.5rem;
$headingFontWeight: 800;
$headingTextTransform: none;
$mainFont: "Anaheim", sans-serif;
// Background generator
@mixin bodyBackground() {
background: $backgroundColor;
}
// Change text colors against dark slide backgrounds
@include dark-bg-text-color($backgroundColor);
// Theme template ------------------------------
@import "reveal.js/css/theme/template/theme";
// ---------------------------------------------

View File

@ -0,0 +1,39 @@
import type { AstroInstance } from "astro";
import { type } from "arktype";
type Opts<T extends Record<string, unknown>> = {
files: Record<string, T>;
schema: type;
};
const astroPageType = type({
"draft?": "boolean",
});
/**
* Get all Astro pages from a given path.
* @param opts - The options for the function.
* @param opts.files - The files to get the Astro pages from. Use import.meta.glob eager: true.
* @param opts.requiredFields - The required fields for the Astro pages.
* @returns The Astro pages in the root of the given path, or looks for index.astro files in subdirectories (single level).
*/
const getAstroPages = <T extends Record<string, unknown> & AstroInstance>({
files,
schema,
}: Opts<T>) =>
Object.values(files).map((module) => {
const validate = schema.and(astroPageType)(module);
if (validate instanceof type.errors)
throw new Error(`Invalid module${module.file}: ${validate.summary}`);
return {
id: (
module.file
.split("/")
.at(module.file.includes("index.astro") ? -2 : -1) ?? ""
).replace(".astro", ""),
...module,
};
});
export default getAstroPages;

26
src/utils/getSlides.ts Normal file
View File

@ -0,0 +1,26 @@
import type { AstroInstance } from "astro";
import getAstroPages from "./getAstroPages";
import { type } from "arktype";
const schema = type({
title: "string",
description: "string",
authors: "string[]",
publishedAt: "string",
});
type Slide = AstroInstance & typeof schema.infer & { [key: string]: unknown };
/**
/**
* Get all slides from the slides directory.
* @returns The slides.
*/
export const getSlides = () =>
getAstroPages<Slide>({
files: import.meta.glob<true, string, Slide>(
["@slides/**/index.astro", "@slides/*.astro"],
{ eager: true },
),
schema,
});

5
svelte.config.js Normal file
View File

@ -0,0 +1,5 @@
import { vitePreprocess } from "@astrojs/svelte";
export default {
preprocess: vitePreprocess(),
};

View File

@ -7,11 +7,17 @@
"@components/*": ["src/components/*"],
"@layouts/*": ["src/layouts/*"],
"@assets/*": ["src/assets/*"],
"@themes/*": ["src/themes/*"]
"@theme/*": ["src/theme/*"],
"@theme": ["src/theme/index.css"],
"@slides/*": ["src/slides/*"],
"@utils/*": ["src/utils/*"]
},
"moduleResolution": "Bundler",
"strictNullChecks": true,
"allowJs": true
"allowJs": true,
"strict": true,
"skipLibCheck": true,
"exactOptionalPropertyTypes": true
},
"exclude": ["dist"]
}

2787
yarn.lock Normal file

File diff suppressed because it is too large Load Diff