@bytesbrains/weblocks 0.2.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.
- package/AGENT.md +214 -0
- package/CATALOG.md +387 -0
- package/CHANGELOG.md +63 -0
- package/LICENSE +21 -0
- package/README.md +294 -0
- package/catalog.json +1850 -0
- package/lib/blocks/about.d.ts +2 -0
- package/lib/blocks/about.js +50 -0
- package/lib/blocks/accordion.d.ts +2 -0
- package/lib/blocks/accordion.js +51 -0
- package/lib/blocks/announcementBar.d.ts +2 -0
- package/lib/blocks/announcementBar.js +56 -0
- package/lib/blocks/appShell.d.ts +2 -0
- package/lib/blocks/appShell.js +54 -0
- package/lib/blocks/auth.d.ts +2 -0
- package/lib/blocks/auth.js +82 -0
- package/lib/blocks/blogList.d.ts +2 -0
- package/lib/blocks/blogList.js +81 -0
- package/lib/blocks/blogPost.d.ts +2 -0
- package/lib/blocks/blogPost.js +60 -0
- package/lib/blocks/carousel.d.ts +2 -0
- package/lib/blocks/carousel.js +56 -0
- package/lib/blocks/contactDetails.d.ts +2 -0
- package/lib/blocks/contactDetails.js +50 -0
- package/lib/blocks/contactForm.d.ts +2 -0
- package/lib/blocks/contactForm.js +100 -0
- package/lib/blocks/cta.d.ts +2 -0
- package/lib/blocks/cta.js +40 -0
- package/lib/blocks/divider.d.ts +2 -0
- package/lib/blocks/divider.js +28 -0
- package/lib/blocks/faq.d.ts +2 -0
- package/lib/blocks/faq.js +52 -0
- package/lib/blocks/features.d.ts +2 -0
- package/lib/blocks/features.js +58 -0
- package/lib/blocks/feed.d.ts +2 -0
- package/lib/blocks/feed.js +81 -0
- package/lib/blocks/footer.d.ts +2 -0
- package/lib/blocks/footer.js +40 -0
- package/lib/blocks/gallery.d.ts +2 -0
- package/lib/blocks/gallery.js +64 -0
- package/lib/blocks/hero.d.ts +2 -0
- package/lib/blocks/hero.js +48 -0
- package/lib/blocks/heroApp.d.ts +2 -0
- package/lib/blocks/heroApp.js +65 -0
- package/lib/blocks/logos.d.ts +2 -0
- package/lib/blocks/logos.js +59 -0
- package/lib/blocks/map.d.ts +2 -0
- package/lib/blocks/map.js +43 -0
- package/lib/blocks/nav.d.ts +2 -0
- package/lib/blocks/nav.js +39 -0
- package/lib/blocks/newsletter.d.ts +2 -0
- package/lib/blocks/newsletter.js +56 -0
- package/lib/blocks/pricing.d.ts +2 -0
- package/lib/blocks/pricing.js +89 -0
- package/lib/blocks/prose.d.ts +5 -0
- package/lib/blocks/prose.js +46 -0
- package/lib/blocks/richText.d.ts +2 -0
- package/lib/blocks/richText.js +46 -0
- package/lib/blocks/services.d.ts +2 -0
- package/lib/blocks/services.js +65 -0
- package/lib/blocks/sidebar.d.ts +2 -0
- package/lib/blocks/sidebar.js +58 -0
- package/lib/blocks/socialLinks.d.ts +2 -0
- package/lib/blocks/socialLinks.js +53 -0
- package/lib/blocks/spacer.d.ts +2 -0
- package/lib/blocks/spacer.js +25 -0
- package/lib/blocks/split.d.ts +2 -0
- package/lib/blocks/split.js +67 -0
- package/lib/blocks/stats.d.ts +2 -0
- package/lib/blocks/stats.js +64 -0
- package/lib/blocks/steps.d.ts +2 -0
- package/lib/blocks/steps.js +63 -0
- package/lib/blocks/tabs.d.ts +2 -0
- package/lib/blocks/tabs.js +57 -0
- package/lib/blocks/team.d.ts +2 -0
- package/lib/blocks/team.js +94 -0
- package/lib/blocks/testimonials.d.ts +2 -0
- package/lib/blocks/testimonials.js +54 -0
- package/lib/blocks/timeline.d.ts +2 -0
- package/lib/blocks/timeline.js +53 -0
- package/lib/blocks/video.d.ts +2 -0
- package/lib/blocks/video.js +61 -0
- package/lib/catalog.d.ts +26 -0
- package/lib/catalog.js +62 -0
- package/lib/generate.d.ts +35 -0
- package/lib/generate.js +160 -0
- package/lib/index.d.ts +19 -0
- package/lib/index.js +11 -0
- package/lib/ops.d.ts +85 -0
- package/lib/ops.js +248 -0
- package/lib/presets.d.ts +16 -0
- package/lib/presets.js +50 -0
- package/lib/pwa.d.ts +30 -0
- package/lib/pwa.js +79 -0
- package/lib/registry.d.ts +53 -0
- package/lib/registry.js +81 -0
- package/lib/render.d.ts +8 -0
- package/lib/render.js +105 -0
- package/lib/runtime.d.ts +68 -0
- package/lib/runtime.js +46 -0
- package/lib/schema.d.ts +67 -0
- package/lib/schema.js +150 -0
- package/lib/tokens.d.ts +37 -0
- package/lib/tokens.js +136 -0
- package/lib/types.d.ts +108 -0
- package/lib/types.js +10 -0
- package/lib/validate.d.ts +10 -0
- package/lib/validate.js +48 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# @bytesbrains/weblocks
|
|
4
|
+
|
|
5
|
+
**The block engine for AI-composable web apps.**
|
|
6
|
+
|
|
7
|
+
An AI composes a `SiteManifest` from a fixed catalog of typed blocks — its entire
|
|
8
|
+
API surface — and the engine validates it and renders one self-contained static
|
|
9
|
+
HTML document. Snap-together “Lego bricks” for web apps: safe by construction,
|
|
10
|
+
provider- and host-neutral, zero runtime dependencies.
|
|
11
|
+
|
|
12
|
+
[](https://www.npmjs.com/package/@bytesbrains/weblocks)
|
|
13
|
+
[](./LICENSE)
|
|
14
|
+
[](https://nodejs.org)
|
|
15
|
+
[](./lib/index.d.ts)
|
|
16
|
+
[](./package.json)
|
|
17
|
+
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
brief ──generateSite(callModel)──▶ SiteManifest ──validate──▶ renderSite ──▶ one static HTML document
|
|
22
|
+
message ──editSite(callModel)────▶ EditOp[] ─────apply────▶ new SiteManifest (version++)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Why
|
|
28
|
+
|
|
29
|
+
Letting a model emit raw HTML/CSS/JS for a whole site is powerful and unsafe:
|
|
30
|
+
malformed output, injection, incoherent styling, and no way to change one thing
|
|
31
|
+
later without regenerating everything. **weblocks gives the AI a closed vocabulary
|
|
32
|
+
and a typed configuration contract** — a catalog of blocks. The model’s job
|
|
33
|
+
shrinks from *“write a correct website”* to *“pick and fill known bricks,”* which
|
|
34
|
+
is exactly what LLMs are reliable at, and the engine guarantees the result is
|
|
35
|
+
valid and coherent.
|
|
36
|
+
|
|
37
|
+
- 🔒 **Closed vocabulary** — a block exists only if it’s in the catalog; the AI
|
|
38
|
+
can never invent markup or emit raw HTML.
|
|
39
|
+
- 🧱 **Illegal states unrepresentable** — a bad edit is *rejected*, never applied.
|
|
40
|
+
- 🛡️ **Total renderer** — every field defaulted, all text escaped, all URLs
|
|
41
|
+
sanitized; it cannot throw, so a broken page is structurally impossible.
|
|
42
|
+
- 🔀 **Validity ⟂ model quality** — page validity comes from the schema +
|
|
43
|
+
renderer, not the model being right. Swap or downgrade models freely.
|
|
44
|
+
- 🎨 **Coherent theming** — one palette swap re-themes the whole app; automatic
|
|
45
|
+
light/dark; contrast-safe fills.
|
|
46
|
+
- 🌐 **Provider- & host-neutral** — you inject the model call; the engine bundles
|
|
47
|
+
no backend, provider, or host.
|
|
48
|
+
|
|
49
|
+
Zero runtime dependencies · pure TypeScript · ESM · Node ≥ 20.
|
|
50
|
+
|
|
51
|
+
## Table of contents
|
|
52
|
+
|
|
53
|
+
- [Install](#install) · [Quickstart](#quickstart) · [Core concepts](#core-concepts)
|
|
54
|
+
- [The AI contract](#the-ai-contract) · [Block catalog](#block-catalog)
|
|
55
|
+
- [Editing](#editing) · [Theming](#theming) · [Powered blocks & runtime](#powered-blocks--runtime) · [PWA](#pwa)
|
|
56
|
+
- [API reference](#api-reference) · [Adding a block](#adding-a-block) · [Local development](#local-development)
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install @bytesbrains/weblocks
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quickstart
|
|
65
|
+
|
|
66
|
+
You supply a `callModel` function (any provider — you own the key). The engine
|
|
67
|
+
turns a brief into a validated manifest and static HTML.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { writeFileSync } from 'node:fs';
|
|
71
|
+
import { generateSite, renderSite } from '@bytesbrains/weblocks';
|
|
72
|
+
|
|
73
|
+
// Bring your own provider: (system, user) => model's text reply.
|
|
74
|
+
const callModel = async ({ system, user }) => {
|
|
75
|
+
const res = await fetch('https://api.your-provider.com/v1/chat/completions', {
|
|
76
|
+
method: 'POST',
|
|
77
|
+
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.API_KEY}` },
|
|
78
|
+
body: JSON.stringify({
|
|
79
|
+
model: 'your-model',
|
|
80
|
+
messages: [{ role: 'system', content: system }, { role: 'user', content: user }],
|
|
81
|
+
}),
|
|
82
|
+
});
|
|
83
|
+
return (await res.json()).choices[0].message.content;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const { ok, manifest, warnings } = await generateSite('a Lisbon bakery landing page', callModel);
|
|
87
|
+
if (ok) writeFileSync('index.html', renderSite(manifest)); // self-contained HTML
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`renderSite(manifest)` returns a complete `<!doctype html>…</html>` string with
|
|
91
|
+
the used blocks’ CSS inlined — no build step, no framework runtime.
|
|
92
|
+
|
|
93
|
+
> **Building an AI application?** Use the latest, most capable models — the
|
|
94
|
+
> catalog is designed to be fed as a function-calling / structured-output schema.
|
|
95
|
+
> See **[`AGENT.md`](./AGENT.md)** for a ready-to-use guide you can hand to a model.
|
|
96
|
+
|
|
97
|
+
## Core concepts
|
|
98
|
+
|
|
99
|
+
A **`SiteManifest`** is the single source of truth the AI composes and edits — it
|
|
100
|
+
is never raw HTML:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
type SiteManifest = {
|
|
104
|
+
meta: { title: string; description: string; lang: string };
|
|
105
|
+
design: DesignTokens; // the shared baseplate (CSS custom properties)
|
|
106
|
+
blocks: Block[]; // ordered, typed page sections
|
|
107
|
+
version: number; // bumped per accepted edit → undo / history / diff
|
|
108
|
+
pwa?: PwaConfig; // opt-in installable PWA
|
|
109
|
+
seo?: SeoConfig; // opt-in <head> meta
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type Block = { id: string; type: string; visible: boolean; config: object; overrides?: object };
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- **`design`** — CSS custom properties every block styles from, so one edit
|
|
116
|
+
restyles the whole app coherently.
|
|
117
|
+
- **`blocks`** — placed bricks; `type` comes from the closed catalog and `config`
|
|
118
|
+
is validated against that block’s schema before it is ever applied.
|
|
119
|
+
|
|
120
|
+
Two walls make a broken page impossible: **schema validation** (the strict gate an
|
|
121
|
+
edit op passes) and the **total renderer** (defaults + escaping, never throws).
|
|
122
|
+
|
|
123
|
+
## The AI contract
|
|
124
|
+
|
|
125
|
+
The catalog is the *only* surface the model is told it may use. It ships with the
|
|
126
|
+
package in two forms, and is also generated at runtime:
|
|
127
|
+
|
|
128
|
+
| Form | What | Use |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| **`catalog.json`** | JSON Schema per block | Function-calling / structured output |
|
|
131
|
+
| **`CATALOG.md`** | Human-readable reference | Docs / review |
|
|
132
|
+
| `catalog()` | `BlockCatalogEntry[]` at runtime | Programmatic |
|
|
133
|
+
| `catalogPrompt()` | Compact string | Cheap system prompt |
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { catalog, catalogPrompt } from '@bytesbrains/weblocks';
|
|
137
|
+
import catalogJson from '@bytesbrains/weblocks/catalog.json' with { type: 'json' };
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Block catalog
|
|
141
|
+
|
|
142
|
+
**37 typed blocks.** Full field reference in [`CATALOG.md`](./CATALOG.md).
|
|
143
|
+
|
|
144
|
+
| Group | Blocks |
|
|
145
|
+
|---|---|
|
|
146
|
+
| Chrome / app-shell | `nav` · `app-shell` · `sidebar` · `announcement-bar` · `footer` |
|
|
147
|
+
| Heroes | `hero` · `hero-app` |
|
|
148
|
+
| Content | `features` · `about` · `rich-text` · `split` · `steps` · `stats` · `services-catalogue` · `pricing` · `logos` · `team` |
|
|
149
|
+
| Media | `gallery` · `carousel` · `video` · `map` |
|
|
150
|
+
| Structured | `timeline` · `tabs` · `accordion` · `testimonials` · `faq` |
|
|
151
|
+
| Collections | `blog-list` · `blog-post` · `feed` |
|
|
152
|
+
| Dynamic (powered) | `contact-form` · `newsletter` · `auth` |
|
|
153
|
+
| Conversion / rhythm | `cta` · `social-links` · `contact-details` · `divider` · `spacer` |
|
|
154
|
+
|
|
155
|
+
`rich-text` and `blog-post` carry **typed** content nodes (headings, paragraphs,
|
|
156
|
+
quotes, lists) — a safe freeform-content escape hatch that is never raw HTML.
|
|
157
|
+
|
|
158
|
+
## Editing
|
|
159
|
+
|
|
160
|
+
Editing is a set of validated verbs, not a regeneration. Drive them from natural
|
|
161
|
+
language (`editSite`) or emit them directly:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { applyOp, editSite } from '@bytesbrains/weblocks';
|
|
165
|
+
|
|
166
|
+
// Natural language → validated ops → new versioned manifest:
|
|
167
|
+
const { manifest: edited, applied } = await editSite(manifest, 'go dark and add a gallery', callModel);
|
|
168
|
+
|
|
169
|
+
// Or emit ops directly (what a chat/inspector produces):
|
|
170
|
+
applyOp(manifest, { op: 'updateBlock', id: 'hero-1', config: { headline: 'New' } });
|
|
171
|
+
|
|
172
|
+
// Array-item ops edit ONE item without rewriting the block:
|
|
173
|
+
applyOp(manifest, { op: 'addItem', id: 'features-1', field: 'items', item: { title: 'Fast' } });
|
|
174
|
+
applyOp(manifest, { op: 'updateItem', id: 'features-1', field: 'items', index: 0, patch: { text: 'Now faster' } });
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Every op is validated before it applies; a bad op is a no-op (with errors), and
|
|
178
|
+
`version` bumps on each accepted edit — so undo / history / diff come for free.
|
|
179
|
+
|
|
180
|
+
**Ops:** `addBlock` · `updateBlock` · `removeBlock` · `moveBlock` · `setVisible` ·
|
|
181
|
+
`addItem` · `updateItem` · `removeItem` · `moveItem` · `setDesignTokens` ·
|
|
182
|
+
`applyPreset` · `setOverrides` · `setMeta`.
|
|
183
|
+
|
|
184
|
+
## Theming
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
import { presetNames } from '@bytesbrains/weblocks';
|
|
188
|
+
|
|
189
|
+
applyOp(manifest, { op: 'applyPreset', name: 'midnight' }); // named token presets
|
|
190
|
+
applyOp(manifest, { op: 'setDesignTokens', patch: { radius: 'round' } }); // patch any tokens
|
|
191
|
+
applyOp(manifest, { op: 'setOverrides', id: 'cta-1', overrides: { primary: '#0af', radius: 'sharp' } }); // per-section
|
|
192
|
+
presetNames(); // ['sand', 'midnight', 'forest', 'mono', 'candy', 'ocean']
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- **Automatic light/dark** — set `design.mode = 'auto'` to follow the viewer’s OS
|
|
196
|
+
theme (supply an optional `design.darkPalette`), or `'light'` / `'dark'` for a
|
|
197
|
+
fixed one.
|
|
198
|
+
- **Contrast-safe fills** — derived `--on-primary` / `--on-accent` tokens keep
|
|
199
|
+
button text legible on *any* palette (no hardcoded colors).
|
|
200
|
+
- **Per-section overrides** — tint one block’s palette / radius / spacing without
|
|
201
|
+
breaking overall coherence.
|
|
202
|
+
|
|
203
|
+
## Powered blocks & runtime
|
|
204
|
+
|
|
205
|
+
Blocks like `contact-form`, `newsletter`, and `auth` need a backend. The engine
|
|
206
|
+
bundles none: a powered block **declares** the capabilities it needs, and your
|
|
207
|
+
host wires them through a tiny adapter.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { renderSite, pathRuntime, runtimeNeeds } from '@bytesbrains/weblocks';
|
|
211
|
+
|
|
212
|
+
runtimeNeeds(manifest); // e.g. [{ type: 'contact-form', capabilities: ['contact-form.submit'] }]
|
|
213
|
+
|
|
214
|
+
// Map every capability to POST /api/<capability>/<blockId> in one line:
|
|
215
|
+
const html = renderSite(manifest, { runtime: pathRuntime('/api') });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
With no runtime, powered blocks render **inert-but-valid** (a disabled control + a
|
|
219
|
+
note), keeping `data-wl-*` hooks so a host can enhance them client-side. Captcha,
|
|
220
|
+
server-side validation, delivery, abuse limits, and identity are the host’s job.
|
|
221
|
+
|
|
222
|
+
## PWA
|
|
223
|
+
|
|
224
|
+
Add a `pwa` field and the engine derives an installable app shell:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { renderSite, emitPwa } from '@bytesbrains/weblocks';
|
|
228
|
+
|
|
229
|
+
manifest.pwa = { name: 'My App', offline: true };
|
|
230
|
+
writeFileSync('index.html', renderSite(manifest)); // adds manifest + SW meta to <head>
|
|
231
|
+
for (const [file, body] of Object.entries(emitPwa(manifest) ?? {})) writeFileSync(file, body);
|
|
232
|
+
// → manifest.webmanifest + sw.js
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## API reference
|
|
236
|
+
|
|
237
|
+
All exports are named; types are shipped (`lib/index.d.ts`).
|
|
238
|
+
|
|
239
|
+
| Area | Exports |
|
|
240
|
+
|---|---|
|
|
241
|
+
| **Compose / edit (AI)** | `generateSite` · `editSite` · `buildGenerationPrompt` · `buildEditPrompt` · `parseManifestResponse` · `parseOpsResponse` |
|
|
242
|
+
| **Render** | `renderSite` |
|
|
243
|
+
| **Edit ops** | `applyOp` · `applyOps` |
|
|
244
|
+
| **Validate** | `validateManifest` · `validateBlock` |
|
|
245
|
+
| **Catalog** | `catalog` · `catalogPrompt` |
|
|
246
|
+
| **Registry** | `REGISTRY` · `getSpec` · `blockTypes` · `needsIsland` |
|
|
247
|
+
| **Theming** | `DEFAULT_TOKENS` · `normalizeTokens` · `tokensToCss` · `sectionOverrideCss` · `readableOn` · `PRESETS` · `presetNames` · `getPreset` |
|
|
248
|
+
| **Runtime** | `NOOP_RUNTIME` · `pathRuntime` · `runtimeNeeds` |
|
|
249
|
+
| **PWA** | `buildWebManifest` · `buildWebManifestJson` · `buildServiceWorker` · `emitPwa` |
|
|
250
|
+
| **Schema utils** | `parse` · `escapeHtml` · `escapeAttr` · `sanitizeUrl` |
|
|
251
|
+
|
|
252
|
+
Core types: `SiteManifest` · `Block` · `DesignTokens` · `Palette` ·
|
|
253
|
+
`SectionOverrides` · `PwaConfig` · `SeoConfig` · `EditOp` · `BlockSpec` ·
|
|
254
|
+
`RuntimeAdapter` · `ModelCall`.
|
|
255
|
+
|
|
256
|
+
`ModelCall` is `(args: { system: string; user: string }) => Promise<string>` — the
|
|
257
|
+
one thing you inject, so the engine never depends on a provider.
|
|
258
|
+
|
|
259
|
+
## Adding a block
|
|
260
|
+
|
|
261
|
+
Register a `BlockSpec` (`type` + `schema` + `css` + `render`, optionally `island`
|
|
262
|
+
/ `runtime`) in `registry.ts`. It must clear the **block definition-of-done**: a
|
|
263
|
+
typed schema (no raw-HTML field), consumes shared tokens, renders totally
|
|
264
|
+
(defaults + escaping, never throws), valid regardless of neighbours. See
|
|
265
|
+
[`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md#adding-a-block-concretely).
|
|
266
|
+
|
|
267
|
+
## Local development
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
npm install # dev deps only (typescript, @types/node)
|
|
271
|
+
npm run build # tsc → lib/
|
|
272
|
+
npm test # block definition-of-done + engine invariants
|
|
273
|
+
npm run example # render a sample → example-output.html
|
|
274
|
+
npm run emit:catalog # regenerate catalog.json + CATALOG.md from code
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Drive it end-to-end with a real model (dev harness — provider is env, not code):
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
PROVIDER=openai OPENAI_API_KEY=sk-… npm run ai -- generate "a Lisbon bakery landing page"
|
|
281
|
+
PROVIDER=openai OPENAI_API_KEY=sk-… npm run ai -- edit "make it dark, add a gallery"
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Documentation
|
|
285
|
+
|
|
286
|
+
- **[`AGENT.md`](./AGENT.md)** — how to use this package from an AI / agent.
|
|
287
|
+
- **[`VISION.md`](./VISION.md)** — principles and direction.
|
|
288
|
+
- **[`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md)** — internals.
|
|
289
|
+
- **[`CATALOG.md`](./CATALOG.md)** — every block’s fields.
|
|
290
|
+
- **[`CHANGELOG.md`](./CHANGELOG.md)** · **[`CONTRIBUTING.md`](./CONTRIBUTING.md)** · **[`SECURITY.md`](./SECURITY.md)**
|
|
291
|
+
|
|
292
|
+
## License
|
|
293
|
+
|
|
294
|
+
[MIT](./LICENSE) © bytesbrains
|