diff --git a/templates/assets/icon.png b/templates/assets/icon.png new file mode 100644 index 0000000..2eaf0bb Binary files /dev/null and b/templates/assets/icon.png differ diff --git a/templates/awizemann-site-status-checker/README.md b/templates/awizemann-site-status-checker/README.md new file mode 100644 index 0000000..e726386 --- /dev/null +++ b/templates/awizemann-site-status-checker/README.md @@ -0,0 +1,39 @@ +# Site Status Checker + +A minimal uptime watchdog that pings a list of URLs once a day, records pass/fail results, and keeps a simple Scarf dashboard up to date. + +**Requires Scarf 2.3+** — this template uses the configuration feature (a form during install, and a Configuration button on the dashboard for editing later). + +## What you get + +- **Configurable site list** — you tell Scarf which URLs to watch during install, via a form. No file editing required. Edit the list later via the **Configuration** button on the project dashboard (slider icon next to the folder). +- **Configurable timeout** — how long to wait per URL before giving up, also set via the form. +- **`.scarf/config.json`** — where your configured values land. The agent reads this at run time; you never need to open it by hand. +- **`status-log.md`** — the agent's append-only log of check results. New runs append a section at the top. Created automatically on first run. +- **`.scarf/dashboard.json`** — Scarf dashboard with live stat widgets (sites up, sites down, last checked), the full list of watched sites with their last-known status, and a usage guide. +- **Cron job `Check site status`** — registered (paused) by the installer; tag `[tmpl:awizemann/site-status-checker]`. Runs daily at 9:00 AM when enabled. Reads your configured sites + timeout, hits each URL, writes results to `status-log.md`, and updates the dashboard. + +## First steps + +1. During install, fill in the Configuration form: add the URLs you want to watch and (optionally) adjust the timeout. Hit Continue, then Install. +2. After install, open the **Cron** sidebar and enable the `[tmpl:awizemann/site-status-checker] Check site status` job. It's paused on install so nothing runs without your explicit say-so. +3. From the project's dashboard, ask your agent to run the job now: *"Run the site status check and update the dashboard."* +4. Future runs happen automatically at 9 AM daily. + +## Changing sites or timeout later + +Click the **Configuration** button (slider icon, dashboard toolbar) to re-open the form pre-filled with your current values. Add, remove, or edit URLs. Save. The next cron run picks up the changes. + +## Customizing + +- **Change the schedule.** Edit the cron job in the Cron sidebar — the schedule field accepts `30m`, `every 2h`, or standard cron expressions like `0 9 * * *`. +- **Change what "down" means.** By default the agent treats any non-2xx/3xx HTTP response as down. If you want to check for specific strings in the body (e.g. "Maintenance"), tell the agent in `AGENTS.md` and it will adapt. +- **Add alerting.** Set a `deliver` target on the cron job (Discord, Slack, Telegram) — the agent will post the run summary there instead of just writing to `status-log.md`. + +## Recommended model + +`claude-haiku-4` works well — this is a simple tool-use task (HTTP GETs + a short summary). Haiku keeps costs low when the cron runs daily. The recommendation appears in the Configuration form; Scarf doesn't auto-switch your active model, so adjust via Settings if you'd like. + +## Uninstalling + +Right-click the project in the sidebar → **Uninstall Template…** (or click the shippingbox icon on the dashboard header). Scarf walks you through exactly what's about to be removed: template-installed files in the project dir, the `[tmpl:…]` cron job, and the Configuration values you entered (`config.json` + Keychain items for any secrets — though this template has none). User-created files (like `status-log.md`) are preserved. diff --git a/templates/awizemann-site-status-checker/dashboard.json b/templates/awizemann-site-status-checker/dashboard.json new file mode 100644 index 0000000..8e5a319 --- /dev/null +++ b/templates/awizemann-site-status-checker/dashboard.json @@ -0,0 +1,75 @@ +{ + "version": 1, + "title": "Site Status", + "description": "Daily uptime check for your watched URLs. The stat widgets, the sites list, and the Site tab's preview URL all update automatically when the cron job runs. Switch to the Site tab to see your first watched site live.", + "theme": { "accent": "green" }, + "sections": [ + { + "title": "Current Status", + "columns": 3, + "widgets": [ + { + "type": "stat", + "title": "Sites Up", + "value": 0, + "icon": "checkmark.circle.fill", + "color": "green", + "subtitle": "responded 2xx/3xx" + }, + { + "type": "stat", + "title": "Sites Down", + "value": 0, + "icon": "xmark.circle.fill", + "color": "red", + "subtitle": "non-2xx, timeout, DNS" + }, + { + "type": "stat", + "title": "Last Checked", + "value": "never", + "icon": "clock", + "color": "blue", + "subtitle": "ISO-8601 timestamp" + } + ] + }, + { + "title": "Watched Sites", + "columns": 1, + "widgets": [ + { + "type": "list", + "title": "Watched Sites (populated after first run)", + "items": [ + { "text": "Run the check once to populate — the agent reads your Configuration and fills this list with live status.", "status": "pending" } + ] + } + ] + }, + { + "title": "Live Site Preview", + "columns": 1, + "widgets": [ + { + "type": "webview", + "title": "First Watched Site", + "url": "https://awizemann.github.io/scarf/", + "height": 420 + } + ] + }, + { + "title": "How to Use", + "columns": 1, + "widgets": [ + { + "type": "text", + "title": "Quick Start", + "format": "markdown", + "content": "**1.** Review your configuration — click the **slider icon** (top-right of this dashboard) to open Configuration. The sites you enter there are what the cron job will check.\n\n**2.** Enable the `[tmpl:awizemann/site-status-checker] Check site status` cron job in the Cron sidebar. It ships paused — nothing runs until you say so.\n\n**3.** Ask your agent: *\"Run the site status check now.\"* The Watched Sites list populates, the stat widgets update, the Site tab's URL switches to your first watched site, and a new entry lands at the top of `status-log.md`.\n\n**4.** Daily at 9 AM the cron job fires automatically. Change the schedule in the Cron sidebar if you want a different cadence.\n\nSwitch to the **Site** tab (next to Dashboard, above) to see your first watched site rendered in a browser. Useful to eyeball a site when the status says up but something still looks off.\n\nSee `README.md` and `AGENTS.md` in the project root for the full spec." + } + ] + } + ] +} diff --git a/templates/awizemann-site-status-checker/index.html b/templates/awizemann-site-status-checker/index.html new file mode 100644 index 0000000..813d658 --- /dev/null +++ b/templates/awizemann-site-status-checker/index.html @@ -0,0 +1,104 @@ + + + + + + Site Status Checker — Scarf Templates + + + + + + + +
+
+
+

