# A Wistle- stop Tour of HTMX Extensions and Використовується HTMX з ядром ASP. NET

<datetime class="hidden">2025-05-02T20:30</datetime>

<!--category-- Javascript, HTMX, ASP.NET Core -->
# Вступ

HTMX - це потужна бібліотека JavaScript, яка надає вам змогу створювати динамічні веб- програми з мінімальною кількістю JavaScript. Вона надає вам змогу робити запити AJAX, поміняти вміст HTML та обробляти події безпосередньо у ваших HTML атрибутах. Я використовую HTMX на даний момент, а з кожним проектом я дізнаюся все більше і більше про його можливості, і, що важливіше, це обмеження.

Тим не менш, я все ще не стверджую, що маю про це знання експерта, я просто хотів поділитися з ним тим, чого навчився.

> **Стаття подружнього партнера:** У цій статті йдеться про систему подій HTMX, суфікси та розширені налаштування. Для візерунків ядра ASP. NET з частковими переглядами, HTMX. NET і пагінією дивіться мою супровідну статтю: [HTMX з частковостями ядра ASP.NET: Ренесансу серверів](/blog/htmx-aspnetcore-partials).

[TOC]

# Події

## Запит щодо приготування

Фаза приготування запиту полягає у тому, де HTMX налаштовує запит перед надсиланням його на сервер. Серед цих параметрів встановлення заголовків, додавання параметрів і обробки вхідних даних користувача. Під час цієї фази буде увімкнено такі події:

```mermaid
flowchart LR

        A[htmx:configRequest] --> B[htmx:confirm] --> C[htmx:prompt] --> D[htmx:abort]

```

## Запит на життєвий цикл

Пропозиційна фаза життєвого циклу полягає у тому, що HTMX надсилає запит на сервер і керує відповіддю. Наступні події буде увімкнено протягом цієї фази:

```mermaid
flowchart LR
 E[htmx:beforeRequest] --> F[htmx:request] --> G[htmx:afterRequest]
```

## Обробка відповідей

У фазі роботи з відповідями HTMX обробляє відповідь на сервер і оновлює DOM. Протягом цієї фази буде увімкнено такі події:

```mermaid
flowchart LR
        H[htmx:beforeOnLoad] --> I[htmx:onLoad]
        I --> J[htmx:beforeSwap] --> K[htmx:swap] --> L[htmx:afterSwap]
        L --> M[htmx:afterSettle] --> N[htmx:afterOnLoad]

```

## Керування журналом

Кроком керування журналом є те, де HTMX оновлює журнал переглядача і адресу URL. Наступні події буде викликано протягом цієї фази:

```mermaid
flowchart LR
 A[htmx:historyRestoreRequest]
 A --> B[htmx:historyPopped]
 B --> C[htmx:historyRestorePage]
```

Як ви бачите, у HTMX передбачено декілька подій, до яких ви можете прив' язатися, щоб змінити запит або відповідь, або навіть історію; HTMX виконує роботу LOT для такої компактної системи. За допомогою кожної з цих систем ви можете змінити спосіб, у який HTMX взаємодіятиме з сервером / клієнтом у досить широкий спосіб.

# Додатки

