edgepress 2026.10.13 → 2026.10.15

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 CHANGED
@@ -8,10 +8,10 @@ EdgePress automatically registers page-specific native WebMCP tools for reading
8
8
 
9
9
  Document previews use the browser-only `document-viewer` package: PDF, DOC/DOCX, PPT/PPTX and XLS/XLSX render inside the page automatically without moving keyboard focus. Articles accept `!document[Report](/documents/report.docx)`; Pages support `type: document`. Remote direct-file URLs require a configured consent service and CORS. See the theme-development guide.
10
10
 
11
- The dependency is pinned to a full commit in the public [document-viewer repository](https://github.com/jsw-teams/document-viewer). Cloud `npm ci` needs no sibling checkout, bundled source snapshot or Office conversion software. Publish and validate document-viewer first, then update the dependency URL and lockfile here. Sites can consume the resulting pinned EdgePress commit without copying either implementation.
11
+ The dependency is pinned to a full commit in the public [document-viewer repository](https://github.com/jsw-teams/document-viewer). Cloud `npm install` needs no sibling checkout, bundled source snapshot or Office conversion software. Publish and validate document-viewer first, then update the dependency URL and lockfile here. Sites can consume the resulting pinned EdgePress commit without copying either implementation.
12
12
 
13
13
  [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/jsw-teams/edgepress)
14
- [![Deploy to EdgeOne Pages](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?repository-url=https%3A%2F%2Fgithub.com%2Fjsw-teams%2Fedgepress&build-command=npm%20run%20build&install-command=npm%20ci&output-directory=dist)
14
+ [![Deploy to EdgeOne Pages](https://cdnstatic.tencentcs.com/edgeone/pages/deploy.svg)](https://edgeone.ai/pages/new?repository-url=https%3A%2F%2Fgithub.com%2Fjsw-teams%2Fedgepress&build-command=npm%20run%20build&install-command=npm%20install&output-directory=dist)
15
15
  [![Deploy to Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fjsw-teams%2Fedgepress)
16
16
  [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/jsw-teams/edgepress)
17
17
  [![Import into ESA Pages](content/assets/edgepress/brand/deploy-esa.svg)](https://esa.console.aliyun.com/edge/pages/creation)
@@ -34,9 +34,15 @@ The builder automatically explains the latest article edit. Readers see formatte
34
34
 
35
35
  Buttons open repository setup. Cloudflare, Vercel, Netlify and EdgeOne support repository templates; ESA opens its official import screen, where you authorize GitHub and select the repository. Every static platform uses `npm run build` and `dist/`; no database or website Function is required. Write posts in Markdown, compose pages from editable rows and elements, choose a shared theme, and build the site into dist/.
36
36
 
37
+ Real VM recording: the actual CLI creates a post, Geany saves its Markdown, and `npm run deploy:local` starts the preview before navigating from the homepage to the article. Playback is manual.
38
+
39
+ [![Real CLI, editor and browser demonstration](content/assets/images/previews/edgepress-cli-local.1b61974af33b2bb1.webp)](content/assets/images/previews/edgepress-cli-local.065373ef0ebd26fa.mp4)
40
+
41
+ [Play recording](content/assets/images/previews/edgepress-cli-local.065373ef0ebd26fa.mp4) · [GIF](content/assets/images/previews/edgepress-cli-local.9697a9de52bef936.gif)
42
+
37
43
  ## Quick start
38
44
 
39
- The commands below install the published npm release and initialize a new project directory. For the latest GitHub changes, use the deployment buttons or clone this repository, then run `npm ci` and `npm run dev` from its root:
45
+ The commands below install the published npm release and initialize a new project directory. For the latest GitHub changes, use the deployment buttons or clone this repository, then run `npm install` and `npm run deploy:local` from its root:
40
46
 
41
47
  mkdir my-edgepress-site
42
48
  cd my-edgepress-site
@@ -45,9 +51,9 @@ The commands below install the published npm release and initialize a new projec
45
51
  npm install
46
52
  npm run dev
47
53
 
48
- Use Node.js 22.12 or newer. Set the real site URL and operator contact in config.yml before publishing. Deploy with edgepress deploy. When developing EdgePress itself, use `npm ci`, then `npm link` to expose the local CLI.
54
+ Use Node.js 22.12 or newer. Set the real site URL and operator contact in config.yml before publishing. Deploy with `npm run deploy:cloudflare`, `npm run deploy:vercel`, `npm run deploy:netlify`, `npm run deploy:esa` or `npm run deploy:edgeone`. `npm run deploy:local` starts the loopback preview without uploading a site. When developing EdgePress itself, use `npm install`, then `npm link` to expose the local CLI.
49
55
 
50
- Run `edgepress init` in a new project directory to scaffold a site pinned to the installed EdgePress release. The initializer keeps the existing package name and scripts.
56
+ Run `edgepress init` in a new project directory to scaffold a site pinned to the installed EdgePress release. The initializer keeps the existing package name and adds the site's CLI scripts.
51
57
 
52
58
  ## Maintainer npm and GitHub releases
53
59
 
@@ -59,6 +65,23 @@ To publish new source, a repository maintainer opens **Actions → Publish npm a
59
65
 
60
66
  **Recovery:** If a run fails *after* pushing its version tag, do not launch a new release blindly: check whether the tag, npm version or GitHub Release already exists and reconcile the incomplete publish first. npm versions cannot be overwritten. Protected branches must explicitly permit the GitHub Actions bot to create the release commit/tag or use a maintainer-approved release branch process.
61
67
 
68
+ ## Visual Pages editor / 可视化页面编辑器
69
+
70
+ Run `npm run editor` (or `npx edgepress editor` in an initialized site), then open **http://127.0.0.1:4178/**. The editor now supports **+ 新建页面**: choose a title, optional URL slug and configured language; the new JSON Page opens immediately in the visual canvas. Existing Pages can still be selected, arranged into 1–4 columns and edited without touching JSON.
71
+
72
+ 从命令行也可以新建页面:
73
+
74
+ ```sh
75
+ edgepress new pages "About our project" --lang en --slug about
76
+ edgepress new pages "关于我们" --lang zh-cn --slug about
77
+ ```
78
+
79
+ The shortcut `edgepress new page` is also accepted. By default, the CLI creates `content/pages/<slug>/<locale>.json` in the site's default language. Existing pages, legacy `.md` equivalents, and symlinked directories cannot be overwritten. The generated `$schema` reference points to the installed framework (or the local development schema). Run `npm run editor` to continue editing, and `npm run build` to validate the actual generated site.
80
+
81
+ **Shared block catalog:** `src/page-block-catalog.json` ships with the npm package and defines starter templates for **all 30 supported Pages block types**. This includes site-reusable `link-directory`, `media-text`, `feature-grid`, `privacy-controller`, `document`, `media-viewer` and consent-controlled integrations used by other frontends. Reuse the public EdgePress catalog from site projects (including private ones); do not copy private pages or integrate site-specific services into the framework. Third-party service, CAPTCHA, ad and local-tool blocks are available as templates but **require site configuration and valid resource URLs** before a build; inserting the template does not activate a service.
82
+
83
+ The editor runs **only on 127.0.0.1**, never on deployed static sites. It enforces origin checks, path restrictions, revision-conflict detection and atomic saves. Unrecognized block properties are kept; complex nested fields can still be edited in the inspector's advanced JSON input. The canvas previews structure, not theme-perfect output. Articles remain Markdown.
84
+
62
85
  ## External link prompts
63
86
 
64
87
  The optional local prompt shows the complete destination URL (including path, query and fragment) once, with no duplicate hostname, and lets the visitor return or continue. It makes no request to the destination before confirmation. Configure exact origins, not wildcard domains:
@@ -271,3 +294,7 @@ Real static website output demonstrates what the builder produces. Product recor
271
294
  Project recordings use manual play/pause and a seekable progress bar, with theme colors and keyboard support. The reproducible CLI/editor workbench lives in [tools/recordings/demo-workbench.mjs](tools/recordings/demo-workbench.mjs); see [the recording guide](content/guides/product-demos.md). Built-in navigation and page icons come from the pinned Lucide 1.52 library, bundled locally with its license.
272
295
 
273
296
  Development files: tests/ contains regression tests and fixtures; tools/assets/ maintains source assets; tools/recordings/ contains desktop capture workflows; tools/verification/ contains browser audits; tools/reports/ holds generated evidence. Run npm test and npm run check after changes.
297
+
298
+ Dependency maintenance uses compatible release ranges without retained lockfiles. The media and document viewers follow Git semver release ranges; audits resolve fresh dependency snapshots outside the checkout and remove them after use. Dependabot checks weekly, including security updates; changed dependencies must pass the full browser suite before release.
299
+
300
+ Weekly automatic dependency maintenance resolves compatible updates, audits and runs this project's regression suite with read-only access, then uses a separate job to commit verified manifest-only repairs. Failed checks or branch protection retain a repair PR; major upgrades are not forced. No lockfiles are retained. The existing package release process remains separate.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "edgepress",
3
3
  "license": "MIT",
4
- "version": "2026.10.13",
4
+ "version": "2026.10.15",
5
5
  "description": "Static websites from Markdown and editable pages, with themes, consent-controlled embeds and readable article updates.",
6
6
  "type": "module",
7
7
  "bin": {
@@ -63,28 +63,32 @@
63
63
  "deploy:edgeone": "node src/cli.js deploy edgeone",
64
64
  "deploy:esa": "node src/cli.js deploy esa",
65
65
  "deploy:netlify": "node src/cli.js deploy netlify",
66
- "fonts:sync": "node tools/assets/sync-fonts.mjs"
66
+ "fonts:sync": "node tools/assets/sync-fonts.mjs",
67
+ "editor": "node src/cli.js editor",
68
+ "audit": "node tools/maintenance/audit-dependencies.mjs low",
69
+ "deploy:local": "node src/cli.js server"
67
70
  },
68
71
  "dependencies": {
69
- "@jsw-teams/document-viewer": "https://codeload.github.com/jsw-teams/document-viewer/tar.gz/ab21d0ee9c078d084b2abee373ffad302928978c",
70
- "@jsw-teams/media-viewer": "https://codeload.github.com/jsw-teams/media-viewer/tar.gz/12eb8983c2df1eabb3aaece44b232c56b4a9e1fe",
72
+ "@jsw-teams/document-viewer": "git+https://github.com/jsw-teams/document-viewer.git#semver:^2026.10.1",
73
+ "@jsw-teams/media-viewer": "git+https://github.com/jsw-teams/media-viewer.git#semver:^2026.10.1",
71
74
  "esbuild": "^0.28.2",
72
75
  "htmlparser2": "^12.0.0",
73
76
  "image-size": "^2.0.4",
74
- "marked": "^18.0.13",
75
- "postcss": "^8.5.15",
77
+ "marked": "^18.1.1",
78
+ "postcss": "^8.5.31",
76
79
  "sanitize-html": "^2.18.1",
77
80
  "yaml": "^2.9.1"
78
81
  },
79
82
  "overrides": {
80
83
  "@file-viewer/doc": {
81
- "dompurify": "3.4.17"
82
- }
84
+ "dompurify": "^3.4.17"
85
+ },
86
+ "undici": "^7.30.1"
83
87
  },
84
88
  "devDependencies": {
85
- "lucide-static": "1.55.0",
86
- "playwright": "^1.63.0",
89
+ "lucide-static": "^1.61.0",
90
+ "playwright": "^1.64.1",
87
91
  "vscode-json-languageservice": "^6.0.0-next.3",
88
- "wrangler": "^4.149.0"
92
+ "wrangler": "^4.152.0"
89
93
  }
90
94
  }
package/src/cli.js CHANGED
@@ -9,12 +9,14 @@ import { spawn } from 'node:child_process';
9
9
  import { acquireBuildLock, buildSite } from './build.js';
10
10
  import { loadConfig } from './config.js';
11
11
  import { slugify } from './content.js';
12
+ import { createPage } from './page-scaffold.js';
12
13
  import { checkPages } from './page-check.js';
13
14
  import { checkCompatibility, writeIterationPlan } from './maintenance.js';
14
15
  import { loadLanguagePacks, translate } from './i18n.js';
15
16
  import { postTemplate } from './post-template.js';
16
17
  import {deploySite} from './deploy.js';
17
18
  import {formatPages} from '../tools/assets/format-pages.mjs';
19
+ import {startPageEditor} from './page-editor.js';
18
20
 
19
21
  const siteGitignore = ['node_modules/', 'dist/', '.edgepress/', '.wrangler/', '.dev.vars', '.vercel/', '.netlify/', '.edgeone/', '.esa/', '.env', '.env.*', '!.env.example', '*.tgz', ''].join('\n');
20
22
  const siteReadme = [
@@ -29,7 +31,7 @@ const siteReadme = [
29
31
  '',
30
32
  'Create an article with `npm run new -- post "My first article"`. Open the printed file path and write Markdown below the second `---` line. The command fills the title, date, and language. Run `npm run dev` to preview and `npm run build` before publishing.',
31
33
  '',
32
- 'Pages use localized JSON files such as `content/pages/home/en.json`; VS Code can fold each row, cell and element and read the local schema. Articles stay in Markdown. Page layouts and theme customization are optional for everyday writing.',
34
+ 'Run `npm run editor` to create and edit Pages visually at http://127.0.0.1:4178/. Or run `npx edgepress new pages "About us" --lang en` to create a JSON Page from the CLI. All Page blocks use shared framework templates; articles remain Markdown.',
33
35
  '',
34
36
  '## Deploy a static website',
35
37
  '',
@@ -81,6 +83,7 @@ async function initializeProject() {
81
83
  generate: 'edgepress generate',
82
84
  build: 'edgepress build',
83
85
  dev: 'edgepress server',
86
+ editor: 'edgepress editor',
84
87
  preview: 'edgepress server',
85
88
  check: 'edgepress check',
86
89
  doctor: 'edgepress doctor',
@@ -89,7 +92,8 @@ async function initializeProject() {
89
92
  'deploy:netlify': 'edgepress deploy netlify',
90
93
  'deploy:vercel': 'edgepress deploy vercel',
91
94
  'deploy:edgeone': 'edgepress deploy edgeone',
92
- 'deploy:esa': 'edgepress deploy esa'
95
+ 'deploy:esa': 'edgepress deploy esa',
96
+ 'deploy:local': 'edgepress server'
93
97
  },
94
98
  overrides: {
95
99
  ...(packageData.overrides || {}),
@@ -248,6 +252,27 @@ async function manageTheme(args, config) {
248
252
  console.log('Selected theme ' + selected.id + '.');
249
253
  }
250
254
 
255
+ async function newPage(args, config) {
256
+ let lang, slug;
257
+ const words = [];
258
+ for (let i = 0; i < args.length; i += 1) {
259
+ const arg = args[i];
260
+ if (arg === '--lang' || arg === '--slug') {
261
+ if (!args[i + 1] || args[i + 1].startsWith('--')) throw new Error('Missing value for ' + arg);
262
+ if (arg === '--lang') lang = args[++i];
263
+ else slug = args[++i];
264
+ } else if (arg.startsWith('--')) throw new Error('Unknown new pages option: ' + arg);
265
+ else words.push(arg);
266
+ }
267
+ const title = words.join(' ').trim();
268
+ if (!title) throw new Error('Usage: edgepress new pages "Page title" [--lang en] [--slug page-slug]');
269
+ const page = await createPage(config.root, {
270
+ title, lang, slug, locales: config.i18n.locales, defaultLocale: config.i18n.defaultLocale
271
+ });
272
+ console.log('Created Page: ' + relative(config.root, page.file));
273
+ console.log('Open the local editor with: npm run editor');
274
+ }
275
+
251
276
  async function createPost(title, config) {
252
277
  if (!title) throw new Error('Usage: edgepress new post "Post title"');
253
278
  const releaseLock = await acquireBuildLock(config.resolvedPaths.cache);
@@ -331,11 +356,19 @@ try {
331
356
  ? await loadConfig()
332
357
  : null;
333
358
  if (command === '--help' || command === '-h' || command === 'help') {
334
- console.log('EdgePress commands: init, new, build, generate, server, check, doctor, iterate, theme, security, deploy, clean');
359
+ console.log('EdgePress commands: init, new, build, generate, server, editor, check, doctor, iterate, theme, security, deploy, clean');
360
+ console.log('Create a Page: edgepress new pages "Page title" [--lang en] [--slug page-slug]');
335
361
  }
336
362
  else if (command === 'build' || command === 'generate') await buildSite();
337
363
  else if (command === 'init') await initializeProject();
338
364
  else if (command === 'preview' || command === 'dev' || command === 'server') await import('./preview.js');
365
+ else if (command === 'editor') {
366
+ const portText = args[0] === '--port' ? args[1] : args[0]?.startsWith('--port=') ? args[0].slice(7) : args.length ? null : '4178';
367
+ if (!/^\d{1,5}$/.test(portText || '') || Number(portText) < 1 || Number(portText) > 65535) throw new Error('Usage: edgepress editor [--port 4178]');
368
+ const editorConfig = await loadConfig();
369
+ const editor = await startPageEditor({ port: Number(portText), root: editorConfig.root, locales: editorConfig.i18n.locales, defaultLocale: editorConfig.i18n.defaultLocale });
370
+ console.log('Local Pages editor: ' + editor.url + ' (Ctrl+C to stop)');
371
+ }
339
372
  else if (command === 'deploy') {
340
373
  const platform = args[0] && !args[0].startsWith('-') ? args.shift() : 'cloudflare';
341
374
  const built = process.env.WORKERS_CI === '1' ? {output:(await loadConfig()).resolvedPaths.output} : await buildSite();
@@ -343,7 +376,10 @@ try {
343
376
  }
344
377
  else if (command === 'theme') await manageTheme(args, await loadConfig());
345
378
  else if (command === 'security') await runSecurityAudit();
346
- else if (command === 'new') await createPost((args[0] === 'post' ? args.slice(1) : args).join(' ').trim(), config);
379
+ else if (command === 'new') {
380
+ if (args[0] === 'pages' || args[0] === 'page') await newPage(args.slice(1), config);
381
+ else await createPost((args[0] === 'post' ? args.slice(1) : args).join(' ').trim(), config);
382
+ }
347
383
  else if (command === 'clean') await clean(config);
348
384
  else if (command === 'check') {
349
385
  await buildSite(config.root);
@@ -362,7 +398,7 @@ try {
362
398
  const compatibilityReport = await checkCompatibility(config);
363
399
  const plan = await writeIterationPlan(config, pageReport, compatibilityReport);
364
400
  await showReport('Safe iteration plan', plan, resolve(config.resolvedPaths.cache, 'reports/iteration-plan.md'));
365
- } else throw new Error('Commands: init, build|generate, server, deploy, new, clean, check, doctor, iterate, theme list|create|install|use, security');
401
+ } else throw new Error('Commands: init, build|generate, server, editor [--port 4178], deploy, new, clean, check, doctor, iterate, theme list|create|install|use, security');
366
402
  } catch (error) {
367
403
  console.error(error?.stack || error);
368
404
  process.exitCode = 1;
@@ -0,0 +1,345 @@
1
+ {
2
+ "version": 1,
3
+ "description": "Shared, public EdgePress page-block presets. Templates are examples, not configuration-specific integrations.",
4
+ "blocks": {
5
+ "hero": {
6
+ "label": "横幅 Hero",
7
+ "category": "layout",
8
+ "template": {
9
+ "type": "hero",
10
+ "title": "页面横幅",
11
+ "text": "介绍页面内容"
12
+ }
13
+ },
14
+ "section": {
15
+ "label": "分区 Section",
16
+ "category": "layout",
17
+ "template": {
18
+ "type": "section",
19
+ "title": "新分区",
20
+ "blocks": [
21
+ {
22
+ "type": "text",
23
+ "text": "分区内容"
24
+ }
25
+ ]
26
+ }
27
+ },
28
+ "heading": {
29
+ "label": "标题 Heading",
30
+ "category": "text",
31
+ "template": {
32
+ "type": "heading",
33
+ "level": 2,
34
+ "text": "新的标题"
35
+ }
36
+ },
37
+ "text": {
38
+ "label": "文字 Text",
39
+ "category": "text",
40
+ "template": {
41
+ "type": "text",
42
+ "text": "在此编辑文字"
43
+ }
44
+ },
45
+ "media-text": {
46
+ "label": "图文 Media + Text",
47
+ "category": "media",
48
+ "template": {
49
+ "type": "media-text",
50
+ "mediaType": "image",
51
+ "src": "/images/example.png",
52
+ "alt": "图片说明",
53
+ "title": "图文标题",
54
+ "text": "图文描述",
55
+ "placement": "left"
56
+ }
57
+ },
58
+ "display-text": {
59
+ "label": "展示文字 Display Text",
60
+ "category": "text",
61
+ "template": {
62
+ "type": "display-text",
63
+ "level": 2,
64
+ "style": "editorial",
65
+ "text": "强调文字"
66
+ }
67
+ },
68
+ "link-list": {
69
+ "label": "链接列表 Link List",
70
+ "category": "links",
71
+ "template": {
72
+ "type": "link-list",
73
+ "title": "链接列表",
74
+ "items": [
75
+ {
76
+ "label": "链接",
77
+ "url": "/"
78
+ }
79
+ ]
80
+ }
81
+ },
82
+ "steps": {
83
+ "label": "步骤 Steps",
84
+ "category": "lists",
85
+ "template": {
86
+ "type": "steps",
87
+ "title": "操作步骤",
88
+ "items": [
89
+ {
90
+ "title": "第一步",
91
+ "text": "操作说明"
92
+ }
93
+ ]
94
+ }
95
+ },
96
+ "code": {
97
+ "label": "代码 Code",
98
+ "category": "text",
99
+ "template": {
100
+ "type": "code",
101
+ "language": "text",
102
+ "code": "示例代码"
103
+ }
104
+ },
105
+ "data-table": {
106
+ "label": "数据表 Data Table",
107
+ "category": "lists",
108
+ "template": {
109
+ "type": "data-table",
110
+ "title": "表格标题",
111
+ "headers": [
112
+ "字段",
113
+ "内容"
114
+ ],
115
+ "rows": [
116
+ [
117
+ "示例",
118
+ "填写内容"
119
+ ]
120
+ ]
121
+ }
122
+ },
123
+ "list": {
124
+ "label": "列表 List",
125
+ "category": "lists",
126
+ "template": {
127
+ "type": "list",
128
+ "title": "列表标题",
129
+ "items": [
130
+ "项目一",
131
+ "项目二"
132
+ ],
133
+ "ordered": false
134
+ }
135
+ },
136
+ "notice": {
137
+ "label": "提示 Notice",
138
+ "category": "text",
139
+ "template": {
140
+ "type": "notice",
141
+ "title": "提示",
142
+ "text": "提示信息",
143
+ "tone": "info"
144
+ }
145
+ },
146
+ "privacy-services": {
147
+ "label": "隐私服务清单",
148
+ "category": "privacy",
149
+ "template": {
150
+ "type": "privacy-services",
151
+ "emptyText": "暂无已配置的服务"
152
+ }
153
+ },
154
+ "privacy-consent": {
155
+ "label": "隐私同意信息",
156
+ "category": "privacy",
157
+ "template": {
158
+ "type": "privacy-consent",
159
+ "storageLabel": "存储位置",
160
+ "expiryLabel": "保留期限",
161
+ "proposedDateLabel": "建议日期",
162
+ "effectiveDateLabel": "生效日期",
163
+ "expiryText": "{days} 天"
164
+ }
165
+ },
166
+ "privacy-controller": {
167
+ "label": "隐私管理者信息",
168
+ "category": "privacy",
169
+ "template": {
170
+ "type": "privacy-controller",
171
+ "missingTitle": "管理者信息未配置",
172
+ "missingText": "请在站点配置中填写管理者信息",
173
+ "nameLabel": "管理者",
174
+ "contactLabel": "联系方式"
175
+ }
176
+ },
177
+ "link-directory": {
178
+ "label": "链接目录 Link Directory",
179
+ "category": "links",
180
+ "template": {
181
+ "type": "link-directory",
182
+ "title": "站点目录",
183
+ "items": [
184
+ {
185
+ "title": "参考网站",
186
+ "text": "站点简介",
187
+ "url": "https://example.com"
188
+ }
189
+ ]
190
+ }
191
+ },
192
+ "feature-grid": {
193
+ "label": "功能卡片 Feature Grid",
194
+ "category": "layout",
195
+ "template": {
196
+ "type": "feature-grid",
197
+ "title": "功能特点",
198
+ "items": [
199
+ {
200
+ "title": "功能名称",
201
+ "text": "功能说明"
202
+ }
203
+ ]
204
+ }
205
+ },
206
+ "image": {
207
+ "label": "图片 Image",
208
+ "category": "media",
209
+ "template": {
210
+ "type": "image",
211
+ "src": "/images/example.png",
212
+ "alt": "替换为图片文字描述"
213
+ }
214
+ },
215
+ "quote": {
216
+ "label": "引用 Quote",
217
+ "category": "text",
218
+ "template": {
219
+ "type": "quote",
220
+ "text": "引用内容",
221
+ "attribution": "来源"
222
+ }
223
+ },
224
+ "cta": {
225
+ "label": "行动按钮 CTA",
226
+ "category": "links",
227
+ "template": {
228
+ "type": "cta",
229
+ "title": "开始使用",
230
+ "text": "行动说明",
231
+ "label": "查看详情",
232
+ "url": "/"
233
+ }
234
+ },
235
+ "faq": {
236
+ "label": "常见问题 FAQ",
237
+ "category": "lists",
238
+ "template": {
239
+ "type": "faq",
240
+ "title": "常见问题",
241
+ "items": [
242
+ {
243
+ "question": "这里写问题",
244
+ "answer": "这里写回答"
245
+ }
246
+ ]
247
+ }
248
+ },
249
+ "oembed": {
250
+ "label": "外部嵌入 oEmbed",
251
+ "category": "integrations",
252
+ "requiresSetup": true,
253
+ "template": {
254
+ "type": "oembed",
255
+ "integration": "configure-service-id",
256
+ "url": "https://example.com/",
257
+ "title": "第三方嵌入"
258
+ }
259
+ },
260
+ "service": {
261
+ "label": "第三方服务 Service",
262
+ "category": "integrations",
263
+ "requiresSetup": true,
264
+ "template": {
265
+ "type": "service",
266
+ "integration": "configure-service-id"
267
+ }
268
+ },
269
+ "post-list": {
270
+ "label": "文章列表 Post List",
271
+ "category": "content",
272
+ "template": {
273
+ "type": "post-list",
274
+ "title": "文章列表",
275
+ "count": 3
276
+ }
277
+ },
278
+ "latest-posts": {
279
+ "label": "最新文章 Latest Posts",
280
+ "category": "content",
281
+ "template": {
282
+ "type": "latest-posts",
283
+ "title": "最新文章",
284
+ "count": 3,
285
+ "paginate": false
286
+ }
287
+ },
288
+ "ad-slot": {
289
+ "label": "广告位置 Ad Slot",
290
+ "category": "integrations",
291
+ "requiresSetup": true,
292
+ "template": {
293
+ "type": "ad-slot",
294
+ "integration": "configure-service-id",
295
+ "label": "广告"
296
+ }
297
+ },
298
+ "captcha": {
299
+ "label": "验证码 CAPTCHA",
300
+ "category": "integrations",
301
+ "requiresSetup": true,
302
+ "template": {
303
+ "type": "captcha",
304
+ "provider": "cloudflare-turnstile",
305
+ "label": "人机验证"
306
+ }
307
+ },
308
+ "document": {
309
+ "label": "文档预览 Document",
310
+ "category": "media",
311
+ "template": {
312
+ "type": "document",
313
+ "title": "文档预览",
314
+ "src": "/documents/sample.pdf"
315
+ }
316
+ },
317
+ "media-viewer": {
318
+ "label": "媒体浏览器 Media Viewer",
319
+ "category": "media",
320
+ "template": {
321
+ "type": "media-viewer",
322
+ "title": "图片集",
323
+ "items": [
324
+ {
325
+ "type": "image",
326
+ "src": "/images/example.png",
327
+ "alt": "图片说明"
328
+ }
329
+ ]
330
+ }
331
+ },
332
+ "local-tool": {
333
+ "label": "本地工具 Local Tool",
334
+ "category": "integrations",
335
+ "requiresSetup": true,
336
+ "template": {
337
+ "type": "local-tool",
338
+ "id": "my-local-tool",
339
+ "title": "本地工具",
340
+ "script": "/assets/tool.js",
341
+ "fallback": "JavaScript 未启用时的提示文字"
342
+ }
343
+ }
344
+ }
345
+ }