@madojs/mado 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/AGENTS.md +291 -0
  2. package/CHANGELOG.md +23 -0
  3. package/LICENSE +21 -0
  4. package/README.md +371 -0
  5. package/ROADMAP.md +52 -0
  6. package/dist/src/component.d.ts +48 -0
  7. package/dist/src/component.js +140 -0
  8. package/dist/src/component.js.map +1 -0
  9. package/dist/src/context.d.ts +40 -0
  10. package/dist/src/context.js +67 -0
  11. package/dist/src/context.js.map +1 -0
  12. package/dist/src/css.d.ts +54 -0
  13. package/dist/src/css.js +137 -0
  14. package/dist/src/css.js.map +1 -0
  15. package/dist/src/devtools.d.ts +22 -0
  16. package/dist/src/devtools.js +63 -0
  17. package/dist/src/devtools.js.map +1 -0
  18. package/dist/src/diagnostics.d.ts +11 -0
  19. package/dist/src/diagnostics.js +28 -0
  20. package/dist/src/diagnostics.js.map +1 -0
  21. package/dist/src/each.d.ts +39 -0
  22. package/dist/src/each.js +35 -0
  23. package/dist/src/each.js.map +1 -0
  24. package/dist/src/forms.d.ts +71 -0
  25. package/dist/src/forms.js +161 -0
  26. package/dist/src/forms.js.map +1 -0
  27. package/dist/src/head.d.ts +19 -0
  28. package/dist/src/head.js +97 -0
  29. package/dist/src/head.js.map +1 -0
  30. package/dist/src/html/bindings.d.ts +78 -0
  31. package/dist/src/html/bindings.js +304 -0
  32. package/dist/src/html/bindings.js.map +1 -0
  33. package/dist/src/html/parser.d.ts +64 -0
  34. package/dist/src/html/parser.js +521 -0
  35. package/dist/src/html/parser.js.map +1 -0
  36. package/dist/src/html/template-types.d.ts +27 -0
  37. package/dist/src/html/template-types.js +8 -0
  38. package/dist/src/html/template-types.js.map +1 -0
  39. package/dist/src/html/template.d.ts +45 -0
  40. package/dist/src/html/template.js +119 -0
  41. package/dist/src/html/template.js.map +1 -0
  42. package/dist/src/html.d.ts +16 -0
  43. package/dist/src/html.js +16 -0
  44. package/dist/src/html.js.map +1 -0
  45. package/dist/src/index.d.ts +35 -0
  46. package/dist/src/index.js +39 -0
  47. package/dist/src/index.js.map +1 -0
  48. package/dist/src/lazy.d.ts +38 -0
  49. package/dist/src/lazy.js +73 -0
  50. package/dist/src/lazy.js.map +1 -0
  51. package/dist/src/lifecycle.d.ts +45 -0
  52. package/dist/src/lifecycle.js +66 -0
  53. package/dist/src/lifecycle.js.map +1 -0
  54. package/dist/src/page.d.ts +161 -0
  55. package/dist/src/page.js +38 -0
  56. package/dist/src/page.js.map +1 -0
  57. package/dist/src/persisted.d.ts +47 -0
  58. package/dist/src/persisted.js +119 -0
  59. package/dist/src/persisted.js.map +1 -0
  60. package/dist/src/resource.d.ts +120 -0
  61. package/dist/src/resource.js +275 -0
  62. package/dist/src/resource.js.map +1 -0
  63. package/dist/src/router/manifest.d.ts +56 -0
  64. package/dist/src/router/manifest.js +302 -0
  65. package/dist/src/router/manifest.js.map +1 -0
  66. package/dist/src/router/match.d.ts +62 -0
  67. package/dist/src/router/match.js +117 -0
  68. package/dist/src/router/match.js.map +1 -0
  69. package/dist/src/router/navigation.d.ts +89 -0
  70. package/dist/src/router/navigation.js +263 -0
  71. package/dist/src/router/navigation.js.map +1 -0
  72. package/dist/src/router.d.ts +13 -0
  73. package/dist/src/router.js +13 -0
  74. package/dist/src/router.js.map +1 -0
  75. package/dist/src/signal.d.ts +67 -0
  76. package/dist/src/signal.js +238 -0
  77. package/dist/src/signal.js.map +1 -0
  78. package/docs/README.md +12 -0
  79. package/docs/en/00-the-mado-way.md +106 -0
  80. package/docs/en/01-routing.md +204 -0
  81. package/docs/en/02-project-layout.md +58 -0
  82. package/docs/en/03-static-bake.md +251 -0
  83. package/docs/en/04-ide-setup.md +162 -0
  84. package/docs/en/05-why-mado.md +193 -0
  85. package/docs/en/06-for-backenders.md +422 -0
  86. package/docs/en/07-llm-pitfalls.md +486 -0
  87. package/docs/en/08-llm-zero-history-test.md +56 -0
  88. package/docs/en/09-shadow-vs-light-dom.md +122 -0
  89. package/docs/en/README.md +16 -0
  90. package/docs/fr/00-the-mado-way.md +108 -0
  91. package/docs/fr/01-routing.md +202 -0
  92. package/docs/fr/02-project-layout.md +58 -0
  93. package/docs/fr/03-static-bake.md +290 -0
  94. package/docs/fr/04-ide-setup.md +162 -0
  95. package/docs/fr/05-why-mado.md +193 -0
  96. package/docs/fr/06-for-backenders.md +432 -0
  97. package/docs/fr/07-llm-pitfalls.md +487 -0
  98. package/docs/fr/08-llm-zero-history-test.md +60 -0
  99. package/docs/fr/09-shadow-vs-light-dom.md +121 -0
  100. package/docs/fr/README.md +16 -0
  101. package/docs/ru/00-the-mado-way.md +93 -0
  102. package/docs/ru/01-routing.md +194 -0
  103. package/docs/ru/02-project-layout.md +57 -0
  104. package/docs/ru/03-static-bake.md +251 -0
  105. package/docs/ru/04-ide-setup.md +144 -0
  106. package/docs/ru/05-why-mado.md +193 -0
  107. package/docs/ru/06-for-backenders.md +422 -0
  108. package/docs/ru/07-llm-pitfalls.md +485 -0
  109. package/docs/ru/08-llm-zero-history-test.md +56 -0
  110. package/docs/ru/09-shadow-vs-light-dom.md +122 -0
  111. package/docs/ru/README.md +14 -0
  112. package/docs/uk/00-the-mado-way.md +54 -0
  113. package/docs/uk/01-routing.md +82 -0
  114. package/docs/uk/02-project-layout.md +46 -0
  115. package/docs/uk/03-static-bake.md +49 -0
  116. package/docs/uk/04-ide-setup.md +26 -0
  117. package/docs/uk/05-why-mado.md +34 -0
  118. package/docs/uk/06-for-backenders.md +50 -0
  119. package/docs/uk/07-llm-pitfalls.md +82 -0
  120. package/docs/uk/08-llm-zero-history-test.md +31 -0
  121. package/docs/uk/09-shadow-vs-light-dom.md +40 -0
  122. package/docs/uk/README.md +16 -0
  123. package/llms.txt +155 -0
  124. package/package.json +81 -0
  125. package/scripts/bake.mjs +406 -0
  126. package/scripts/bundle.mjs +146 -0
  127. package/scripts/cli.mjs +382 -0
  128. package/scripts/new.mjs +80 -0
  129. package/scripts/preview.mjs +176 -0
  130. package/scripts/release-notes.mjs +66 -0
  131. package/scripts/showcase-regression.mjs +392 -0
  132. package/server/serve.mjs +292 -0
  133. package/starters/crud/README.md +21 -0
  134. package/starters/crud/index.html +20 -0
  135. package/starters/crud/package.json +17 -0
  136. package/starters/crud/src/components/app-shell.ts +51 -0
  137. package/starters/crud/src/components/ticket-detail.ts +33 -0
  138. package/starters/crud/src/components/ticket-form.ts +69 -0
  139. package/starters/crud/src/components/ticket-list.ts +66 -0
  140. package/starters/crud/src/lib/api.ts +76 -0
  141. package/starters/crud/src/main.ts +12 -0
  142. package/starters/crud/src/pages/home.ts +18 -0
  143. package/starters/crud/src/pages/not-found.ts +12 -0
  144. package/starters/crud/src/pages/ticket-detail.ts +6 -0
  145. package/starters/crud/src/pages/ticket-new.ts +6 -0
  146. package/starters/crud/src/pages/tickets.ts +6 -0
  147. package/starters/crud/src/routes.ts +9 -0
  148. package/starters/crud/src/styles/global.ts +155 -0
  149. package/starters/crud/tsconfig.json +15 -0
  150. package/starters/minimal/README.md +19 -0
  151. package/starters/minimal/index.html +20 -0
  152. package/starters/minimal/package.json +17 -0
  153. package/starters/minimal/src/components/app-counter.ts +31 -0
  154. package/starters/minimal/src/main.ts +9 -0
  155. package/starters/minimal/src/pages/home.ts +18 -0
  156. package/starters/minimal/src/pages/not-found.ts +14 -0
  157. package/starters/minimal/src/routes.ts +6 -0
  158. package/starters/minimal/src/styles/global.ts +60 -0
  159. package/starters/minimal/tsconfig.json +15 -0
  160. package/templates/page-detail.ts +63 -0
  161. package/templates/page-form.ts +94 -0
  162. package/templates/page-list.ts +79 -0
