Compact export toolbar and inline HTML assets
This commit is contained in:
+52
-20
@@ -14,6 +14,13 @@ function escapeHtml(value: string): string {
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
const icons = {
|
||||
download: `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v10m0 0 4-4m-4 4-4-4"/><path d="M5 18.5h14"/></svg>`,
|
||||
image: `<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="5" width="16" height="14" rx="2"/><path d="m7 16 4-4 3 3 2-2 3 3"/><circle cx="8.5" cy="8.5" r="1.2"/></svg>`,
|
||||
share: `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8.5 12.5 15.5 8.5M8.5 11.5 15.5 15.5"/><circle cx="6.5" cy="12" r="2.5"/><circle cx="17.5" cy="7.5" r="2.5"/><circle cx="17.5" cy="16.5" r="2.5"/></svg>`,
|
||||
chevron: `<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m8 10 4 4 4-4"/></svg>`
|
||||
};
|
||||
|
||||
function css(): string {
|
||||
return `
|
||||
:root {
|
||||
@@ -95,9 +102,19 @@ h1 { margin: 0; font-size: clamp(28px, 5vw, 44px); line-height: 1.12; letter-spa
|
||||
.content a[data-citation] { color: var(--muted); font-size: 0.92em; white-space: nowrap; text-decoration-thickness: 1px; text-underline-offset: 2px; }
|
||||
.content a[data-citation] img { width: 14px; height: 14px; max-width: 14px; max-height: 14px; border-radius: 3px; object-fit: cover; vertical-align: -2px; margin: 0 3px 0 4px; }
|
||||
.downloads { border-top: 1px solid var(--border); background: var(--soft); }
|
||||
.downloads-inner { max-width: 880px; margin: 0 auto; padding: 22px 20px; display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
|
||||
.button, select { min-height: 38px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); color: var(--text); padding: 8px 12px; font: inherit; font-size: 14px; text-decoration: none; }
|
||||
.button.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
|
||||
.downloads-inner { max-width: 880px; margin: 0 auto; padding: 14px 20px; display: flex; flex-wrap: nowrap; gap: 8px; align-items: center; overflow-x: auto; }
|
||||
.icon-button { width: 36px; height: 36px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); color: var(--text); padding: 0; display: inline-grid; place-items: center; flex: 0 0 auto; cursor: pointer; text-decoration: none; }
|
||||
.icon-button svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
||||
.icon-button:hover { background: color-mix(in srgb, var(--panel) 88%, var(--text) 12%); }
|
||||
.shot-control { display: inline-flex; align-items: stretch; flex: 0 0 auto; position: relative; }
|
||||
.shot-control .shot-main { border-radius: 8px 0 0 8px; border-right: 0; }
|
||||
.shot-control details { position: relative; }
|
||||
.shot-control summary { list-style: none; border-radius: 0 8px 8px 0; width: 28px; }
|
||||
.shot-control summary::-webkit-details-marker { display: none; }
|
||||
.shot-control summary svg { width: 15px; height: 15px; }
|
||||
.shot-menu { position: absolute; right: 0; bottom: calc(100% + 8px); z-index: 4; min-width: 150px; padding: 6px; border: 1px solid var(--border); border-radius: 8px; background: var(--panel); box-shadow: 0 12px 30px rgba(0,0,0,.12); }
|
||||
.shot-menu button { width: 100%; border: 0; border-radius: 6px; background: transparent; color: var(--text); padding: 8px 10px; font: inherit; font-size: 13px; text-align: left; cursor: pointer; }
|
||||
.shot-menu button:hover, .shot-menu button[data-current="true"] { background: var(--soft); }
|
||||
.source { max-width: 880px; margin: 0 auto; padding: 18px 20px 34px; color: var(--muted); font-size: 12px; }
|
||||
.source a { color: var(--muted); }
|
||||
.failure { max-width: 720px; margin: 0 auto; padding: 80px 20px; }
|
||||
@@ -113,14 +130,21 @@ h1 { margin: 0; font-size: clamp(28px, 5vw, 44px); line-height: 1.12; letter-spa
|
||||
.message.user .content { border-radius: 18px; padding: 9px 14px; }
|
||||
.avatar, .assistant-icon { width: 26px; height: 26px; }
|
||||
.content ul, .content ol { padding-left: 1.25em; }
|
||||
.downloads-inner { padding: 18px 16px; align-items: stretch; }
|
||||
.button, select { width: 100%; }
|
||||
.downloads-inner { padding: 12px 16px; }
|
||||
}`;
|
||||
}
|
||||
|
||||
function pageScript(slug: string): string {
|
||||
return `
|
||||
(function () {
|
||||
function currentVariant() {
|
||||
var device = matchMedia("(max-width: 640px)").matches ? "mobile" : "desktop";
|
||||
var theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
return device + "-" + theme;
|
||||
}
|
||||
function downloadShot(variant) {
|
||||
location.href = "/aishare/${slug}/screenshots/" + variant + ".png";
|
||||
}
|
||||
var params = new URLSearchParams(location.search);
|
||||
var theme = params.get("theme");
|
||||
if (theme === "light" || theme === "dark") document.documentElement.dataset.theme = theme;
|
||||
@@ -140,13 +164,15 @@ function pageScript(slug: string): string {
|
||||
setTimeout(function () { button.textContent = "Copy"; }, 1400);
|
||||
});
|
||||
});
|
||||
var form = document.querySelector("[data-shot-form]");
|
||||
if (form) form.addEventListener("submit", function (event) {
|
||||
event.preventDefault();
|
||||
var device = form.querySelector("[name=device]").value;
|
||||
var selectedTheme = form.querySelector("[name=theme]").value;
|
||||
if (selectedTheme === "system") selectedTheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
location.href = "/aishare/${slug}/screenshots/" + device + "-" + selectedTheme + ".png";
|
||||
var defaultShot = document.querySelector("[data-shot-current]");
|
||||
if (defaultShot) defaultShot.addEventListener("click", function () {
|
||||
downloadShot(currentVariant());
|
||||
});
|
||||
document.querySelectorAll("[data-shot-variant]").forEach(function (button) {
|
||||
if (button.dataset.shotVariant === currentVariant()) button.dataset.current = "true";
|
||||
button.addEventListener("click", function () {
|
||||
downloadShot(button.dataset.shotVariant);
|
||||
});
|
||||
});
|
||||
})();`;
|
||||
}
|
||||
@@ -189,14 +215,20 @@ export function renderConversationPage(slug: string, conversation: NormalizedCon
|
||||
</main>
|
||||
<section class="downloads">
|
||||
<div class="downloads-inner">
|
||||
<a class="button primary" href="/aishare/${slug}/download.zip">Download HTML ZIP</a>
|
||||
<a class="button" href="/aishare/${slug}/download.html">Download HTML</a>
|
||||
<form data-shot-form style="display: contents">
|
||||
<select name="device" aria-label="Screenshot size"><option value="desktop">Desktop</option><option value="mobile">Mobile</option></select>
|
||||
<select name="theme" aria-label="Screenshot theme"><option value="system">Current theme</option><option value="light">Light</option><option value="dark">Dark</option></select>
|
||||
<button class="button" type="submit">Download screenshot</button>
|
||||
</form>
|
||||
<button class="button" type="button" data-copy>Copy link</button>
|
||||
<a class="icon-button" href="/aishare/${slug}/download" aria-label="Download export" title="Download export">${icons.download}</a>
|
||||
<div class="shot-control">
|
||||
<button class="icon-button shot-main" type="button" data-shot-current aria-label="Download screenshot" title="Download screenshot">${icons.image}</button>
|
||||
<details>
|
||||
<summary class="icon-button" aria-label="Choose screenshot variant" title="Choose screenshot variant">${icons.chevron}</summary>
|
||||
<div class="shot-menu">
|
||||
<button type="button" data-shot-variant="desktop-light">Desktop light</button>
|
||||
<button type="button" data-shot-variant="desktop-dark">Desktop dark</button>
|
||||
<button type="button" data-shot-variant="mobile-light">Mobile light</button>
|
||||
<button type="button" data-shot-variant="mobile-dark">Mobile dark</button>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
<button class="icon-button" type="button" data-copy aria-label="Copy link" title="Copy link">${icons.share}</button>
|
||||
</div>
|
||||
</section>
|
||||
<footer class="source">
|
||||
|
||||
Reference in New Issue
Block a user