add shortcodes as the first internal/ext feature
A call is `{{< name key="value" >}}` alone on a line, parsed by a goldmark block
parser into an AST node and rendered by executing a theme template of that name
(ADR-0036). `figure` ships; `include` and `gallery` need the including bundle's
directory, which the parser does not carry yet, so they wait.
The layering did the design work here. internal/render may not import
internal/ext, so render.New takes a callback that receives a Partial and returns
Markdown extensions, and cmd/khosra/wire.go holds the only list of enabled
features. Empty that list and the engine still builds and serves — which is the
property extensions.md says the contract should have.
Raw HTML stays disabled. An author's text reaches a page only as arguments that
html/template escapes in context, which the real binary shows: a hostile alt
becomes <script> and src="javascript:…" becomes #ZgotmplZ. Getting
contextual escaping from the standard library rather than writing it is the whole
reason a fragment renders this instead of the feature.
parseSet became variadic so the fragment set reuses it rather than growing a
second copy of the overlay logic; `Partial` takes map[string]string after the
advisory correctly flagged `any` as generality nothing had asked for.
This commit is contained in:
+21
-4
@@ -73,6 +73,23 @@ these functions. An address is not chrome (`content-model.md`).
|
||||
A site root cannot add or override a phrase yet. A theme needing its own words writes them in its own
|
||||
block; site-supplied strings wait for the settings cascade (`ideas/deferred-decisions.md`).
|
||||
|
||||
## Shortcode fragments
|
||||
|
||||
`templates/shortcodes.html` holds one named template per shortcode, and that is where a shortcode's markup
|
||||
lives — the engine parses the call and supplies its arguments, never any HTML (ADR-0036).
|
||||
|
||||
| Shortcode | Template | Receives |
|
||||
|---|---|---|
|
||||
| `{{< figure src="…" alt="…" caption="…" >}}` | `figure` | `.src`, `.alt`, `.caption` — every argument as written, escaped on output |
|
||||
|
||||
Arguments arrive as strings and are escaped by `html/template` like any other data, which is what keeps an
|
||||
author's text out of the markup. A call whose template is missing renders nothing and logs; it never fails
|
||||
the page.
|
||||
|
||||
The set is overlaid the same way as the page kinds: a site's `templates/shortcodes.html` is parsed after
|
||||
the embedded one, so redefining `figure` replaces it and any fragment left alone is inherited. Argument
|
||||
names are contract, added but never renamed.
|
||||
|
||||
## The stability rule
|
||||
|
||||
Fields and names are **added, never renamed or removed**. Absence is always legal: a template reading a
|
||||
@@ -137,10 +154,10 @@ two drift together.
|
||||
## Overriding it
|
||||
|
||||
A site root's `templates/` is parsed **after** the embedded set, and the last definition of a name wins, so
|
||||
a theme redefines one named block and inherits the document (ADR-0019). Per kind, exactly two files are
|
||||
overlaid — `base.html` and that kind's block file (`page.html` or `list.html`). Overlaying every site
|
||||
template into every set would let a listing's `main` leak into bundle pages, which is the collision
|
||||
per-kind sets exist to prevent.
|
||||
a theme redefines one named block and inherits the document (ADR-0019). Each set is built from named files
|
||||
and only those are overlaid — `base.html` plus that kind's block file (`page.html` or `list.html`), and
|
||||
`shortcodes.html` on its own. Overlaying every site template into every set would let a listing's `main`
|
||||
leak into bundle pages, which is the collision per-kind sets exist to prevent.
|
||||
|
||||
`templates/theme.css` in the site root replaces the reference stylesheet entirely; there is no merging.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user