feat: prepare DFTE 1.0.2

This commit is contained in:
2026-08-27 14:25:38 +10:00
parent 361803c2b3
commit 6a5b3aab9e
22 changed files with 361 additions and 309 deletions
+29
View File
@@ -0,0 +1,29 @@
# Async web responses
Build a `PlaceholderRegistry` once during setup, but allocate a fresh `TemplateContext` for each request. A shared context would mix rendering state when clients overlap.
```cpp
#include <TemplateEngine.h>
#include <TemplateEngineAsyncWeb.h>
std::shared_ptr<PlaceholderRegistry> registry;
void sendTemplate(AsyncWebServerRequest* request, const char* root) {
auto context = std::make_shared<TemplateContext>();
context->setRegistry(registry.get());
TemplateRenderer::initializeContext(*context, root);
request->onDisconnect([context]() mutable { context.reset(); });
AsyncWebServerResponse* response =
TemplateEngineAsyncWeb::beginSafeTemplateResponse(
request, "text/html; charset=utf-8", context, 128
);
request->send(response);
}
```
The response retains the context while it streams. Releasing the request-owned `shared_ptr` on disconnect prevents state from leaking into later requests.
Use [StreamingAsync](../examples/StreamingAsync/) for a complete SoftAP/captive-portal project.
Back to [documentation](README.md) · [project overview](../README.md).
+25
View File
@@ -0,0 +1,25 @@
# Configuration and memory limits
DFTE object layouts depend on fixed `DFTE_*` compile-time limits. Define any changes through shared PlatformIO `build_flags` so the library and every consuming translation unit agree on the same layout.
```ini
build_flags =
-DDFTE_BUFFER_SIZE=768
-DDFTE_MAX_STACK_DEPTH=24
-DDFTE_PLACEHOLDER_NAME_SIZE=32
-DDFTE_MAX_ITERATIONS=80
```
| Flag | Default | Meaning |
| --- | --- | --- |
| `DFTE_BUFFER_SIZE` | 512 | Streaming buffer size in `TemplateContext` |
| `DFTE_MAX_STACK_DEPTH` | 16 | Render stack frames; nested templates usually need two frames each |
| `DFTE_PLACEHOLDER_NAME_SIZE` | 24 | Maximum token length including `%` characters |
| `DFTE_PROGMEM_CHUNK_SIZE` | 512 | PROGMEM source copy window |
| `DFTE_RAM_CHUNK_SIZE` | 128 | RAM source copy window |
| `DFTE_MAX_ITERATIONS` | 50 | Safety cap for one render call |
| `DFTE_MAX_PLACEHOLDERS_DEFAULT` | 16 | Default registry constructor capacity |
`DFTE_MAX_PLACEHOLDERS_DEFAULT` is only a constructor default; pass an explicit registry capacity where a device needs more. DeviceFramework runtime template parameters do not change DFTE’s compile-time object layout.
Back to [documentation](README.md) · [project overview](../README.md).
+16
View File
@@ -0,0 +1,16 @@
# Development and releases
Released applications should use a public Git tag. While changing DFTE with a sibling library, select a local `symlink://` or `file://` dependency from an ignored PlatformIO override rather than changing tracked application dependencies.
Before a release, update `library.json`, `CHANGELOG.md`, and the relevant guides, then run:
```bash
./scripts/check-docs.sh
./scripts/test.sh compile --platform esp8266
./scripts/test.sh compile --platform esp32
./scripts/prepare-release.sh vMAJOR.MINOR.PATCH --tag
```
Push the branch and annotated tag. GitHub Actions repeats the board-free compile checks, validates the package, and creates a GitHub Release from that version’s changelog section. It does not publish to the PlatformIO Registry.
Back to [documentation](README.md) · [project overview](../README.md).
+42
View File
@@ -0,0 +1,42 @@
# Getting started
DFTE renders a named root template through a `TemplateContext`. Register templates and placeholder values once, then ask the renderer for fixed-size chunks until completion.
```cpp
#include <Arduino.h>
#include <TemplateEngine.h>
static const char ROOT[] PROGMEM = R"DFTE(
<h1>%TITLE%</h1><p>Uptime: %UPTIME%</p>
)DFTE";
PlaceholderRegistry registry;
TemplateContext context;
void setup() {
Serial.begin(115200);
registry.registerProgmemData(PSTR("%TITLE%"), PSTR("DFTE Quickstart"));
registry.registerRamData(PSTR("%UPTIME%"), []() -> const char* {
static char value[16];
snprintf(value, sizeof(value), "%lus", millis() / 1000);
return value;
});
registry.registerProgmemTemplate(PSTR("%ROOT%"), ROOT);
context.setRegistry(&registry);
TemplateRenderer::initializeContext(context, PSTR("%ROOT%"));
}
void loop() {
uint8_t chunk[128];
if (!TemplateRenderer::isComplete(context) && !TemplateRenderer::hasError(context)) {
const size_t written = TemplateRenderer::renderNextChunk(context, chunk, sizeof(chunk));
Serial.write(chunk, written);
}
}
```
`registerProgmemData()` is for static flash data. `registerRamData()` takes a getter for a value that can change as chunks are rendered. `registerProgmemTemplate()` lets a placeholder expand to another template.
For a buildable project, start with [HelloPlaceholder](../examples/HelloPlaceholder/). Next: [template language](TEMPLATE_LANGUAGE.md).
Back to [documentation](README.md) · [project overview](../README.md).
+20
View File
@@ -0,0 +1,20 @@
# DFTE documentation
DFTE documentation is organised by the rendering problem being solved.
| I want to… | Read |
| --- | --- |
| Render a first template | [Getting started](GETTING_STARTED.md) |
| Use placeholders, templates, conditions, dynamic fragments, or iterators | [Template language](TEMPLATE_LANGUAGE.md) |
| Stream a response through ESPAsyncWebServer | [Async web responses](ASYNC_WEB.md) |
| Change buffer, stack, or placeholder limits safely | [Configuration](CONFIGURATION.md) |
| Build the standalone examples | [Examples](../examples/README.md) |
| Run checks or prepare a release | [Testing](TESTING.md) · [Development](DEVELOPMENT.md) |
## Documentation rules
- The template-language guide owns DFTE semantics and lifecycle contracts.
- The configuration guide owns ABI-sensitive build flags.
- The project README is an entry point and example chooser, not a full API manual.
Back to the [project overview](../README.md).
+30
View File
@@ -0,0 +1,30 @@
# Template language and lifecycle
Every token has the form `%NAME%`. DFTE resolves it from a `PlaceholderRegistry` according to its registered type.
| Registered type | Purpose |
| --- | --- |
| `registerProgmemData()` | Static flash-resident text |
| `registerRamData()` | Getter returning current `const char*` data |
| `registerProgmemTemplate()` | Nested PROGMEM template |
| `registerDynamicTemplate()` | Template fragment supplied at render time |
| `registerConditional()` | Choose a true, false, or skipped delegate |
| `registerIterator()` | Stream repeated item templates through an iterator handle |
## Context lifecycle
1. Populate a registry during setup.
2. Attach it to a `TemplateContext`.
3. Initialise the context with the root placeholder.
4. Call `renderNextChunk()` until complete or error.
5. Call `reset()` before reusing a completed context for another root.
Do not treat `isComplete()` as a success result by itself; check `hasError()` as well.
## Iterators
An iterator opens a handle, produces `IteratorItemView` values, then closes the handle. Handles are closed when rendering finishes, resets, stalls, or fails. The `open`, `next`, and `close` callbacks must therefore tolerate early termination.
Use [AsyncDashboardDemo](../examples/AsyncDashboardDemo/) for a complete iterator example. Use [NestedLayouts](../examples/NestedLayouts/) for partials and conditions.
Back to [documentation](README.md) · [project overview](../README.md).
+14
View File
@@ -0,0 +1,14 @@
# Testing
The two PlatformIO Unity commands compile the complete DFTE test suites without uploading or executing them, so they require no attached board.
```bash
./scripts/test.sh compile --platform esp8266
./scripts/test.sh compile --platform esp32
```
The test environments include the library sources with `test_build_src = yes`. CI runs both target checks on the maintained branch and pull requests.
The standalone examples are buildable PlatformIO projects; see [examples](../examples/README.md).
Back to [documentation](README.md) · [project overview](../README.md).