Одним з найпотужніших аспектів HTMX є здатність до [створити суфікси назв](https://v2-0v2-0.htmx.org/extensions/) щоб розширити його можливості. у моєму випадку, як правило, `htmx:configRequest` щоб додати до запиту додаткові параметри. Це корисно, якщо ви бажаєте передати додаткові дані серверу без зміни коду HTML або JavaScript.

Інші суфікси можуть мати гачок `htmx:beforeRequest` змінити запит перед надсиланням, але **після** більшість інших суфіксів, які з' єднують `configRequest`; як в `beforeRequest` елементи на зразок `HX-Vals` і `HX-Include`s вже прив' язано до лунки (або у рядку запиту\ query).
Ти можеш навіть гайнути. `htmx:afterSwap` призначено для виконання дій після зміни вмісту. Комбіновано з клієнтськими бібліотеками, на зразок [Альпійський.js](https://alpinejs.dev/) або [Літ](https://lit.dev/) Ви можете створювати потужні динамічні програми з мінімальним кодом.

HTMX є вбудованими додатками на зразок `hx-boost` і `hx-swap-oob` За допомогою цього пункту ви зможете покращити функціональні можливості HTMX без написання будь- якого з нетипових кодів. Але існують випадки, коли вам потрібно створити власні додатки для виконання певних вимог.

Наприклад, у вас може виникнути потреба у додаванні нетипових заголовків до ваших запитів, зміні навантаження на запити або обробці окремих подій у унікальний спосіб.

Щоб досягти цього ГТМX, ви можете скористатися такими зручними точками інтеграції:

```javascript

{
  /**
   * init(api)
   * Called once when the extension is initialized.
   * Use it to set up internal state, store references, or access HTMX utility functions via the api parameter.
   */
  init: function(api) {
    return null;
  },

  /**
   * getSelectors()
   * Returns additional CSS selectors that HTMX should monitor.
   * Useful if your extension needs to handle custom elements or dynamic behavior.
   */
  getSelectors: function() {
    return null;
  },

  /**
   * onEvent(name, evt)
   * Called on every HTMX event (e.g., htmx:beforeRequest, htmx:afterSwap).
   * Return false to cancel the event or stop propagation.
   */
  onEvent: function(name, evt) {
    return true;
  },

  /**
   * transformResponse(text, xhr, elt)
   * Modify the raw response text before it is parsed and swapped into the DOM.
   * Use this to sanitize or preprocess HTML.
   */
  transformResponse: function(text, xhr, elt) {
    return text;
  },

  /**
   * isInlineSwap(swapStyle)
   * Return true if your extension will handle this swap style manually.
   * This tells HTMX to skip default behavior.
   */
  isInlineSwap: function(swapStyle) {
    return false;
  },

  /**
   * handleSwap(swapStyle, target, fragment, settleInfo)
   * Perform custom DOM manipulation if you implement a custom swap style.
   * Return true to prevent HTMX's default swap.
   */
  handleSwap: function(swapStyle, target, fragment, settleInfo) {
    return false;
  },

  /**
   * encodeParameters(xhr, parameters, elt)
   * Modify or serialize request parameters before sending.
   * Return null to use default URL/form encoding.
   * Return a string to override with a custom payload (e.g., JSON).
   */
  encodeParameters: function(xhr, parameters, elt) {
    return null;
  }
}
```

HTMX - це декілька попередньо вбудованих додатків, які ви можете надати [прочитати тут](https://htmx.org/extensions/).

Наприклад, зручно [вбудований суфікс](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/json-enc/README.md) `json-encode` Надає вам змогу надсилати дані JSON у тілі запиту замість даних форми, закодованих за адресою URL. Ця можливість буде корисною, якщо ви бажаєте надіслати на сервер складні структури або масиви даних.
Ви можете побачити, що цей гачок вміщується в 3 події

- `init` - для налаштування розширення і збереження посилання на API HTMX
- `onEvent` - встановити `Content-Type` заголовок до `application/json` якщо запит налаштовано
- `encodeParameters` - для перевизначення типового кодування форми, закодованого за адресою URL і послідовного кодування параметрів JSON. Крім того, буде повернуто рядок, за допомогою якого HTMX не використовуватиме типове кодування формату, закодоване за адресою URL.

```javascript
(function() {
  let api
  htmx.defineExtension('json-enc', {
    init: function(apiRef) {
      api = apiRef
    },

    onEvent: function(name, evt) {
      if (name === 'htmx:configRequest') {
        evt.detail.headers['Content-Type'] = 'application/json'
      }
    },

    encodeParameters: function(xhr, parameters, elt) {
      xhr.overrideMimeType('text/json')

      const object = {}
      parameters.forEach(function(value, key) {
        if (Object.hasOwn(object, key)) {
          if (!Array.isArray(object[key])) {
            object[key] = [object[key]]
          }
          object[key].push(value)
        } else {
          object[key] = value
        }
      })

      const vals = api.getExpressionVars(elt)
      Object.keys(object).forEach(function(key) {
        // FormData encodes values as strings, restore hx-vals/hx-vars with their initial types
        object[key] = Object.hasOwn(vals, key) ? vals[key] : object[key]
      })

      return (JSON.stringify(object))
    }
  })
})()
```

Або навіть простіше, але НАВІТЬ БІЛЬШЕ РУКИ `hx-debug` суфікс, який додає `HX-Debug` заголовок запиту. Цей пункт буде корисним для усування вад і використання журналу, оскільки він надасть вам змогу переглянути дані щодо запиту та відповіді у консолі dev.

```javascript
(function() {
  htmx.defineExtension('debug', {
    onEvent: function(name, evt) {
      if (console.debug) {
        console.debug(name, evt)
      } else if (console) {
        console.log('DEBUG:', name, evt)
      } else {
        throw new Error('NO CONSOLE SUPPORTED')
      }
    }
  })
})()

```

Є багато більше, включаючи дуже потужну [розширення інтерфейсу програми на стороні клієнта](https://github.com/bigskysoftware/htmx-extensions/tree/main/src/client-side-templates) Це надає вам змогу використовувати клієнтські бібліотеки для перетворення отриманих даних JSON у HTML. Це корисно для створення динамічних UI без довіри до відображення на стороні сервера.

# Деякі нетипові розширення

## Ідентифікатори динамічного рядка

Наприклад, в нещодавньому проекті я використовував обмінки HTMX OOB для оновлення кількості рядків у таблиці.
Щоб зробити це, я хотів знати, які рядки зараз показуються у таблиці, отже, я оновив лише видимі рядки.

### Розширення

```javascript
export default {
    encodeParameters: function (xhr, parameters, elt) {
        const ext = elt.getAttribute('hx-ext') || '';
        if (!ext.split(',').map(e => e.trim()).includes('dynamic-rowids')) {
            return null; // Use default behavior
        }

        const id = elt.dataset.id;
        const approve = elt.dataset.approve === 'true';
        const minimal = elt.dataset.minimal === 'true';
        const single = elt.dataset.single === 'true';

        const target = elt.dataset.target;
        const payload = { id, approve, minimal, single };

        if (approve && target) {
            const table = document.querySelector(target);
            if (table) {
                const rowIds = Array.from(table.querySelectorAll('tr[id^="row-"]'))
                    .map(row => row.id.replace('row-', ''));
                payload.rowIds = rowIds;
            }
        }

        // Merge payload into the parameters object
        Object.assign(parameters, payload);
        return null; // Return null to continue with default URL-encoded form encoding
    }
}
```

### Використання

Щоб використати його, нам потрібно додати розширення до нашої конфігурації HTMX.
Отже, у вашому файлі, що вказує на js (якщо ви використовуєте модулі; йоу має бути) ви можете зробити щось на зразок цього:

```javascript
import dynamicRowIds from "./dynamicRowIds"; // Load the file

htmx.defineExtension("dynamic-rowids", dynamicRowIds); // Register the extension
```

Потім, для будь-якого елементу, який ви хочете використати, ви можете додати `hx-ext` атрибут зі значенням `dynamic-rowids`.

```html
                <button
                    hx-ext="dynamic-rowids"
                    data-target="#my-table"
                    data-id="@Model.Id"
                    data-param1="true"
                    data-param2="false"
                    data-param3="@Model.Whatever"
                    hx-post
                    hx-controller="Contoller"
                    hx-action="Action"
                >
                    <i class='bx bx-check text-xl text-white'></i>
                </button>

```

## Зберігати параметри

Це ще одне просте розширення HTMX, на цей раз з'єднане з `htmx:configRequest` оскільки ми змінюємо адресу URL перед надсиланням запиту. Цей суфікс буде корисним, якщо ви використовуєте фільтрування, засноване на запитах тощо, і бажаєте, щоб деякі запити зберігали існуючі фільтри, а інші - не (наприклад, " назва " і " startdate ," але не " page " або " sort ").

Це SIMILAR, але не зовсім те саме, що існуюче розширення HTMX [stop- params](https://github.com/bigskysoftware/htmx-extensions/blob/main/src/path-params/README.md)

### Розширення

Ви бачите, що ми гачок `onEvent` слухати `htmx:configRequest` Замечательно.

Тоді ми:

- Отримати елемент, який викликав подію
- Get the `preserve-params-exclude` атрибут елемента (якщо такий існує) і поділено на масив ключів для виключення (отже, ми не додаємо їх до запиту)
- Отримати поточні параметри URL з адреси вікна
- Отримати нові параметри з URL запиту
- Прокручувати поточні параметри і перевіряти чи їх немає у списку виключення, а не у нових параметрах
- Якщо їх немає, ми додаємо їх до нових параметрів.
- Нарешті, ми встановимо нові параметри до URL запиту і повернемо " true," щоб продовжити запит.

```javascript
export default {
    onEvent: function (name, evt) {
        if (name !== 'htmx:configRequest') return;
        const el = evt.detail.elt;
        const excludeStr = el.getAttribute('preserve-params-exclude') || '';
        const exclude = excludeStr.split(',').map(s => s.trim());

        const currentParams = new URLSearchParams(window.location.search);
        const newParams = new URLSearchParams(evt.detail.path.split('?')[1] || '');

        currentParams.forEach((value, key) => {
            if (!exclude.includes(key) && !newParams.has(key)) {
                newParams.set(key, value);
            }
        });

        evt.detail.path = evt.detail.path.split('?')[0] + '?' + newParams.toString();

        return true;
    }
};
```

Тут я використовую те, що необхідно [HTMX.Net](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main) Допоміжні помічники, такі, як `hx-controller` і `hx-action` є помічниками міток, які створюють правильні атрибути HTMX для вас. Крім того, Do not translate the keyword between brackets (e. g. ServerName, ServerAdmin, etc.) `hx-route-<x>` значення, які слід передати маршруту. Цей параметр дуже корисний, оскільки надає вам змогу використовувати C# код для створення правильних значень для атрибутів, а не для жорсткого кодування їх у HTML.

### Використання

Бути розширенням, ним дуже просто користуватися:

Спочатку нам потрібно додати суфікс до нашої конфігурації HTMX.

```javascript

import preserveParams from './preserveParams.js';
htmx.defineExtension('preserve-params', preserveParams);
```

ЗАУВАЖЕННЯ: ви помітите, що типові розширення HTMX використовують метод " автозавантаження " для завантаження додатка.

```javascript
// Autoloading the extension and registering it
(function() {
  htmx.defineExtension('debug', {
}
```

Це непоганий спосіб зробити це, якщо ви використовуєте HTMX у немодульному середовищі. Але, якщо ви використовуєте модулі (якими ви маєте бути), краще використовувати ці модулі `import` оператор, який слід завантажити суфікс, а потім явно зареєструвати його на вашу `htmx` Приклад. За допомогою цього пункту ви зможете скористатися перевагами пересування деревом і завантажити лише потрібні вам розширення.

Потім у вашому елементі ви можете додати `hx-ext` атрибут зі значенням `preserve-params` і `preserve-params-exclude` атрибут з відокремленим комами списком параметрів, які виключать з запиту.

```html

<a class="btn-outline-icon"
   hx-controller="MyController"
   hx-action="MyAction"
   hx-route-myparam="@MyParam"
   hx-push-url="true"
   hx-ext="preserve-params"
   preserve-params-exclude="page,sort"
   hx-target="#page-content"
   hx-swap="innerHTML show:top"
   hx-indicator>
    <i class="bx bx-search"></i>
</a>
```

У цьому випадку через `event.detail.path` з новим `myparam` значення, встановлене у цій змінній, замінить її на наше нове значення, але збереже всі інші (окрім `page` і `sort`Отже, ми можемо передавати будь - які фільтри, встановлені у адресі URL на сервер, не турбуючись про те, що вони програються, коли ми робимо новий запит.

# Ядро ASP. NET

Однією з акуратних речей, пов' язаних з HTMX, є те, що більшість її взаємодії з сервером відбувається за допомогою заголовків HTTP. За допомогою цих заголовків ви можете вказати сервер з багатим контекстом того, що було викликано запитом, що надасть вам змогу відповідати відповідно з ваших кінцевих точок ядра ASP. NET (ASP. NET) або Razor.

Знову ключовим компонентом цього є [HTMX.Net](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main). Між багатьма речами, яких вона постачає є охайні `Request` суфікси для виявлення запитів HTMX. Цей параметр корисний для визначення, чи було виконано запит HTMX, чи ні, і відповідно до цього можна його виконати.

Він також має власний механізм для передачі збудників.

```csharp

Response.Htmx(h => {
    h.WithTrigger("yes")
     .WithTrigger("cool", timing: HtmxTriggerTiming.AfterSettle)
     .WithTrigger("neat", new { valueForFrontEnd= 42, status= "Done!" }, timing: HtmxTriggerTiming.AfterSwap);
});
```

Натисніть Urls тощо... etc...
Халід зробив чудову роботу, створюючи набір розширень, щоб було легше працювати з HTMX у ядрах ASP.NET.

Це ключовий інструмент у моїй панелі інструментів під час роботи з HTMX і ASP.NET Core. [**Заціни!**](https://github.com/khalidabuhakmeh/Htmx.Net/tree/main)

## Загальні заголовки запитів HTMX

Тут йде спад найкорисніших заголовків, які HTMX надсилає з кожним проханням:

  .
|----------------------------|------------------------------------------------------------------------------------------------------------------|
# HX- Request} Завжди встановлюється у значення true для будь- якого запиту на запит HTMX- ініціалізації. Великою перевагою для виявлення дзвінків HTMX у середній системі або контролерах.
♪ HX-Target ♪ [д] елементу цілі у DOM, на який буде обмінюватися реакція.'
♪ HX-Tr} id елемента, який викликає запит (e. g. a button). *
♪ HX-Trger-} [Назва крапального елементу (використовуйте для форм).}
♪ HX-Prompt використовує вхідні дані від hx-prompt.}
♪ HX- Current- URL} URL навігатора, коли було висунуто запит. Корисно для ведення лісозаготівель та контексту. 
♪ HX- History- Restorre-Request ♪} якщо запит є частиною історії відновлення після навігації (e.g, back button).

## Запит розширеньThe role of the transaction, in present tense

Я досить широко використовую ці додатки у своїй програмі ASP.NET. Комбіновано з програмами HTMX.Net, на зразок `Request.IsHtmx()` , `Request.IsHtmxBoosted()` і `Request.IsHtmxNonBoosted()` ви легко можете визначити запити HTMX і відповідно відповісти на них.

Наприклад, у мене є дуже простий суфікс для запиту, за допомогою якого я можу визначити, чи не націлився мій головний запит. `#page-content` Якщо це так, то я знаю, що мені слід відправити назад частинку.
ЗАУВАЖЕННЯ: багато хто не розуміє, що можна вказати частинну " повну сторінку ," а потім просто пропустити компонування.

```csharp
        if (Request.PageContentTarget())
        {   
            Response.PushUrl(Request);
            return PartialView("List", vm);
        }
        return View("List", vm);
        
        public static class RequestExtensions
{
        
        public static bool PageContentTarget(this HttpRequest request)
    {
        bool isPageContentTarget = request.Headers.TryGetValue("hx-target", out var pageContentHeader) 
                                   && pageContentHeader == "page-content";
        
        return isPageContentTarget;
    }
    }

```

## Розширення відповіді

Окрім суфіксів запиту, ви також можете створити розширення відповіді для надсилання записів подій до клієнта. Це корисно для вмикання подій на стороні клієнта.

### Приклад SweetAlert

Наприклад, [в моїй інтеграції SweetAlert2](https://www.mostlylucid.net/blog/usingsweetalertforhxindicators) Увімкнути закриття діалогового вікна за допомогою перемикача, який встановлено на сервері.

```javascript
    document.body.addEventListener('sweetalert:close', closeSweetAlertLoader);

```

Ця дія активується з сервера як подія- перемикач HTMX.

```csharp

    public static void CloseSweetAlert(this HttpResponse response)
    {
        response.Headers.Append("HX-Trigger" , JsonSerializer.Serialize(new
        {
            sweetalert = "close"
        }));

    }
```

За допомогою цього пункту можна увімкнути `sweetalert:close` запис події на стороні клієнта, який надасть вам змогу закрити діалогове вікно. Крім того, ви можете передати дані клієнту за допомогою команди `HX-Trigger` заголовок. Цей пункт буде корисним для передавання даних з сервера на клієнт без потреби у зміні коду HTML або JavaScript.

Як бачите, дуже легко слухати ці події, просто додаючи слухача подій до тіла. я використовую JSON в основному, як це завжди правильно кодується.

### ShowToast

Раніше я писав про свій метод тостів. [тут](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx)Про це варто також згадати. Щоб просто дозволити серверу запустити сповіщення про тост на стороні клієнта, я встановила гачок у цьому розширенні відповіді.

```csharp
    public static void ShowToast(this HttpResponse response, string message, bool success = true)
    {
        response.Headers.Append("HX-Trigger", JsonSerializer.Serialize(new
        {
            showToast = new
            {
                toast = message,
                issuccess =success
            }
        }));

    }
```

Потім я зв'язуюся з клієнтом події і дзвоню `showToast` функція.

```javascript
import { showToast, showHTMXToast } from './toast';

window.showHTMXToast = showHTMXToast;

document.body.addEventListener("showToast", showHTMXToast);
```

Це кличе мене. `showToast` function і well, показує тост; знову ж таки, щоб дізнатися більше про це [у статті ](https://www.mostlylucid.net/blog/showingtoastandswappingwithhtmx).

```javascript


export function showHTMXToast(event) {
    const xhr = event?.detail?.xhr;
    let type = 'success';
    let message = xhr?.responseText || 'Done!';

    try {
        const data = xhr ? JSON.parse(xhr.responseText) : event.detail;

        if (data.toast) message = data.toast;
        if ('issuccess' in data) {
            type = data.issuccess === false ? 'error' : 'success';
        } else if (xhr?.status >= 400) {
            type = 'error';
        } else if (xhr?.status >= 300) {
            type = 'warning';
        }

    } catch {
        if (xhr?.status >= 400) type = 'error';
        else if (xhr?.status >= 300) type = 'warning';
    }

    showToast(message, 3000, type);
}
```

## Включення

Ну ось і все, екскурсія по GTMX і ASP.NET Core. Я сподіваюся, що це буде корисним і інформативним. Якщо у вас є питання або коментарі, будь ласка, не вагайтеся коментувати нижче.

## Супутні статті щодо цього блогу

### Стаття для друзів

Ця стаття є частиною двосторонньої серії HTMX з ядром ASP. NET:

1. **[HTMX з частинками ядра ASP. NET](/blog/htmx-aspnetcore-partials)** - Зосереджується на інтеграції ядра ASP.NET, часткових переглядах, HTMX.NET і пагіні
2. **Ця стаття** - Глибоко занурившись у події HTMX, життєвий цикл, архітектуру розширення та нетипові розширення

### Додаткові статті HTMX

- [Додавання прив' язки до HTMX](/blog/addpagingwithhtmx) - Реалізація за допомогою HTMX.
- [ASP. NET Core Caching з HTMX](/blog/aspnetcachingwithhtmx) - Стратегічні стратегії кешування запитів HTMX
- [Автоповторення з альпійськими та HTMX](/blog/autorefreshwithalpineandhtmx) - Об' єднання альпійських.js з HTMX для реагуючих компонентів
- [Як зробити ваш сайт більш схожим на SPA з HTMX](/blog/htmxtomakeyoursitemorespalike) - Навігація, подібна до SPA, без оболонки JavaScript
- [Використання SweetAlert для індикаторів HX](/blog/usingsweetalertforhxindicators) - Чудовий завантажувальний індикатор з HTMX
- [Показ тоста і поміняння місцями з HTMX](/blog/showingtoastandswappingwithhtmx) - Сповіщення про тости на сервері

## Подальше читання

**Офіційна документація HTMX:**

- [Документація з HTMX](https://htmx.org/docs/) - Офіційна документація HTMX
- [Посилання на події HTMX](https://htmx.org/events/) - Повний список подій HTMX
- [Розширення HTMX](https://htmx.org/extensions/) Офіційні розширення HTMX
- [Приклади HTMX](https://htmx.org/examples/) - Наведу приклади візерунків HTMX.
- [Нарис HTMX](https://htmx.org/essays/) - Розумні статті про філософію HTMX

**& Інструменти бібліотек:**

- [HTMX. NET GitHub](https://github.com/khalidabuhakmeh/Htmx.Net) - Код бібліотеки HTMX. NET
- [HTMX. NET NOGE](https://www.nuget.org/packages/Htmx/) - HTMX. NET на NUGE
- [Веб- сайт Халіда Абухаке](https://khalidabuhakmeh.com/) - Творець HTMX.NET
- [Документація з Billian.js](https://alpinejs.dev/) - Офіційні альпійські.js docs

**Ресурси спільноти:**

- [HTMX Dispord](https://htmx.org/discord) - Підтримка активного спільноти
- [Книга hypermedia Systems](https://hypermedia.systems/) - Безкоштовна онлайн книга про створення гіпермедіадичних програм