mirror of
https://github.com/sergeyyarkov/yarkov.tech.git
synced 2026-10-11 10:29:21 +03:00
upd deps + build toc
This commit is contained in:
parent
69af4e882d
commit
56132501cd
878
package-lock.json
generated
878
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -18,13 +18,13 @@
|
|||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/image": "^0.16.2",
|
"@astrojs/image": "^0.16.6",
|
||||||
"@astrojs/rss": "^2.3.1",
|
"@astrojs/rss": "^2.3.2",
|
||||||
"@astrojs/sitemap": "^1.2.1",
|
"@astrojs/sitemap": "^1.2.2",
|
||||||
"@astrojs/solid-js": "^2.1.0",
|
"@astrojs/solid-js": "^2.1.0",
|
||||||
"@netlify/functions": "^1.4.0",
|
"@netlify/functions": "^1.4.0",
|
||||||
"@sindresorhus/slugify": "^2.2.0",
|
"@sindresorhus/slugify": "^2.2.0",
|
||||||
"astro": "^2.1.7",
|
"astro": "^2.3.0",
|
||||||
"astro-icon": "^0.8.0",
|
"astro-icon": "^0.8.0",
|
||||||
"astro-netlify-cms": "^0.5.3",
|
"astro-netlify-cms": "^0.5.3",
|
||||||
"hast-util-to-string": "^2.0.0",
|
"hast-util-to-string": "^2.0.0",
|
||||||
@ -36,10 +36,10 @@
|
|||||||
"rehype-autolink-headings": "^6.1.1",
|
"rehype-autolink-headings": "^6.1.1",
|
||||||
"rehype-slug": "^5.1.0",
|
"rehype-slug": "^5.1.0",
|
||||||
"sharp": "^0.32.0",
|
"sharp": "^0.32.0",
|
||||||
"solid-js": "^1.6.15"
|
"solid-js": "^1.7.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/mime-types": "^2.1.1",
|
"@types/mime-types": "^2.1.1",
|
||||||
"sass": "^1.60.0"
|
"sass": "^1.62.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/components/TableOfContents.astro
Normal file
23
src/components/TableOfContents.astro
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
import { buildToc } from "@root/utils";
|
||||||
|
import type { MarkdownHeading } from "astro";
|
||||||
|
import TableOfContentsHeading from "./TableOfContentsHeading.astro";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
headings: MarkdownHeading[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const toc = buildToc(Astro.props.headings);
|
||||||
|
---
|
||||||
|
|
||||||
|
<nav class="toc">
|
||||||
|
<h2>Содержание</h2>
|
||||||
|
<ul>
|
||||||
|
{toc.map((heading) => <TableOfContentsHeading heading={heading} />)}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<style lang="scss" is:global>
|
||||||
|
.toc {
|
||||||
|
}
|
||||||
|
</style>
|
||||||
16
src/components/TableOfContentsHeading.astro
Normal file
16
src/components/TableOfContentsHeading.astro
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
import type { MarkdownHeadingToc } from "@root/utils";
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
heading: MarkdownHeadingToc;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { heading } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<a href={`#${heading.slug}`}>{heading.text}</a>
|
||||||
|
<ul>
|
||||||
|
{heading.subheadings.map((subheading) => <Astro.self heading={subheading} />)}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
@ -412,7 +412,7 @@ Now after opening the port you can send commands, don't forget to set the carria
|
|||||||
|
|
||||||
The web application will consist of just a few elements: an input box where we can write our commands, buttons to open and close the serial port, connection status. You can see the finished version [here](https://web-serial-example.netlify.app/).
|
The web application will consist of just a few elements: an input box where we can write our commands, buttons to open and close the serial port, connection status. You can see the finished version [here](https://web-serial-example.netlify.app/).
|
||||||
|
|
||||||
### Create HTML markup and styles
|
## Create HTML markup and styles
|
||||||
|
|
||||||
The HTML markup will look like this:
|
The HTML markup will look like this:
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,6 @@ tags:
|
|||||||
pubDate: 2022-11-04T21:00:00.000Z
|
pubDate: 2022-11-04T21:00:00.000Z
|
||||||
updatedDate: 2023-02-13T16:34:23.326Z
|
updatedDate: 2023-02-13T16:34:23.326Z
|
||||||
---
|
---
|
||||||
|
|
||||||
# Что вы узнаете
|
# Что вы узнаете
|
||||||
|
|
||||||
По итогу вы узнаете, как отправлять и принимать данные от устройства с последовательным портом в браузере, разработаете демонстрационное веб-приложение, которое отправляет команды в микроконтроллер с последовательным портом и напишите прошивку для микроконтроллера [AVR128DA48](http://ww1.microchip.com/downloads/en/DeviceDoc/40002183A.pdf) для обработки этих команд. Готовый вариант проекта можно посмотреть [здесь](https://web-serial-example.netlify.app/), исходники загружены в [репозиторий](https://github.com/sergeyyarkov/avr128da48_web-serial-example) на GitHub.
|
По итогу вы узнаете, как отправлять и принимать данные от устройства с последовательным портом в браузере, разработаете демонстрационное веб-приложение, которое отправляет команды в микроконтроллер с последовательным портом и напишите прошивку для микроконтроллера [AVR128DA48](http://ww1.microchip.com/downloads/en/DeviceDoc/40002183A.pdf) для обработки этих команд. Готовый вариант проекта можно посмотреть [здесь](https://web-serial-example.netlify.app/), исходники загружены в [репозиторий](https://github.com/sergeyyarkov/avr128da48_web-serial-example) на GitHub.
|
||||||
@ -49,17 +48,17 @@ WebSerial API - это браузерный API, который предоста
|
|||||||
|
|
||||||
UART передает данные последовательно по одному биту в одном из трех режимов:
|
UART передает данные последовательно по одному биту в одном из трех режимов:
|
||||||
|
|
||||||
- односторонний режим - данные отправляются только в одном направлении, от передатчика к приемнику
|
* односторонний режим - данные отправляются только в одном направлении, от передатчика к приемнику
|
||||||
- полудуплексный режим - устройства могут передавать и принимать данные по очереди
|
* полудуплексный режим - устройства могут передавать и принимать данные по очереди
|
||||||
- полнодуплексный режим - устройства могут передавать и принимать данные одновременно
|
* полнодуплексный режим - устройства могут передавать и принимать данные одновременно
|
||||||
|
|
||||||
Так как в этом проекте предполагается использование асинхронной версии UART, то соответственно необходимо устанавливать **одинаковую** скорость передачи битов в секунду (`baud rate`, `bps`) на двух устройствах. Наиболее распространенные скорости являются: **4800, 9600, 19.2 К, 57.6К и 115.2К.**
|
Так как в этом проекте предполагается использование асинхронной версии UART, то соответственно необходимо устанавливать **одинаковую** скорость передачи битов в секунду (`baud rate`, `bps`) на двух устройствах. Наиболее распространенные скорости являются: **4800, 9600, 19.2 К, 57.6К и 115.2К.**
|
||||||
|
|
||||||
Данные передаются в виде так называемых `пакетов`, где каждый пакет содержит в себе:
|
Данные передаются в виде так называемых `пакетов`, где каждый пакет содержит в себе:
|
||||||
|
|
||||||
- стартовый и стоповый биты - стартовый бит сигнализирует о поступлении битов данных, стоповый соответственно о конце данных.
|
* стартовый и стоповый биты - стартовый бит сигнализирует о поступлении битов данных, стоповый соответственно о конце данных.
|
||||||
- биты данных - пользовательские данные, которые поступают сразу после стартового бита, может содержаться от 5 до 8 битов.
|
* биты данных - пользовательские данные, которые поступают сразу после стартового бита, может содержаться от 5 до 8 битов.
|
||||||
- бит четности - необязательный бит, который идет после битов данных и перед стоповым битом и используется для обнаружения ошибок.
|
* бит четности - необязательный бит, который идет после битов данных и перед стоповым битом и используется для обнаружения ошибок.
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img src="/media/uart-packet.jpg" width="700" height="300" alt="Структура UART пакета" format="webp">
|
<img src="/media/uart-packet.jpg" width="700" height="300" alt="Структура UART пакета" format="webp">
|
||||||
@ -84,7 +83,7 @@ UART передает данные последовательно по одно
|
|||||||
|
|
||||||
Как я упомянул ранее, прошивка будет написана для микроконтроллера AVR128DA48. На самом деле неважно какой микроконтроллер вы используете, вам достаточно нужно уметь отправлять и принимать данные используя UART периферию вашего микроконтроллера.
|
Как я упомянул ранее, прошивка будет написана для микроконтроллера AVR128DA48. На самом деле неважно какой микроконтроллер вы используете, вам достаточно нужно уметь отправлять и принимать данные используя UART периферию вашего микроконтроллера.
|
||||||
|
|
||||||
Суть программы будет очень проста: необходимо написать некий **_обработчик команд_** который будет принимать на вход строку (например `led_toggle`) и вызывать необходимую функцию, которая будет выполнять какое-то действие, в данном случае переключать светодиод.
|
Суть программы будет очень проста: необходимо написать некий ***обработчик команд*** который будет принимать на вход строку (например `led_toggle`) и вызывать необходимую функцию, которая будет выполнять какое-то действие, в данном случае переключать светодиод.
|
||||||
|
|
||||||
Постановка задачи ясна, теперь рассмотрим реализацию данной программы. Я буду использовать среду разработки MPLAB X IDE и язык C для написания прошивки.
|
Постановка задачи ясна, теперь рассмотрим реализацию данной программы. Я буду использовать среду разработки MPLAB X IDE и язык C для написания прошивки.
|
||||||
|
|
||||||
@ -104,10 +103,10 @@ UART передает данные последовательно по одно
|
|||||||
#endif /* CONSTANTS_H */
|
#endif /* CONSTANTS_H */
|
||||||
```
|
```
|
||||||
|
|
||||||
- `F_CPU` - тактовая частота микроконтроллера
|
* `F_CPU` - тактовая частота микроконтроллера
|
||||||
- `BAUD_RATE` - скорость передачи данных UART
|
* `BAUD_RATE` - скорость передачи данных UART
|
||||||
- `BUFFER_SIZE` - размер буфера, куда будем складывать поступающие данные
|
* `BUFFER_SIZE` - размер буфера, куда будем складывать поступающие данные
|
||||||
- `EOT` - End-of-Transmission, ASCII символ конца передачи данных
|
* `EOT` - End-of-Transmission, ASCII символ конца передачи данных
|
||||||
|
|
||||||
## Инициализация USART
|
## Инициализация USART
|
||||||
|
|
||||||
@ -157,11 +156,11 @@ void USART1_Initialize(void) {
|
|||||||
|
|
||||||
Для полнодуплексного режима инициализация асинхронной версии USART происходит следующим образом:
|
Для полнодуплексного режима инициализация асинхронной версии USART происходит следующим образом:
|
||||||
|
|
||||||
- Конфигурация скорости передачи данных путем записывания значения в регистр `USARTn.BAUD`
|
* Конфигурация скорости передачи данных путем записывания значения в регистр `USARTn.BAUD`
|
||||||
- Конфигурация размера `фрейма`, в нашем случае это 8 бит.
|
* Конфигурация размера `фрейма`, в нашем случае это 8 бит.
|
||||||
- Конфигурация пина `TX` на выход и пина `RX` на вход.
|
* Конфигурация пина `TX` на выход и пина `RX` на вход.
|
||||||
- Включение прерывания на то, когда закончился прием данных (когда пришел один пакет).
|
* Включение прерывания на то, когда закончился прием данных (когда пришел один пакет).
|
||||||
- Включение приемника и передатчика.
|
* Включение приемника и передатчика.
|
||||||
|
|
||||||
Теперь, рассмотрим функции отправки данных:
|
Теперь, рассмотрим функции отправки данных:
|
||||||
|
|
||||||
@ -213,9 +212,9 @@ void command_list(void);
|
|||||||
|
|
||||||
Для решения этой задачи я предлагаю создавать массив структур, где в каждой структуре будет содержаться указатель на функцию которую нам нужно выполнить, и название самой команды.
|
Для решения этой задачи я предлагаю создавать массив структур, где в каждой структуре будет содержаться указатель на функцию которую нам нужно выполнить, и название самой команды.
|
||||||
|
|
||||||
- `command_define` будет создавать новую структуру в массиве.
|
* `command_define` будет создавать новую структуру в массиве.
|
||||||
- `command_process` будет вызывать соответствующую функцию по имени команды.
|
* `command_process` будет вызывать соответствующую функцию по имени команды.
|
||||||
- `command_list` будет отправлять по UART информацию о существующих командах.
|
* `command_list` будет отправлять по UART информацию о существующих командах.
|
||||||
|
|
||||||
Реализовать задуманное можно следующим образом создав файл `command.c`:
|
Реализовать задуманное можно следующим образом создав файл `command.c`:
|
||||||
|
|
||||||
@ -369,9 +368,9 @@ void hello_world(void) {
|
|||||||
|
|
||||||
С помощью функции `command_define` мы можем определить, по какой переданной команде по USART запускать функцию:
|
С помощью функции `command_define` мы можем определить, по какой переданной команде по USART запускать функцию:
|
||||||
|
|
||||||
- `led_toggle` будет переключать светодиод.
|
* `led_toggle` будет переключать светодиод.
|
||||||
- `hello_world` выводить сообщение “Hello world”.
|
* `hello_world` выводить сообщение “Hello world”.
|
||||||
- `command_list` будет показывать список имеющихся команд.
|
* `command_list` будет показывать список имеющихся команд.
|
||||||
|
|
||||||
## Демонстрация работы
|
## Демонстрация работы
|
||||||
|
|
||||||
@ -379,10 +378,10 @@ void hello_world(void) {
|
|||||||
|
|
||||||
Выберете ваше устройство из списка (в моем случае это `/dev/ttyACM1`) и установите следующие настройки:
|
Выберете ваше устройство из списка (в моем случае это `/dev/ttyACM1`) и установите следующие настройки:
|
||||||
|
|
||||||
- Скорость: **9600** бод
|
* Скорость: **9600** бод
|
||||||
- Количество битов: **8**
|
* Количество битов: **8**
|
||||||
- Четность: **None**
|
* Четность: **None**
|
||||||
- Стоповые биты: **1**
|
* Стоповые биты: **1**
|
||||||
|
|
||||||
<figure>
|
<figure>
|
||||||
<img
|
<img
|
||||||
@ -414,7 +413,7 @@ void hello_world(void) {
|
|||||||
|
|
||||||
Веб-приложение будет состоять всего из нескольких элементов: поле для ввода, куда мы сможем писать наши команды, кнопки для открытия и закрытия последовательного порта, статус соединения. Готовый вариант можно посмотреть [здесь](https://web-serial-example.netlify.app/).
|
Веб-приложение будет состоять всего из нескольких элементов: поле для ввода, куда мы сможем писать наши команды, кнопки для открытия и закрытия последовательного порта, статус соединения. Готовый вариант можно посмотреть [здесь](https://web-serial-example.netlify.app/).
|
||||||
|
|
||||||
### Создание разметки HTML и стилей
|
## Создание разметки HTML и стилей
|
||||||
|
|
||||||
Разметка HTML будет выглядеть следующим образом:
|
Разметка HTML будет выглядеть следующим образом:
|
||||||
|
|
||||||
@ -586,12 +585,12 @@ class SerialPortHandler {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) является классом, который кодирует строку в массив без знаковых 8-ми битных целых чисел [`Uint8Array`](https://webidl.spec.whatwg.org/#idl-Uint8Array).
|
* [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) является классом, который кодирует строку в массив без знаковых 8-ми битных целых чисел [`Uint8Array`](https://webidl.spec.whatwg.org/#idl-Uint8Array).
|
||||||
- [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder#examples) наоборот, декодирует в строку. Экземпляры этих классов нам понадобятся, т.к мы будет оперировать данными с типом `Uint8Array`.
|
* [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder#examples) наоборот, декодирует в строку. Экземпляры этих классов нам понадобятся, т.к мы будет оперировать данными с типом `Uint8Array`.
|
||||||
- Функции `onConnect` и `onDisconnect` будут вызываться при подключении или отключении устройства.
|
* Функции `onConnect` и `onDisconnect` будут вызываться при подключении или отключении устройства.
|
||||||
- Свойство [`options`](https://wicg.github.io/serial/#serialoptions-dictionary) содержит необходимые параметры, которые будут использоваться для передачи и приема данных.
|
* Свойство [`options`](https://wicg.github.io/serial/#serialoptions-dictionary) содержит необходимые параметры, которые будут использоваться для передачи и приема данных.
|
||||||
- Свойство [`port`](https://wicg.github.io/serial/#serialport-interface) - объект, который будет содержать методы для работы с портом и информацию об устройстве после установки соединения.
|
* Свойство [`port`](https://wicg.github.io/serial/#serialport-interface) - объект, который будет содержать методы для работы с портом и информацию об устройстве после установки соединения.
|
||||||
- Функция `setupListeners` просто будет добавлять обработчики событий.
|
* Функция `setupListeners` просто будет добавлять обработчики событий.
|
||||||
|
|
||||||
Далее рассматривается реализация этих методов.
|
Далее рассматривается реализация этих методов.
|
||||||
|
|
||||||
@ -640,12 +639,12 @@ const port = await navigator.serial.requestPort({
|
|||||||
|
|
||||||
После того как пользователь выбрал устройство, метод `requestPort` возвращает [`port`](https://wicg.github.io/serial/#serialport-interface), который можно открыть передав туда [параметры](https://wicg.github.io/serial/#serialoptions-dictionary):
|
После того как пользователь выбрал устройство, метод `requestPort` возвращает [`port`](https://wicg.github.io/serial/#serialport-interface), который можно открыть передав туда [параметры](https://wicg.github.io/serial/#serialoptions-dictionary):
|
||||||
|
|
||||||
- `baudRate` : скорость передачи данных.
|
* `baudRate` : скорость передачи данных.
|
||||||
- `dataBits` : количество бит данных во фрейме (7 или 8).
|
* `dataBits` : количество бит данных во фрейме (7 или 8).
|
||||||
- `stopBits` : количество стоповых битов в конце пакета (1 или 2).
|
* `stopBits` : количество стоповых битов в конце пакета (1 или 2).
|
||||||
- `parity` : режим четности ( `none` , `even` или `odd` ).
|
* `parity` : режим четности ( `none` , `even` или `odd` ).
|
||||||
- `bufferSize` : размер буферов чтения и записи, которые должны быть созданы.
|
* `bufferSize` : размер буферов чтения и записи, которые должны быть созданы.
|
||||||
- `flowControl` : режим управления потоком ( `none` или `hardware` ).
|
* `flowControl` : режим управления потоком ( `none` или `hardware` ).
|
||||||
|
|
||||||
Свойство `baudRate` является единственным обязательным параметром, остальные же являются необязательными и имеют [значения по умолчанию](https://wicg.github.io/serial/#serialoptions-dictionary).
|
Свойство `baudRate` является единственным обязательным параметром, остальные же являются необязательными и имеют [значения по умолчанию](https://wicg.github.io/serial/#serialoptions-dictionary).
|
||||||
|
|
||||||
@ -893,8 +892,8 @@ setInterval(async () => {
|
|||||||
|
|
||||||
# Полезные материалы
|
# Полезные материалы
|
||||||
|
|
||||||
- [Статус поддержки браузеров Can I use](https://caniuse.com/web-serial)
|
* [Статус поддержки браузеров Can I use](https://caniuse.com/web-serial)
|
||||||
- [Документация к WebSerial API](https://wicg.github.io/serial/)
|
* [Документация к WebSerial API](https://wicg.github.io/serial/)
|
||||||
- [Подробное руководство по Stream API](https://web.dev/streams/)
|
* [Подробное руководство по Stream API](https://web.dev/streams/)
|
||||||
- [Список USB идентификаторов](https://devicehunt.com/all-usb-vendors)
|
* [Список USB идентификаторов](https://devicehunt.com/all-usb-vendors)
|
||||||
- [Доступ к различным устройствам из браузера](https://web.dev/devices-introduction/)
|
* [Доступ к различным устройствам из браузера](https://web.dev/devices-introduction/)
|
||||||
@ -1,4 +1,5 @@
|
|||||||
---
|
---
|
||||||
|
import type { MarkdownHeading } from "astro";
|
||||||
import type { CollectionEntry } from "astro:content";
|
import type { CollectionEntry } from "astro:content";
|
||||||
import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/constants";
|
import { DEFAULT_LANGUAGE, SITE_METADATA } from "@root/constants";
|
||||||
import BaseHead from "@components/BaseHead.astro";
|
import BaseHead from "@components/BaseHead.astro";
|
||||||
@ -6,14 +7,16 @@ import BaseLayout from "@components/BaseLayout.astro";
|
|||||||
import ArticleContent from "@components/ArticleContent.astro";
|
import ArticleContent from "@components/ArticleContent.astro";
|
||||||
import ArticlePost from "@components/ArticlePost.astro";
|
import ArticlePost from "@components/ArticlePost.astro";
|
||||||
import OpenGraph from "@components/OpenGraph.astro";
|
import OpenGraph from "@components/OpenGraph.astro";
|
||||||
|
import TableOfContents from "@root/components/TableOfContents.astro";
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
lang?: string | undefined;
|
lang?: string | undefined;
|
||||||
article: CollectionEntry<"blog">;
|
article: CollectionEntry<"blog">;
|
||||||
|
headings: MarkdownHeading[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
const canonicalUrl = new URL(Astro.url.pathname, Astro.site);
|
||||||
const { lang = DEFAULT_LANGUAGE, article } = Astro.props as Props;
|
const { lang = DEFAULT_LANGUAGE, article, headings } = Astro.props as Props;
|
||||||
const {
|
const {
|
||||||
data: { title, description, coverImage },
|
data: { title, description, coverImage },
|
||||||
} = article;
|
} = article;
|
||||||
@ -36,6 +39,7 @@ const {
|
|||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<!-- <TableOfContents headings={headings} /> -->
|
||||||
<ArticlePost lang={lang} article={article}>
|
<ArticlePost lang={lang} article={article}>
|
||||||
<ArticleContent>
|
<ArticleContent>
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@ -36,11 +36,11 @@ export async function getStaticPaths() {
|
|||||||
|
|
||||||
type Props = { article: CollectionEntry<"blog">; availableOnLang: CollectionEntry<"blog">[] };
|
type Props = { article: CollectionEntry<"blog">; availableOnLang: CollectionEntry<"blog">[] };
|
||||||
|
|
||||||
const { Content } = await Astro.props.article.render();
|
const { Content, headings } = await Astro.props.article.render();
|
||||||
const { article, availableOnLang } = Astro.props;
|
const { article, availableOnLang } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<ArticleLayout lang={article.id.split("/")[0]} article={article}>
|
<ArticleLayout lang={article.id.split("/")[0]} article={article} headings={headings}>
|
||||||
{
|
{
|
||||||
availableOnLang.length > 1 && (
|
availableOnLang.length > 1 && (
|
||||||
<div class="languages-available">
|
<div class="languages-available">
|
||||||
|
|||||||
@ -3,7 +3,8 @@
|
|||||||
color-scheme: light;
|
color-scheme: light;
|
||||||
|
|
||||||
/* Fonts */
|
/* Fonts */
|
||||||
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji;
|
--font-fallback: -apple-system, BlinkMacSystemFont, Segoe UI, Arial, sans-serif, Apple Color Emoji,
|
||||||
|
Segoe UI Emoji;
|
||||||
--font-family-base: "Rubik", var(--font-fallback);
|
--font-family-base: "Rubik", var(--font-fallback);
|
||||||
--font-family-monospace: "IBM Plex Mono", Menlo, monospace;
|
--font-family-monospace: "IBM Plex Mono", Menlo, monospace;
|
||||||
--font-size-xs: 0.75rem;
|
--font-size-xs: 0.75rem;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import type { MarkdownHeading } from "astro";
|
||||||
import type { CollectionEntry } from "astro:content";
|
import type { CollectionEntry } from "astro:content";
|
||||||
import { DEFAULT_LANGUAGE } from "@root/constants";
|
import { DEFAULT_LANGUAGE } from "@root/constants";
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
@ -62,3 +63,20 @@ export function formatToArticleBlocks(articles: CollectionEntry<"blog">[]) {
|
|||||||
export function getUniqueTags(articles: CollectionEntry<"blog">[]): string[] {
|
export function getUniqueTags(articles: CollectionEntry<"blog">[]): string[] {
|
||||||
return Array.from(new Set(articles.map((a) => a.data.tags).flat()));
|
return Array.from(new Set(articles.map((a) => a.data.tags).flat()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type MarkdownHeadingToc = MarkdownHeading & { subheadings: MarkdownHeading[] };
|
||||||
|
|
||||||
|
export function buildToc(headings: MarkdownHeading[]) {
|
||||||
|
const toc: MarkdownHeadingToc[] = [];
|
||||||
|
const parentHeadings = new Map();
|
||||||
|
headings.forEach((h) => {
|
||||||
|
const heading = { ...h, subheadings: [] };
|
||||||
|
parentHeadings.set(heading.depth, heading);
|
||||||
|
if (heading.depth === 1) {
|
||||||
|
toc.push(heading);
|
||||||
|
} else {
|
||||||
|
parentHeadings.get(heading.depth - 1).subheadings.push(heading);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return toc;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user