Fix screenshot assets and code blocks

This commit is contained in:
Codex
2026-07-08 05:46:12 +00:00
parent 743c1332fd
commit 02419f7161
3 changed files with 51 additions and 4 deletions
+28 -1
View File
@@ -56,7 +56,7 @@ async function downloadAsset(assetUrl: string, slug: string): Promise<string | n
const filename = safeAssetName(assetUrl, contentType, bytes);
await fs.ensureDir(artifactAssetsDir(slug));
await fs.writeFile(path.join(artifactAssetsDir(slug), filename), bytes);
return `/aishare/${slug}/assets/${filename}`;
return `assets/${filename}`;
}
async function localizeAssets(conversation: NormalizedConversation, slug: string): Promise<{ failures: string[] }> {
@@ -103,6 +103,33 @@ function normalizeCopiedHtml(html: string): string {
$("script, style, iframe, object, embed, form, button, textarea, input, nav").remove();
function escapeCode(value: string): string {
return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
$("pre")
.toArray()
.filter((pre) => $(pre).parents("pre").length === 0)
.forEach((pre) => {
const block = $(pre);
const nestedCode = block.find("pre code").last();
const directCode = block.children("code").first();
const codeNode = nestedCode.length ? nestedCode : directCode.length ? directCode : block.find("code").last();
const code = codeNode.length ? codeNode.text() : block.text();
if (!code.trim()) return;
const labelProbe = block.clone();
labelProbe.find("pre, code").remove();
const label = labelProbe.text().replace(/\s+/g, " ").trim();
const language = label.length > 0 && label.length <= 24 ? label : "";
block.replaceWith(
`<div class="code-block">` +
`<div class="code-header"><span>${escapeCode(language || "code")}</span><button type="button" data-copy-code>Copy</button></div>` +
`<pre><code>${escapeCode(code.trimEnd())}</code></pre>` +
`</div>`
);
});
$("a").each((_, anchor) => {
const link = $(anchor);
const decorativeImages = link.find("img").filter((_, image) => {
+16 -1
View File
@@ -80,9 +80,14 @@ h1 { margin: 0; font-size: clamp(28px, 5vw, 44px); line-height: 1.12; letter-spa
.content li > p { margin: 0.2em 0; }
.content li > ul, .content li > ol { margin: 0.35em 0 0.45em; padding-left: 1.25em; }
.content strong { font-weight: 700; }
.content pre { margin: 1em 0; overflow: auto; padding: 14px 16px; border-radius: 8px; background: var(--code-bg); color: var(--code); font-size: 14px; line-height: 1.55; }
.content pre { margin: 1em 0; overflow: auto; padding: 14px 16px; border-radius: 8px; background: var(--code-bg); color: var(--code); font-size: 14px; line-height: 1.55; white-space: pre; overflow-wrap: normal; word-break: normal; }
.content code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Noto Sans Mono", monospace; }
.content :not(pre) > code { background: var(--code-bg); border-radius: 5px; padding: 0.1em 0.32em; }
.code-block { margin: 1em 0; border-radius: 8px; background: var(--code-bg); overflow: hidden; }
.code-block .code-header { min-height: 34px; display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 10px 6px 14px; color: var(--muted); font-size: 12px; border-bottom: 1px solid var(--border); }
.code-block .code-header span:empty::before { content: "code"; }
.code-block [data-copy-code] { border: 0; background: transparent; color: var(--muted); font: inherit; font-size: 12px; padding: 4px 6px; cursor: pointer; }
.code-block pre { margin: 0; border-radius: 0; background: transparent; }
.content table { width: 100%; border-collapse: collapse; margin: 1em 0; display: block; overflow-x: auto; }
.content th, .content td { border: 1px solid var(--border); padding: 8px 10px; text-align: left; vertical-align: top; }
.content blockquote { border-left: 3px solid var(--border); margin: 1em 0; padding-left: 14px; color: var(--muted); }
@@ -125,6 +130,16 @@ function pageScript(slug: string): string {
copy.textContent = "Copied";
setTimeout(function () { copy.textContent = "Copy link"; }, 1400);
});
document.querySelectorAll("[data-copy-code]").forEach(function (button) {
button.addEventListener("click", async function () {
var block = button.closest(".code-block");
var code = block && block.querySelector("code");
if (!code) return;
await navigator.clipboard.writeText(code.textContent || "");
button.textContent = "Copied";
setTimeout(function () { button.textContent = "Copy"; }, 1400);
});
});
var form = document.querySelector("[data-shot-form]");
if (form) form.addEventListener("submit", function (event) {
event.preventDefault();
+7 -2
View File
@@ -20,6 +20,7 @@ import {
const app = express();
app.disable("x-powered-by");
app.enable("strict routing");
app.use(express.json({ limit: "1mb" }));
function requireAdmin(req: Request, res: Response, next: NextFunction): void {
@@ -49,7 +50,7 @@ function publicCapture(row: ReturnType<typeof store.getCapture>) {
createdAt: row.created_at,
updatedAt: row.updated_at,
capturedAt: row.captured_at,
publicUrl: row.slug ? `${config.baseUrl}/${row.slug}` : null
publicUrl: row.slug ? `${config.baseUrl}/${row.slug}/` : null
};
}
@@ -177,7 +178,7 @@ app.get(`${config.basePath}/:slug/download.zip`, async (req, res, next) => {
}
});
app.get(`${config.basePath}/:slug`, async (req, res) => {
app.get(`${config.basePath}/:slug/`, async (req, res) => {
const row = store.getCaptureBySlug(req.params.slug);
if (!row) {
res.status(404).send("Not found");
@@ -193,6 +194,10 @@ app.get(`${config.basePath}/:slug`, async (req, res) => {
.send(`<!doctype html><meta name="robots" content="noindex"><title>${row.title || "AI Share"}</title><p>${row.error_public || `Capture is ${row.status}.`}</p>`);
});
app.get(`${config.basePath}/:slug`, (req, res) => {
res.redirect(308, `${config.basePath}/${req.params.slug}/`);
});
app.get(config.basePath, (_req, res) => {
res.redirect(301, `${config.basePath}/admin`);
});