@cloudgrid-io/mcp 0.12.0 → 0.13.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/package.json +1 -1
- package/src/corpus/capability-map.md +21 -2
- package/src/corpus/examples/crm/index.md +273 -0
- package/src/corpus/examples/kanban/index.md +256 -0
- package/src/corpus/templates/admin-dashboard/README.md +75 -0
- package/src/corpus/templates/admin-dashboard/cloudgrid.yaml +15 -0
- package/src/corpus/templates/admin-dashboard/index.md +78 -0
- package/src/corpus/templates/admin-dashboard/services/web/app/api/orders/route.js +64 -0
- package/src/corpus/templates/admin-dashboard/services/web/app/api/users/route.js +63 -0
- package/src/corpus/templates/admin-dashboard/services/web/app/layout.js +39 -0
- package/src/corpus/templates/admin-dashboard/services/web/app/page.js +73 -0
- package/src/corpus/templates/admin-dashboard/services/web/app/resource-table.js +94 -0
- package/src/corpus/templates/admin-dashboard/services/web/lib/db.js +32 -0
- package/src/corpus/templates/admin-dashboard/services/web/package.json +16 -0
- package/src/corpus/templates/api-docs/README.md +28 -0
- package/src/corpus/templates/api-docs/cloudgrid.yaml +10 -0
- package/src/corpus/templates/api-docs/index.html +136 -0
- package/src/corpus/templates/api-docs/index.md +30 -0
- package/src/corpus/templates/changelog/README.md +27 -0
- package/src/corpus/templates/changelog/cloudgrid.yaml +10 -0
- package/src/corpus/templates/changelog/index.html +80 -0
- package/src/corpus/templates/changelog/index.md +30 -0
- package/src/corpus/templates/crm/README.md +72 -0
- package/src/corpus/templates/crm/cloudgrid.yaml +15 -0
- package/src/corpus/templates/crm/index.md +68 -0
- package/src/corpus/templates/crm/services/web/app/api/contacts/route.js +80 -0
- package/src/corpus/templates/crm/services/web/app/contact-manager.js +102 -0
- package/src/corpus/templates/crm/services/web/app/layout.js +35 -0
- package/src/corpus/templates/crm/services/web/app/page.js +34 -0
- package/src/corpus/templates/crm/services/web/lib/db.js +32 -0
- package/src/corpus/templates/crm/services/web/package.json +16 -0
- package/src/corpus/templates/docs-site/README.md +28 -0
- package/src/corpus/templates/docs-site/cloudgrid.yaml +10 -0
- package/src/corpus/templates/docs-site/index.html +122 -0
- package/src/corpus/templates/docs-site/index.md +30 -0
- package/src/corpus/templates/kanban/README.md +72 -0
- package/src/corpus/templates/kanban/cloudgrid.yaml +15 -0
- package/src/corpus/templates/kanban/index.md +68 -0
- package/src/corpus/templates/kanban/services/web/app/api/cards/route.js +70 -0
- package/src/corpus/templates/kanban/services/web/app/board.js +93 -0
- package/src/corpus/templates/kanban/services/web/app/layout.js +37 -0
- package/src/corpus/templates/kanban/services/web/app/page.js +32 -0
- package/src/corpus/templates/kanban/services/web/lib/db.js +32 -0
- package/src/corpus/templates/kanban/services/web/package.json +16 -0
- package/src/corpus/templates/portfolio/README.md +29 -0
- package/src/corpus/templates/portfolio/cloudgrid.yaml +10 -0
- package/src/corpus/templates/portfolio/index.html +126 -0
- package/src/corpus/templates/portfolio/index.md +30 -0
- package/src/corpus/templates/saas-marketing/README.md +30 -0
- package/src/corpus/templates/saas-marketing/cloudgrid.yaml +10 -0
- package/src/corpus/templates/saas-marketing/index.html +212 -0
- package/src/corpus/templates/saas-marketing/index.md +30 -0
- package/src/corpus/templates/status-page/README.md +28 -0
- package/src/corpus/templates/status-page/cloudgrid.yaml +10 -0
- package/src/corpus/templates/status-page/index.html +117 -0
- package/src/corpus/templates/status-page/index.md +30 -0
- package/src/corpus/templates/task-manager/README.md +72 -0
- package/src/corpus/templates/task-manager/cloudgrid.yaml +15 -0
- package/src/corpus/templates/task-manager/index.md +68 -0
- package/src/corpus/templates/task-manager/services/web/app/api/tasks/route.js +77 -0
- package/src/corpus/templates/task-manager/services/web/app/layout.js +39 -0
- package/src/corpus/templates/task-manager/services/web/app/page.js +33 -0
- package/src/corpus/templates/task-manager/services/web/app/task-list.js +110 -0
- package/src/corpus/templates/task-manager/services/web/lib/db.js +32 -0
- package/src/corpus/templates/task-manager/services/web/package.json +16 -0
- package/src/corpus/templates/waitlist/README.md +38 -0
- package/src/corpus/templates/waitlist/cloudgrid.yaml +10 -0
- package/src/corpus/templates/waitlist/index.html +69 -0
- package/src/corpus/templates/waitlist/index.md +41 -0
- package/src/corpus/workflows/admin-dashboard.md +102 -0
- package/src/corpus/workflows/api-docs.md +36 -0
- package/src/corpus/workflows/changelog.md +33 -0
- package/src/corpus/workflows/crm.md +101 -0
- package/src/corpus/workflows/docs-site.md +35 -0
- package/src/corpus/workflows/kanban.md +102 -0
- package/src/corpus/workflows/portfolio.md +35 -0
- package/src/corpus/workflows/saas-marketing.md +37 -0
- package/src/corpus/workflows/status-page.md +35 -0
- package/src/corpus/workflows/task-manager.md +101 -0
- package/src/corpus/workflows/waitlist.md +42 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
|
|
2
|
+
# this yaml is only for plugging it as an OWNED static runtime (async, local
|
|
3
|
+
# edition only). To publish the page, prefer gridctl_drop, not grid plug.
|
|
4
|
+
#
|
|
5
|
+
# needs: {} # none — static, no infrastructure
|
|
6
|
+
name: portfolio
|
|
7
|
+
services:
|
|
8
|
+
web:
|
|
9
|
+
type: static
|
|
10
|
+
path: /
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{FULL_NAME}} — {{ROLE}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg: #0b0f17; --panel: #121826; --fg: #e8edf5; --muted: #9aa7bd;
|
|
10
|
+
--accent: #5b8cff; --border: #223049;
|
|
11
|
+
}
|
|
12
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
13
|
+
html { scroll-behavior: smooth; }
|
|
14
|
+
body {
|
|
15
|
+
background: var(--bg); color: var(--fg);
|
|
16
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
17
|
+
-webkit-font-smoothing: antialiased; line-height: 1.6;
|
|
18
|
+
}
|
|
19
|
+
.wrap { max-width: 900px; margin: 0 auto; padding: 0 1.25rem; }
|
|
20
|
+
nav { display: flex; justify-content: space-between; align-items: center; padding: 1.25rem; max-width: 900px; margin: 0 auto; }
|
|
21
|
+
nav .brand { font-weight: 600; letter-spacing: -0.01em; }
|
|
22
|
+
nav .links { display: flex; gap: 1.25rem; }
|
|
23
|
+
nav .links a { color: var(--muted); text-decoration: none; font-size: 0.92rem; }
|
|
24
|
+
nav .links a:hover { color: var(--fg); }
|
|
25
|
+
|
|
26
|
+
section { padding: clamp(2.5rem, 7vw, 5rem) 0; }
|
|
27
|
+
.hero { text-align: left; }
|
|
28
|
+
.hero .eyebrow { color: var(--accent); font-weight: 600; font-size: 0.9rem; }
|
|
29
|
+
.hero h1 { font-size: clamp(2.4rem, 7vw, 4rem); letter-spacing: -0.02em; line-height: 1.05; margin: 0.5rem 0; }
|
|
30
|
+
.hero .role { color: var(--muted); font-size: 1.3rem; margin-bottom: 1rem; }
|
|
31
|
+
.hero p { color: var(--muted); max-width: 54ch; font-size: 1.1rem; }
|
|
32
|
+
|
|
33
|
+
h2 { font-size: clamp(1.5rem, 4vw, 2rem); letter-spacing: -0.01em; margin-bottom: 1.25rem; }
|
|
34
|
+
.about p { color: var(--muted); max-width: 60ch; }
|
|
35
|
+
|
|
36
|
+
.skills { display: flex; flex-wrap: wrap; gap: 0.6rem; }
|
|
37
|
+
.skill { background: var(--panel); border: 1px solid var(--border); border-radius: 999px; padding: 0.4rem 0.9rem; font-size: 0.9rem; }
|
|
38
|
+
|
|
39
|
+
.projects { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
|
|
40
|
+
.project { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 1.5rem; display: grid; gap: 0.6rem; align-content: start; }
|
|
41
|
+
.project h3 { font-size: 1.2rem; letter-spacing: -0.01em; }
|
|
42
|
+
.project p { color: var(--muted); }
|
|
43
|
+
.project .tags { display: flex; flex-wrap: wrap; gap: 0.4rem; }
|
|
44
|
+
.project .tags span { font-size: 0.75rem; color: var(--muted); border: 1px solid var(--border); border-radius: 6px; padding: 0.1rem 0.5rem; }
|
|
45
|
+
.project a { color: var(--accent); text-decoration: none; font-size: 0.9rem; font-weight: 600; }
|
|
46
|
+
|
|
47
|
+
.contact { text-align: center; border-top: 1px solid var(--border); }
|
|
48
|
+
.contact .actions { display: flex; gap: 0.75rem; justify-content: center; flex-wrap: wrap; margin-top: 1rem; }
|
|
49
|
+
.btn { display: inline-block; background: var(--accent); color: #fff; text-decoration: none; padding: 0.75rem 1.4rem; border-radius: 10px; font-weight: 600; }
|
|
50
|
+
.btn.ghost { background: transparent; border: 1px solid var(--border); color: var(--fg); }
|
|
51
|
+
footer { color: var(--muted); font-size: 0.85rem; text-align: center; padding-bottom: 2.5rem; }
|
|
52
|
+
</style>
|
|
53
|
+
</head>
|
|
54
|
+
<body>
|
|
55
|
+
<nav>
|
|
56
|
+
<span class="brand">{{FULL_NAME}}</span>
|
|
57
|
+
<div class="links">
|
|
58
|
+
<a href="#about">About</a>
|
|
59
|
+
<a href="#work">Work</a>
|
|
60
|
+
<a href="#contact">Contact</a>
|
|
61
|
+
</div>
|
|
62
|
+
</nav>
|
|
63
|
+
|
|
64
|
+
<div class="wrap">
|
|
65
|
+
<section class="hero">
|
|
66
|
+
<span class="eyebrow">{{HERO_EYEBROW}}</span>
|
|
67
|
+
<h1>{{FULL_NAME}}</h1>
|
|
68
|
+
<div class="role">{{ROLE}}</div>
|
|
69
|
+
<p>{{HERO_INTRO}}</p>
|
|
70
|
+
</section>
|
|
71
|
+
|
|
72
|
+
<section id="about" class="about">
|
|
73
|
+
<h2>About</h2>
|
|
74
|
+
<p>{{ABOUT_BODY}}</p>
|
|
75
|
+
<div class="skills" style="margin-top:1.25rem">
|
|
76
|
+
<!-- SKILLS_START -->
|
|
77
|
+
<span class="skill">{{SKILL_1}}</span>
|
|
78
|
+
<span class="skill">{{SKILL_2}}</span>
|
|
79
|
+
<span class="skill">{{SKILL_3}}</span>
|
|
80
|
+
<span class="skill">{{SKILL_4}}</span>
|
|
81
|
+
<span class="skill">{{SKILL_5}}</span>
|
|
82
|
+
<!-- Add more <span class="skill">...</span> as needed. -->
|
|
83
|
+
<!-- SKILLS_END -->
|
|
84
|
+
</div>
|
|
85
|
+
</section>
|
|
86
|
+
|
|
87
|
+
<section id="work">
|
|
88
|
+
<h2>Selected work</h2>
|
|
89
|
+
<div class="projects">
|
|
90
|
+
<!-- PROJECTS_START -->
|
|
91
|
+
<div class="project">
|
|
92
|
+
<h3>{{PROJECT_1_TITLE}}</h3>
|
|
93
|
+
<p>{{PROJECT_1_BODY}}</p>
|
|
94
|
+
<div class="tags"><span>{{PROJECT_1_TAG_1}}</span><span>{{PROJECT_1_TAG_2}}</span></div>
|
|
95
|
+
<a href="{{PROJECT_1_LINK}}">View project</a>
|
|
96
|
+
</div>
|
|
97
|
+
<div class="project">
|
|
98
|
+
<h3>{{PROJECT_2_TITLE}}</h3>
|
|
99
|
+
<p>{{PROJECT_2_BODY}}</p>
|
|
100
|
+
<div class="tags"><span>{{PROJECT_2_TAG_1}}</span><span>{{PROJECT_2_TAG_2}}</span></div>
|
|
101
|
+
<a href="{{PROJECT_2_LINK}}">View project</a>
|
|
102
|
+
</div>
|
|
103
|
+
<div class="project">
|
|
104
|
+
<h3>{{PROJECT_3_TITLE}}</h3>
|
|
105
|
+
<p>{{PROJECT_3_BODY}}</p>
|
|
106
|
+
<div class="tags"><span>{{PROJECT_3_TAG_1}}</span><span>{{PROJECT_3_TAG_2}}</span></div>
|
|
107
|
+
<a href="{{PROJECT_3_LINK}}">View project</a>
|
|
108
|
+
</div>
|
|
109
|
+
<!-- Add more .project cards as needed. -->
|
|
110
|
+
<!-- PROJECTS_END -->
|
|
111
|
+
</div>
|
|
112
|
+
</section>
|
|
113
|
+
|
|
114
|
+
<section id="contact" class="contact">
|
|
115
|
+
<h2>Get in touch</h2>
|
|
116
|
+
<p style="color:var(--muted)">{{CONTACT_BODY}}</p>
|
|
117
|
+
<div class="actions">
|
|
118
|
+
<a class="btn" href="mailto:{{EMAIL}}">Email me</a>
|
|
119
|
+
<a class="btn ghost" href="{{PROFILE_LINK}}">{{PROFILE_LABEL}}</a>
|
|
120
|
+
</div>
|
|
121
|
+
</section>
|
|
122
|
+
|
|
123
|
+
<footer>{{FOOTER_TEXT}}</footer>
|
|
124
|
+
</div>
|
|
125
|
+
</body>
|
|
126
|
+
</html>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Template: portfolio (static)
|
|
2
|
+
|
|
3
|
+
A single self-contained HTML personal / freelancer portfolio: a hero intro, an about section, a skills list, a projects grid, and a contact section. **No infrastructure** — no database, no backend, no `needs:`.
|
|
4
|
+
State (if any) lives only in memory and is lost on refresh.
|
|
5
|
+
|
|
6
|
+
## How it deploys
|
|
7
|
+
|
|
8
|
+
To publish this page, use **`gridctl_drop`** (or `gridctl_plug` with the inline
|
|
9
|
+
HTML): it deploys as an **inspiration** — instant, and works on the **hosted**
|
|
10
|
+
edition (Claude Web) as well as local. The reference `cloudgrid.yaml` in this
|
|
11
|
+
directory (`type: static`) is ONLY for plugging the page as an OWNED static
|
|
12
|
+
runtime (async, local edition only); the fast path is `gridctl_drop`.
|
|
13
|
+
|
|
14
|
+
The fillable HTML is the real template — fetch it with
|
|
15
|
+
`gridctl_fetch("template", "portfolio")` and fill in the user's content.
|
|
16
|
+
|
|
17
|
+
## cloudgrid.yaml (reference only)
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
# static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
|
|
21
|
+
# this yaml is only for plugging it as an OWNED static runtime (async, local
|
|
22
|
+
# edition only). To publish the page, prefer gridctl_drop, not grid plug.
|
|
23
|
+
#
|
|
24
|
+
# needs: {} # none — static, no infrastructure
|
|
25
|
+
name: portfolio
|
|
26
|
+
services:
|
|
27
|
+
web:
|
|
28
|
+
type: static
|
|
29
|
+
path: /
|
|
30
|
+
```
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# saas-marketing
|
|
2
|
+
|
|
3
|
+
A single self-contained HTML SaaS marketing site (hero, features, pricing tiers, testimonials, FAQ, and a closing call to action). Richer than the landing-page template.
|
|
4
|
+
|
|
5
|
+
Static inspiration — a single self-contained `index.html` (inline CSS and JS,
|
|
6
|
+
no external CDNs). It deploys instantly via `gridctl_drop` and works on every
|
|
7
|
+
CloudGrid edition, including hosted.
|
|
8
|
+
|
|
9
|
+
## Fill it in
|
|
10
|
+
|
|
11
|
+
Fetch the template with `gridctl_fetch("template", "saas-marketing")`, then replace the
|
|
12
|
+
placeholders:
|
|
13
|
+
|
|
14
|
+
- The product name, tagline, and hero eyebrow/headline/subheadline.
|
|
15
|
+
- The call-to-action label and (optional) target for the primary button.
|
|
16
|
+
- One block per feature (title + short description). Add or remove `.feature` blocks.
|
|
17
|
+
- The three pricing plans: name, price, period, and feature list. The middle plan is marked "Most popular".
|
|
18
|
+
- The testimonial quotes and who said them.
|
|
19
|
+
- The FAQ question/answer pairs (`<details>` blocks, client-side accordion).
|
|
20
|
+
|
|
21
|
+
Keep it a single self-contained HTML file. Do not add external scripts,
|
|
22
|
+
stylesheets, fonts, or large embedded media.
|
|
23
|
+
|
|
24
|
+
## Deploy
|
|
25
|
+
|
|
26
|
+
- Hosted MCP edition: call the drop tool with the HTML.
|
|
27
|
+
- Local MCP / CLI edition: write the HTML to a file and run `gridctl plug`.
|
|
28
|
+
|
|
29
|
+
It deploys as an inspiration and returns a live share URL right away. Re-deploy
|
|
30
|
+
with the same entity id to update the same URL.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
|
|
2
|
+
# this yaml is only for plugging it as an OWNED static runtime (async, local
|
|
3
|
+
# edition only). To publish the page, prefer gridctl_drop, not grid plug.
|
|
4
|
+
#
|
|
5
|
+
# needs: {} # none — static, no infrastructure
|
|
6
|
+
name: saas-marketing
|
|
7
|
+
services:
|
|
8
|
+
web:
|
|
9
|
+
type: static
|
|
10
|
+
path: /
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{PRODUCT_NAME}} — {{TAGLINE}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg: #0b0f17;
|
|
10
|
+
--panel: #121826;
|
|
11
|
+
--panel-2: #0f1523;
|
|
12
|
+
--fg: #e8edf5;
|
|
13
|
+
--muted: #9aa7bd;
|
|
14
|
+
--accent: #5b8cff;
|
|
15
|
+
--accent-2: #7c6bff;
|
|
16
|
+
--border: #223049;
|
|
17
|
+
--ok: #34d399;
|
|
18
|
+
}
|
|
19
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
20
|
+
html { scroll-behavior: smooth; }
|
|
21
|
+
body {
|
|
22
|
+
background: var(--bg);
|
|
23
|
+
color: var(--fg);
|
|
24
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
25
|
+
-webkit-font-smoothing: antialiased;
|
|
26
|
+
line-height: 1.55;
|
|
27
|
+
}
|
|
28
|
+
a { color: inherit; }
|
|
29
|
+
section { padding: clamp(3rem, 8vw, 6rem) clamp(1.25rem, 6vw, 6rem); border-top: 1px solid var(--border); }
|
|
30
|
+
.container { max-width: 1080px; margin: 0 auto; }
|
|
31
|
+
nav {
|
|
32
|
+
display: flex; align-items: center; justify-content: space-between; gap: 1rem;
|
|
33
|
+
padding: 1.1rem clamp(1.25rem, 6vw, 6rem);
|
|
34
|
+
border-bottom: 1px solid var(--border);
|
|
35
|
+
position: sticky; top: 0; background: rgba(11,15,23,0.85); backdrop-filter: blur(8px); z-index: 10;
|
|
36
|
+
}
|
|
37
|
+
nav .brand { font-weight: 600; letter-spacing: -0.01em; }
|
|
38
|
+
nav .links { display: flex; gap: 1.5rem; }
|
|
39
|
+
nav .links a { color: var(--muted); text-decoration: none; font-size: 0.95rem; }
|
|
40
|
+
nav .links a:hover { color: var(--fg); }
|
|
41
|
+
.btn {
|
|
42
|
+
display: inline-block; background: var(--accent); color: #fff; text-decoration: none;
|
|
43
|
+
padding: 0.75rem 1.4rem; border-radius: 10px; font-weight: 600; font-size: 0.95rem;
|
|
44
|
+
border: 1px solid transparent;
|
|
45
|
+
}
|
|
46
|
+
.btn:hover { filter: brightness(1.08); }
|
|
47
|
+
.btn.ghost { background: transparent; border-color: var(--border); color: var(--fg); }
|
|
48
|
+
@media (max-width: 640px) { nav .links { display: none; } }
|
|
49
|
+
|
|
50
|
+
.hero { text-align: center; display: grid; gap: 1.25rem; place-items: center; border-top: none; }
|
|
51
|
+
.hero .eyebrow { color: var(--accent); font-weight: 600; font-size: 0.9rem; letter-spacing: 0.02em; }
|
|
52
|
+
.hero h1 { font-size: clamp(2.4rem, 6vw, 4.2rem); line-height: 1.05; letter-spacing: -0.02em; max-width: 18ch; }
|
|
53
|
+
.hero p { font-size: clamp(1.05rem, 2.4vw, 1.4rem); color: var(--muted); max-width: 52ch; }
|
|
54
|
+
.hero .actions { display: flex; gap: 0.75rem; flex-wrap: wrap; justify-content: center; margin-top: 0.5rem; }
|
|
55
|
+
|
|
56
|
+
.section-head { text-align: center; display: grid; gap: 0.75rem; place-items: center; margin-bottom: 2.5rem; }
|
|
57
|
+
.section-head h2 { font-size: clamp(1.8rem, 4.5vw, 2.6rem); letter-spacing: -0.01em; }
|
|
58
|
+
.section-head p { color: var(--muted); max-width: 48ch; }
|
|
59
|
+
|
|
60
|
+
.features { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
|
|
61
|
+
.feature { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 1.75rem; }
|
|
62
|
+
.feature .icon { width: 40px; height: 40px; border-radius: 10px; background: linear-gradient(135deg, var(--accent), var(--accent-2)); margin-bottom: 1rem; }
|
|
63
|
+
.feature h3 { font-size: 1.15rem; margin-bottom: 0.5rem; letter-spacing: -0.01em; }
|
|
64
|
+
.feature p { color: var(--muted); }
|
|
65
|
+
|
|
66
|
+
.pricing { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); align-items: start; }
|
|
67
|
+
.plan { background: var(--panel); border: 1px solid var(--border); border-radius: 16px; padding: 2rem; display: grid; gap: 1rem; }
|
|
68
|
+
.plan.featured { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
|
|
69
|
+
.plan .badge { align-self: start; font-size: 0.75rem; color: var(--accent); border: 1px solid var(--accent); border-radius: 999px; padding: 0.15rem 0.6rem; }
|
|
70
|
+
.plan h3 { font-size: 1.3rem; letter-spacing: -0.01em; }
|
|
71
|
+
.plan .price { font-size: 2.4rem; font-weight: 700; letter-spacing: -0.02em; }
|
|
72
|
+
.plan .price span { font-size: 1rem; color: var(--muted); font-weight: 400; }
|
|
73
|
+
.plan ul { list-style: none; display: grid; gap: 0.6rem; color: var(--muted); }
|
|
74
|
+
.plan li::before { content: "✓"; color: var(--ok); margin-right: 0.5rem; }
|
|
75
|
+
|
|
76
|
+
.testimonials { display: grid; gap: 1.25rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
|
|
77
|
+
.quote { background: var(--panel-2); border: 1px solid var(--border); border-radius: 14px; padding: 1.75rem; }
|
|
78
|
+
.quote blockquote { font-size: 1.05rem; margin-bottom: 1rem; }
|
|
79
|
+
.quote .who { color: var(--muted); font-size: 0.9rem; }
|
|
80
|
+
|
|
81
|
+
.faq { display: grid; gap: 0.75rem; max-width: 760px; margin: 0 auto; }
|
|
82
|
+
details { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 1rem 1.25rem; }
|
|
83
|
+
details summary { cursor: pointer; font-weight: 600; list-style: none; display: flex; justify-content: space-between; align-items: center; }
|
|
84
|
+
details summary::-webkit-details-marker { display: none; }
|
|
85
|
+
details summary::after { content: "+"; color: var(--muted); }
|
|
86
|
+
details[open] summary::after { content: "–"; }
|
|
87
|
+
details p { color: var(--muted); margin-top: 0.75rem; }
|
|
88
|
+
|
|
89
|
+
.cta-band { text-align: center; display: grid; gap: 1.25rem; place-items: center; background: linear-gradient(135deg, rgba(91,140,255,0.12), rgba(124,107,255,0.12)); }
|
|
90
|
+
.cta-band h2 { font-size: clamp(1.8rem, 4.5vw, 3rem); letter-spacing: -0.01em; max-width: 22ch; }
|
|
91
|
+
|
|
92
|
+
footer { padding: 2.5rem clamp(1.25rem, 6vw, 6rem); border-top: 1px solid var(--border); color: var(--muted); font-size: 0.9rem; text-align: center; }
|
|
93
|
+
</style>
|
|
94
|
+
</head>
|
|
95
|
+
<body>
|
|
96
|
+
<nav>
|
|
97
|
+
<span class="brand">{{PRODUCT_NAME}}</span>
|
|
98
|
+
<div class="links">
|
|
99
|
+
<a href="#features">Features</a>
|
|
100
|
+
<a href="#pricing">Pricing</a>
|
|
101
|
+
<a href="#faq">FAQ</a>
|
|
102
|
+
</div>
|
|
103
|
+
<a class="btn" href="{{CTA_HREF}}">{{CTA_LABEL}}</a>
|
|
104
|
+
</nav>
|
|
105
|
+
|
|
106
|
+
<section class="hero">
|
|
107
|
+
<span class="eyebrow">{{HERO_EYEBROW}}</span>
|
|
108
|
+
<h1>{{HERO_HEADLINE}}</h1>
|
|
109
|
+
<p>{{HERO_SUBHEAD}}</p>
|
|
110
|
+
<div class="actions">
|
|
111
|
+
<a class="btn" href="{{CTA_HREF}}">{{CTA_LABEL}}</a>
|
|
112
|
+
<a class="btn ghost" href="#features">See how it works</a>
|
|
113
|
+
</div>
|
|
114
|
+
</section>
|
|
115
|
+
|
|
116
|
+
<section id="features">
|
|
117
|
+
<div class="container">
|
|
118
|
+
<div class="section-head">
|
|
119
|
+
<h2>{{FEATURES_HEADLINE}}</h2>
|
|
120
|
+
<p>{{FEATURES_SUBHEAD}}</p>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="features">
|
|
123
|
+
<!-- FEATURES_START -->
|
|
124
|
+
<div class="feature"><div class="icon"></div><h3>{{FEATURE_1_TITLE}}</h3><p>{{FEATURE_1_BODY}}</p></div>
|
|
125
|
+
<div class="feature"><div class="icon"></div><h3>{{FEATURE_2_TITLE}}</h3><p>{{FEATURE_2_BODY}}</p></div>
|
|
126
|
+
<div class="feature"><div class="icon"></div><h3>{{FEATURE_3_TITLE}}</h3><p>{{FEATURE_3_BODY}}</p></div>
|
|
127
|
+
<div class="feature"><div class="icon"></div><h3>{{FEATURE_4_TITLE}}</h3><p>{{FEATURE_4_BODY}}</p></div>
|
|
128
|
+
<!-- Add more <div class="feature">...</div> blocks as needed. -->
|
|
129
|
+
<!-- FEATURES_END -->
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</section>
|
|
133
|
+
|
|
134
|
+
<section id="pricing">
|
|
135
|
+
<div class="container">
|
|
136
|
+
<div class="section-head">
|
|
137
|
+
<h2>{{PRICING_HEADLINE}}</h2>
|
|
138
|
+
<p>{{PRICING_SUBHEAD}}</p>
|
|
139
|
+
</div>
|
|
140
|
+
<div class="pricing">
|
|
141
|
+
<!-- PRICING_START -->
|
|
142
|
+
<div class="plan">
|
|
143
|
+
<h3>{{PLAN_1_NAME}}</h3>
|
|
144
|
+
<div class="price">{{PLAN_1_PRICE}}<span>/{{PLAN_1_PERIOD}}</span></div>
|
|
145
|
+
<ul>
|
|
146
|
+
<li>{{PLAN_1_FEATURE_1}}</li>
|
|
147
|
+
<li>{{PLAN_1_FEATURE_2}}</li>
|
|
148
|
+
<li>{{PLAN_1_FEATURE_3}}</li>
|
|
149
|
+
</ul>
|
|
150
|
+
<a class="btn ghost" href="{{CTA_HREF}}">{{PLAN_1_CTA}}</a>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="plan featured">
|
|
153
|
+
<span class="badge">Most popular</span>
|
|
154
|
+
<h3>{{PLAN_2_NAME}}</h3>
|
|
155
|
+
<div class="price">{{PLAN_2_PRICE}}<span>/{{PLAN_2_PERIOD}}</span></div>
|
|
156
|
+
<ul>
|
|
157
|
+
<li>{{PLAN_2_FEATURE_1}}</li>
|
|
158
|
+
<li>{{PLAN_2_FEATURE_2}}</li>
|
|
159
|
+
<li>{{PLAN_2_FEATURE_3}}</li>
|
|
160
|
+
</ul>
|
|
161
|
+
<a class="btn" href="{{CTA_HREF}}">{{PLAN_2_CTA}}</a>
|
|
162
|
+
</div>
|
|
163
|
+
<div class="plan">
|
|
164
|
+
<h3>{{PLAN_3_NAME}}</h3>
|
|
165
|
+
<div class="price">{{PLAN_3_PRICE}}<span>/{{PLAN_3_PERIOD}}</span></div>
|
|
166
|
+
<ul>
|
|
167
|
+
<li>{{PLAN_3_FEATURE_1}}</li>
|
|
168
|
+
<li>{{PLAN_3_FEATURE_2}}</li>
|
|
169
|
+
<li>{{PLAN_3_FEATURE_3}}</li>
|
|
170
|
+
</ul>
|
|
171
|
+
<a class="btn ghost" href="{{CTA_HREF}}">{{PLAN_3_CTA}}</a>
|
|
172
|
+
</div>
|
|
173
|
+
<!-- PRICING_END -->
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</section>
|
|
177
|
+
|
|
178
|
+
<section id="testimonials">
|
|
179
|
+
<div class="container">
|
|
180
|
+
<div class="section-head"><h2>{{TESTIMONIALS_HEADLINE}}</h2></div>
|
|
181
|
+
<div class="testimonials">
|
|
182
|
+
<!-- TESTIMONIALS_START -->
|
|
183
|
+
<div class="quote"><blockquote>{{QUOTE_1}}</blockquote><div class="who">{{QUOTE_1_WHO}}</div></div>
|
|
184
|
+
<div class="quote"><blockquote>{{QUOTE_2}}</blockquote><div class="who">{{QUOTE_2_WHO}}</div></div>
|
|
185
|
+
<div class="quote"><blockquote>{{QUOTE_3}}</blockquote><div class="who">{{QUOTE_3_WHO}}</div></div>
|
|
186
|
+
<!-- TESTIMONIALS_END -->
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</section>
|
|
190
|
+
|
|
191
|
+
<section id="faq">
|
|
192
|
+
<div class="container">
|
|
193
|
+
<div class="section-head"><h2>{{FAQ_HEADLINE}}</h2></div>
|
|
194
|
+
<div class="faq">
|
|
195
|
+
<!-- FAQ_START -->
|
|
196
|
+
<details><summary>{{FAQ_1_Q}}</summary><p>{{FAQ_1_A}}</p></details>
|
|
197
|
+
<details><summary>{{FAQ_2_Q}}</summary><p>{{FAQ_2_A}}</p></details>
|
|
198
|
+
<details><summary>{{FAQ_3_Q}}</summary><p>{{FAQ_3_A}}</p></details>
|
|
199
|
+
<details><summary>{{FAQ_4_Q}}</summary><p>{{FAQ_4_A}}</p></details>
|
|
200
|
+
<!-- FAQ_END -->
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
</section>
|
|
204
|
+
|
|
205
|
+
<section class="cta-band">
|
|
206
|
+
<h2>{{CLOSING_HEADLINE}}</h2>
|
|
207
|
+
<a class="btn" href="{{CTA_HREF}}">{{CTA_LABEL}}</a>
|
|
208
|
+
</section>
|
|
209
|
+
|
|
210
|
+
<footer>{{FOOTER_TEXT}}</footer>
|
|
211
|
+
</body>
|
|
212
|
+
</html>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Template: saas-marketing (static)
|
|
2
|
+
|
|
3
|
+
A single self-contained HTML SaaS marketing site (hero, features, pricing tiers, testimonials, FAQ, and a closing call to action). Richer than the landing-page template. **No infrastructure** — no database, no backend, no `needs:`.
|
|
4
|
+
State (if any) lives only in memory and is lost on refresh.
|
|
5
|
+
|
|
6
|
+
## How it deploys
|
|
7
|
+
|
|
8
|
+
To publish this page, use **`gridctl_drop`** (or `gridctl_plug` with the inline
|
|
9
|
+
HTML): it deploys as an **inspiration** — instant, and works on the **hosted**
|
|
10
|
+
edition (Claude Web) as well as local. The reference `cloudgrid.yaml` in this
|
|
11
|
+
directory (`type: static`) is ONLY for plugging the page as an OWNED static
|
|
12
|
+
runtime (async, local edition only); the fast path is `gridctl_drop`.
|
|
13
|
+
|
|
14
|
+
The fillable HTML is the real template — fetch it with
|
|
15
|
+
`gridctl_fetch("template", "saas-marketing")` and fill in the user's content.
|
|
16
|
+
|
|
17
|
+
## cloudgrid.yaml (reference only)
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
# static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
|
|
21
|
+
# this yaml is only for plugging it as an OWNED static runtime (async, local
|
|
22
|
+
# edition only). To publish the page, prefer gridctl_drop, not grid plug.
|
|
23
|
+
#
|
|
24
|
+
# needs: {} # none — static, no infrastructure
|
|
25
|
+
name: saas-marketing
|
|
26
|
+
services:
|
|
27
|
+
web:
|
|
28
|
+
type: static
|
|
29
|
+
path: /
|
|
30
|
+
```
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# status-page
|
|
2
|
+
|
|
3
|
+
A single self-contained HTML service status page: an overall banner, a component list with up / degraded / down pills, per-component uptime bars, and an incident history log. Status is baked into the page.
|
|
4
|
+
|
|
5
|
+
Static inspiration — a single self-contained `index.html` (inline CSS and JS,
|
|
6
|
+
no external CDNs). It deploys instantly via `gridctl_drop` and works on every
|
|
7
|
+
CloudGrid edition, including hosted.
|
|
8
|
+
|
|
9
|
+
## Fill it in
|
|
10
|
+
|
|
11
|
+
Fetch the template with `gridctl_fetch("template", "status-page")`, then replace the
|
|
12
|
+
placeholders:
|
|
13
|
+
|
|
14
|
+
- The overall status banner text and state class (`ok` / `degraded` / `down`).
|
|
15
|
+
- One row per component: name, a status pill, and a 90-day uptime bar (the bar segments are inline divs you set to ok/degraded/down).
|
|
16
|
+
- The incident history entries: date, title, state, and a short resolution note.
|
|
17
|
+
- All state is baked into the markup — this is a display-only static page, no live polling.
|
|
18
|
+
|
|
19
|
+
Keep it a single self-contained HTML file. Do not add external scripts,
|
|
20
|
+
stylesheets, fonts, or large embedded media.
|
|
21
|
+
|
|
22
|
+
## Deploy
|
|
23
|
+
|
|
24
|
+
- Hosted MCP edition: call the drop tool with the HTML.
|
|
25
|
+
- Local MCP / CLI edition: write the HTML to a file and run `gridctl plug`.
|
|
26
|
+
|
|
27
|
+
It deploys as an inspiration and returns a live share URL right away. Re-deploy
|
|
28
|
+
with the same entity id to update the same URL.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
|
|
2
|
+
# this yaml is only for plugging it as an OWNED static runtime (async, local
|
|
3
|
+
# edition only). To publish the page, prefer gridctl_drop, not grid plug.
|
|
4
|
+
#
|
|
5
|
+
# needs: {} # none — static, no infrastructure
|
|
6
|
+
name: status-page
|
|
7
|
+
services:
|
|
8
|
+
web:
|
|
9
|
+
type: static
|
|
10
|
+
path: /
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>{{SERVICE_NAME}} status</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg: #0b0f17; --panel: #121826; --fg: #e8edf5; --muted: #9aa7bd;
|
|
10
|
+
--border: #223049; --ok: #34d399; --degraded: #fbbf24; --down: #f87171;
|
|
11
|
+
}
|
|
12
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
13
|
+
body {
|
|
14
|
+
background: var(--bg); color: var(--fg);
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
16
|
+
-webkit-font-smoothing: antialiased; line-height: 1.5;
|
|
17
|
+
}
|
|
18
|
+
.wrap { max-width: 840px; margin: 0 auto; padding: clamp(1.5rem, 5vw, 3rem) 1.25rem; }
|
|
19
|
+
header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 1.75rem; gap: 1rem; flex-wrap: wrap; }
|
|
20
|
+
header .brand { font-weight: 600; font-size: 1.15rem; letter-spacing: -0.01em; }
|
|
21
|
+
header .updated { color: var(--muted); font-size: 0.85rem; }
|
|
22
|
+
|
|
23
|
+
.banner { border-radius: 14px; padding: 1.25rem 1.5rem; margin-bottom: 2rem; font-weight: 600; display: flex; align-items: center; gap: 0.75rem; border: 1px solid var(--border); }
|
|
24
|
+
.banner .dot { width: 12px; height: 12px; border-radius: 50%; }
|
|
25
|
+
.banner.ok { background: rgba(52,211,153,0.1); } .banner.ok .dot { background: var(--ok); }
|
|
26
|
+
.banner.degraded { background: rgba(251,191,36,0.1); } .banner.degraded .dot { background: var(--degraded); }
|
|
27
|
+
.banner.down { background: rgba(248,113,113,0.1); } .banner.down .dot { background: var(--down); }
|
|
28
|
+
|
|
29
|
+
h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); margin: 2rem 0 0.75rem; }
|
|
30
|
+
.component { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 1.1rem 1.25rem; margin-bottom: 0.75rem; }
|
|
31
|
+
.component .top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.75rem; }
|
|
32
|
+
.component .name { font-weight: 600; }
|
|
33
|
+
.pill { font-size: 0.8rem; padding: 0.2rem 0.65rem; border-radius: 999px; font-weight: 600; }
|
|
34
|
+
.pill.ok { color: var(--ok); background: rgba(52,211,153,0.12); }
|
|
35
|
+
.pill.degraded { color: var(--degraded); background: rgba(251,191,36,0.12); }
|
|
36
|
+
.pill.down { color: var(--down); background: rgba(248,113,113,0.12); }
|
|
37
|
+
.uptime { display: flex; gap: 2px; align-items: flex-end; height: 30px; }
|
|
38
|
+
.uptime span { flex: 1; height: 100%; border-radius: 2px; background: var(--ok); opacity: 0.85; }
|
|
39
|
+
.uptime span.degraded { background: var(--degraded); }
|
|
40
|
+
.uptime span.down { background: var(--down); }
|
|
41
|
+
.uptime-label { display: flex; justify-content: space-between; color: var(--muted); font-size: 0.75rem; margin-top: 0.4rem; }
|
|
42
|
+
|
|
43
|
+
.incident { border-left: 3px solid var(--border); padding: 0.5rem 0 0.5rem 1rem; margin-bottom: 1.5rem; }
|
|
44
|
+
.incident.ok { border-color: var(--ok); }
|
|
45
|
+
.incident.degraded { border-color: var(--degraded); }
|
|
46
|
+
.incident.down { border-color: var(--down); }
|
|
47
|
+
.incident .date { color: var(--muted); font-size: 0.8rem; }
|
|
48
|
+
.incident .title { font-weight: 600; margin: 0.2rem 0; }
|
|
49
|
+
.incident p { color: var(--muted); font-size: 0.92rem; }
|
|
50
|
+
footer { color: var(--muted); font-size: 0.85rem; margin-top: 2.5rem; text-align: center; }
|
|
51
|
+
</style>
|
|
52
|
+
</head>
|
|
53
|
+
<body>
|
|
54
|
+
<div class="wrap">
|
|
55
|
+
<header>
|
|
56
|
+
<span class="brand">{{SERVICE_NAME}}</span>
|
|
57
|
+
<span class="updated">Last updated {{LAST_UPDATED}}</span>
|
|
58
|
+
</header>
|
|
59
|
+
|
|
60
|
+
<!-- Overall banner: set the class to ok / degraded / down. -->
|
|
61
|
+
<div class="banner ok">
|
|
62
|
+
<span class="dot"></span>
|
|
63
|
+
<span>{{OVERALL_STATUS_TEXT}}</span>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<h2>Components</h2>
|
|
67
|
+
<!-- COMPONENTS_START -->
|
|
68
|
+
<!-- One .component per service. Set the .pill class (ok/degraded/down) and
|
|
69
|
+
mark any non-ok bars in .uptime with class degraded or down. -->
|
|
70
|
+
<div class="component">
|
|
71
|
+
<div class="top"><span class="name">{{COMPONENT_1_NAME}}</span><span class="pill ok">Operational</span></div>
|
|
72
|
+
<div class="uptime">
|
|
73
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
|
74
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
|
75
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="uptime-label"><span>90 days ago</span><span>{{COMPONENT_1_UPTIME}} uptime</span><span>today</span></div>
|
|
78
|
+
</div>
|
|
79
|
+
<div class="component">
|
|
80
|
+
<div class="top"><span class="name">{{COMPONENT_2_NAME}}</span><span class="pill degraded">Degraded</span></div>
|
|
81
|
+
<div class="uptime">
|
|
82
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
|
83
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span class="degraded"></span><span class="degraded"></span>
|
|
84
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
|
85
|
+
</div>
|
|
86
|
+
<div class="uptime-label"><span>90 days ago</span><span>{{COMPONENT_2_UPTIME}} uptime</span><span>today</span></div>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="component">
|
|
89
|
+
<div class="top"><span class="name">{{COMPONENT_3_NAME}}</span><span class="pill ok">Operational</span></div>
|
|
90
|
+
<div class="uptime">
|
|
91
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
|
92
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
|
93
|
+
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="uptime-label"><span>90 days ago</span><span>{{COMPONENT_3_UPTIME}} uptime</span><span>today</span></div>
|
|
96
|
+
</div>
|
|
97
|
+
<!-- COMPONENTS_END -->
|
|
98
|
+
|
|
99
|
+
<h2>Incident history</h2>
|
|
100
|
+
<!-- INCIDENTS_START -->
|
|
101
|
+
<div class="incident ok">
|
|
102
|
+
<div class="date">{{INCIDENT_1_DATE}}</div>
|
|
103
|
+
<div class="title">{{INCIDENT_1_TITLE}}</div>
|
|
104
|
+
<p>{{INCIDENT_1_BODY}}</p>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="incident down">
|
|
107
|
+
<div class="date">{{INCIDENT_2_DATE}}</div>
|
|
108
|
+
<div class="title">{{INCIDENT_2_TITLE}}</div>
|
|
109
|
+
<p>{{INCIDENT_2_BODY}}</p>
|
|
110
|
+
</div>
|
|
111
|
+
<!-- Add more .incident blocks (class ok/degraded/down) as needed. -->
|
|
112
|
+
<!-- INCIDENTS_END -->
|
|
113
|
+
|
|
114
|
+
<footer>{{FOOTER_TEXT}}</footer>
|
|
115
|
+
</div>
|
|
116
|
+
</body>
|
|
117
|
+
</html>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Template: status-page (static)
|
|
2
|
+
|
|
3
|
+
A single self-contained HTML service status page: an overall banner, a component list with up / degraded / down pills, per-component uptime bars, and an incident history log. Status is baked into the page. **No infrastructure** — no database, no backend, no `needs:`.
|
|
4
|
+
State (if any) lives only in memory and is lost on refresh.
|
|
5
|
+
|
|
6
|
+
## How it deploys
|
|
7
|
+
|
|
8
|
+
To publish this page, use **`gridctl_drop`** (or `gridctl_plug` with the inline
|
|
9
|
+
HTML): it deploys as an **inspiration** — instant, and works on the **hosted**
|
|
10
|
+
edition (Claude Web) as well as local. The reference `cloudgrid.yaml` in this
|
|
11
|
+
directory (`type: static`) is ONLY for plugging the page as an OWNED static
|
|
12
|
+
runtime (async, local edition only); the fast path is `gridctl_drop`.
|
|
13
|
+
|
|
14
|
+
The fillable HTML is the real template — fetch it with
|
|
15
|
+
`gridctl_fetch("template", "status-page")` and fill in the user's content.
|
|
16
|
+
|
|
17
|
+
## cloudgrid.yaml (reference only)
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
# static — deploys as an inspiration (instant, works on hosted) via gridctl_drop;
|
|
21
|
+
# this yaml is only for plugging it as an OWNED static runtime (async, local
|
|
22
|
+
# edition only). To publish the page, prefer gridctl_drop, not grid plug.
|
|
23
|
+
#
|
|
24
|
+
# needs: {} # none — static, no infrastructure
|
|
25
|
+
name: status-page
|
|
26
|
+
services:
|
|
27
|
+
web:
|
|
28
|
+
type: static
|
|
29
|
+
path: /
|
|
30
|
+
```
|