Compare commits
10 Commits
236b2cad7c
...
97977d44d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97977d44d8 | ||
|
|
8f90133e1e | ||
|
|
1c281f220d | ||
|
|
967ad59394 | ||
|
|
32af9bde3d | ||
|
|
be2a96ac22 | ||
|
|
03cd0f35bc | ||
|
|
0614e2b7ef | ||
|
|
8ca43b3785 | ||
|
|
9955f9ed7b |
14
.vscode/settings.json
vendored
Normal file
14
.vscode/settings.json
vendored
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,9 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from "astro/config";
|
||||||
|
|
||||||
|
import svelte from "@astrojs/svelte";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({});
|
export default defineConfig({
|
||||||
|
integrations: [svelte()],
|
||||||
|
});
|
||||||
|
|||||||
30
biome.json
Normal file
30
biome.json
Normal 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
60
example/server.cjs
Executable 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');
|
||||||
|
});
|
||||||
14
package.json
14
package.json
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "presentations",
|
"name": "slides",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -9,11 +9,15 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astro": "^5.0.5",
|
"@astrojs/svelte": "^7.0.4",
|
||||||
|
"astro": "^5.2.3",
|
||||||
"reveal.js": "^5.1.0",
|
"reveal.js": "^5.1.0",
|
||||||
"sass": "^1.83.0"
|
"svelte": "^5.19.7",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"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
3081
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -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 };
|
|
||||||
@ -5,7 +5,9 @@ export interface Props {
|
|||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
import Layout from "./BaseLayout.astro";
|
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;
|
const { title, authors, description } = Astro.props;
|
||||||
---
|
---
|
||||||
@ -14,20 +16,6 @@ const { title, authors, description } = Astro.props;
|
|||||||
<Fragment slot="head">
|
<Fragment slot="head">
|
||||||
{description && <meta name="description" content={description} />}
|
{description && <meta name="description" content={description} />}
|
||||||
{authors.map((author) => <meta name="author" content={author} />)}
|
{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>
|
</Fragment>
|
||||||
<div class="reveal">
|
<div class="reveal">
|
||||||
<div class="slides">
|
<div class="slides">
|
||||||
@ -38,7 +26,7 @@ const { title, authors, description } = Astro.props;
|
|||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script >
|
||||||
import Reveal from "reveal.js";
|
import Reveal from "reveal.js";
|
||||||
import Highlight from "reveal.js/plugin/highlight/highlight.esm.js";
|
import Highlight from "reveal.js/plugin/highlight/highlight.esm.js";
|
||||||
import Zoom from "reveal.js/plugin/zoom/zoom.esm.js";
|
import Zoom from "reveal.js/plugin/zoom/zoom.esm.js";
|
||||||
|
|||||||
@ -1,21 +1,17 @@
|
|||||||
---
|
---
|
||||||
import SlideLayout from "@layouts/SlideLayout.astro";
|
import SlideLayout from "@layouts/SlideLayout.astro";
|
||||||
import { getCollection, render } from "astro:content";
|
import { getSlides } from "@utils/getSlides";
|
||||||
|
|
||||||
export const getStaticPaths = async () =>
|
export const getStaticPaths = () =>
|
||||||
(await getCollection("slides")).map((slide) => ({
|
getSlides().map((slide) => ({ params: { id: slide.id }, props: { slide } }));
|
||||||
params: { id: slide.id },
|
|
||||||
props: { slide },
|
|
||||||
}));
|
|
||||||
|
|
||||||
const { slide } = Astro.props;
|
const { slide } = Astro.props;
|
||||||
const { Content } = await render(slide);
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<SlideLayout
|
<SlideLayout
|
||||||
title={slide.data.title}
|
title={slide.title}
|
||||||
authors={slide.data.authors}
|
authors={slide.authors}
|
||||||
description={slide.data.description}
|
description={slide.description}
|
||||||
>
|
>
|
||||||
<Content />
|
<slide.default />
|
||||||
</SlideLayout>
|
</SlideLayout>
|
||||||
|
|||||||
@ -1,33 +1,33 @@
|
|||||||
---
|
---
|
||||||
import Layout from "@layouts/BaseLayout.astro";
|
import Layout from "@layouts/BaseLayout.astro";
|
||||||
import { getCollection } from "astro:content";
|
import { getSlides } from "@utils/getSlides";
|
||||||
|
|
||||||
const slides = (await getCollection("slides")).sort((c1, c2) =>
|
const slides = getSlides()
|
||||||
c1.data.title > c2.data.title ? -1 : 1
|
.filter(({ draft }) => !draft)
|
||||||
);
|
.sort((c1, c2) => (c1.title > c2.title ? -1 : 1));
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Home">
|
<Layout title="Home">
|
||||||
<h1>Slides</h1>
|
<h1>Слайды</h1>
|
||||||
<p>Here you can find a list of all available slides:</p>
|
<p>Список всех доступных слайдов:</p>
|
||||||
{
|
{
|
||||||
slides.map((slide) => (
|
slides.map((slide) => (
|
||||||
<a href={`/${slide.id}`}>
|
<a href={`/${slide.id}`}>
|
||||||
<h2>{slide.data.title}</h2>
|
<h2>{slide.title}</h2>
|
||||||
<p>{slide.data.description}</p>
|
<p>{slide.description}</p>
|
||||||
<small>{slide.data.publishedAt.toLocaleDateString()}</small>
|
<small>{new Date(slide.publishedAt).toLocaleDateString()}</small>
|
||||||
</a>
|
</a>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<style lang="scss">
|
<style>
|
||||||
@use "@themes/hnrq";
|
@import "@theme";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: hnrq.$backgroundColor;
|
background-color: var(--background);
|
||||||
color: hnrq.$mainColor;
|
color: var(--main-color);
|
||||||
font-family: hnrq.$mainFont;
|
font-family: var(--main-font);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -43,17 +43,17 @@ const slides = (await getCollection("slides")).sort((c1, c2) =>
|
|||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
font-family: hnrq.$headingFont;
|
font-family: var(--heading-font);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
color: hnrq.$mainColor;
|
color: var(--main-color);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: all 0.2s ease-in-out;
|
transition: filter 0.2s ease-in-out;
|
||||||
|
background-color: var(--background);
|
||||||
&:hover {
|
&:hover {
|
||||||
background: hnrq.$mainColor;
|
filter: invert(1);
|
||||||
color: hnrq.$backgroundColor;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
182
src/slides/signals/index.astro
Normal file
182
src/slides/signals/index.astro
Normal 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>
|
||||||
@ -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
318
src/theme/index.css
Normal 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
38
src/theme/variables.css
Normal 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;
|
||||||
|
}
|
||||||
@ -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";
|
|
||||||
// ---------------------------------------------
|
|
||||||
39
src/utils/getAstroPages.ts
Normal file
39
src/utils/getAstroPages.ts
Normal 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
26
src/utils/getSlides.ts
Normal 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
5
svelte.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { vitePreprocess } from "@astrojs/svelte";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
preprocess: vitePreprocess(),
|
||||||
|
};
|
||||||
@ -7,11 +7,17 @@
|
|||||||
"@components/*": ["src/components/*"],
|
"@components/*": ["src/components/*"],
|
||||||
"@layouts/*": ["src/layouts/*"],
|
"@layouts/*": ["src/layouts/*"],
|
||||||
"@assets/*": ["src/assets/*"],
|
"@assets/*": ["src/assets/*"],
|
||||||
"@themes/*": ["src/themes/*"]
|
"@theme/*": ["src/theme/*"],
|
||||||
|
"@theme": ["src/theme/index.css"],
|
||||||
|
"@slides/*": ["src/slides/*"],
|
||||||
|
"@utils/*": ["src/utils/*"]
|
||||||
},
|
},
|
||||||
"moduleResolution": "Bundler",
|
"moduleResolution": "Bundler",
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
"allowJs": true
|
"allowJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"exactOptionalPropertyTypes": true
|
||||||
},
|
},
|
||||||
"exclude": ["dist"]
|
"exclude": ["dist"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user