@opnpress/opnpress-cli 0.1.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/README.md +72 -0
- package/dist/build.js +518 -0
- package/dist/cli.js +68 -0
- package/dist/config.js +210 -0
- package/dist/content.js +109 -0
- package/dist/init.js +45 -0
- package/dist/render.js +1975 -0
- package/dist/server.js +97 -0
- package/dist/utils.js +45 -0
- package/package.json +51 -0
- package/templates/.github/workflows/build-pages.yml +37 -0
- package/templates/.skills/README.md +6 -0
- package/templates/.skills/add-shortcode.md +18 -0
- package/templates/.skills/create-page.md +17 -0
- package/templates/.skills/deployment-checks.md +75 -0
- package/templates/.skills/integrations.md +6 -0
- package/templates/.skills/link-audit.md +17 -0
- package/templates/.skills/setup-integrations.md +84 -0
- package/templates/.skills/shortcodes.md +152 -0
- package/templates/.skills/site-audit.md +20 -0
- package/templates/.skills/theme-customization.md +17 -0
- package/templates/.skills/update-header-footer.md +15 -0
- package/templates/.skills/update-navigation.md +16 -0
- package/templates/.skills/update-page.md +16 -0
- package/templates/README.md +32 -0
- package/templates/content/pages/index.md +8 -0
- package/templates/navigation.yaml +20 -0
- package/templates/site.config.yaml +80 -0
- package/templates/theme.config.yaml +42 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { promises as fs } from 'node:fs';
|
|
4
|
+
function contentTypeFor(filePath) {
|
|
5
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
6
|
+
switch (ext) {
|
|
7
|
+
case '.html':
|
|
8
|
+
return 'text/html; charset=utf-8';
|
|
9
|
+
case '.css':
|
|
10
|
+
return 'text/css; charset=utf-8';
|
|
11
|
+
case '.js':
|
|
12
|
+
return 'text/javascript; charset=utf-8';
|
|
13
|
+
case '.json':
|
|
14
|
+
return 'application/json; charset=utf-8';
|
|
15
|
+
case '.xml':
|
|
16
|
+
return 'application/xml; charset=utf-8';
|
|
17
|
+
case '.txt':
|
|
18
|
+
return 'text/plain; charset=utf-8';
|
|
19
|
+
case '.md':
|
|
20
|
+
return 'text/markdown; charset=utf-8';
|
|
21
|
+
case '.svg':
|
|
22
|
+
return 'image/svg+xml';
|
|
23
|
+
case '.png':
|
|
24
|
+
return 'image/png';
|
|
25
|
+
case '.jpg':
|
|
26
|
+
case '.jpeg':
|
|
27
|
+
return 'image/jpeg';
|
|
28
|
+
case '.webp':
|
|
29
|
+
return 'image/webp';
|
|
30
|
+
case '.ico':
|
|
31
|
+
return 'image/x-icon';
|
|
32
|
+
default:
|
|
33
|
+
return 'application/octet-stream';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function resolveFile(distDir, requestUrl) {
|
|
37
|
+
const urlPath = decodeURIComponent(new URL(requestUrl, 'http://localhost').pathname);
|
|
38
|
+
const cleanPath = urlPath === '/' ? '/index.html' : urlPath;
|
|
39
|
+
const directPath = path.join(distDir, cleanPath.replace(/^\//, ''));
|
|
40
|
+
try {
|
|
41
|
+
const stat = await fs.stat(directPath);
|
|
42
|
+
if (stat.isFile()) {
|
|
43
|
+
return directPath;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// fall through
|
|
48
|
+
}
|
|
49
|
+
if (!path.extname(cleanPath)) {
|
|
50
|
+
const nestedIndex = path.join(distDir, cleanPath.replace(/^\//, ''), 'index.html');
|
|
51
|
+
try {
|
|
52
|
+
const stat = await fs.stat(nestedIndex);
|
|
53
|
+
if (stat.isFile()) {
|
|
54
|
+
return nestedIndex;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// fall through
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
export async function startPreviewServer(distDir) {
|
|
64
|
+
const server = createServer(async (req, res) => {
|
|
65
|
+
const requestUrl = req.url ?? '/';
|
|
66
|
+
const filePath = await resolveFile(distDir, requestUrl);
|
|
67
|
+
if (!filePath) {
|
|
68
|
+
res.statusCode = 404;
|
|
69
|
+
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
|
|
70
|
+
res.end('Not found');
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const body = await fs.readFile(filePath);
|
|
74
|
+
res.statusCode = 200;
|
|
75
|
+
res.setHeader('Content-Type', contentTypeFor(filePath));
|
|
76
|
+
res.end(body);
|
|
77
|
+
});
|
|
78
|
+
await new Promise((resolve) => {
|
|
79
|
+
server.listen(0, '127.0.0.1', () => resolve());
|
|
80
|
+
});
|
|
81
|
+
const address = server.address();
|
|
82
|
+
const url = `http://127.0.0.1:${address.port}/`;
|
|
83
|
+
return {
|
|
84
|
+
url,
|
|
85
|
+
close: async () => {
|
|
86
|
+
await new Promise((resolve, reject) => {
|
|
87
|
+
server.close((error) => {
|
|
88
|
+
if (error) {
|
|
89
|
+
reject(error);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
resolve();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export function toPosix(input) {
|
|
4
|
+
return input.split(path.sep).join('/');
|
|
5
|
+
}
|
|
6
|
+
export function slugify(input) {
|
|
7
|
+
return input
|
|
8
|
+
.trim()
|
|
9
|
+
.toLowerCase()
|
|
10
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
11
|
+
.replace(/^-+|-+$/g, '');
|
|
12
|
+
}
|
|
13
|
+
export function escapeHtml(input) {
|
|
14
|
+
return String(input)
|
|
15
|
+
.replace(/&/g, '&')
|
|
16
|
+
.replace(/</g, '<')
|
|
17
|
+
.replace(/>/g, '>')
|
|
18
|
+
.replace(/"/g, '"')
|
|
19
|
+
.replace(/'/g, ''');
|
|
20
|
+
}
|
|
21
|
+
export async function ensureDir(dir) {
|
|
22
|
+
await fs.mkdir(dir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
export async function writeFileEnsured(filePath, content) {
|
|
25
|
+
await ensureDir(path.dirname(filePath));
|
|
26
|
+
await fs.writeFile(filePath, content, 'utf8');
|
|
27
|
+
}
|
|
28
|
+
export async function walkFiles(root) {
|
|
29
|
+
const results = [];
|
|
30
|
+
const entries = await fs.readdir(root, { withFileTypes: true });
|
|
31
|
+
for (const entry of entries) {
|
|
32
|
+
const fullPath = path.join(root, entry.name);
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
results.push(...(await walkFiles(fullPath)));
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (entry.isFile()) {
|
|
38
|
+
results.push(fullPath);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
export function isFullHtmlDocument(html) {
|
|
44
|
+
return /<html[\s>]/i.test(html) || /<!doctype html>/i.test(html);
|
|
45
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opnpress/opnpress-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/OpnPress/OpnPressCli.git"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"templates",
|
|
13
|
+
"README.md",
|
|
14
|
+
"package.json"
|
|
15
|
+
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"opnPrs": "./dist/cli.js",
|
|
21
|
+
"opnPress": "./dist/cli.js"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.build.json",
|
|
25
|
+
"check": "tsc --noEmit",
|
|
26
|
+
"dev": "tsx src/cli.ts",
|
|
27
|
+
"init": "tsx src/cli.ts init",
|
|
28
|
+
"run": "tsx src/cli.ts run",
|
|
29
|
+
"test": "vitest run"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"gray-matter": "^4.0.3",
|
|
33
|
+
"rehype-autolink-headings": "^7.1.0",
|
|
34
|
+
"rehype-raw": "^7.0.0",
|
|
35
|
+
"rehype-slug": "^6.0.0",
|
|
36
|
+
"rehype-stringify": "^10.0.1",
|
|
37
|
+
"remark-gfm": "^4.0.1",
|
|
38
|
+
"remark-parse": "^11.0.0",
|
|
39
|
+
"remark-rehype": "^11.1.2",
|
|
40
|
+
"tsx": "^4.20.3",
|
|
41
|
+
"unified": "^11.0.5",
|
|
42
|
+
"yaml": "^2.8.1",
|
|
43
|
+
"zod": "^3.25.67"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^24.3.0",
|
|
47
|
+
"playwright": "^1.59.1",
|
|
48
|
+
"typescript": "^5.9.2",
|
|
49
|
+
"vitest": "^3.2.4"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Build and Deploy Pages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
concurrency:
|
|
13
|
+
group: cloudflare-pages
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
deploy:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
steps:
|
|
20
|
+
- name: Checkout
|
|
21
|
+
uses: actions/checkout@v4
|
|
22
|
+
|
|
23
|
+
- name: Setup Node
|
|
24
|
+
uses: actions/setup-node@v4
|
|
25
|
+
with:
|
|
26
|
+
node-version: '22'
|
|
27
|
+
cache: npm
|
|
28
|
+
|
|
29
|
+
- name: Build site
|
|
30
|
+
run: npx -y @opnpress/opnpress-cli@latest opnPress build
|
|
31
|
+
|
|
32
|
+
- name: Publish to Cloudflare Pages
|
|
33
|
+
uses: cloudflare/wrangler-action@v3
|
|
34
|
+
with:
|
|
35
|
+
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
36
|
+
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
37
|
+
command: pages deploy dist --project-name=${{ secrets.CLOUDFLARE_PROJECT_NAME }} --branch=${{ github.ref_name }}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Add Shortcode
|
|
2
|
+
|
|
3
|
+
Use this skill when adding a shortcode block to an existing page.
|
|
4
|
+
|
|
5
|
+
## What To Do
|
|
6
|
+
|
|
7
|
+
- read `.skills/shortcodes.md` before choosing the block
|
|
8
|
+
- prefer the simplest shortcode that fits the page intent
|
|
9
|
+
- keep page-local links relative, and use `source.md` only when you explicitly want the source mirror
|
|
10
|
+
- use integration-backed blocks only when the content comes from `site.config.yaml`
|
|
11
|
+
- keep the page content readable to an LLM without needing rendered HTML
|
|
12
|
+
|
|
13
|
+
## Recommended Checks
|
|
14
|
+
|
|
15
|
+
- verify the shortcode matches the content type and source of truth
|
|
16
|
+
- verify links between pages stay relative
|
|
17
|
+
- verify the build still passes after the page change
|
|
18
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Create Page
|
|
2
|
+
|
|
3
|
+
Use this skill when adding a new page to an OpnPress site.
|
|
4
|
+
|
|
5
|
+
## What To Do
|
|
6
|
+
|
|
7
|
+
- choose the correct file path under `content/pages/`, `content/services/`, or `content/locations/`
|
|
8
|
+
- add YAML frontmatter with title, description, and layout
|
|
9
|
+
- keep the page focused on one intent
|
|
10
|
+
- validate the build after creating the file
|
|
11
|
+
|
|
12
|
+
## Recommended Checks
|
|
13
|
+
|
|
14
|
+
- verify the route matches the file path
|
|
15
|
+
- verify navigation points to the page if needed
|
|
16
|
+
- verify the page renders without broken links
|
|
17
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Deployment Checks
|
|
2
|
+
|
|
3
|
+
Use this skill before publishing a site or opening a release.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
- run the other relevant skills
|
|
8
|
+
- identify what is missing before publish
|
|
9
|
+
- generate a report with actionable fixes
|
|
10
|
+
- save the report for later reference
|
|
11
|
+
- capture user answers as durable decisions so future checks can reuse them
|
|
12
|
+
|
|
13
|
+
## Trigger Order
|
|
14
|
+
|
|
15
|
+
1. `site-audit`
|
|
16
|
+
2. `link-audit`
|
|
17
|
+
3. `theme-customization`
|
|
18
|
+
4. `setup-integrations`
|
|
19
|
+
5. `update-navigation`
|
|
20
|
+
6. `update-header-footer`
|
|
21
|
+
7. `create-page` or `update-page` for page-specific fixes
|
|
22
|
+
|
|
23
|
+
## What To Check
|
|
24
|
+
|
|
25
|
+
- missing required page metadata
|
|
26
|
+
- broken internal links
|
|
27
|
+
- orphaned or unreachable pages
|
|
28
|
+
- navigation completeness
|
|
29
|
+
- header and footer consistency
|
|
30
|
+
- integration configuration
|
|
31
|
+
- SEO and AI metadata
|
|
32
|
+
- build output readiness
|
|
33
|
+
- publish target readiness
|
|
34
|
+
|
|
35
|
+
## Required Output
|
|
36
|
+
|
|
37
|
+
Write a report with these sections:
|
|
38
|
+
|
|
39
|
+
- Summary
|
|
40
|
+
- Blocking issues
|
|
41
|
+
- Non-blocking recommendations
|
|
42
|
+
- Pages to create or update
|
|
43
|
+
- Navigation or footer changes
|
|
44
|
+
- Integration fixes
|
|
45
|
+
- Publish readiness verdict
|
|
46
|
+
- Saved decisions
|
|
47
|
+
|
|
48
|
+
## Report Rules
|
|
49
|
+
|
|
50
|
+
- be specific about file paths
|
|
51
|
+
- explain why each item matters
|
|
52
|
+
- recommend the exact next action
|
|
53
|
+
- separate blockers from recommendations
|
|
54
|
+
- if nothing is blocking, say so clearly
|
|
55
|
+
|
|
56
|
+
## Saving The Report
|
|
57
|
+
|
|
58
|
+
- save the report in the repository under `.opnpress/reports/deployment-checks.md`
|
|
59
|
+
- if the folder does not exist, create it
|
|
60
|
+
- append the date and time of the check to the top of the report
|
|
61
|
+
- keep the latest report easy to find
|
|
62
|
+
|
|
63
|
+
## Learning From The User
|
|
64
|
+
|
|
65
|
+
- if the user answers questions during the check, record those answers in the report under `Saved Decisions`
|
|
66
|
+
- preserve the answers in a reusable note so future checks can apply them automatically
|
|
67
|
+
- when a decision is repeated, prefer the saved answer over asking again
|
|
68
|
+
|
|
69
|
+
## Recommended Behavior
|
|
70
|
+
|
|
71
|
+
- treat this as the last step before publish
|
|
72
|
+
- if a blocker exists, stop and report it
|
|
73
|
+
- if no blockers exist, confirm the site is ready for deployment
|
|
74
|
+
- prefer concrete file edits over vague advice
|
|
75
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Link Audit
|
|
2
|
+
|
|
3
|
+
Use this skill when checking for broken or orphaned pages.
|
|
4
|
+
|
|
5
|
+
## What To Check
|
|
6
|
+
|
|
7
|
+
- broken internal links
|
|
8
|
+
- orphaned content pages
|
|
9
|
+
- missing navigation targets
|
|
10
|
+
- mismatched route and file paths
|
|
11
|
+
|
|
12
|
+
## Output Style
|
|
13
|
+
|
|
14
|
+
- list the broken or orphaned path
|
|
15
|
+
- explain where it is referenced, or not referenced
|
|
16
|
+
- recommend whether to link it, delete it, or redirect it
|
|
17
|
+
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Setup Integrations
|
|
2
|
+
|
|
3
|
+
Use this skill when adding or changing integrations.
|
|
4
|
+
|
|
5
|
+
## What To Do
|
|
6
|
+
|
|
7
|
+
- define the provider in `site.config.yaml`
|
|
8
|
+
- author the semantic block in markdown
|
|
9
|
+
- keep the content provider-agnostic
|
|
10
|
+
- prefer integration-backed blocks for shared site data
|
|
11
|
+
- validate the page after rendering
|
|
12
|
+
|
|
13
|
+
## Supported Blocks
|
|
14
|
+
|
|
15
|
+
- `contact-form`
|
|
16
|
+
- `shareable-links`
|
|
17
|
+
- `socials-links` from `site.socials`
|
|
18
|
+
- `company-info`
|
|
19
|
+
- `contact-links`
|
|
20
|
+
- `booking-calendar`
|
|
21
|
+
- `maps`
|
|
22
|
+
|
|
23
|
+
## Media Shortcodes
|
|
24
|
+
|
|
25
|
+
- `video`
|
|
26
|
+
|
|
27
|
+
Treat `video` as a shortcode with `provider` and `url` params, not as a separate integration category.
|
|
28
|
+
|
|
29
|
+
## Page-Local Shortcodes
|
|
30
|
+
|
|
31
|
+
- `contact-card`
|
|
32
|
+
- `mailto`
|
|
33
|
+
- `tel`
|
|
34
|
+
|
|
35
|
+
Use `contact-card` for page-local contact blocks such as franchise locations or branch pages.
|
|
36
|
+
Use `mailto` and `tel` for single-use contact actions inside page content.
|
|
37
|
+
|
|
38
|
+
## Social Profiles
|
|
39
|
+
|
|
40
|
+
Use `site.socials` for public profile links and social metadata.
|
|
41
|
+
|
|
42
|
+
Supported keys:
|
|
43
|
+
|
|
44
|
+
- `twitter`
|
|
45
|
+
- `x`
|
|
46
|
+
- `github`
|
|
47
|
+
- `facebook`
|
|
48
|
+
- `instagram`
|
|
49
|
+
- `linkedin`
|
|
50
|
+
- `youtube`
|
|
51
|
+
- `tiktok`
|
|
52
|
+
- `threads`
|
|
53
|
+
- `mastodon`
|
|
54
|
+
- `bluesky`
|
|
55
|
+
- `website`
|
|
56
|
+
- `email`
|
|
57
|
+
|
|
58
|
+
Each value can be a handle or a full URL. The renderer converts handles into public profile URLs where possible.
|
|
59
|
+
|
|
60
|
+
## Config Fields
|
|
61
|
+
|
|
62
|
+
- `site.site.*` for site identity and branding
|
|
63
|
+
- `site.seo.defaultImage` for crawl and preview defaults
|
|
64
|
+
- `site.socials.*` for social identity links
|
|
65
|
+
- `site.branding.poweredByOpnPress` for footer attribution
|
|
66
|
+
- `site.deployment.provider` and `site.deployment.cloudflare.*` for publishing
|
|
67
|
+
- `site.integrations.contactForm.*`
|
|
68
|
+
- `site.integrations.shareableLinks.*`
|
|
69
|
+
- `site.integrations.companyInfo.*`
|
|
70
|
+
- `site.integrations.contactLinks.*`
|
|
71
|
+
- `site.integrations.bookingCalendar.*`
|
|
72
|
+
- `site.integrations.maps.*`
|
|
73
|
+
- `contact-card` shortcode params: `name`, `tagline`, `logo`, `address`, `mapUrl`, `phone`, `email`, `hours`, `website`
|
|
74
|
+
- `contact-links` shortcode params: `variant`, `showIcons`, `showLabels`
|
|
75
|
+
- `mailto` shortcode params: `email`, `subject`, `body`, `label`, `showIcon`
|
|
76
|
+
- `tel` shortcode params: `phone`, `label`, `showIcon`
|
|
77
|
+
- `video` shortcode params: `provider`, `url`, and optional presentation fields
|
|
78
|
+
|
|
79
|
+
## Recommended Checks
|
|
80
|
+
|
|
81
|
+
- verify required config values are present
|
|
82
|
+
- verify the block renders instead of falling back to escaped code
|
|
83
|
+
- verify the resulting links stay relative when they should
|
|
84
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Shortcodes
|
|
2
|
+
|
|
3
|
+
This file is for LLM-facing reference only.
|
|
4
|
+
|
|
5
|
+
It is not rendered as a site page. The build copies it into `dist/shortcodes.md` and references it from `llms.txt`.
|
|
6
|
+
|
|
7
|
+
## Principles
|
|
8
|
+
|
|
9
|
+
- Pages render directly from markdown or standalone HTML.
|
|
10
|
+
- Shortcodes exist for structured sections, not hidden renderer layouts.
|
|
11
|
+
- Some shortcodes are integration display hooks: they only control placement, not the underlying data source.
|
|
12
|
+
- All links between markdown pages, mirrors, and related content should stay relative so an LLM can infer the site structure and traverse it without resolving absolute URLs first.
|
|
13
|
+
|
|
14
|
+
## Shared Presentation
|
|
15
|
+
|
|
16
|
+
Most shortcodes support the same shared presentation props:
|
|
17
|
+
|
|
18
|
+
- `title`
|
|
19
|
+
- `description`
|
|
20
|
+
- `eyebrow`
|
|
21
|
+
- `anchor`
|
|
22
|
+
- `backgroundImage`
|
|
23
|
+
- `backgroundPosition`
|
|
24
|
+
- `backgroundSize`
|
|
25
|
+
- `backgroundRepeat`
|
|
26
|
+
- `backgroundColor`
|
|
27
|
+
- `overlay`
|
|
28
|
+
- `textColor`
|
|
29
|
+
- `padding`
|
|
30
|
+
- `minHeight`
|
|
31
|
+
- `maxWidth`
|
|
32
|
+
- `align`
|
|
33
|
+
|
|
34
|
+
Keep these relative-path friendly for assets and backgrounds.
|
|
35
|
+
|
|
36
|
+
## Page-Structure Shortcodes
|
|
37
|
+
|
|
38
|
+
### `cardrow`
|
|
39
|
+
|
|
40
|
+
Render a row or grid of cards on a markdown page.
|
|
41
|
+
|
|
42
|
+
Prefer explicit card objects with relative `href` values such as `about/`.
|
|
43
|
+
Use `source.md` only when the card intentionally points to the LLM/source mirror.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
|
|
47
|
+
```md
|
|
48
|
+
:::cardrow
|
|
49
|
+
title: Explore the starter site
|
|
50
|
+
description: Cards should normally point at rendered pages.
|
|
51
|
+
cards:
|
|
52
|
+
- title: Pages
|
|
53
|
+
href: about/
|
|
54
|
+
body: Create markdown pages with YAML frontmatter.
|
|
55
|
+
- title: Integrations
|
|
56
|
+
href: integrations/
|
|
57
|
+
body: Render semantic blocks for forms, social links, maps, video, and calendars.
|
|
58
|
+
- title: Services
|
|
59
|
+
href: services/
|
|
60
|
+
body: Create service pages with the same markdown-first workflow.
|
|
61
|
+
:::
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### `pagelist`
|
|
65
|
+
|
|
66
|
+
Render a list of pages from a folder.
|
|
67
|
+
|
|
68
|
+
The list links to each page's rendered route by default. Use source mirrors only when the list is specifically for LLM traversal or source inspection.
|
|
69
|
+
|
|
70
|
+
### `contact-card`
|
|
71
|
+
|
|
72
|
+
Render a page-local company or branch contact card.
|
|
73
|
+
|
|
74
|
+
Use this for franchise locations, offices, or branch pages.
|
|
75
|
+
|
|
76
|
+
It accepts the same contact fields as the `company-info` integration, but on a single page.
|
|
77
|
+
|
|
78
|
+
### `contact-links`
|
|
79
|
+
|
|
80
|
+
Render the reusable contact links that come from the site-level `contact-links` integration.
|
|
81
|
+
|
|
82
|
+
This shortcode only decides where the configured contact links appear on the page.
|
|
83
|
+
|
|
84
|
+
### `mailto`
|
|
85
|
+
|
|
86
|
+
Render a single email link or compact email block.
|
|
87
|
+
|
|
88
|
+
Use `email`, `subject`, `body`, `label`, and `showIcon` in the shortcode body.
|
|
89
|
+
|
|
90
|
+
### `tel`
|
|
91
|
+
|
|
92
|
+
Render a single telephone link or compact phone block.
|
|
93
|
+
|
|
94
|
+
Use `phone`, `label`, and `showIcon` in the shortcode body.
|
|
95
|
+
|
|
96
|
+
## Integration-Backed Blocks
|
|
97
|
+
|
|
98
|
+
### `contact-form`
|
|
99
|
+
|
|
100
|
+
Render a provider-backed contact form.
|
|
101
|
+
|
|
102
|
+
Requires `site.integrations.contactForm.action`.
|
|
103
|
+
|
|
104
|
+
### `shareable-links`
|
|
105
|
+
|
|
106
|
+
Render share links for the current page.
|
|
107
|
+
|
|
108
|
+
Requires `site.integrations.shareableLinks.enabled`.
|
|
109
|
+
|
|
110
|
+
### `socials-links`
|
|
111
|
+
|
|
112
|
+
Render configured social profile links from `site.socials`.
|
|
113
|
+
|
|
114
|
+
### `booking-calendar`
|
|
115
|
+
|
|
116
|
+
Render a scheduling embed.
|
|
117
|
+
|
|
118
|
+
Requires `site.integrations.bookingCalendar.embedUrl`.
|
|
119
|
+
|
|
120
|
+
### `maps`
|
|
121
|
+
|
|
122
|
+
Render a map embed.
|
|
123
|
+
|
|
124
|
+
Requires `site.integrations.maps.embedUrl`.
|
|
125
|
+
|
|
126
|
+
### `company-info`
|
|
127
|
+
|
|
128
|
+
Render canonical company/contact information from site config.
|
|
129
|
+
|
|
130
|
+
This is the site-wide version of the contact card and can be reused in headers, footers, and contact areas.
|
|
131
|
+
|
|
132
|
+
### `contact-links`
|
|
133
|
+
|
|
134
|
+
Render the reusable contact link cluster from site config.
|
|
135
|
+
|
|
136
|
+
This is the site-wide display hook for contact actions like email, phone, website, and map links.
|
|
137
|
+
|
|
138
|
+
## Media Shortcodes
|
|
139
|
+
|
|
140
|
+
### `video`
|
|
141
|
+
|
|
142
|
+
Render a video embed from shortcode params. This is a shortcode, not a site integration category.
|
|
143
|
+
|
|
144
|
+
Provider should usually be `youtube`, `vimeo`, or `loom`.
|
|
145
|
+
|
|
146
|
+
## Markdown And HTML Mirrors
|
|
147
|
+
|
|
148
|
+
- Markdown pages are published as HTML and mirrored as `source.md`.
|
|
149
|
+
- Custom HTML pages are published as HTML and mirrored as `source.html`.
|
|
150
|
+
- `llms.txt` lists both types separately and labels them with `markdown` or `html`.
|
|
151
|
+
- The hidden `[system-reminder]` marker on markdown pages points to the `source.md` mirror, but visible navigation should normally point to the rendered page.
|
|
152
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Site Audit
|
|
2
|
+
|
|
3
|
+
Use this skill when checking a site for missing pieces and recommending fixes.
|
|
4
|
+
|
|
5
|
+
## What To Check
|
|
6
|
+
|
|
7
|
+
- required frontmatter fields
|
|
8
|
+
- page titles and descriptions
|
|
9
|
+
- canonical and social metadata
|
|
10
|
+
- navigation completeness
|
|
11
|
+
- footer branding
|
|
12
|
+
- integration config presence
|
|
13
|
+
- build output completeness
|
|
14
|
+
|
|
15
|
+
## Output Style
|
|
16
|
+
|
|
17
|
+
- list what is missing
|
|
18
|
+
- explain the impact
|
|
19
|
+
- recommend the next fix in file terms
|
|
20
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Theme Customization
|
|
2
|
+
|
|
3
|
+
Use this skill when changing the site theme.
|
|
4
|
+
|
|
5
|
+
## MVP Surface
|
|
6
|
+
|
|
7
|
+
- colors
|
|
8
|
+
- backgrounds
|
|
9
|
+
- fonts
|
|
10
|
+
- sizing
|
|
11
|
+
- limited layout controls
|
|
12
|
+
|
|
13
|
+
## Recommended Checks
|
|
14
|
+
|
|
15
|
+
- verify the theme still reads well on mobile
|
|
16
|
+
- verify contrast and spacing remain usable
|
|
17
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Update Header And Footer
|
|
2
|
+
|
|
3
|
+
Use this skill when changing global header or footer content.
|
|
4
|
+
|
|
5
|
+
## What To Do
|
|
6
|
+
|
|
7
|
+
- edit the site shell, not individual pages, for global header/footer changes
|
|
8
|
+
- keep branding links intentional
|
|
9
|
+
- preserve readable navigation on mobile
|
|
10
|
+
|
|
11
|
+
## Recommended Checks
|
|
12
|
+
|
|
13
|
+
- verify the header still links home correctly
|
|
14
|
+
- verify the footer still includes required brand or attribution links
|
|
15
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Update Navigation
|
|
2
|
+
|
|
3
|
+
Use this skill when editing site navigation.
|
|
4
|
+
|
|
5
|
+
## What To Do
|
|
6
|
+
|
|
7
|
+
- update `navigation.yaml`
|
|
8
|
+
- keep labels short and clear
|
|
9
|
+
- use relative internal paths for site pages
|
|
10
|
+
- keep external links explicit
|
|
11
|
+
|
|
12
|
+
## Recommended Checks
|
|
13
|
+
|
|
14
|
+
- verify every internal navigation target exists
|
|
15
|
+
- verify footer and header nav stay consistent with the site structure
|
|
16
|
+
|