init
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
package web
|
||||
|
||||
import "embed"
|
||||
|
||||
// Files contains the self-contained web UI and public schemas.
|
||||
//
|
||||
//go:embed templates/*.html static/*
|
||||
var Files embed.FS
|
||||
@@ -0,0 +1,126 @@
|
||||
(() => {
|
||||
const cfg = window.APP_CONFIG;
|
||||
if (!cfg) return;
|
||||
const base = cfg.baseURL.replace(/\/$/, '');
|
||||
const form = document.getElementById('generator-form');
|
||||
if (!form) return;
|
||||
const $ = id => document.getElementById(id);
|
||||
const presets = {
|
||||
'no-ai': {extent:'none', activities:'', review:'editorial'},
|
||||
research: {extent:'assisted', activities:'research', review:'editorial'},
|
||||
summary: {extent:'assisted', activities:'summarisation', review:'editorial'},
|
||||
full: {extent:'full', activities:'generation', review:'editorial'}
|
||||
};
|
||||
|
||||
function value(id) {
|
||||
const el = $(id);
|
||||
return el && !el.disabled ? el.value.trim() : '';
|
||||
}
|
||||
|
||||
function setArticleParams(params) {
|
||||
params.set('mode', 'article');
|
||||
const fields = {
|
||||
text: 'article-text',
|
||||
coverImage: 'article-cover-image',
|
||||
image: 'article-image',
|
||||
research: 'article-research',
|
||||
translation: 'article-translation',
|
||||
code: 'article-code'
|
||||
};
|
||||
Object.entries(fields).forEach(([name, id]) => {
|
||||
params.set(name + 'Extent', $(id).value);
|
||||
params.set(name + 'Review', $(`${id}-review`).value);
|
||||
});
|
||||
}
|
||||
|
||||
function update(applyPreset = false) {
|
||||
const mode = $('declaration-mode').value;
|
||||
const articleMode = mode === 'article';
|
||||
$('single-fields').hidden = articleMode;
|
||||
$('article-fields').hidden = !articleMode;
|
||||
|
||||
const preset = $('preset').value;
|
||||
if (!articleMode && applyPreset && presets[preset]) {
|
||||
$('extent').value = presets[preset].extent;
|
||||
$('activities').value = presets[preset].activities;
|
||||
$('review').value = presets[preset].review;
|
||||
}
|
||||
|
||||
const lang = $('lang').value;
|
||||
const locale = cfg.locales[lang] || cfg.locales.en;
|
||||
const assurance = $('assurance').value;
|
||||
const assuranceDescription = locale.text[`assurance_${assurance}_description`];
|
||||
if ($('assurance-description') && assuranceDescription) {
|
||||
$('assurance-description').textContent = assuranceDescription;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
if (articleMode) {
|
||||
setArticleParams(params);
|
||||
} else {
|
||||
if (preset) params.set('preset', preset);
|
||||
params.set('component', $('component').value);
|
||||
params.set('extent', $('extent').value);
|
||||
if ($('activities').value.trim()) params.set('activities', $('activities').value.trim());
|
||||
params.set('review', $('review').value);
|
||||
}
|
||||
params.set('lang', lang);
|
||||
params.set('assurance', assurance);
|
||||
if ($('subject').value.trim()) params.set('subject', $('subject').value.trim());
|
||||
|
||||
if (cfg.capabilities.custom_text) {
|
||||
if (value('custom-title')) params.set('customTitle', value('custom-title'));
|
||||
if (value('custom-description')) params.set('customDescription', value('custom-description'));
|
||||
}
|
||||
if (cfg.capabilities.custom_badge) {
|
||||
if (value('badge-label')) params.set('badgeLabel', value('badge-label'));
|
||||
if (value('badge-message')) params.set('badgeMessage', value('badge-message'));
|
||||
if (value('left-color')) params.set('leftColor', value('left-color'));
|
||||
if (value('right-color')) params.set('rightColor', value('right-color'));
|
||||
}
|
||||
|
||||
const declarationURL = `${base}/declaration?${params}`;
|
||||
const badgeParams = new URLSearchParams(params);
|
||||
badgeParams.set('theme', $('theme').value);
|
||||
badgeParams.set('link', declarationURL);
|
||||
const badgeURL = `${base}/v1/badge.svg?${badgeParams}`;
|
||||
const manifestURL = `${base}/v1/declaration.json?${params}`;
|
||||
const alt = articleMode ? ((cfg.locales[lang] || cfg.locales.en).text.badge_article || 'Article transparency') : 'AI usage disclosure';
|
||||
|
||||
$('preview-badge').src = badgeURL;
|
||||
$('preview-badge').alt = alt;
|
||||
$('preview-link').href = declarationURL;
|
||||
$('html-code').value = `<a href="${declarationURL}"><img src="${badgeURL}" alt="${alt}"></a>`;
|
||||
$('markdown-code').value = `[](${declarationURL})`;
|
||||
$('json-code').value = manifestURL;
|
||||
}
|
||||
|
||||
|
||||
document.querySelectorAll('#article-fields select[data-review]').forEach(extentSelect => {
|
||||
extentSelect.addEventListener('change', () => {
|
||||
const reviewSelect = $(extentSelect.dataset.review);
|
||||
if (!reviewSelect) return;
|
||||
if (extentSelect.value === 'none') {
|
||||
reviewSelect.value = 'none';
|
||||
} else if (reviewSelect.value === 'none') {
|
||||
reviewSelect.value = 'editorial';
|
||||
}
|
||||
update(false);
|
||||
});
|
||||
});
|
||||
|
||||
form.addEventListener('input', event => update(event.target.id === 'preset'));
|
||||
form.addEventListener('change', event => update(event.target.id === 'preset'));
|
||||
document.querySelector('.pro-panel')?.addEventListener('input', () => update(false));
|
||||
$('copy-html')?.addEventListener('click', async () => {
|
||||
await navigator.clipboard.writeText($('html-code').value);
|
||||
const locale = cfg.locales[$('lang').value] || cfg.locales.en;
|
||||
$('copy-html').textContent = locale.text.copied || 'Copied';
|
||||
setTimeout(() => { $('copy-html').textContent = locale.text.copy_html || 'Copy HTML'; }, 1600);
|
||||
});
|
||||
$('page-language')?.addEventListener('change', event => {
|
||||
const url = new URL(window.location.href);
|
||||
url.searchParams.set('lang', event.target.value);
|
||||
window.location.href = url.toString();
|
||||
});
|
||||
update(true);
|
||||
})();
|
||||
@@ -0,0 +1,22 @@
|
||||
(() => {
|
||||
document.getElementById('marketing-language')?.addEventListener('change', event => {
|
||||
const next = new URL(window.location.href);
|
||||
next.searchParams.set('lang', event.target.value);
|
||||
window.location.assign(next.toString());
|
||||
});
|
||||
|
||||
document.querySelectorAll('.copy-code').forEach(button => {
|
||||
button.addEventListener('click', async () => {
|
||||
const target = document.getElementById(button.dataset.copyTarget);
|
||||
if (!target) return;
|
||||
const original = button.dataset.label || button.textContent;
|
||||
try {
|
||||
await navigator.clipboard.writeText(target.textContent);
|
||||
button.textContent = button.dataset.copied || original;
|
||||
window.setTimeout(() => { button.textContent = original; }, 1200);
|
||||
} catch (_) {
|
||||
button.textContent = original;
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,60 @@
|
||||
{{define "declaration.html"}}
|
||||
<!doctype html>
|
||||
<html lang="{{.Lang}}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{{.Title}} · {{.Name}}</title>
|
||||
<meta name="description" content="{{.Description}}">
|
||||
<link rel="canonical" href="{{.CanonicalURL}}">
|
||||
<link rel="describedby" type="application/ld+json" href="{{.ManifestURL}}">
|
||||
{{range .LanguageLinks}}<link rel="alternate" hreflang="{{.Code}}" href="{{.AbsoluteURL}}">{{end}}
|
||||
<link rel="alternate" hreflang="x-default" href="{{.DefaultLanguageURL}}">
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
<script type="application/ld+json">{{.JSONLD}}</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header"><a class="brand" href="/?lang={{.Lang}}">{{.Name}}</a><nav><a href="/?lang={{.Lang}}">{{index .Text "nav_generator"}}</a><a href="{{.ManifestURL}}">JSON-LD</a></nav></header>
|
||||
<main class="declaration-page">
|
||||
<a class="back" href="/?lang={{.Lang}}">← {{index .Text "back"}}</a>
|
||||
<article class="declaration-card">
|
||||
<div class="declaration-toolbar">
|
||||
<p class="eyebrow">{{index .Text "declaration_eyebrow"}} · Schema {{.Declaration.SchemaVersion}}</p>
|
||||
<form class="declaration-language" method="get" action="/declaration">
|
||||
<label for="declaration-language">{{index .Text "field_language"}}</label>
|
||||
<select id="declaration-language" aria-label="{{index .Text "field_language"}}" onchange="window.location.assign(this.value)">
|
||||
{{range .LanguageLinks}}<option value="{{.URL}}" lang="{{.Code}}"{{if .Current}} selected{{end}}>{{.Name}}</option>{{end}}
|
||||
</select>
|
||||
<noscript><div class="language-link-list">{{range .LanguageLinks}}<a href="{{.URL}}" lang="{{.Code}}"{{if .Current}} aria-current="page"{{end}}>{{.Name}}</a>{{end}}</div></noscript>
|
||||
</form>
|
||||
</div>
|
||||
<h1>{{.Title}}</h1>
|
||||
<p class="lead">{{.Description}}</p>
|
||||
<p class="declaration-badge"><img src="{{.BadgeURL}}" alt="{{.Title}}"></p>
|
||||
|
||||
<section class="summary-section" aria-labelledby="summary-title">
|
||||
<h2 id="summary-title">{{index .Text "article_summary_heading"}}</h2>
|
||||
<div class="summary-copy">{{range .Summary}}<p>{{.}}</p>{{end}}</div>
|
||||
</section>
|
||||
|
||||
<section class="component-section" aria-labelledby="component-title">
|
||||
<h2 id="component-title">{{index .Text "article_table_heading"}}</h2>
|
||||
<div class="component-table-wrap">
|
||||
<table class="component-table">
|
||||
<colgroup><col class="component-col"><col class="extent-col"><col class="activity-col"><col class="review-col"></colgroup>
|
||||
<thead><tr><th scope="col">{{index .Text "table_component"}}</th><th scope="col">{{index .Text "table_extent"}}</th><th scope="col">{{index .Text "table_activities"}}</th><th scope="col">{{index .Text "table_review"}}</th></tr></thead>
|
||||
<tbody>{{range .ComponentRows}}<tr><th scope="row">{{.Name}}</th><td>{{.Extent}}</td><td>{{.Activities}}</td><td>{{.Review}}</td></tr>{{if .Note}}<tr class="component-note"><td colspan="4">{{.Note}}</td></tr>{{end}}{{end}}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<dl class="facts metadata-facts">
|
||||
{{range .Facts}}<div><dt>{{.Label}}</dt><dd>{{if .Link}}<a href="{{.Link}}">{{.Value}}</a>{{else}}{{.Value}}{{end}}</dd></div>{{end}}
|
||||
</dl>
|
||||
<div class="notice"><strong>{{index .Text "transparency_label"}}</strong> {{index .Text "transparency_text"}}</div>
|
||||
</article>
|
||||
</main>
|
||||
<footer><span><a href="{{.ManifestURL}}">{{index .Text "manifest"}}</a></span><span><a href="{{.ContactURL}}">{{index .Text "nav_background"}}</a></span></footer>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
@@ -0,0 +1,140 @@
|
||||
{{define "index.html"}}
|
||||
<!doctype html>
|
||||
<html lang="{{.Lang}}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{{.Name}}</title>
|
||||
<meta name="description" content="{{index .Text "meta_description"}}">
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<a class="brand" href="/?lang={{.Lang}}">{{.Name}}</a>
|
||||
<nav><a href="/product?lang={{.Lang}}">{{index .Text "nav_product"}}</a><a href="#generator">{{index .Text "nav_generator"}}</a><a href="#api">{{index .Text "nav_api"}}</a><a href="{{.ContactURL}}">{{index .Text "nav_background"}}</a></nav>
|
||||
<label class="language-switch"><span class="sr-only">{{index .Text "field_language"}}</span><select id="page-language">
|
||||
{{range .Languages}}<option value="{{.Code}}" {{if eq $.Lang .Code}}selected{{end}}>{{.Name}}</option>{{end}}
|
||||
</select></label>
|
||||
</header>
|
||||
<main>
|
||||
<section class="hero">
|
||||
<p class="eyebrow">{{index .Text "standard_eyebrow"}}</p>
|
||||
<h1>{{index .Text "hero_title"}}</h1>
|
||||
<p class="lead">{{index .Text "hero_lead"}}</p>
|
||||
<div class="badge-row">
|
||||
{{range .Presets}}<a href="/declaration?preset={{.Value}}&lang={{$.Lang}}"><img src="/badge/{{.Value}}.svg?theme=mono&link=auto&lang={{$.Lang}}" alt="{{.Label}}"></a>{{end}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="generator" class="panel">
|
||||
<div>
|
||||
<p class="eyebrow">{{index .Text "generator_eyebrow"}}</p>
|
||||
<h2>{{index .Text "generator_title"}}</h2>
|
||||
<p>{{index .Text "generator_intro"}}</p>
|
||||
</div>
|
||||
<form id="generator-form" class="generator-grid">
|
||||
<label class="wide">{{index .Text "field_mode"}}
|
||||
<select id="declaration-mode" name="mode"><option value="single">{{index .Text "mode_single"}}</option><option value="article">{{index .Text "mode_article"}}</option></select>
|
||||
</label>
|
||||
<div id="single-fields" class="form-subgrid wide">
|
||||
<label>{{index .Text "field_preset"}}
|
||||
<select id="preset" name="preset">
|
||||
{{range .Presets}}<option value="{{.Value}}">{{.Label}}</option>{{end}}
|
||||
<option value="">{{index .Text "option_custom"}}</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>{{index .Text "field_component"}}
|
||||
<select id="component" name="component">{{range .Components}}<option value="{{.Value}}">{{.Label}}</option>{{end}}</select>
|
||||
</label>
|
||||
<label>{{index .Text "field_extent"}}
|
||||
<select id="extent" name="extent">{{range .Extents}}<option value="{{.Value}}">{{.Label}}</option>{{end}}</select>
|
||||
</label>
|
||||
<label>{{index .Text "field_review"}}
|
||||
<select id="review" name="review">{{range .Reviews}}<option value="{{.Value}}">{{.Label}}</option>{{end}}</select>
|
||||
</label>
|
||||
<label class="wide">{{index .Text "field_activities"}}
|
||||
<input id="activities" name="activities" value="research" placeholder="research,summarisation">
|
||||
<small>{{index .Text "activities_help"}}</small>
|
||||
</label>
|
||||
</div>
|
||||
<fieldset id="article-fields" class="article-builder wide" hidden>
|
||||
<legend>{{index .Text "article_fields"}}</legend>
|
||||
<p>{{index .Text "article_help"}}</p>
|
||||
<div class="article-component-grid">
|
||||
<div class="article-component"><label>Text<select id="article-text" data-review="article-text-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-text-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
|
||||
<div class="article-component"><label>{{index .Text "fact_activities"}} / Recherche<select id="article-research" data-review="article-research-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "assisted"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-research-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "editorial"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
|
||||
<div class="article-component"><label>Cover / {{index .Text "fact_component"}}<select id="article-cover-image" data-review="article-cover-image-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-cover-image-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
|
||||
<div class="article-component"><label>Images<select id="article-image" data-review="article-image-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "full"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-image-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "editorial"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
|
||||
<div class="article-component"><label>Translation<select id="article-translation" data-review="article-translation-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-translation-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
|
||||
<div class="article-component"><label>Code<select id="article-code" data-review="article-code-review">{{range .Extents}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label><label>{{index .Text "field_review"}}<select id="article-code-review">{{range .Reviews}}<option value="{{.Value}}" {{if eq .Value "none"}}selected{{end}}>{{.Label}}</option>{{end}}</select></label></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<label>{{index .Text "field_language"}}
|
||||
<select id="lang" name="lang">{{range .Languages}}<option value="{{.Code}}" {{if eq $.Lang .Code}}selected{{end}}>{{.Name}}</option>{{end}}</select>
|
||||
</label>
|
||||
<label>{{index .Text "field_theme"}}
|
||||
<select id="theme" name="theme"><option value="mono">{{index .Text "theme_mono"}}</option><option value="color">{{index .Text "theme_color"}}</option></select>
|
||||
</label>
|
||||
<label class="wide">{{index .Text "field_assurance"}}
|
||||
<select id="assurance" name="assurance">{{range .Assurances}}<option value="{{.Value}}">{{.Label}}</option>{{end}}</select>
|
||||
<small id="assurance-description">{{index .Text "assurance_selfDeclared_description"}}</small>
|
||||
</label>
|
||||
<label class="wide">{{index .Text "field_subject"}}
|
||||
<input id="subject" name="subject" type="url" placeholder="https://example.org/article">
|
||||
</label>
|
||||
</form>
|
||||
|
||||
{{if or .CustomText .CustomBadge}}
|
||||
<section class="pro-panel enabled" aria-labelledby="pro-title">
|
||||
<div class="pro-heading">
|
||||
<div><p class="eyebrow">{{index .Text "pro_eyebrow"}}</p><h2 id="pro-title">{{index .Text "pro_title"}}</h2></div>
|
||||
<span class="edition-pill">{{.License.Edition}}</span>
|
||||
</div>
|
||||
<p>{{index .Text "pro_enabled"}}</p>
|
||||
<div class="generator-grid compact">
|
||||
<label>{{index .Text "custom_title"}}
|
||||
<input id="custom-title" maxlength="120" {{if not .CustomText}}disabled placeholder="{{index .Text "pro_required"}}"{{end}}>
|
||||
</label>
|
||||
<label>{{index .Text "custom_description"}}
|
||||
<input id="custom-description" maxlength="500" {{if not .CustomText}}disabled placeholder="{{index .Text "pro_required"}}"{{end}}>
|
||||
</label>
|
||||
<label>{{index .Text "custom_badge_label"}}
|
||||
<input id="badge-label" maxlength="40" {{if not .CustomBadge}}disabled placeholder="{{index .Text "pro_required"}}"{{end}}>
|
||||
</label>
|
||||
<label>{{index .Text "custom_badge_message"}}
|
||||
<input id="badge-message" maxlength="80" {{if not .CustomBadge}}disabled placeholder="{{index .Text "pro_required"}}"{{end}}>
|
||||
</label>
|
||||
<label>{{index .Text "custom_left_color"}}
|
||||
<input id="left-color" maxlength="7" pattern="#[0-9A-Fa-f]{6}" placeholder="#262626" {{if not .CustomBadge}}disabled{{end}}>
|
||||
</label>
|
||||
<label>{{index .Text "custom_right_color"}}
|
||||
<input id="right-color" maxlength="7" pattern="#[0-9A-Fa-f]{6}" placeholder="#2b6cb0" {{if not .CustomBadge}}disabled{{end}}>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
||||
|
||||
<div class="preview-card">
|
||||
<p class="eyebrow">{{index .Text "preview"}}</p>
|
||||
<a id="preview-link" href="#"><img id="preview-badge" alt="{{index .Text "preview"}}"></a>
|
||||
<div class="code-tabs">
|
||||
<label>HTML<textarea id="html-code" readonly></textarea></label>
|
||||
<label>Markdown<textarea id="markdown-code" readonly></textarea></label>
|
||||
<label>JSON-LD URL<textarea id="json-code" readonly></textarea></label>
|
||||
</div>
|
||||
<button id="copy-html" type="button">{{index .Text "copy_html"}}</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="api" class="docs-grid">
|
||||
<article><p class="eyebrow">SVG</p><h2>{{index .Text "api_badge_title"}}</h2><code>{{.BaseURL}}/badge/research.svg?lang={{.Lang}}</code><p>{{index .Text "api_badge_desc"}}</p></article>
|
||||
<article><p class="eyebrow">JSON-LD</p><h2>{{index .Text "api_manifest_title"}}</h2><code>{{.BaseURL}}/v1/declaration.json?preset=research&lang={{.Lang}}</code><p>{{index .Text "api_manifest_desc"}}</p></article>
|
||||
<article><p class="eyebrow">HA</p><h2>{{index .Text "api_stateless_title"}}</h2><p>{{index .Text "api_stateless_desc"}}</p></article>
|
||||
</section>
|
||||
</main>
|
||||
<footer><span>AI Usage Disclosure · App 1.6.1 · Schema 1.1 · {{.License.Edition}}</span><span>{{index .Text "footer_no_legal"}}</span></footer>
|
||||
<script>window.APP_CONFIG={{.AppConfig}};</script>
|
||||
<script src="/static/app.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
@@ -0,0 +1,126 @@
|
||||
{{define "marketing.html"}}
|
||||
<!doctype html>
|
||||
<html lang="{{.Lang}}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{{.Name}} · {{.Marketing.NavFeatures}}</title>
|
||||
<meta name="description" content="{{.Marketing.MetaDescription}}">
|
||||
<link rel="canonical" href="{{.BaseURL}}/product?lang={{.Lang}}">
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body class="marketing-page">
|
||||
<header class="site-header marketing-header">
|
||||
<a class="brand" href="/product?lang={{.Lang}}">{{.Name}}</a>
|
||||
<nav>
|
||||
<a href="#features">{{.Marketing.NavFeatures}}</a>
|
||||
<a href="#install">{{.Marketing.NavInstall}}</a>
|
||||
<a href="/?lang={{.Lang}}#generator">{{.Marketing.NavGenerator}}</a>
|
||||
<a href="{{.ContactURL}}">{{.Marketing.NavBackground}}</a>
|
||||
</nav>
|
||||
<label class="language-switch"><span class="sr-only">{{.Marketing.LanguageLabel}}</span><select id="marketing-language">
|
||||
{{range .Languages}}<option value="{{.Code}}" {{if eq $.Lang .Code}}selected{{end}}>{{.Name}}</option>{{end}}
|
||||
</select></label>
|
||||
</header>
|
||||
|
||||
<main class="marketing-main">
|
||||
<section class="marketing-hero">
|
||||
<div class="marketing-hero-copy">
|
||||
<p class="eyebrow">{{.Marketing.HeroEyebrow}}</p>
|
||||
<h1>{{.Marketing.HeroTitle}}</h1>
|
||||
<p class="lead">{{.Marketing.HeroLead}}</p>
|
||||
<div class="cta-row">
|
||||
<a class="button primary" href="/?lang={{.Lang}}#generator">{{.Marketing.PrimaryCTA}}</a>
|
||||
</div>
|
||||
<ul class="proof-list" aria-label="Product highlights">
|
||||
{{range .Marketing.Proof}}<li>{{.}}</li>{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="product-demo" aria-label="Badge examples">
|
||||
<div class="demo-window">
|
||||
<div class="demo-bar"><span></span><span></span><span></span></div>
|
||||
<div class="demo-content">
|
||||
<p class="demo-url">{{.BaseURL}}/badge/research.svg</p>
|
||||
<img src="/badge/research.svg?lang={{.Lang}}&theme=color" alt="Research badge">
|
||||
<img src="/badge/summary.svg?lang={{.Lang}}&theme=mono" alt="Summary badge">
|
||||
<img src="/badge/full.svg?lang={{.Lang}}&theme=color" alt="Full generation badge">
|
||||
<pre><code>{
|
||||
"@type": "AIUsageDeclaration",
|
||||
"schemaVersion": "1.1",
|
||||
"assurance": "selfDeclared"
|
||||
}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="features" class="marketing-section">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">{{.Marketing.FeaturesEyebrow}}</p>
|
||||
<h2>{{.Marketing.FeaturesTitle}}</h2>
|
||||
<p>{{.Marketing.FeaturesLead}}</p>
|
||||
</div>
|
||||
<div class="feature-grid">
|
||||
{{range .Marketing.Features}}
|
||||
<article class="feature-card">
|
||||
<p class="feature-kicker">{{.Kicker}}</p>
|
||||
<h3>{{.Title}}</h3>
|
||||
<p>{{.Description}}</p>
|
||||
</article>
|
||||
{{end}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="install" class="marketing-section install-section">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">{{.Marketing.InstallEyebrow}}</p>
|
||||
<h2>{{.Marketing.InstallTitle}}</h2>
|
||||
<p>{{.Marketing.InstallLead}}</p>
|
||||
</div>
|
||||
<div class="install-list">
|
||||
{{range $index, $method := .Marketing.InstallMethods}}
|
||||
<details class="install-method" {{if eq $index 0}}open{{end}}>
|
||||
<summary><span><strong>{{$method.Title}}</strong><small>{{$method.Summary}}</small></span><span aria-hidden="true">+</span></summary>
|
||||
<div class="code-block">
|
||||
<button type="button" class="copy-code" data-copy-target="install-{{$method.ID}}" data-label="{{$.Marketing.CopyLabel}}" data-copied="{{$.Marketing.CopiedLabel}}">{{$.Marketing.CopyLabel}}</button>
|
||||
<pre id="install-{{$method.ID}}"><code>{{$method.Code}}</code></pre>
|
||||
</div>
|
||||
</details>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<div class="config-panel">
|
||||
<h3>{{.Marketing.ConfigTitle}}</h3>
|
||||
<div class="comparison-wrap">
|
||||
<table class="config-table">
|
||||
<thead><tr><th>{{.Marketing.ConfigVariable}}</th><th>{{.Marketing.ConfigDefault}}</th><th>{{.Marketing.ConfigMeaning}}</th></tr></thead>
|
||||
<tbody>{{range .Marketing.Config}}<tr><td><code>{{.Name}}</code></td><td><code>{{.Default}}</code></td><td>{{.Description}}</td></tr>{{end}}</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="marketing-section faq-section">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">{{.Marketing.FAQEyebrow}}</p>
|
||||
<h2>{{.Marketing.FAQTitle}}</h2>
|
||||
</div>
|
||||
<div class="faq-grid">
|
||||
{{range .Marketing.FAQs}}<details><summary>{{.Question}}</summary><p>{{.Answer}}</p></details>{{end}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="final-cta">
|
||||
<h2>{{.Marketing.FinalTitle}}</h2>
|
||||
<p>{{.Marketing.FinalLead}}</p>
|
||||
<div class="cta-row">
|
||||
<a class="button primary" href="/?lang={{.Lang}}#generator">{{.Marketing.FinalPrimary}}</a>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer><span>{{.Marketing.Footer}}</span><span>App 1.6 · Schema 1.1 · {{.License.Edition}}</span></footer>
|
||||
<script src="/static/marketing.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
{{end}}
|
||||
Reference in New Issue
Block a user