mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-08 02:14:37 +00:00
68f6b98fcf
Phase D of v2.3 template configuration — closes the loop between the Swift app and the catalog pipeline. Authors can now ship schemaful bundles; the Python validator enforces the same invariants the Swift installer does; the catalog site displays the schema so visitors see what they'll need to configure before installing. Python validator (tools/build-catalog.py): - SUPPORTED_SCHEMA_VERSIONS accepts both 1 and 2 (v1 bundles are unchanged; v2 adds optional manifest.config). - New _validate_config_schema function mirrors the Swift ProjectConfigService.validateSchema rules: unique keys, supported types, enum option presence + unique values, list itemType == "string", secret-field cannot declare a default, modelRecommendation.preferred non-empty when present. - _validate_contents_claim cross-checks contents.config (field count) against config.schema actual length — mismatch refused. - TemplateRecord.to_catalog_entry exposes `config` in catalog.json so the site can render the schema. - render_site copies each bundle's template.json to the detail dir as manifest.json (only when the manifest has a config block — keeps the served tree lean and makes "no manifest.json" a meaningful 404 signal in the frontend). - catalog.json's own schemaVersion stays at 1 (independent of per- template manifest schemaVersion). Python tests (tools/test_build_catalog.py): 8 new cases in a new ConfigSchemaValidationTests suite — accepts schemaful bundle, rejects duplicate keys, rejects secret-with-default, rejects enum-without- options, rejects unsupported field type, rejects contents.config count mismatch, rejects unsupported list itemType, legacy v1 manifests pass unchanged. 24/24 Python tests total. Site (site/widgets.js): - New renderConfigSchema(container, config) — mirrors the display on the Scarf install preview. Renders each field as a <dt>/<dd> pair with type + required badges; enum shows choice labels; list fields show min/max bounds; string fields show pattern/length; secret fields get a "Stored in Keychain" reassurance. Optional modelRecommendation panel at the bottom with preferred + rationale + alternatives. - The renderer is display-only — the site never collects values; that's the Scarf app's job. template.html.tmpl adds a #config-schema <section>. The inline script fetches manifest.json from the detail dir; on success hands the config block to ScarfWidgets.renderConfigSchema; on 404 (schema-less templates) silently leaves the section empty. CSS in styles.css adds a config-schema panel matching the accent-green aesthetic. 24/24 Python + 50/50 Swift tests pass. site-status-checker still renders correctly (schema-less; manifest.json isn't copied for it). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
105 lines
3.6 KiB
Cheetah
105 lines
3.6 KiB
Cheetah
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{{NAME}} — Scarf Templates</title>
|
|
<meta name="description" content="{{DESC}}">
|
|
<link rel="stylesheet" href="../styles.css">
|
|
<link rel="icon" type="image/png" href="../assets/icon.png">
|
|
</head>
|
|
<body>
|
|
<header class="site-header">
|
|
<a class="brand" href="..">
|
|
<img src="../assets/icon.png" alt="" width="40" height="40">
|
|
<span class="brand-name">Scarf Templates</span>
|
|
</a>
|
|
<nav class="site-nav">
|
|
<a href="..">Catalog</a>
|
|
<a href="https://github.com/awizemann/scarf">GitHub</a>
|
|
<a href="https://github.com/awizemann/scarf/blob/main/templates/CONTRIBUTING.md">Contribute</a>
|
|
</nav>
|
|
</header>
|
|
|
|
<main class="detail">
|
|
<section class="detail-header">
|
|
<div>
|
|
<h1>{{NAME}} <span class="version">v{{VERSION}}</span></h1>
|
|
<p class="desc">{{DESC}}</p>
|
|
<p class="meta">
|
|
<span class="author">by {{AUTHOR_HTML}}</span>
|
|
<span class="id">{{ID}}</span>
|
|
<span class="category">{{CATEGORY}}</span>
|
|
</p>
|
|
<p class="tags">{{TAGS_HTML}}</p>
|
|
</div>
|
|
<div class="install-actions">
|
|
<a class="btn btn-primary" href="{{SCARF_INSTALL_URL}}">Install with Scarf</a>
|
|
<a class="btn btn-secondary" href="{{INSTALL_URL_ENCODED}}">Download .scarftemplate</a>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="detail-dashboard">
|
|
<h2>Live dashboard preview</h2>
|
|
<p class="detail-dashboard-note">
|
|
Exactly what you'll see inside Scarf after install. Values shown here are
|
|
placeholders; the agent updates them each time the cron job runs.
|
|
</p>
|
|
<div id="dashboard-preview"></div>
|
|
</section>
|
|
|
|
<section class="detail-config">
|
|
<div id="config-schema"></div>
|
|
</section>
|
|
|
|
<section class="detail-readme">
|
|
<h2>README</h2>
|
|
<div id="readme-body"></div>
|
|
</section>
|
|
</main>
|
|
|
|
<footer class="site-footer">
|
|
<p>
|
|
Scarf is open source:
|
|
<a href="https://github.com/awizemann/scarf">github.com/awizemann/scarf</a>.
|
|
</p>
|
|
</footer>
|
|
|
|
<script src="../widgets.js"></script>
|
|
<script>
|
|
// Fetch + render dashboard + README + config schema at page load.
|
|
// Dashboard + README live next to index.html in this template's
|
|
// detail dir; the config schema comes from the sibling manifest.json
|
|
// that the build-catalog renderer also copies in.
|
|
(async function () {
|
|
const dashboardEl = document.getElementById("dashboard-preview");
|
|
const readmeEl = document.getElementById("readme-body");
|
|
const configEl = document.getElementById("config-schema");
|
|
try {
|
|
const d = await fetch("dashboard.json").then(r => r.json());
|
|
ScarfWidgets.renderDashboard(dashboardEl, d);
|
|
} catch (e) {
|
|
dashboardEl.textContent = "Could not load dashboard preview.";
|
|
}
|
|
try {
|
|
const md = await fetch("README.md").then(r => r.text());
|
|
readmeEl.innerHTML = ScarfWidgets.renderMarkdown(md);
|
|
} catch (e) {
|
|
readmeEl.textContent = "Could not load README.";
|
|
}
|
|
try {
|
|
// manifest.json may not exist for schema-less templates — that's
|
|
// fine, we just leave the config section empty.
|
|
const res = await fetch("manifest.json");
|
|
if (res.ok) {
|
|
const manifest = await res.json();
|
|
ScarfWidgets.renderConfigSchema(configEl, manifest.config);
|
|
}
|
|
} catch (e) {
|
|
// Silent — config-schema display is optional.
|
|
}
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|