@@ -0,0 +1,422 @@
1
+ # Mado для бекендеров
2
+
3
+ > Ты пишешь на Go / Rust / .NET / Java / Python и тебе нужно запилить веб-морду.
4
+ > Эта страница — mental model Mado за 10 минут, на твоём языке.
5
+
6
+ ---
7
+
8
+ ## Главная аналогия
9
+
10
+ Mado устроен **как HTTP-сервер**. Серьёзно:
11
+
12
+ | Серверный мир | Mado |
13
+ |---|---|
14
+ | HTTP-роутер (chi, axum, mux) | `routes()` — манифест путей |
15
+ | Handler `func(req, resp)` | `page({ view: (ctx) => html\`...\` })` |
16
+ | Middleware | `layout` в `nested()` (оборачивает handler) |
17
+ | Шаблонизатор (Jinja, Handlebars) | `html\`\`` tagged template |
18
+ | HTTP-клиент с кешем | `resource()` — fetch + cache + invalidation |
19
+ | Reactive variable / atom | `signal()` — реактивный геттер |
20
+ | Background goroutine / task | `effect()` — авто-перезапускается при изменении сигнала |
21
+ | `defer cleanup()` | `ctx.onDispose(fn)` в setup компонента |
22
+ | ENV-переменные | `createContext()` + `provide()`/`inject()` |
23
+
24
+ Если ты понимаешь HTTP-сервер, ты понимаешь Mado.
25
+
26
+ ---
27
+
28
+ ## Файловая структура — как у обычного приложения
29
+
30
+ ```
31
+ src/
32
+ ├── routes.ts ← манифест путей (как router.go в chi)
33
+ ├── main.ts ← entry point (как main.go: настройка + run)
34
+ ├── pages/ ← по одному файлу на страницу (как handler.go)
35
+ ├── components/ ← переиспользуемые UI (как helpers/)
36
+ ├── layouts/ ← обёртки для групп страниц (как middleware/)
37
+ └── lib/ ← бизнес-логика, API-клиент (как service/, repo/)
38
+ ```
39
+
40
+ Один файл = одна страница. Никакой file-based magic routing — всё руками в `routes.ts`.
41
+
42
+ ---
43
+
44
+ ## Hello World — серверная аналогия
45
+
46
+ ### Go (chi) — для сравнения
47
+
48
+ ```go
49
+ r := chi.NewRouter()
50
+ r.Get("/", func(w http.ResponseWriter, r *http.Request) {
51
+ w.Write([]byte("<h1>Hello</h1>"))
52
+ })
53
+ r.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
54
+ id := chi.URLParam(r, "id")
55
+ fmt.Fprintf(w, "<h1>User %s</h1>", id)
56
+ })
57
+ http.ListenAndServe(":8080", r)
58
+ ```
59
+
60
+ ### Mado — то же самое
61
+
62
+ ```ts
63
+ // src/routes.ts
64
+ import { routes } from "@madojs/mado";
65
+
66
+ export default routes({
67
+ "/": () => import("./pages/home.js"),
68
+ "/users/:id": () => import("./pages/user.js"),
69
+ });
70
+ ```
71
+
72
+ ```ts
73
+ // src/pages/home.ts
74
+ import { page, html } from "@madojs/mado";
75
+ export default page({
76
+ view: () => html`<h1>Hello</h1>`,
77
+ });
78
+ ```
79
+
80
+ ```ts
81
+ // src/pages/user.ts
82
+ import { page, html } from "@madojs/mado";
83
+ export default page<{ id: string }>({
84
+ view: ({ params }) => html`<h1>User ${params.id}</h1>`,
85
+ });
86
+ ```
87
+
88
+ Параметры пути попадают в `params` — так же, как в `chi.URLParam`.
89
+
90
+ ---
91
+
92
+ ## Сигналы — это reactive variable
93
+
94
+ Если ты писал на Erlang/Elixir с `Agent`, или на Rust с `Arc<Mutex<T>>`, или просто хранил state в struct и обновлял его — `signal` это то же самое, плюс **авто-перерисовка** компонентов, которые этот state читают.
95
+
96
+ ```ts
97
+ import { signal, effect } from "@madojs/mado";
98
+
99
+ // "переменная" с подпиской
100
+ const count = signal(0);
101
+
102
+ // читать
103
+ console.log(count()); // 0
104
+
105
+ // писать
106
+ count.set(5);
107
+
108
+ // "горутина", которая запускается на каждое изменение
109
+ effect(() => {
110
+ console.log("count is now", count());
111
+ });
112
+ // → выведет "count is now 5"
113
+
114
+ count.set(10);
115
+ // → выведет "count is now 10"
116
+ ```
117
+
118
+ Никаких правил вида «нельзя в условии». Сигнал — это просто функция-геттер. Где её прочитали — туда и подписались.
119
+
120
+ ---
121
+
122
+ ## `resource()` — HTTP-клиент с кешем (как `cache.GetOrSet`)
123
+
124
+ Это **самая полезная абстракция для бекендера**. Это как Redis с автоматической инвалидацией, только в браузере.
125
+
126
+ ```ts
127
+ import { resource, mutation, jsonFetcher, invalidate } from "@madojs/mado";
128
+
129
+ // "репозиторий пользователя"
130
+ const userId = signal(1);
131
+
132
+ const user = resource(
133
+ () => `/api/users/${userId()}`, // ключ кеша (реактивный!)
134
+ jsonFetcher<User>(), // как загружать
135
+ { staleTime: 60_000 }, // 60 секунд кеш
136
+ );
137
+
138
+ // в компоненте:
139
+ user.data(); // User | undefined
140
+ user.error(); // Error | null
141
+ user.loading(); // boolean
142
+
143
+ // мутация (как POST/PUT)
144
+ const save = mutation<User, User>(
145
+ (u) => fetch("/api/users", { method: "POST", body: JSON.stringify(u) }).then(r => r.json()),
146
+ { invalidates: ["/api/users*"] }, // glob-инвалидация — как `cache.Drop("users:*")`
147
+ );
148
+
149
+ await save.run(newUser);
150
+ // автоматически: user.data() обновится, если совпал glob
151
+ ```
152
+
153
+ Если бы такая абстракция была в Go-мире для серверных кешей — мы бы все плакали от счастья.
154
+
155
+ ---
156
+
157
+ ## Компоненты = handler с собственной памятью
158
+
159
+ Компонент — это **handler**, который рендерит свой кусок UI. У него есть:
160
+
161
+ - параметры (attributes/properties);
162
+ - внутренний state (`signal`'ы);
163
+ - lifecycle: `connectedCallback` (как Init), `disconnectedCallback` (как Close).
164
+
165
+ ```ts
166
+ import { component, html, signal } from "@madojs/mado";
167
+
168
+ component("x-counter", () => {
169
+ const count = signal(0);
170
+
171
+ return () => html`
172
+ <button @click=${() => count.update(n => n + 1)}>
173
+ Clicks: ${count}
174
+ </button>
175
+ `;
176
+ });
177
+ ```
178
+
179
+ Использование:
180
+
181
+ ```ts
182
+ html`<x-counter></x-counter>`
183
+ ```
184
+
185
+ Регистрируем тег `<x-counter>` в браузере — он становится «функцией», которую можно вставлять в HTML. Это **нативный** механизм браузера (Web Components), Mado только склеивает его с сигналами.
186
+
187
+ ---
188
+
189
+ ## Формы — как `form.Validate()` на бекенде
190
+
191
+ Mado использует **нативную HTML5-валидацию**, plus добавляет state-tracking.
192
+
193
+ ```ts
194
+ import { useForm } from "@madojs/mado";
195
+
196
+ const f = useForm({
197
+ email: { required: true, type: "email" },
198
+ age: { required: true, type: "number", min: 18 },
199
+ });
200
+
201
+ // в шаблоне:
202
+ html`
203
+ <form @submit=${f.onSubmit(async (v) => {
204
+ await api.save(v);
205
+ f.reset();
206
+ })}>
207
+ <input name="email" .value=${() => f.values().email ?? ""}
208
+ @input=${f.onInput} @blur=${f.onBlur} />
209
+
210
+ ${() => f.errors().email && f.touched().email
211
+ ? html`<small>${f.errors().email}</small>`
212
+ : null}
213
+
214
+ <button ?disabled=${() => !f.isValid() || f.submitting()}>Save</button>
215
+ </form>
216
+ `;
217
+ ```
218
+
219
+ Кастомная валидация — `validate: (values) => errors | null`. Никаких Yup-схем и зависимостей.
220
+
221
+ ---
222
+
223
+ ## Контекст = DI / dependency injection
224
+
225
+ Как в Go ты передаёшь `context.Context` через стек вызовов — так в Mado контекст пробрасывается через DOM-дерево.
226
+
227
+ ```ts
228
+ import { createContext, provide, inject } from "@madojs/mado";
229
+
230
+ // объявляем "тип" зависимости
231
+ const ApiCtx = createContext<ApiClient>(defaultApiClient);
232
+
233
+ // в корневом компоненте — предоставляем
234
+ component("x-app", ({ host }) => {
235
+ provide(host, ApiCtx, new ApiClient("https://api.example.com"));
236
+ return () => html`<x-page/>`;
237
+ });
238
+
239
+ // в любом дочернем — забираем
240
+ component("x-page", ({ host }) => {
241
+ const api = inject(host, ApiCtx); // signal<ApiClient>
242
+ return () => html`<div>API version: ${() => api().version}</div>`;
243
+ });
244
+ ```
245
+
246
+ Это как `context.WithValue` / `ctx.Value` в Go, только реактивное.
247
+
248
+ ---
249
+
250
+ ## SEO — не SSR, а `bake` (как `templ generate` в Go)
251
+
252
+ Если ты привык к серверному рендерингу для SEO, в Mado это решается иначе: **prerender на build**.
253
+
254
+ ```ts
255
+ // src/pages/product.ts
256
+ export default page({
257
+ bake: {
258
+ paths: () => api.allProductSlugs(), // build-time fetch
259
+ data: ({ slug }) => api.getProduct(slug),
260
+ revalidate: 3600,
261
+ },
262
+ head: ({ slug }, data) => ({
263
+ description: data.description,
264
+ canonical: `/product/${slug}`,
265
+ og: { title: data.name, image: data.image },
266
+ }),
267
+ view: ({ params }) => html`<x-product data-slug=${params.slug}/>`,
268
+ });
269
+ ```
270
+
271
+ ```bash
272
+ npm run bake # → out/product/iphone-15/index.html (+ sitemap)
273
+ ```
274
+
275
+ Краулер видит готовый HTML с meta-тегами. Пользователь видит то же самое + интерактив после загрузки JS.
276
+
277
+ Подробнее: [`03-static-bake.md`](./03-static-bake.md).
278
+
279
+ ---
280
+
281
+ ## Типичные задачи бекендера — рецепты
282
+
283
+ ### CRUD-страница со списком
284
+
285
+ ```ts
286
+ import { page, html, resource, each, signal } from "@madojs/mado";
287
+
288
+ export default page({
289
+ view: () => {
290
+ const users = resource(() => "/api/users", jsonFetcher<User[]>());
291
+
292
+ return html`
293
+ ${() => users.loading() ? html`<p>Loading…</p>` : null}
294
+ ${() => users.error() ? html`<p>Error: ${users.error()!.message}</p>` : null}
295
+ <ul>
296
+ ${() => each(users.data() ?? [], u => u.id, u => html`
297
+ <li><a href="/users/${u.id}" data-link>${u.name}</a></li>
298
+ `)}
299
+ </ul>
300
+ `;
301
+ },
302
+ });
303
+ ```
304
+
305
+ ### Форма с POST
306
+
307
+ ```ts
308
+ import { useForm, mutation } from "@madojs/mado";
309
+
310
+ const createUser = mutation<NewUser, User>(
311
+ (u) => fetch("/api/users", { method: "POST", body: JSON.stringify(u) }).then(r => r.json()),
312
+ { invalidates: ["/api/users*"] },
313
+ );
314
+
315
+ // в page.view:
316
+ const f = useForm({ name: { required: true } });
317
+
318
+ html`
319
+ <form @submit=${f.onSubmit(async (v) => {
320
+ await createUser.run(v);
321
+ navigate("/users");
322
+ })}>
323
+ <input name="name" @input=${f.onInput}>
324
+ <button>Create</button>
325
+ </form>
326
+ `;
327
+ ```
328
+
329
+ ### Защищённая зона (auth middleware)
330
+
331
+ ```ts
332
+ // src/layouts/auth-layout.ts
333
+ import { page, html, effect } from "@madojs/mado";
334
+ import { isAuthed, navigate } from "../lib/auth.js";
335
+
336
+ export default page({
337
+ view: ({ child }) => {
338
+ effect(() => {
339
+ if (!isAuthed()) navigate("/login");
340
+ });
341
+ return html`<div class="app-shell">${child}</div>`;
342
+ },
343
+ });
344
+ ```
345
+
346
+ ```ts
347
+ // src/routes.ts
348
+ import { routes, nested } from "@madojs/mado";
349
+
350
+ export default routes({
351
+ "/login": () => import("./pages/login.js"),
352
+
353
+ "/app/*": nested({
354
+ layout: () => import("./layouts/auth-layout.js"),
355
+ routes: {
356
+ "dashboard": () => import("./pages/dashboard.js"),
357
+ "users": () => import("./pages/users.js"),
358
+ },
359
+ }),
360
+ });
361
+ ```
362
+
363
+ ### Глобальный API-клиент (как singleton в Go)
364
+
365
+ ```ts
366
+ // src/lib/api.ts
367
+ export class ApiClient {
368
+ constructor(private base: string) {}
369
+ get<T>(path: string): Promise<T> {
370
+ return fetch(this.base + path).then(r => r.json());
371
+ }
372
+ }
373
+
374
+ export const api = new ApiClient("/api");
375
+ ```
376
+
377
+ Используется напрямую `import { api } from '...'` либо через `createContext` для тестируемости.
378
+
379
+ ---
380
+
381
+ ## Что **не** нужно учить (хорошие новости)
382
+
383
+ - **Хуки и правила хуков.** Нет в Mado. Сигналы — обычные функции.
384
+ - **VDOM и reconciliation.** Нет. Сигналы обновляют DOM напрямую, точечно.
385
+ - **Webpack/Vite-конфиги.** Нет билда. `tsc → браузер`.
386
+ - **`useEffect` dependency arrays.** `effect()` сам видит, что ты прочитал.
387
+ - **State management библиотеки** (Redux/Zustand). Сигналы + context.
388
+ - **CSS-in-JS трансформации.** Shadow DOM + `css\`\`` + cssVars.
389
+ - **Routing v6 → v7 migration guide.** `routes()` — 500 строк, читается за 20 минут.
390
+
391
+ ---
392
+
393
+ ## Что **придётся** освоить (честно)
394
+
395
+ Это новые концепции. Не страшные, но плюс к React/Vue базе:
396
+
397
+ 1. **Custom Elements / Shadow DOM.** `<x-foo>` — это не div, это полноценный элемент с собственным DOM. Слоты, scoped CSS. Один вечер чтения MDN.
398
+ 2. **`attribute` vs `property`.** Attribute — это строка в HTML (`data-id="5"`), property — JS-свойство (`el.id = 5`). `?attr=${flag}` и `.prop=${value}` в шаблонах — это про разные вещи. Главное правило: **числа/объекты/массивы — через `.prop`, флаги — через `?attr`, строки — через `attr`**.
399
+ 3. **Сигналы.** Если первый раз — на 10 минут зависнешь, потом проще, чем хуки.
400
+ 4. **`html\`\``-шаблоны.** Это просто JS-функция с подсветкой через [lit-plugin](./04-ide-setup.md). Не магия.
401
+
402
+ Всё остальное — стандартный браузер + TypeScript.
403
+
404
+ ---
405
+
406
+ ## Чего не хватает (честно)
407
+
408
+ - Нет hot reload, только full reload через SSE. Достаточно для большинства, но не как у Vite.
409
+ - Нет dev-tools браузерного расширения. Используется `localStorage.madoDebug = '1'` + console.
410
+ - Нет StackBlitz-стартеров (пока).
411
+ - Нет AI-ассистента, который знает Mado так же хорошо, как React. Будут вопросы — читай `src/`, там не страшно.
412
+
413
+ ---
414
+
415
+ ## Дальше
416
+
417
+ - **[`01-routing.md`](./01-routing.md)** — детально про роутер.
418
+ - **[`02-project-layout.md`](./02-project-layout.md)** — структура проекта.
419
+ - **[`03-static-bake.md`](./03-static-bake.md)** — SEO без SSR.
420
+ - **[`examples/showcase/`](../../examples/showcase/)** — полный пример (landing + admin).
421
+
422
+ Если что-то непонятно — open issue, или просто открой исходник. Это правда читается за вечер.