Site Status Checker v1.1.0

+

A daily uptime check for a list of URLs you configure on install. Writes status to status-log.md and updates the dashboard with current counts.

+

+ by Alan Wizemann + awizemann/site-status-checker + monitoring +

+

monitoringuptimecronstarterconfigurable

+
+ +
+ +
+

Live dashboard preview

+

+ Exactly what you'll see inside Scarf after install. Values shown here are + placeholders; the agent updates them each time the cron job runs. +

+
+
+ +
+
+
+ +
+

README

+
+
+
+ + + + + + + diff --git a/templates/awizemann-site-status-checker/manifest.json b/templates/awizemann-site-status-checker/manifest.json new file mode 100644 index 0000000..5ce1279 --- /dev/null +++ b/templates/awizemann-site-status-checker/manifest.json @@ -0,0 +1,50 @@ +{ + "schemaVersion": 2, + "id": "awizemann/site-status-checker", + "name": "Site Status Checker", + "version": "1.1.0", + "minScarfVersion": "2.3.0", + "minHermesVersion": "0.9.0", + "author": { + "name": "Alan Wizemann", + "url": "https://github.com/awizemann/scarf" + }, + "description": "A daily uptime check for a list of URLs you configure on install. Writes status to status-log.md and updates the dashboard with current counts.", + "category": "monitoring", + "tags": ["monitoring", "uptime", "cron", "starter", "configurable"], + "contents": { + "dashboard": true, + "agentsMd": true, + "cron": 1, + "config": 2 + }, + "config": { + "schema": [ + { + "key": "sites", + "type": "list", + "itemType": "string", + "label": "Sites to Watch", + "description": "One URL per item. HTTP or HTTPS. You can add and remove entries after install via the Configuration button on the dashboard.", + "required": true, + "minItems": 1, + "maxItems": 25, + "default": ["https://example.com", "https://example.org"] + }, + { + "key": "timeout_seconds", + "type": "number", + "label": "Request Timeout (seconds)", + "description": "How long to wait for each URL before giving up.", + "required": false, + "min": 1, + "max": 60, + "default": 10 + } + ], + "modelRecommendation": { + "preferred": "claude-haiku-4", + "rationale": "Simple tool-use task — HTTP GETs + a short summary. Haiku is plenty and keeps cost low when the cron runs daily." + } + } +} diff --git a/templates/catalog.json b/templates/catalog.json new file mode 100644 index 0000000..7abaeb5 --- /dev/null +++ b/templates/catalog.json @@ -0,0 +1,68 @@ +{ + "generated": true, + "schemaVersion": 1, + "templates": [ + { + "author": { + "name": "Alan Wizemann", + "url": "https://github.com/awizemann/scarf" + }, + "bundleSha256": "0a20802a8830a7cfdd1afa2888e42e113c9a17a37439384a3037d32ad1f24c1f", + "bundleSize": 7569, + "category": "monitoring", + "config": { + "modelRecommendation": { + "preferred": "claude-haiku-4", + "rationale": "Simple tool-use task \u2014 HTTP GETs + a short summary. Haiku is plenty and keeps cost low when the cron runs daily." + }, + "schema": [ + { + "default": [ + "https://example.com", + "https://example.org" + ], + "description": "One URL per item. HTTP or HTTPS. You can add and remove entries after install via the Configuration button on the dashboard.", + "itemType": "string", + "key": "sites", + "label": "Sites to Watch", + "maxItems": 25, + "minItems": 1, + "required": true, + "type": "list" + }, + { + "default": 10, + "description": "How long to wait for each URL before giving up.", + "key": "timeout_seconds", + "label": "Request Timeout (seconds)", + "max": 60, + "min": 1, + "required": false, + "type": "number" + } + ] + }, + "contents": { + "agentsMd": true, + "config": 2, + "cron": 1, + "dashboard": true + }, + "description": "A daily uptime check for a list of URLs you configure on install. Writes status to status-log.md and updates the dashboard with current counts.", + "detailSlug": "awizemann-site-status-checker", + "id": "awizemann/site-status-checker", + "installUrl": "https://raw.githubusercontent.com/awizemann/scarf/main/templates/awizemann/site-status-checker/site-status-checker.scarftemplate", + "minHermesVersion": "0.9.0", + "minScarfVersion": "2.3.0", + "name": "Site Status Checker", + "tags": [ + "monitoring", + "uptime", + "cron", + "starter", + "configurable" + ], + "version": "1.1.0" + } + ] +} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..c091574 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,48 @@ + + + + + + Scarf Templates + + + + + + + +
+

