@rettangoli/sites 1.0.0-rc5 → 1.0.1
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/README.md +52 -10
- package/package.json +7 -3
- package/src/builtinTemplateFunctions.js +34 -3
- package/src/cli/watch.js +10 -1
- package/src/createSiteBuilder.js +98 -15
- package/templates/default/README.md +22 -1
- package/templates/default/static/css/theme.css +226 -9
- package/templates/default/templates/base.yaml +2 -2
- package/templates/default/templates/post.yaml +2 -2
- package/sites/README.md +0 -107
- package/sites/partials/docs/mobile-nav.yaml +0 -7
- package/sites/templates/default/base.yaml +0 -20
- package/sites/templates/docs/documentation.yaml +0 -34
- package/sites/templates/rettangoli-dev/base.yaml +0 -24
- package/sites/templates/rettangoli-dev/documentation.yaml +0 -33
- package/sites/templates/rettangoli-dev/fe-documentation.yaml +0 -33
- package/sites/templates/rettangoli-dev/sites-documentation.yaml +0 -33
- package/sites/templates/rettangoli-dev/vt-documentation.yaml +0 -33
package/README.md
CHANGED
|
@@ -71,7 +71,8 @@ build:
|
|
|
71
71
|
keepMarkdownFiles: false
|
|
72
72
|
imports:
|
|
73
73
|
templates:
|
|
74
|
-
|
|
74
|
+
base: https://example.com/templates/base.yaml
|
|
75
|
+
docs: https://example.com/templates/docs.yaml
|
|
75
76
|
partials:
|
|
76
77
|
docs/nav: https://example.com/partials/docs-nav.yaml
|
|
77
78
|
```
|
|
@@ -93,7 +94,7 @@ Example mappings:
|
|
|
93
94
|
- `pages/docs/intro.md` -> `_site/docs/intro/index.html` and `_site/docs/intro.md`
|
|
94
95
|
|
|
95
96
|
`imports` lets you map aliases to remote YAML files (HTTP/HTTPS only). Use aliases in pages/templates:
|
|
96
|
-
- page frontmatter: `template: docs
|
|
97
|
+
- page frontmatter: `template: base` or `template: docs`
|
|
97
98
|
- template/page content: `$partial: docs/nav`
|
|
98
99
|
|
|
99
100
|
Imported files are cached on disk under `.rettangoli/sites/imports/{templates|partials}/` (hashed filenames).
|
|
@@ -104,16 +105,43 @@ When an alias exists both remotely and locally, local files under `templates/` a
|
|
|
104
105
|
|
|
105
106
|
If you want to publish a manual `llms.txt`, place it in `static/llms.txt`; it will be copied to `_site/llms.txt`.
|
|
106
107
|
|
|
107
|
-
##
|
|
108
|
+
## System Frontmatter
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
Use `_bind` to map global data keys into page-local variables.
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
Example:
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
---
|
|
116
|
+
template: base
|
|
117
|
+
_bind:
|
|
118
|
+
docs: feDocs
|
|
119
|
+
---
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This resolves `docs` from `data/feDocs.yaml` for that page.
|
|
123
|
+
`_bind` is a system property and is not exposed to templates directly.
|
|
124
|
+
|
|
125
|
+
Rules:
|
|
126
|
+
|
|
127
|
+
- `_bind` must be an object
|
|
128
|
+
- each `_bind` value must be a non-empty string
|
|
129
|
+
- each `_bind` value must point to an existing `data/*.yaml` key
|
|
130
|
+
- `_bind` is removed from public frontmatter before rendering/collections
|
|
131
|
+
|
|
132
|
+
Binding order:
|
|
115
133
|
|
|
116
|
-
|
|
134
|
+
1. build page context from `deepMerge(globalData, frontmatterWithoutSystemKeys)`
|
|
135
|
+
2. apply `_bind` aliases on top (alias wins for that key)
|
|
136
|
+
|
|
137
|
+
## Reusable Asset Package
|
|
138
|
+
|
|
139
|
+
`@rettangoli/sites` is the engine only.
|
|
140
|
+
|
|
141
|
+
Reusable themes, templates, partials, helper assets, schemas, and VT coverage now live in `packages/rettangoli-sitekit/` and publish from `@rettangoli/sitekit`.
|
|
142
|
+
|
|
143
|
+
Use `@rettangoli/sitekit` when you want curated importable site assets.
|
|
144
|
+
Keep `@rettangoli/sites` for build/watch/init behavior.
|
|
117
145
|
|
|
118
146
|
## Template Authoring Pattern
|
|
119
147
|
|
|
@@ -159,12 +187,14 @@ Available in YAML templates/pages without extra setup:
|
|
|
159
187
|
- `formatDate(value, format = "YYYYMMDDHHmmss", useUtc = true)`
|
|
160
188
|
- `now(format = "YYYYMMDDHHmmss", useUtc = true)`
|
|
161
189
|
- `sort(list, key, order = "asc")`
|
|
190
|
+
- `chunk(list, size = 1, pad = false, fillValue = null)`
|
|
162
191
|
- `md(content)`
|
|
163
192
|
- `toQueryString(object)`
|
|
164
193
|
|
|
165
|
-
`formatDate` tokens: `YYYY`, `MM`, `DD`, `HH`, `mm`, `ss`.
|
|
194
|
+
`formatDate` tokens: `YYYY`, `MMM`, `MM`, `DD`, `D`, `HH`, `mm`, `ss`.
|
|
166
195
|
`decodeURI`/`decodeURIComponent` return the original input when decoding fails.
|
|
167
196
|
`sort` supports `order` as `asc` or `desc` (default: `asc`), accepts dot-path keys (for example `data.date`), and returns a new array.
|
|
197
|
+
`chunk` splits arrays into rows of `size`; with `pad = true`, the last row is padded with `fillValue`.
|
|
168
198
|
`md` returns raw rendered HTML from Markdown for template insertion.
|
|
169
199
|
|
|
170
200
|
## Screenshots
|
|
@@ -177,6 +207,16 @@ Use VT against your generated site:
|
|
|
177
207
|
2. Add `vt` config in `rettangoli.config.yaml`.
|
|
178
208
|
3. Run `rtgl vt generate`, `rtgl vt report`, and `rtgl vt accept`.
|
|
179
209
|
|
|
210
|
+
Docker runtime (recommended for stable Playwright/browser versions):
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
IMAGE="han4wluc/rtgl:playwright-v1.57.0-rtgl-v1.0.5"
|
|
214
|
+
docker pull "$IMAGE"
|
|
215
|
+
docker run --rm -v "$PWD:/workspace" -w /workspace "$IMAGE" rtgl vt screenshot
|
|
216
|
+
docker run --rm -v "$PWD:/workspace" -w /workspace "$IMAGE" rtgl vt report
|
|
217
|
+
docker run --rm -v "$PWD:/workspace" -w /workspace "$IMAGE" rtgl vt accept
|
|
218
|
+
```
|
|
219
|
+
|
|
180
220
|
Example:
|
|
181
221
|
|
|
182
222
|
```yaml
|
|
@@ -200,6 +240,8 @@ url: /
|
|
|
200
240
|
|
|
201
241
|
`bun run preview` (or any equivalent local server command) must serve your built site on `vt.url` (for example serving `_site/` on port `4173`).
|
|
202
242
|
|
|
243
|
+
For a maintained example asset pack and VT lab, see `packages/rettangoli-sitekit/`.
|
|
244
|
+
|
|
203
245
|
## Full Architecture And Analysis
|
|
204
246
|
|
|
205
247
|
See `docs/architecture-and-analysis.md` for:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rettangoli/sites",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Generate static sites using Markdown and YAML for docs, blogs, and marketing sites.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Luciano Hanyon Wu",
|
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"src",
|
|
16
16
|
"components",
|
|
17
|
-
"templates"
|
|
18
|
-
"sites"
|
|
17
|
+
"templates"
|
|
19
18
|
],
|
|
20
19
|
"dependencies": {
|
|
21
20
|
"gray-matter": "^4.0.3",
|
|
@@ -49,6 +48,11 @@
|
|
|
49
48
|
"admin",
|
|
50
49
|
"dashboard"
|
|
51
50
|
],
|
|
51
|
+
"repository": {
|
|
52
|
+
"type": "git",
|
|
53
|
+
"url": "https://github.com/yuusoft-org/rettangoli",
|
|
54
|
+
"directory": "packages/rettangoli-sites"
|
|
55
|
+
},
|
|
52
56
|
"bugs": {
|
|
53
57
|
"url": "https://github.com/yuusoft-org/rettangoli/issues"
|
|
54
58
|
},
|
|
@@ -25,16 +25,20 @@ function formatDateImpl(value, format = 'YYYYMMDDHHmmss', useUtc = true) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const read = (localGetter, utcGetter) => (useUtc ? utcGetter.call(date) : localGetter.call(date));
|
|
28
|
+
const monthIndex = read(date.getMonth, date.getUTCMonth);
|
|
29
|
+
const day = read(date.getDate, date.getUTCDate);
|
|
28
30
|
const tokens = {
|
|
29
31
|
YYYY: String(read(date.getFullYear, date.getUTCFullYear)),
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
MMM: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][monthIndex],
|
|
33
|
+
MM: pad2(monthIndex + 1),
|
|
34
|
+
DD: pad2(day),
|
|
35
|
+
D: String(day),
|
|
32
36
|
HH: pad2(read(date.getHours, date.getUTCHours)),
|
|
33
37
|
mm: pad2(read(date.getMinutes, date.getUTCMinutes)),
|
|
34
38
|
ss: pad2(read(date.getSeconds, date.getUTCSeconds)),
|
|
35
39
|
};
|
|
36
40
|
|
|
37
|
-
return String(format).replace(/YYYY|MM|DD|HH|mm|ss/g, (token) => tokens[token]);
|
|
41
|
+
return String(format).replace(/YYYY|MMM|MM|DD|D|HH|mm|ss/g, (token) => tokens[token]);
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
function jsonStringify(value, space = 0) {
|
|
@@ -141,6 +145,32 @@ function sortImpl(value, key, order = 'asc') {
|
|
|
141
145
|
});
|
|
142
146
|
}
|
|
143
147
|
|
|
148
|
+
function chunkImpl(value, size = 1, pad = false, fillValue = null) {
|
|
149
|
+
if (!Array.isArray(value)) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const parsedSize = Number(size);
|
|
154
|
+
if (!Number.isFinite(parsedSize) || parsedSize <= 0) {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const chunkSize = Math.max(1, Math.trunc(parsedSize));
|
|
159
|
+
const rows = [];
|
|
160
|
+
for (let index = 0; index < value.length; index += chunkSize) {
|
|
161
|
+
rows.push(value.slice(index, index + chunkSize));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (pad && rows.length > 0) {
|
|
165
|
+
const lastRow = rows[rows.length - 1];
|
|
166
|
+
while (lastRow.length < chunkSize) {
|
|
167
|
+
lastRow.push(fillValue);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return rows;
|
|
172
|
+
}
|
|
173
|
+
|
|
144
174
|
function mdImpl(content) {
|
|
145
175
|
return {
|
|
146
176
|
__html: markdownRenderer.render(String(content ?? '')),
|
|
@@ -156,6 +186,7 @@ export const builtinTemplateFunctions = {
|
|
|
156
186
|
formatDate: formatDateImpl,
|
|
157
187
|
now: (format = 'YYYYMMDDHHmmss', useUtc = true) => formatDateImpl(new Date(), format, useUtc),
|
|
158
188
|
sort: sortImpl,
|
|
189
|
+
chunk: chunkImpl,
|
|
159
190
|
md: mdImpl,
|
|
160
191
|
toQueryString,
|
|
161
192
|
};
|
package/src/cli/watch.js
CHANGED
|
@@ -7,6 +7,14 @@ import { loadSiteConfig } from '../utils/loadSiteConfig.js';
|
|
|
7
7
|
|
|
8
8
|
const RELOAD_MODES = new Set(['body', 'full']);
|
|
9
9
|
|
|
10
|
+
function normalizePort(port) {
|
|
11
|
+
const normalizedPort = Number(port);
|
|
12
|
+
if (!Number.isInteger(normalizedPort) || normalizedPort < 1 || normalizedPort > 65535) {
|
|
13
|
+
throw new Error(`Invalid port "${port}". Allowed values: integers from 1 to 65535.`);
|
|
14
|
+
}
|
|
15
|
+
return normalizedPort;
|
|
16
|
+
}
|
|
17
|
+
|
|
10
18
|
export function createClientScript(reloadMode = 'body') {
|
|
11
19
|
const shouldUseBodyReplacement = reloadMode === 'body';
|
|
12
20
|
const reloadSnippet = shouldUseBodyReplacement
|
|
@@ -332,6 +340,7 @@ const watchSite = async (options = {}) => {
|
|
|
332
340
|
quiet = false,
|
|
333
341
|
reloadMode = 'body'
|
|
334
342
|
} = options;
|
|
343
|
+
const normalizedPort = normalizePort(port);
|
|
335
344
|
const normalizedReloadMode = String(reloadMode).toLowerCase();
|
|
336
345
|
if (!RELOAD_MODES.has(normalizedReloadMode)) {
|
|
337
346
|
throw new Error(`Invalid reload mode "${reloadMode}". Allowed values: body, full.`);
|
|
@@ -347,7 +356,7 @@ const watchSite = async (options = {}) => {
|
|
|
347
356
|
logger.log('Initial build complete');
|
|
348
357
|
|
|
349
358
|
// Start custom dev server
|
|
350
|
-
const server = new DevServer(
|
|
359
|
+
const server = new DevServer(normalizedPort, path.resolve(rootDir, outputPath), logger, normalizedReloadMode);
|
|
351
360
|
server.start();
|
|
352
361
|
|
|
353
362
|
// Watch all relevant directories
|
package/src/createSiteBuilder.js
CHANGED
|
@@ -40,6 +40,41 @@ function isObject(item) {
|
|
|
40
40
|
return item && typeof item === 'object' && !Array.isArray(item);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
function splitSystemFrontmatter(frontmatter, globalData, pagePath) {
|
|
44
|
+
const normalized = isObject(frontmatter) ? { ...frontmatter } : {};
|
|
45
|
+
const bindConfig = normalized._bind;
|
|
46
|
+
delete normalized._bind;
|
|
47
|
+
|
|
48
|
+
if (bindConfig === undefined) {
|
|
49
|
+
return { frontmatter: normalized, bindings: {} };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!isObject(bindConfig)) {
|
|
53
|
+
throw new Error(`Invalid _bind in ${pagePath}: expected an object mapping local names to global data keys.`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const bindings = {};
|
|
57
|
+
for (const [rawLocalKey, rawSourceKey] of Object.entries(bindConfig)) {
|
|
58
|
+
const localKey = String(rawLocalKey).trim();
|
|
59
|
+
if (localKey === '') {
|
|
60
|
+
throw new Error(`Invalid _bind in ${pagePath}: local key names must be non-empty.`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (typeof rawSourceKey !== 'string' || rawSourceKey.trim() === '') {
|
|
64
|
+
throw new Error(`Invalid _bind in ${pagePath} for "${localKey}": expected a non-empty global data key string.`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const sourceKey = rawSourceKey.trim();
|
|
68
|
+
if (!Object.prototype.hasOwnProperty.call(globalData, sourceKey)) {
|
|
69
|
+
throw new Error(`Invalid _bind in ${pagePath} for "${localKey}": global data key "${sourceKey}" not found.`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
bindings[localKey] = globalData[sourceKey];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { frontmatter: normalized, bindings };
|
|
76
|
+
}
|
|
77
|
+
|
|
43
78
|
function parseYamlWithContext(content, contextLabel) {
|
|
44
79
|
try {
|
|
45
80
|
return yaml.load(content, { schema: yaml.JSON_SCHEMA });
|
|
@@ -157,6 +192,39 @@ function readImportedYamlFromCache(fs, cachePath, aliasLabel) {
|
|
|
157
192
|
return parseYamlWithContext(content, `${aliasLabel} (cache: ${cachePath})`);
|
|
158
193
|
}
|
|
159
194
|
|
|
195
|
+
function resolveDirentKind(fs, itemPath, item) {
|
|
196
|
+
if (item.isDirectory()) {
|
|
197
|
+
return 'directory';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (item.isFile()) {
|
|
201
|
+
return 'file';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (typeof item.isSymbolicLink === 'function' && item.isSymbolicLink()) {
|
|
205
|
+
let stats;
|
|
206
|
+
try {
|
|
207
|
+
stats = fs.statSync(itemPath);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
throw new Error(`Broken symbolic link at "${itemPath}": ${error.message}`);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (stats.isDirectory()) {
|
|
213
|
+
return 'directory';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (stats.isFile()) {
|
|
217
|
+
return 'file';
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function isSchemaSidecarFile(fileName) {
|
|
225
|
+
return fileName.endsWith('.schema.yaml') || fileName.endsWith('.schema.yml');
|
|
226
|
+
}
|
|
227
|
+
|
|
160
228
|
async function fetchRemoteYaml(url, fetchImpl, aliasLabel) {
|
|
161
229
|
const effectiveFetch = fetchImpl || globalThis.fetch;
|
|
162
230
|
if (typeof effectiveFetch !== 'function') {
|
|
@@ -315,13 +383,18 @@ export function createSiteBuilder({
|
|
|
315
383
|
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
316
384
|
items.forEach(item => {
|
|
317
385
|
const itemPath = path.join(dir, item.name);
|
|
318
|
-
|
|
386
|
+
const itemKind = resolveDirentKind(fs, itemPath, item);
|
|
387
|
+
if (itemKind === 'directory') {
|
|
319
388
|
const newBasePath = basePath ? `${basePath}/${item.name}` : item.name;
|
|
320
389
|
readPartialsRecursively(itemPath, newBasePath);
|
|
321
390
|
return;
|
|
322
391
|
}
|
|
323
392
|
|
|
324
|
-
if (
|
|
393
|
+
if (
|
|
394
|
+
itemKind !== 'file' ||
|
|
395
|
+
(!item.name.endsWith('.yaml') && !item.name.endsWith('.yml')) ||
|
|
396
|
+
isSchemaSidecarFile(item.name)
|
|
397
|
+
) {
|
|
325
398
|
return;
|
|
326
399
|
}
|
|
327
400
|
|
|
@@ -364,12 +437,17 @@ export function createSiteBuilder({
|
|
|
364
437
|
|
|
365
438
|
items.forEach(item => {
|
|
366
439
|
const itemPath = path.join(dir, item.name);
|
|
440
|
+
const itemKind = resolveDirentKind(fs, itemPath, item);
|
|
367
441
|
|
|
368
|
-
if (
|
|
442
|
+
if (itemKind === 'directory') {
|
|
369
443
|
// Recursively read subdirectories
|
|
370
444
|
const newBasePath = basePath ? `${basePath}/${item.name}` : item.name;
|
|
371
445
|
readTemplatesRecursively(itemPath, newBasePath);
|
|
372
|
-
} else if (
|
|
446
|
+
} else if (
|
|
447
|
+
itemKind === 'file' &&
|
|
448
|
+
(item.name.endsWith('.yaml') || item.name.endsWith('.yml')) &&
|
|
449
|
+
!isSchemaSidecarFile(item.name)
|
|
450
|
+
) {
|
|
373
451
|
// Read and convert YAML file
|
|
374
452
|
const fileContent = fs.readFileSync(itemPath, 'utf8');
|
|
375
453
|
const nameWithoutExt = path.basename(item.name, path.extname(item.name));
|
|
@@ -411,13 +489,15 @@ export function createSiteBuilder({
|
|
|
411
489
|
for (const item of items) {
|
|
412
490
|
const itemPath = path.join(fullDir, item.name);
|
|
413
491
|
const relativePath = basePath ? path.join(basePath, item.name) : item.name;
|
|
492
|
+
const itemKind = resolveDirentKind(fs, itemPath, item);
|
|
414
493
|
|
|
415
|
-
if (
|
|
494
|
+
if (itemKind === 'directory') {
|
|
416
495
|
// Recursively scan subdirectories
|
|
417
496
|
scanPages(dir, relativePath);
|
|
418
|
-
} else if (
|
|
497
|
+
} else if (itemKind === 'file' && (item.name.endsWith('.yaml') || item.name.endsWith('.yml') || item.name.endsWith('.md'))) {
|
|
419
498
|
// Extract frontmatter and content
|
|
420
499
|
const { frontmatter, content } = extractFrontmatterAndContent(itemPath);
|
|
500
|
+
const { frontmatter: publicFrontmatter } = splitSystemFrontmatter(frontmatter, globalData, itemPath);
|
|
421
501
|
|
|
422
502
|
// Calculate URL
|
|
423
503
|
const baseFileName = item.name.replace(/\.(yaml|yml|md)$/, '');
|
|
@@ -435,9 +515,9 @@ export function createSiteBuilder({
|
|
|
435
515
|
}
|
|
436
516
|
|
|
437
517
|
// Process tags
|
|
438
|
-
if (
|
|
518
|
+
if (publicFrontmatter.tags) {
|
|
439
519
|
// Normalize tags to array
|
|
440
|
-
const tags = Array.isArray(
|
|
520
|
+
const tags = Array.isArray(publicFrontmatter.tags) ? publicFrontmatter.tags : [publicFrontmatter.tags];
|
|
441
521
|
|
|
442
522
|
// Add to collections
|
|
443
523
|
tags.forEach(tag => {
|
|
@@ -447,7 +527,7 @@ export function createSiteBuilder({
|
|
|
447
527
|
collections[trimmedTag] = [];
|
|
448
528
|
}
|
|
449
529
|
collections[trimmedTag].push({
|
|
450
|
-
data:
|
|
530
|
+
data: publicFrontmatter,
|
|
451
531
|
url: url,
|
|
452
532
|
content: content
|
|
453
533
|
});
|
|
@@ -471,6 +551,7 @@ export function createSiteBuilder({
|
|
|
471
551
|
if (!quiet) console.log(`Processing ${pagePath}...`);
|
|
472
552
|
|
|
473
553
|
const { frontmatter, content: rawContent } = extractFrontmatterAndContent(pagePath);
|
|
554
|
+
const { frontmatter: publicFrontmatter, bindings: boundData } = splitSystemFrontmatter(frontmatter, globalData, pagePath);
|
|
474
555
|
|
|
475
556
|
// Calculate URL for current page
|
|
476
557
|
let url;
|
|
@@ -489,7 +570,8 @@ export function createSiteBuilder({
|
|
|
489
570
|
}
|
|
490
571
|
|
|
491
572
|
// Deep merge global data with frontmatter and collections for the page context
|
|
492
|
-
const pageData = deepMerge(globalData,
|
|
573
|
+
const pageData = deepMerge(globalData, publicFrontmatter);
|
|
574
|
+
Object.assign(pageData, boundData);
|
|
493
575
|
pageData.collections = collections;
|
|
494
576
|
pageData.page = { url };
|
|
495
577
|
pageData.build = { isScreenshotMode };
|
|
@@ -521,11 +603,11 @@ export function createSiteBuilder({
|
|
|
521
603
|
|
|
522
604
|
// Find the template specified in frontmatter
|
|
523
605
|
let templateToUse = null;
|
|
524
|
-
if (
|
|
606
|
+
if (publicFrontmatter.template) {
|
|
525
607
|
// Look up template by exact path
|
|
526
|
-
templateToUse = templates[
|
|
608
|
+
templateToUse = templates[publicFrontmatter.template];
|
|
527
609
|
if (!templateToUse) {
|
|
528
|
-
throw new Error(`Template "${
|
|
610
|
+
throw new Error(`Template "${publicFrontmatter.template}" not found in ${pagePath}. Available templates: ${Object.keys(templates).join(', ')}`);
|
|
529
611
|
}
|
|
530
612
|
}
|
|
531
613
|
|
|
@@ -617,11 +699,12 @@ export function createSiteBuilder({
|
|
|
617
699
|
for (const item of items) {
|
|
618
700
|
const itemPath = path.join(fullDir, item.name);
|
|
619
701
|
const relativePath = basePath ? path.join(basePath, item.name) : item.name;
|
|
702
|
+
const itemKind = resolveDirentKind(fs, itemPath, item);
|
|
620
703
|
|
|
621
|
-
if (
|
|
704
|
+
if (itemKind === 'directory') {
|
|
622
705
|
// Recursively process subdirectories
|
|
623
706
|
await processAllPages(dir, relativePath);
|
|
624
|
-
} else if (
|
|
707
|
+
} else if (itemKind === 'file') {
|
|
625
708
|
if (item.name.endsWith('.yaml') || item.name.endsWith('.yml')) {
|
|
626
709
|
// Process YAML file
|
|
627
710
|
const outputFileName = item.name.replace(/\.(yaml|yml)$/, '.html');
|
|
@@ -228,12 +228,14 @@ Use these directly in `${...}` expressions:
|
|
|
228
228
|
- `formatDate(value, format = "YYYYMMDDHHmmss", useUtc = true)`
|
|
229
229
|
- `now(format = "YYYYMMDDHHmmss", useUtc = true)`
|
|
230
230
|
- `sort(list, key, order = "asc")`
|
|
231
|
+
- `chunk(list, size = 1, pad = false, fillValue = null)`
|
|
231
232
|
- `md(content)`
|
|
232
233
|
- `toQueryString(object)`
|
|
233
234
|
|
|
234
|
-
Date format tokens: `YYYY`, `MM`, `DD`, `HH`, `mm`, `ss`.
|
|
235
|
+
Date format tokens: `YYYY`, `MMM`, `MM`, `DD`, `D`, `HH`, `mm`, `ss`.
|
|
235
236
|
`decodeURI`/`decodeURIComponent` return the original input when decoding fails.
|
|
236
237
|
`sort` supports `order` as `asc` or `desc` (default: `asc`), accepts dot-path keys (for example `data.date`), and returns a new array.
|
|
238
|
+
`chunk` splits arrays into rows of `size`; with `pad = true`, the last row is padded with `fillValue`.
|
|
237
239
|
`md` returns raw rendered HTML from Markdown for template insertion.
|
|
238
240
|
|
|
239
241
|
## Static Files
|
|
@@ -242,3 +244,22 @@ Everything in `static/` is copied to `_site/`:
|
|
|
242
244
|
|
|
243
245
|
- `static/css/theme.css` → `_site/css/theme.css`
|
|
244
246
|
- `static/images/logo.png` → `_site/images/logo.png`
|
|
247
|
+
|
|
248
|
+
## Theme Classes
|
|
249
|
+
|
|
250
|
+
`static/css/theme.css` now includes multiple themes in one file.
|
|
251
|
+
Set exactly one class on `body` (or `html`) to choose the active palette.
|
|
252
|
+
|
|
253
|
+
Examples:
|
|
254
|
+
- `slate-dark` (default in starter templates)
|
|
255
|
+
- `slate-light`
|
|
256
|
+
- `mono-dark`
|
|
257
|
+
- `mono-light`
|
|
258
|
+
- `catppuccin-mocha`
|
|
259
|
+
- `catppuccin-macchiato`
|
|
260
|
+
- `catppuccin-frappe`
|
|
261
|
+
- `catppuccin-latte`
|
|
262
|
+
- `github-dark`
|
|
263
|
+
- `github-light`
|
|
264
|
+
- `nord-dark`
|
|
265
|
+
- `nord-light`
|
|
@@ -23,7 +23,7 @@ body {
|
|
|
23
23
|
height: 100%;
|
|
24
24
|
width: 100%;
|
|
25
25
|
margin: 0;
|
|
26
|
-
font-family:
|
|
26
|
+
font-family: var(--font-family-sans);
|
|
27
27
|
display: flex;
|
|
28
28
|
flex-direction: column;
|
|
29
29
|
background-color: var(--background);
|
|
@@ -34,14 +34,16 @@ body {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
a {
|
|
37
|
-
color:
|
|
38
|
-
text-decoration:
|
|
37
|
+
color: var(--anchor-color);
|
|
38
|
+
text-decoration: var(--anchor-text-decoration);
|
|
39
39
|
text-decoration-thickness: 1px;
|
|
40
40
|
text-underline-offset: 4px;
|
|
41
41
|
text-decoration-color: var(--muted-foreground);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
a:hover {
|
|
45
|
+
color: var(--anchor-color-hover);
|
|
46
|
+
text-decoration: var(--anchor-text-decoration-hover);
|
|
45
47
|
text-decoration-color: var(--foreground);
|
|
46
48
|
}
|
|
47
49
|
|
|
@@ -51,6 +53,7 @@ a:hover {
|
|
|
51
53
|
|
|
52
54
|
:root {
|
|
53
55
|
--width-stretch: 100%;
|
|
56
|
+
--font-family-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
54
57
|
|
|
55
58
|
--spacing-xs: 2px;
|
|
56
59
|
--spacing-sm: 4px;
|
|
@@ -64,6 +67,7 @@ a:hover {
|
|
|
64
67
|
--border-radius-lg: 8px;
|
|
65
68
|
--border-radius-xl: 16px;
|
|
66
69
|
--border-radius-f: 50%;
|
|
70
|
+
--border-radius-full: 9999px;
|
|
67
71
|
|
|
68
72
|
--border-width-xs: 1px;
|
|
69
73
|
--border-width-sm: 2px;
|
|
@@ -71,9 +75,9 @@ a:hover {
|
|
|
71
75
|
--border-width-lg: 8px;
|
|
72
76
|
--border-width-xl: 16px;
|
|
73
77
|
|
|
74
|
-
--shadow-sm: 0px 2px 6px rgba(0, 0, 0, .45);
|
|
75
|
-
--shadow-md: 0px 5px 12px rgba(0, 0, 0, .45);
|
|
76
|
-
--shadow-lg: 0px 10px 24px rgba(0, 0, 0, .45);
|
|
78
|
+
--shadow-sm: 0px 2px 6px rgba(0, 0, 0, .45), 0px 3px 5px rgba(0, 0, 0, .35), inset 0px .5px 0px rgba(255, 255, 255, .08), inset 0px 0px .5px rgba(255, 255, 255, .35);
|
|
79
|
+
--shadow-md: 0px 5px 12px rgba(0, 0, 0, .45), 0px 3px 5px rgba(0, 0, 0, .35), inset 0px .5px 0px rgba(255, 255, 255, .08), inset 0px 0px .5px rgba(255, 255, 255, .35);
|
|
80
|
+
--shadow-lg: 0px 10px 24px rgba(0, 0, 0, .45), 0px 3px 5px rgba(0, 0, 0, .35), inset 0px .5px 0px rgba(255, 255, 255, .08), inset 0px 0px .5px rgba(255, 255, 255, .35);
|
|
77
81
|
|
|
78
82
|
--h1-font-size: 3rem;
|
|
79
83
|
--h1-font-weight: 600;
|
|
@@ -115,10 +119,74 @@ a:hover {
|
|
|
115
119
|
--xs-line-height: 1;
|
|
116
120
|
--xs-letter-spacing: normal;
|
|
117
121
|
|
|
122
|
+
--anchor-color: inherit;
|
|
123
|
+
--anchor-color-hover: inherit;
|
|
124
|
+
--anchor-text-decoration: underline;
|
|
125
|
+
--anchor-text-decoration-hover: underline;
|
|
126
|
+
|
|
127
|
+
/* Sensible fallback in case no theme class is set. */
|
|
128
|
+
--primary: oklch(0.922 0 0);
|
|
129
|
+
--primary-foreground: oklch(0.305 0 0);
|
|
130
|
+
--secondary: oklch(0.269 0 0);
|
|
131
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
132
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
133
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
134
|
+
--background: rgb(29 29 29);
|
|
135
|
+
--foreground: rgb(242 242 242);
|
|
136
|
+
--muted: oklch(0.269 0 0);
|
|
137
|
+
--muted-foreground: oklch(0.708 0 0);
|
|
138
|
+
--accent: oklch(0.371 0 0);
|
|
139
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
140
|
+
--border: oklch(1 0 0 / 10%);
|
|
141
|
+
--input: oklch(1 0 0 / 15%);
|
|
142
|
+
--ring: oklch(0.556 0 0);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* rtgl mono */
|
|
146
|
+
.mono-light {
|
|
147
|
+
--primary: #171717;
|
|
148
|
+
--primary-foreground: #fafafa;
|
|
149
|
+
--secondary: #f5f5f5;
|
|
150
|
+
--secondary-foreground: #171717;
|
|
151
|
+
--destructive: #e40014;
|
|
152
|
+
--destructive-foreground: #fcf3f3;
|
|
153
|
+
--background: #ffffff;
|
|
154
|
+
--foreground: #0a0a0a;
|
|
155
|
+
--muted: #f5f5f5;
|
|
156
|
+
--muted-foreground: #737373;
|
|
157
|
+
--accent: #f5f5f5;
|
|
158
|
+
--accent-foreground: #171717;
|
|
159
|
+
--border: #e5e5e5;
|
|
160
|
+
--input: #e5e5e5;
|
|
161
|
+
--ring: #a1a1a1;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.mono-dark {
|
|
165
|
+
--background: #0a0a0a;
|
|
166
|
+
--foreground: #fafafa;
|
|
167
|
+
--primary: #e5e5e5;
|
|
168
|
+
--primary-foreground: #171717;
|
|
169
|
+
--secondary: #262626;
|
|
170
|
+
--secondary-foreground: #fafafa;
|
|
171
|
+
--muted: #262626;
|
|
172
|
+
--muted-foreground: #a1a1a1;
|
|
173
|
+
--accent: #404040;
|
|
174
|
+
--accent-foreground: #fafafa;
|
|
175
|
+
--destructive: #ff6568;
|
|
176
|
+
--destructive-foreground: #df2225;
|
|
177
|
+
--border: #ffffff1a;
|
|
178
|
+
--input: #ffffff26;
|
|
179
|
+
--ring: #737373;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* rtgl slate */
|
|
183
|
+
.slate-light {
|
|
118
184
|
--primary: oklch(0.205 0 0);
|
|
119
185
|
--primary-foreground: oklch(0.985 0 0);
|
|
120
186
|
--secondary: oklch(0.97 0 0);
|
|
121
187
|
--secondary-foreground: oklch(0.205 0 0);
|
|
188
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
189
|
+
--destructive-foreground: oklch(0.145 0 0);
|
|
122
190
|
--background: oklch(1 0 0);
|
|
123
191
|
--foreground: oklch(0.145 0 0);
|
|
124
192
|
--muted: oklch(0.97 0 0);
|
|
@@ -130,9 +198,9 @@ a:hover {
|
|
|
130
198
|
--ring: oklch(0.708 0 0);
|
|
131
199
|
}
|
|
132
200
|
|
|
133
|
-
.dark {
|
|
134
|
-
--background:
|
|
135
|
-
--foreground:
|
|
201
|
+
.slate-dark {
|
|
202
|
+
--background: rgb(29 29 29);
|
|
203
|
+
--foreground: rgb(242 242 242);
|
|
136
204
|
--primary: oklch(0.922 0 0);
|
|
137
205
|
--primary-foreground: oklch(0.305 0 0);
|
|
138
206
|
--secondary: oklch(0.269 0 0);
|
|
@@ -141,11 +209,160 @@ a:hover {
|
|
|
141
209
|
--muted-foreground: oklch(0.708 0 0);
|
|
142
210
|
--accent: oklch(0.371 0 0);
|
|
143
211
|
--accent-foreground: oklch(0.985 0 0);
|
|
212
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
213
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
144
214
|
--border: oklch(1 0 0 / 10%);
|
|
145
215
|
--input: oklch(1 0 0 / 15%);
|
|
146
216
|
--ring: oklch(0.556 0 0);
|
|
147
217
|
}
|
|
148
218
|
|
|
219
|
+
/* catppuccin */
|
|
220
|
+
.catppuccin-latte {
|
|
221
|
+
--primary: #1e66f5;
|
|
222
|
+
--primary-foreground: #eff1f5;
|
|
223
|
+
--secondary: #ccd0da;
|
|
224
|
+
--secondary-foreground: #4c4f69;
|
|
225
|
+
--destructive: #d20f39;
|
|
226
|
+
--destructive-foreground: #eff1f5;
|
|
227
|
+
--background: #eff1f5;
|
|
228
|
+
--foreground: #4c4f69;
|
|
229
|
+
--muted: #e6e9ef;
|
|
230
|
+
--muted-foreground: #6c6f85;
|
|
231
|
+
--accent: #dce0e8;
|
|
232
|
+
--accent-foreground: #4c4f69;
|
|
233
|
+
--border: #bcc0cc;
|
|
234
|
+
--input: #ccd0da;
|
|
235
|
+
--ring: #7287fd;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.catppuccin-frappe {
|
|
239
|
+
--primary: #8caaee;
|
|
240
|
+
--primary-foreground: #303446;
|
|
241
|
+
--secondary: #414559;
|
|
242
|
+
--secondary-foreground: #c6d0f5;
|
|
243
|
+
--destructive: #e78284;
|
|
244
|
+
--destructive-foreground: #303446;
|
|
245
|
+
--background: #303446;
|
|
246
|
+
--foreground: #c6d0f5;
|
|
247
|
+
--muted: #414559;
|
|
248
|
+
--muted-foreground: #a5adce;
|
|
249
|
+
--accent: #51576d;
|
|
250
|
+
--accent-foreground: #c6d0f5;
|
|
251
|
+
--border: #626880;
|
|
252
|
+
--input: #51576d;
|
|
253
|
+
--ring: #babbf1;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.catppuccin-macchiato {
|
|
257
|
+
--primary: #8aadf4;
|
|
258
|
+
--primary-foreground: #24273a;
|
|
259
|
+
--secondary: #363a4f;
|
|
260
|
+
--secondary-foreground: #cad3f5;
|
|
261
|
+
--destructive: #ed8796;
|
|
262
|
+
--destructive-foreground: #24273a;
|
|
263
|
+
--background: #24273a;
|
|
264
|
+
--foreground: #cad3f5;
|
|
265
|
+
--muted: #363a4f;
|
|
266
|
+
--muted-foreground: #a5adcb;
|
|
267
|
+
--accent: #494d64;
|
|
268
|
+
--accent-foreground: #cad3f5;
|
|
269
|
+
--border: #6e738d;
|
|
270
|
+
--input: #494d64;
|
|
271
|
+
--ring: #b7bdf8;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.catppuccin-mocha {
|
|
275
|
+
--background: #1e1e2e;
|
|
276
|
+
--foreground: #cdd6f4;
|
|
277
|
+
--primary: #89b4fa;
|
|
278
|
+
--primary-foreground: #1e1e2e;
|
|
279
|
+
--secondary: #313244;
|
|
280
|
+
--secondary-foreground: #cdd6f4;
|
|
281
|
+
--muted: #313244;
|
|
282
|
+
--muted-foreground: #a6adc8;
|
|
283
|
+
--accent: #45475a;
|
|
284
|
+
--accent-foreground: #cdd6f4;
|
|
285
|
+
--destructive: #f38ba8;
|
|
286
|
+
--destructive-foreground: #1e1e2e;
|
|
287
|
+
--border: #585b70;
|
|
288
|
+
--input: #45475a;
|
|
289
|
+
--ring: #b4befe;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* common extras */
|
|
293
|
+
.github-light {
|
|
294
|
+
--background: #ffffff;
|
|
295
|
+
--foreground: #1f2328;
|
|
296
|
+
--primary: #0969da;
|
|
297
|
+
--primary-foreground: #ffffff;
|
|
298
|
+
--secondary: #f6f8fa;
|
|
299
|
+
--secondary-foreground: #24292f;
|
|
300
|
+
--muted: #f6f8fa;
|
|
301
|
+
--muted-foreground: #57606a;
|
|
302
|
+
--accent: #ddf4ff;
|
|
303
|
+
--accent-foreground: #0969da;
|
|
304
|
+
--destructive: #cf222e;
|
|
305
|
+
--destructive-foreground: #ffffff;
|
|
306
|
+
--border: #d0d7de;
|
|
307
|
+
--input: #d0d7de;
|
|
308
|
+
--ring: #0969da;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.github-dark {
|
|
312
|
+
--background: #0d1117;
|
|
313
|
+
--foreground: #c9d1d9;
|
|
314
|
+
--primary: #58a6ff;
|
|
315
|
+
--primary-foreground: #0d1117;
|
|
316
|
+
--secondary: #21262d;
|
|
317
|
+
--secondary-foreground: #c9d1d9;
|
|
318
|
+
--muted: #21262d;
|
|
319
|
+
--muted-foreground: #8b949e;
|
|
320
|
+
--accent: #30363d;
|
|
321
|
+
--accent-foreground: #c9d1d9;
|
|
322
|
+
--destructive: #f85149;
|
|
323
|
+
--destructive-foreground: #0d1117;
|
|
324
|
+
--border: #30363d;
|
|
325
|
+
--input: #30363d;
|
|
326
|
+
--ring: #58a6ff;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.nord-light {
|
|
330
|
+
--background: #eceff4;
|
|
331
|
+
--foreground: #2e3440;
|
|
332
|
+
--primary: #5e81ac;
|
|
333
|
+
--primary-foreground: #eceff4;
|
|
334
|
+
--secondary: #e5e9f0;
|
|
335
|
+
--secondary-foreground: #2e3440;
|
|
336
|
+
--muted: #e5e9f0;
|
|
337
|
+
--muted-foreground: #4c566a;
|
|
338
|
+
--accent: #d8dee9;
|
|
339
|
+
--accent-foreground: #2e3440;
|
|
340
|
+
--destructive: #bf616a;
|
|
341
|
+
--destructive-foreground: #eceff4;
|
|
342
|
+
--border: #d8dee9;
|
|
343
|
+
--input: #d8dee9;
|
|
344
|
+
--ring: #81a1c1;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.nord-dark {
|
|
348
|
+
--background: #2e3440;
|
|
349
|
+
--foreground: #eceff4;
|
|
350
|
+
--primary: #88c0d0;
|
|
351
|
+
--primary-foreground: #2e3440;
|
|
352
|
+
--secondary: #3b4252;
|
|
353
|
+
--secondary-foreground: #eceff4;
|
|
354
|
+
--muted: #3b4252;
|
|
355
|
+
--muted-foreground: #d8dee9;
|
|
356
|
+
--accent: #434c5e;
|
|
357
|
+
--accent-foreground: #eceff4;
|
|
358
|
+
--destructive: #bf616a;
|
|
359
|
+
--destructive-foreground: #eceff4;
|
|
360
|
+
--border: #4c566a;
|
|
361
|
+
--input: #4c566a;
|
|
362
|
+
--ring: #88c0d0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
149
366
|
h1 {
|
|
150
367
|
font-size: var(--h1-font-size);
|
|
151
368
|
font-weight: var(--h1-font-weight);
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
- $if site.assets.loadConstructStyleSheetsPolyfill:
|
|
8
8
|
- script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js":
|
|
9
9
|
- $if site.assets.loadUiFromCdn:
|
|
10
|
-
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-
|
|
11
|
-
- body.dark:
|
|
10
|
+
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc15/dist/rettangoli-iife-ui.min.js":
|
|
11
|
+
- body.slate-dark:
|
|
12
12
|
- rtgl-view w="f":
|
|
13
13
|
- rtgl-view h="64":
|
|
14
14
|
- rtgl-view w="f" ah="c":
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
- $if site.assets.loadConstructStyleSheetsPolyfill:
|
|
8
8
|
- script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js":
|
|
9
9
|
- $if site.assets.loadUiFromCdn:
|
|
10
|
-
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-
|
|
11
|
-
- body.dark:
|
|
10
|
+
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc15/dist/rettangoli-iife-ui.min.js":
|
|
11
|
+
- body.slate-dark:
|
|
12
12
|
- rtgl-view h="64":
|
|
13
13
|
- rtgl-view w="f" ah="c":
|
|
14
14
|
- rtgl-view d="h" g="xl" pb="lg" md-w="100vw" lg-w="768" w="1024" ph="lg":
|
package/sites/README.md
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
# Rettangoli Sites Import Assets
|
|
2
|
-
|
|
3
|
-
This folder contains publish-only YAML templates/partials for `@rettangoli/sites` URL imports.
|
|
4
|
-
These files are distribution assets, not `@rettangoli/sites` runtime source code.
|
|
5
|
-
|
|
6
|
-
## Docs Bundle
|
|
7
|
-
|
|
8
|
-
Template URL:
|
|
9
|
-
|
|
10
|
-
`https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/docs/documentation.yaml`
|
|
11
|
-
|
|
12
|
-
Partial URL:
|
|
13
|
-
|
|
14
|
-
`https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/partials/docs/mobile-nav.yaml`
|
|
15
|
-
|
|
16
|
-
`sites.config.yaml` example:
|
|
17
|
-
|
|
18
|
-
```yaml
|
|
19
|
-
imports:
|
|
20
|
-
templates:
|
|
21
|
-
docs/documentation: https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/docs/documentation.yaml
|
|
22
|
-
partials:
|
|
23
|
-
docs/mobile-nav: https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/partials/docs/mobile-nav.yaml
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Page frontmatter example:
|
|
27
|
-
|
|
28
|
-
```yaml
|
|
29
|
-
---
|
|
30
|
-
template: docs/documentation
|
|
31
|
-
title: Getting Started
|
|
32
|
-
sidebarId: intro
|
|
33
|
-
---
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Required template data:
|
|
37
|
-
|
|
38
|
-
- `title`
|
|
39
|
-
- `docsLayout.sidebar.header` (object for sidebar header)
|
|
40
|
-
- `docsLayout.sidebar.items` (sidebar items)
|
|
41
|
-
- `docsLayout.assets.stylesheets` (array of stylesheet URLs)
|
|
42
|
-
- `docsLayout.assets.scripts` (array of script URLs)
|
|
43
|
-
|
|
44
|
-
Optional data:
|
|
45
|
-
|
|
46
|
-
- `docsLayout.metaDescription`
|
|
47
|
-
- `docsLayout.canonicalUrl`
|
|
48
|
-
|
|
49
|
-
Create `data/docsLayout.yaml`:
|
|
50
|
-
|
|
51
|
-
```yaml
|
|
52
|
-
assets:
|
|
53
|
-
stylesheets:
|
|
54
|
-
- https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc14/dist/themes/base.css
|
|
55
|
-
- https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc14/dist/themes/theme-rtgl-slate.css
|
|
56
|
-
scripts:
|
|
57
|
-
- https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js
|
|
58
|
-
- https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc14/dist/rettangoli-iife-ui.min.js
|
|
59
|
-
|
|
60
|
-
metaDescription: Documentation portal
|
|
61
|
-
canonicalUrl: https://example.com/docs/getting-started/
|
|
62
|
-
|
|
63
|
-
sidebar:
|
|
64
|
-
header:
|
|
65
|
-
label: Docs
|
|
66
|
-
href: /
|
|
67
|
-
items:
|
|
68
|
-
- title: Introduction
|
|
69
|
-
type: groupLabel
|
|
70
|
-
items:
|
|
71
|
-
- id: intro
|
|
72
|
-
title: Introduction
|
|
73
|
-
href: /docs/introduction/
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Rettangoli.dev Shell Templates
|
|
77
|
-
|
|
78
|
-
These templates mirror `apps/rettangoli.dev/templates/*` so the app can consume
|
|
79
|
-
them via URL imports instead of local template files.
|
|
80
|
-
|
|
81
|
-
Template URLs:
|
|
82
|
-
|
|
83
|
-
- `https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/base.yaml`
|
|
84
|
-
- `https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/documentation.yaml`
|
|
85
|
-
- `https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/fe-documentation.yaml`
|
|
86
|
-
- `https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/sites-documentation.yaml`
|
|
87
|
-
- `https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/vt-documentation.yaml`
|
|
88
|
-
|
|
89
|
-
`sites.config.yaml` alias example:
|
|
90
|
-
|
|
91
|
-
```yaml
|
|
92
|
-
imports:
|
|
93
|
-
templates:
|
|
94
|
-
base: https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/base.yaml
|
|
95
|
-
documentation: https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/documentation.yaml
|
|
96
|
-
fe-documentation: https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/fe-documentation.yaml
|
|
97
|
-
sites-documentation: https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/sites-documentation.yaml
|
|
98
|
-
vt-documentation: https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/rettangoli-dev/vt-documentation.yaml
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## Default Scaffold Base Template
|
|
102
|
-
|
|
103
|
-
This template mirrors `packages/rettangoli-sites/templates/default/templates/base.yaml`.
|
|
104
|
-
|
|
105
|
-
Template URL:
|
|
106
|
-
|
|
107
|
-
- `https://cdn.jsdelivr.net/npm/@rettangoli/sites@<version>/sites/templates/default/base.yaml`
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
- $if docsLayout && docsLayout.sidebar && docsLayout.sidebar.header && docsLayout.sidebar.items:
|
|
2
|
-
- 'rtgl-view hide md-show w=f d=v bgc=bg':
|
|
3
|
-
- 'rtgl-view pos=fix w=f h=56 bgc=bg av=c ph=lg bwb=xs z=10 style="top:0;left:0"':
|
|
4
|
-
- 'a href="${docsLayout.sidebar.header.href}" style="text-decoration:none;display:contents;color:inherit;"':
|
|
5
|
-
- rtgl-text s=md: ${docsLayout.sidebar.header.label}
|
|
6
|
-
- rtgl-view h=56:
|
|
7
|
-
- rtgl-sidebar selected-item-id=${sidebarId} header="${encodeURIComponent(jsonStringify(docsLayout.sidebar.header))}" items="${encodeURIComponent(jsonStringify(docsLayout.sidebar.items))}" w=f:
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
- html lang="en":
|
|
2
|
-
- head:
|
|
3
|
-
- meta charset="utf-8":
|
|
4
|
-
- meta name="viewport" content="width=device-width,initial-scale=1":
|
|
5
|
-
- title: ${title} - ${site.name}
|
|
6
|
-
- link rel="stylesheet" href="/css/theme.css":
|
|
7
|
-
- $if site.assets.loadConstructStyleSheetsPolyfill:
|
|
8
|
-
- script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js":
|
|
9
|
-
- $if site.assets.loadUiFromCdn:
|
|
10
|
-
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/rettangoli-iife-ui.min.js":
|
|
11
|
-
- body.dark:
|
|
12
|
-
- rtgl-view w="f":
|
|
13
|
-
- rtgl-view h="64":
|
|
14
|
-
- rtgl-view w="f" ah="c":
|
|
15
|
-
- rtgl-view w="f" ph="lg" pb="lg" ah="c" md-w="100vw" lg-w="768" w="1024":
|
|
16
|
-
- "${content}"
|
|
17
|
-
- rtgl-view h="30vh":
|
|
18
|
-
- $partial: footer
|
|
19
|
-
- rtgl-view pos="abs" edge="t" ah="c" bgc="bg":
|
|
20
|
-
- $partial: header
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
- html lang="en":
|
|
2
|
-
- head:
|
|
3
|
-
- meta charset="utf-8":
|
|
4
|
-
- meta name="viewport" content="width=device-width,initial-scale=1":
|
|
5
|
-
- title: ${title}
|
|
6
|
-
- $if docsLayout && docsLayout.metaDescription:
|
|
7
|
-
- meta name="description" content="${docsLayout.metaDescription}":
|
|
8
|
-
- $if docsLayout && docsLayout.canonicalUrl:
|
|
9
|
-
- link rel="canonical" href="${docsLayout.canonicalUrl}":
|
|
10
|
-
- $if docsLayout && docsLayout.assets && docsLayout.assets.stylesheets:
|
|
11
|
-
- $for href in docsLayout.assets.stylesheets:
|
|
12
|
-
- link rel="stylesheet" href="${href}":
|
|
13
|
-
- $if docsLayout && docsLayout.assets && docsLayout.assets.scripts:
|
|
14
|
-
- $for src in docsLayout.assets.scripts:
|
|
15
|
-
- script src="${src}":
|
|
16
|
-
|
|
17
|
-
- body.dark:
|
|
18
|
-
- $partial: docs/mobile-nav
|
|
19
|
-
docsLayout: ${docsLayout}
|
|
20
|
-
sidebarId: ${sidebarId}
|
|
21
|
-
|
|
22
|
-
- rtgl-view bgc="bg" w="f" d=h:
|
|
23
|
-
- $if docsLayout && docsLayout.sidebar && docsLayout.sidebar.header && docsLayout.sidebar.items:
|
|
24
|
-
- 'rtgl-view md-hide pos=fix h=100vh bgc=bg style="left: 0"':
|
|
25
|
-
- rtgl-sidebar selected-item-id=${sidebarId} header="${encodeURIComponent(jsonStringify(docsLayout.sidebar.header))}" items="${encodeURIComponent(jsonStringify(docsLayout.sidebar.items))}":
|
|
26
|
-
- rtgl-view md-hide xl-show hide w=272:
|
|
27
|
-
- rtgl-view w="1fg" ah=c:
|
|
28
|
-
- rtgl-view pv="lg" lg-w="100vw" w="720" ph="lg" sv id="content-container":
|
|
29
|
-
- rtgl-view hide md-show h=56:
|
|
30
|
-
- rtgl-text s="h1" mb="lg": ${title}
|
|
31
|
-
- "${content}"
|
|
32
|
-
- rtgl-view h=200:
|
|
33
|
-
- 'rtgl-view xl-hide style="position: fixed; right: 0"':
|
|
34
|
-
- rtgl-page-outline id="page-outline" target-id="content-container" scroll-container-id="window" offset-top="80":
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
- html lang="en":
|
|
2
|
-
- head:
|
|
3
|
-
- $partial: seo1
|
|
4
|
-
page: ${page}
|
|
5
|
-
seo: ${seo}
|
|
6
|
-
title: ${title}
|
|
7
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/base.css":
|
|
8
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/theme-rtgl-slate.css":
|
|
9
|
-
- script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js":
|
|
10
|
-
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/rettangoli-iife-ui.min.js":
|
|
11
|
-
- script src="/public/rtgl-icons.js":
|
|
12
|
-
|
|
13
|
-
- body.dark:
|
|
14
|
-
- rtgl-view:
|
|
15
|
-
- rtgl-view pos="abs" edge="t" ah="c" bgc="bg":
|
|
16
|
-
- $partial: navbar1
|
|
17
|
-
title: ${site.navbar.title}
|
|
18
|
-
cta: ${site.navbar.cta}
|
|
19
|
-
- rtgl-view w=f:
|
|
20
|
-
- rtgl-view h="48":
|
|
21
|
-
- rtgl-view w="f" ah="c":
|
|
22
|
-
- rtgl-view w=f ph="lg" pb="lg" ah=c:
|
|
23
|
-
- "${content}"
|
|
24
|
-
- script src="/public/mobile-nav.js":
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
- html lang="en":
|
|
2
|
-
- head:
|
|
3
|
-
- $partial: seo1
|
|
4
|
-
page: ${page}
|
|
5
|
-
seo: ${seo}
|
|
6
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/base.css":
|
|
7
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/theme-rtgl-slate.css":
|
|
8
|
-
- script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js":
|
|
9
|
-
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/rettangoli-iife-ui.min.js":
|
|
10
|
-
- script src="/public/rtgl-icons.js":
|
|
11
|
-
|
|
12
|
-
- body.dark:
|
|
13
|
-
- $partial: mobile-nav1
|
|
14
|
-
headerLabel: ${docs.header.label}
|
|
15
|
-
headerHref: ${docs.header.href}
|
|
16
|
-
header: ${docs.header}
|
|
17
|
-
items: ${docs.items}
|
|
18
|
-
sidebarId: ${sidebarId}
|
|
19
|
-
|
|
20
|
-
- rtgl-view bgc="bg" w="f" d=h:
|
|
21
|
-
- 'rtgl-view md-hide pos=fix h=100vh bgc=bg style="left: 0"':
|
|
22
|
-
- rtgl-sidebar selected-item-id=${sidebarId} header="${encodeURIComponent(jsonStringify(docs.header))}" items="${encodeURIComponent(jsonStringify(docs.items))}":
|
|
23
|
-
- rtgl-view md-hide xl-show hide w=272:
|
|
24
|
-
- rtgl-view w="1fg" ah=c:
|
|
25
|
-
- rtgl-view pv="lg" lg-w="100vw" w="720" ph="lg" sv id="content-container":
|
|
26
|
-
- rtgl-view hide md-show h=56:
|
|
27
|
-
- rtgl-text s="h1" mb="lg": ${title}
|
|
28
|
-
- "${content}"
|
|
29
|
-
- rtgl-view h=200:
|
|
30
|
-
- 'rtgl-view xl-hide style="position: fixed; right: 0"':
|
|
31
|
-
- rtgl-page-outline id="page-outline" target-id="content-container" scroll-container-id="window" offset-top="80":
|
|
32
|
-
|
|
33
|
-
- script src="/public/mobile-nav.js":
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
- html lang="en":
|
|
2
|
-
- head:
|
|
3
|
-
- $partial: seo1
|
|
4
|
-
page: ${page}
|
|
5
|
-
seo: ${seo}
|
|
6
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/base.css":
|
|
7
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/theme-rtgl-slate.css":
|
|
8
|
-
- script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js":
|
|
9
|
-
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/rettangoli-iife-ui.min.js":
|
|
10
|
-
- script src="/public/rtgl-icons.js":
|
|
11
|
-
|
|
12
|
-
- body.dark:
|
|
13
|
-
- $partial: mobile-nav1
|
|
14
|
-
headerLabel: ${feDocs.header.label}
|
|
15
|
-
headerHref: ${feDocs.header.href}
|
|
16
|
-
header: ${feDocs.header}
|
|
17
|
-
items: ${feDocs.items}
|
|
18
|
-
sidebarId: ${sidebarId}
|
|
19
|
-
|
|
20
|
-
- rtgl-view bgc="bg" w="f" d=h:
|
|
21
|
-
- 'rtgl-view md-hide pos=fix h=100vh bgc=bg style="left: 0"':
|
|
22
|
-
- rtgl-sidebar selected-item-id=${sidebarId} header="${encodeURIComponent(jsonStringify(feDocs.header))}" items="${encodeURIComponent(jsonStringify(feDocs.items))}":
|
|
23
|
-
- rtgl-view md-hide xl-show hide w=272:
|
|
24
|
-
- rtgl-view w="1fg" ah=c:
|
|
25
|
-
- rtgl-view pv="lg" lg-w="100vw" w="720" ph="lg" sv id="content-container":
|
|
26
|
-
- rtgl-view hide md-show h=56:
|
|
27
|
-
- rtgl-text s="h1" mb="lg": ${title}
|
|
28
|
-
- "${content}"
|
|
29
|
-
- rtgl-view h=200:
|
|
30
|
-
- 'rtgl-view xl-hide style="position: fixed; right: 0"':
|
|
31
|
-
- rtgl-page-outline id="page-outline" target-id="content-container" scroll-container-id="window" offset-top="80":
|
|
32
|
-
|
|
33
|
-
- script src="/public/mobile-nav.js":
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
- html lang="en":
|
|
2
|
-
- head:
|
|
3
|
-
- $partial: seo1
|
|
4
|
-
page: ${page}
|
|
5
|
-
seo: ${seo}
|
|
6
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/base.css":
|
|
7
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/theme-rtgl-slate.css":
|
|
8
|
-
- script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js":
|
|
9
|
-
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/rettangoli-iife-ui.min.js":
|
|
10
|
-
- script src="/public/rtgl-icons.js":
|
|
11
|
-
|
|
12
|
-
- body.dark:
|
|
13
|
-
- $partial: mobile-nav1
|
|
14
|
-
headerLabel: ${sitesDocs.header.label}
|
|
15
|
-
headerHref: ${sitesDocs.header.href}
|
|
16
|
-
header: ${sitesDocs.header}
|
|
17
|
-
items: ${sitesDocs.items}
|
|
18
|
-
sidebarId: ${sidebarId}
|
|
19
|
-
|
|
20
|
-
- rtgl-view bgc="bg" w="f" d=h:
|
|
21
|
-
- 'rtgl-view md-hide pos=fix h=100vh bgc=bg style="left: 0"':
|
|
22
|
-
- rtgl-sidebar selected-item-id=${sidebarId} header="${encodeURIComponent(jsonStringify(sitesDocs.header))}" items="${encodeURIComponent(jsonStringify(sitesDocs.items))}":
|
|
23
|
-
- rtgl-view md-hide xl-show hide w=272:
|
|
24
|
-
- rtgl-view w="1fg" ah=c:
|
|
25
|
-
- rtgl-view pv="lg" lg-w="100vw" w="720" ph="lg" sv id="content-container":
|
|
26
|
-
- rtgl-view hide md-show h=56:
|
|
27
|
-
- rtgl-text s="h1" mb="lg": ${title}
|
|
28
|
-
- "${content}"
|
|
29
|
-
- rtgl-view h=200:
|
|
30
|
-
- 'rtgl-view xl-hide style="position: fixed; right: 0"':
|
|
31
|
-
- rtgl-page-outline id="page-outline" target-id="content-container" scroll-container-id="window" offset-top="80":
|
|
32
|
-
|
|
33
|
-
- script src="/public/mobile-nav.js":
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
- html lang="en":
|
|
2
|
-
- head:
|
|
3
|
-
- $partial: seo1
|
|
4
|
-
page: ${page}
|
|
5
|
-
seo: ${seo}
|
|
6
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/base.css":
|
|
7
|
-
- link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/themes/theme-rtgl-slate.css":
|
|
8
|
-
- script src="https://cdn.jsdelivr.net/npm/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.min.js":
|
|
9
|
-
- script src="https://cdn.jsdelivr.net/npm/@rettangoli/ui@1.0.0-rc13/dist/rettangoli-iife-ui.min.js":
|
|
10
|
-
- script src="/public/rtgl-icons.js":
|
|
11
|
-
|
|
12
|
-
- body.dark:
|
|
13
|
-
- $partial: mobile-nav1
|
|
14
|
-
headerLabel: ${vtDocs.header.label}
|
|
15
|
-
headerHref: ${vtDocs.header.href}
|
|
16
|
-
header: ${vtDocs.header}
|
|
17
|
-
items: ${vtDocs.items}
|
|
18
|
-
sidebarId: ${sidebarId}
|
|
19
|
-
|
|
20
|
-
- rtgl-view bgc="bg" w="f" d=h:
|
|
21
|
-
- 'rtgl-view md-hide pos=fix h=100vh bgc=bg style="left: 0"':
|
|
22
|
-
- rtgl-sidebar selected-item-id=${sidebarId} header="${encodeURIComponent(jsonStringify(vtDocs.header))}" items="${encodeURIComponent(jsonStringify(vtDocs.items))}":
|
|
23
|
-
- rtgl-view md-hide xl-show hide w=272:
|
|
24
|
-
- rtgl-view w="1fg" ah=c:
|
|
25
|
-
- rtgl-view pv="lg" lg-w="100vw" w="720" ph="lg" sv id="content-container":
|
|
26
|
-
- rtgl-view hide md-show h=56:
|
|
27
|
-
- rtgl-text s="h1" mb="lg": ${title}
|
|
28
|
-
- "${content}"
|
|
29
|
-
- rtgl-view h=200:
|
|
30
|
-
- 'rtgl-view xl-hide style="position: fixed; right: 0"':
|
|
31
|
-
- rtgl-page-outline id="page-outline" target-id="content-container" scroll-container-id="window" offset-top="80":
|
|
32
|
-
|
|
33
|
-
- script src="/public/mobile-nav.js":
|