Pre-packaged projects for Scarf

+

+ Browse 1 community template — each ships with a + ready-to-install Scarf dashboard, a cross-agent AGENTS.md, optional + cron jobs and skills. Click a template to see what it does; one click installs + it into Scarf. +

+
+ +
+
+

Site Status Checker

A daily uptime check for a list of URLs you configure on install. Writes status to status-log.md and updates the dashboard with current counts.

Alan Wizemannv1.1.0
monitoringuptimecronstarterconfigurable
+
+
+ + + + diff --git a/templates/styles.css b/templates/styles.css new file mode 100644 index 0000000..ad9a87a --- /dev/null +++ b/templates/styles.css @@ -0,0 +1,441 @@ +/* Scarf Templates — catalog site. + * Vanilla CSS, no framework. Matches Scarf's green accent and keeps + * decoration minimal — the live dashboard preview is the point. */ + +:root { + --fg: #1a1a1a; + --fg-muted: #666; + --bg: #fafafa; + --bg-card: #ffffff; + --border: #e5e5e5; + --accent: #2aa876; /* scarf green */ + --accent-dark: #1f7f5a; + --red: #d9534f; + --blue: #3498db; + --orange: #f0ad4e; + --radius: 8px; + --shadow: 0 1px 2px rgba(0,0,0,0.04), 0 4px 12px rgba(0,0,0,0.04); + --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +} + +@media (prefers-color-scheme: dark) { + :root { + --fg: #e5e5e5; + --fg-muted: #9a9a9a; + --bg: #141414; + --bg-card: #1e1e1e; + --border: #2a2a2a; + --accent: #3abf8a; + --accent-dark: #2aa876; + --shadow: 0 1px 2px rgba(0,0,0,0.3), 0 4px 12px rgba(0,0,0,0.3); + } +} + +* { box-sizing: border-box; } + +body { + margin: 0; + font-family: var(--sans); + color: var(--fg); + background: var(--bg); + line-height: 1.5; +} + +code, pre { + font-family: var(--mono); + font-size: 0.92em; +} + +pre { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 12px 16px; + overflow-x: auto; +} +code { + background: rgba(0,0,0,0.05); + padding: 2px 5px; + border-radius: 4px; +} +pre code { background: transparent; padding: 0; } + +a { color: var(--accent-dark); text-decoration: none; } +a:hover { text-decoration: underline; } + +h1, h2, h3 { line-height: 1.25; } + +/* ---------- header / footer ---------- */ + +.site-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 16px 32px; + border-bottom: 1px solid var(--border); + background: var(--bg-card); +} +.brand { + display: flex; + align-items: center; + gap: 12px; + color: var(--fg); +} +.brand:hover { text-decoration: none; } +.brand-name { font-weight: 600; font-size: 18px; } +.site-nav a { + margin-left: 20px; + color: var(--fg-muted); + font-size: 14px; +} + +.site-footer { + padding: 32px; + text-align: center; + color: var(--fg-muted); + font-size: 14px; + border-top: 1px solid var(--border); + margin-top: 40px; +} + +/* ---------- landing ---------- */ + +.hero { + padding: 48px 32px 24px; + max-width: 720px; + margin: 0 auto; + text-align: center; +} +.hero h1 { font-size: 32px; margin: 0 0 12px; } +.hero p { color: var(--fg-muted); } + +.catalog { + max-width: 1100px; + margin: 0 auto; + padding: 16px 32px 48px; +} +.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 16px; +} +.card { + display: block; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 20px; + box-shadow: var(--shadow); + color: inherit; + transition: transform 0.12s ease, box-shadow 0.12s ease; +} +.card:hover { + transform: translateY(-2px); + box-shadow: 0 2px 4px rgba(0,0,0,0.06), 0 8px 24px rgba(0,0,0,0.06); + text-decoration: none; +} +.card h3 { margin: 0 0 6px; font-size: 18px; } +.card .desc { color: var(--fg-muted); margin: 0 0 14px; font-size: 14px; } +.card .meta { + display: flex; + justify-content: space-between; + font-size: 12px; + color: var(--fg-muted); + margin-bottom: 8px; +} +.card .author { font-weight: 500; } +.card .version { font-family: var(--mono); } +.tags { display: flex; flex-wrap: wrap; gap: 4px; } +.tag { + display: inline-block; + padding: 2px 8px; + background: rgba(42,168,118,0.15); + color: var(--accent-dark); + border-radius: 10px; + font-size: 11px; +} + +/* ---------- template detail page ---------- */ + +.detail { + max-width: 900px; + margin: 0 auto; + padding: 32px; +} +.detail-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 32px; + margin-bottom: 40px; + padding-bottom: 24px; + border-bottom: 1px solid var(--border); +} +.detail-header h1 { margin: 0 0 4px; } +.detail-header h1 .version { + font-family: var(--mono); + font-size: 16px; + color: var(--fg-muted); + font-weight: 400; +} +.detail-header .desc { color: var(--fg-muted); margin: 0 0 12px; } +.detail-header .meta { + display: flex; + gap: 16px; + font-size: 13px; + color: var(--fg-muted); + margin-bottom: 8px; +} +.detail-header .id { font-family: var(--mono); } + +.install-actions { display: flex; flex-direction: column; gap: 8px; min-width: 200px; } +.btn { + display: inline-block; + padding: 10px 20px; + border-radius: var(--radius); + font-weight: 500; + text-align: center; + font-size: 14px; +} +.btn-primary { + background: var(--accent); + color: white; +} +.btn-primary:hover { background: var(--accent-dark); text-decoration: none; color: white; } +.btn-secondary { + background: transparent; + color: var(--fg); + border: 1px solid var(--border); +} +.btn-secondary:hover { border-color: var(--accent); text-decoration: none; color: var(--accent-dark); } + +.detail-dashboard { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 24px; + margin-bottom: 32px; +} +.detail-dashboard h2 { margin-top: 0; } +.detail-dashboard-note { + color: var(--fg-muted); + font-size: 13px; + margin-top: 4px; + margin-bottom: 20px; +} + +.detail-readme h2 { margin-top: 0; } +.detail-readme > div { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 24px; +} + +/* ---------- config schema panel (v2.3) ---------- */ + +.detail-config { margin-bottom: 32px; } +.detail-config:empty, .detail-config > div:empty { display: none; } + +.config-schema { + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 24px; +} +.config-schema-header { margin-top: 0; } +.config-schema-desc { + color: var(--fg-muted); + font-size: 13px; + margin-top: 4px; + margin-bottom: 16px; +} +.config-schema-list { + margin: 0; + padding: 0; + display: grid; + grid-template-columns: 1fr; + gap: 12px; +} +.config-field-header { + display: flex; + align-items: baseline; + gap: 8px; + margin-top: 4px; + font-weight: 500; +} +.config-field-key { font-family: var(--mono); font-size: 13px; } +.config-field-type { + font-family: var(--mono); + font-size: 11px; + padding: 1px 6px; + border-radius: 10px; + background: rgba(0,0,0,0.08); + color: var(--fg-muted); +} +.config-field-required { + font-size: 11px; + color: var(--red); + text-transform: uppercase; + letter-spacing: 0.5px; + padding: 1px 6px; + border-radius: 10px; + background: rgba(217,83,79,0.12); +} +.config-field-body { + margin: 0 0 4px 0; + padding-left: 0; + font-size: 14px; +} +.config-field-label { + font-size: 14px; + margin-bottom: 2px; +} +.config-field-description { + color: var(--fg-muted); + font-size: 13px; + margin-bottom: 4px; +} +.config-field-constraint { + font-size: 12px; + color: var(--fg-muted); + font-style: italic; +} + +.config-model-rec { + margin-top: 20px; + padding: 14px 16px; + border-radius: var(--radius); + background: rgba(42,168,118,0.08); + border: 1px solid rgba(42,168,118,0.2); +} +.config-model-label { + font-size: 11px; + color: var(--accent-dark); + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: 600; + margin-bottom: 4px; +} +.config-model-preferred { + font-family: var(--mono); + font-size: 14px; + margin-bottom: 4px; +} +.config-model-rationale { + color: var(--fg-muted); + font-size: 13px; +} +.config-model-alternatives { + color: var(--fg-muted); + font-size: 12px; + margin-top: 4px; +} + +/* ---------- dashboard preview ---------- */ + +.dashboard-header h1.dashboard-title { margin: 0 0 4px; font-size: 22px; } +.dashboard-desc { color: var(--fg-muted); margin: 0 0 24px; font-size: 14px; } +.dashboard-section { margin-bottom: 24px; } +.section-title { margin: 0 0 10px; font-size: 14px; font-weight: 600; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.5px; } + +.widget-grid { + display: grid; + grid-template-columns: repeat(var(--cols, 3), 1fr); + gap: 12px; +} + +.widget { + background: var(--bg); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 14px 16px; +} + +.widget-title { + font-size: 12px; + font-weight: 500; + color: var(--fg-muted); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +/* stat */ +.widget-stat { display: flex; flex-direction: column; gap: 6px; } +.widget-stat-top { display: flex; align-items: center; gap: 8px; } +.widget-stat-icon { font-size: 14px; color: var(--fg-muted); } +.widget-stat-value { font-size: 32px; font-weight: 600; line-height: 1.1; } +.widget-stat-subtitle { font-size: 11px; color: var(--fg-muted); } +.widget-stat[data-color="green"] .widget-stat-icon { color: var(--accent); } +.widget-stat[data-color="red"] .widget-stat-icon { color: var(--red); } +.widget-stat[data-color="blue"] .widget-stat-icon { color: var(--blue); } +.widget-stat[data-color="orange"] .widget-stat-icon { color: var(--orange); } + +/* progress */ +.widget-progress-label { font-size: 13px; margin: 6px 0 8px; } +.progress-bar { height: 8px; background: rgba(0,0,0,0.05); border-radius: 4px; overflow: hidden; } +.progress-fill { height: 100%; background: var(--accent); border-radius: 4px; } + +/* text */ +.widget-text-body { font-size: 14px; margin-top: 6px; } +.widget-text-body h1 { font-size: 20px; margin: 12px 0 8px; } +.widget-text-body h2 { font-size: 17px; margin: 10px 0 6px; } +.widget-text-body h3 { font-size: 14px; margin: 8px 0 4px; } +.widget-text-body p { margin: 8px 0; } +.widget-text-body ul, .widget-text-body ol { padding-left: 22px; } + +/* table */ +.data-table { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 8px; } +.data-table th, .data-table td { padding: 6px 8px; border-bottom: 1px solid var(--border); text-align: left; } +.data-table th { font-weight: 500; color: var(--fg-muted); } + +/* list */ +.widget-list-items { margin: 6px 0 0; padding-left: 18px; font-size: 13px; } +.widget-list-item { + display: flex; + justify-content: space-between; + gap: 12px; + padding: 3px 0; +} +.widget-list-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.widget-list-status { + font-size: 10px; + padding: 2px 6px; + border-radius: 10px; + background: rgba(0,0,0,0.08); + color: var(--fg-muted); + text-transform: uppercase; + letter-spacing: 0.5px; + flex-shrink: 0; +} +.widget-list-status[data-status="up"] { background: rgba(42,168,118,0.18); color: var(--accent-dark); } +.widget-list-status[data-status="down"] { background: rgba(217,83,79,0.18); color: var(--red); } + +/* chart */ +.widget-chart-svg { width: 100%; height: auto; margin-top: 8px; } +.chart-axis { stroke: var(--border); stroke-width: 1; } +.chart-line { fill: none; stroke-width: 2; } +.chart-line[data-color="accent"], .chart-bar[data-color="accent"] { stroke: var(--accent); fill: var(--accent); } +.chart-line[data-color="red"], .chart-bar[data-color="red"] { stroke: var(--red); fill: var(--red); } +.chart-line[data-color="blue"], .chart-bar[data-color="blue"] { stroke: var(--blue); fill: var(--blue); } +.chart-line[data-color="orange"], .chart-bar[data-color="orange"] { stroke: var(--orange); fill: var(--orange); } +.widget-chart-empty { color: var(--fg-muted); font-size: 13px; padding: 20px 0; text-align: center; } + +/* webview */ +.widget-webview iframe { border: 1px solid var(--border); border-radius: 6px; margin-top: 8px; } + +/* unknown */ +.widget-unknown-body { color: var(--fg-muted); font-size: 13px; margin-top: 6px; } + +/* ---------- responsive ---------- */ + +@media (max-width: 680px) { + .site-header { padding: 12px 16px; } + .site-nav a { margin-left: 12px; font-size: 13px; } + .hero { padding: 32px 16px 16px; } + .catalog, .detail { padding: 16px; } + .detail-header { flex-direction: column; gap: 16px; } + .install-actions { flex-direction: row; min-width: 0; } + .btn { flex: 1; } +} diff --git a/templates/widgets.js b/templates/widgets.js new file mode 100644 index 0000000..fc72e7e --- /dev/null +++ b/templates/widgets.js @@ -0,0 +1,523 @@ +// Scarf dashboard widget renderer — the dogfood piece. +// +// Takes the SAME `dashboard.json` shape the Scarf macOS app renders +// (see scarf/scarf/Core/Models/ProjectDashboard.swift) and produces an +// HTML approximation for the catalog site. A template's detail page +// shows a live preview of exactly what the user's project dashboard +// will look like after install. +// +// Widget types mirrored from the Swift dispatcher: +// stat — big number + label + icon + color +// progress — label + 0..1 bar +// text — markdown (tiny subset renderer) +// table — plain HTML table +// list — bulleted list with optional status badge +// chart — SVG line/bar by series +// webview — sandboxed