');
const filesCount = filesIndex && body.includes('id="files"') ? filesIndex.files.length : 0;
// 古い記録から今の話への道(2026-10-28)。検索から古い記録に着いた人が、そこで止まらないように。
// 公開から60日より前の記録に出す。同じシリーズに60日以内の記録があればその最新へ(「{シリーズ名}の最新」)、なければサイト全体の最新の記録へ(「いま進めている記録」)
const DAY_MS = 864e5, OLD_DAYS = 60;
const fresh = (c) => Date.now() - new Date(c.published).getTime() < OLD_DAYS * DAY_MS;
const isOld = !isEnglish && Date.now() - new Date(r.published).getTime() >= OLD_DAYS * DAY_MS;
const seriesNow = isOld ? (await allCards()).find((c) => c.series === r.series && c.slug !== r.slug && !/-en$/.test(c.slug) && fresh(c)) : null;
const siteNow = isOld && !seriesNow ? (await allCards()).find((c) => !/-en$/.test(c.slug) && c.slug !== r.slug) : null;
const nowCard = seriesNow || siteNow;
// AIで読む(2026-11-01):読み手が使っている AI を開き、この記事の全文(/record/{slug}.md。src/lib/fulltext.ts)を読ませる質問を入れた状態にする。
// ChatGPT・Claude・Perplexity は住所の後ろの q= で入力欄に入る。Gemini は入れる方法がないので、質問文をコピーしてから開く。
// 前回選んだ AI を見た人のブラウザに覚え(p2m-site-ai)、次から一番上に出す。押した数は /api/ev-stats(場所 ai)で見る
const mdUrl = `${SITE}${fullTextPath(r.slug)}`;
// 全文ページの住所に ?via=(どの AI から来たか)を付ける。AI が読みに来たとき、ボタン経由だと worker/index.js が分けて数える(/api/cr-stats の「AIで読む経由」)
const aiPromptFor = (via) => {
const md = `${mdUrl}?via=${via}`;
return isEnglish
? `Please read this article and answer my questions.\nArticle: ${url}\nFull text (including code and logs): ${md}\n\nFirst, briefly summarize what was done and what was found. Then tell me what I should check to reproduce it in my own environment.`
: `この記事を読んで、質問に答えてください。\n記事: ${url}\n全文(コード・実行ログを含む): ${md}\n\nまず、何をして何が分かったのかを短くまとめてください。そのあと、私が同じことを自分の環境で試すときに、確かめることを教えてください。`;
};
const aiQ = (via) => encodeURIComponent(aiPromptFor(via));
const AI_TARGETS = [
{ id: 'chatgpt', name: 'ChatGPT', href: `https://chatgpt.com/?q=${aiQ('chatgpt')}` },
{ id: 'claude', name: 'Claude', href: `https://claude.ai/new?q=${aiQ('claude')}` },
{ id: 'perplexity', name: 'Perplexity', href: `https://www.perplexity.ai/search?q=${aiQ('perplexity')}` },
{ id: 'gemini', name: 'Gemini', href: 'https://gemini.google.com/app', copy: aiPromptFor('gemini') },
];
const aiCopyPrompt = aiPromptFor('copy');
const ym = (iso) => { const d = new Date(new Date(iso).getTime() + 9 * 3600e3); return `${d.getUTCFullYear()}年${d.getUTCMonth() + 1}月`; };
const codeLang = { csharp: 'C#', json: 'JSON', yaml: 'YAML', markdown: 'Markdown', python: 'Python', text: 'Text' };
const jsonLd = [
{
'@context': 'https://schema.org', '@type': 'BlogPosting',
headline: r.title, description, url, mainEntityOfPage: url,
inLanguage: isEn ? 'en' : 'ja',
datePublished: r.published, dateModified: rec?.updated || r.published,
image: ogImage ? [abs(ogImage)] : undefined,
articleSection: r.series, keywords: r.tags?.length ? r.tags.join(', ') : undefined,
author: AUTHOR, publisher: PUBLISHER,
isPartOf: { '@type': 'Blog', name: SITE_NAME, url: SITE + '/' },
hasPart: filesIndex ? [
{ '@type': 'DigitalDocument', name: `${r.day} 記事の全文(${filesIndex.article_full_type === 'text/html' ? 'HTML' : 'Markdown'})`, encodingFormat: filesIndex.article_full_type || 'text/markdown', url: abs(filesIndex.article_full || filesIndex.article_full_markdown) },
...filesIndex.files.map((f) => f.lang === 'csharp' || f.lang === 'yaml' || /\.(cs|prefab|asset|mat)\.txt$/.test(f.file)
? { '@type': 'SoftwareSourceCode', name: f.subheading || f.heading || f.file, programmingLanguage: codeLang[f.lang] || f.lang, url: abs(f.url) }
: { '@type': 'Dataset', name: [f.heading, f.subheading].filter(Boolean).join(' / ') || f.file, url: abs(f.url) }),
] : undefined,
},
{
'@context': 'https://schema.org', '@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: SITE_NAME, item: SITE + '/' },
...(seriesPage ? [{ '@type': 'ListItem', position: 2, name: seriesPage[1].title, item: `${SITE}/${seriesPage[0]}` }] : []),
{ '@type': 'ListItem', position: seriesPage ? 3 : 2, name: r.title, item: url },
],
},
];
---
{r.thumb ? : r.cover &&
}
{r.title}
{nowCard && (
{ym(r.published)}の記録です。{seriesNow ? `${seriesName}の最新` : 'いま進めている記録'}
{nowCard.day} {nowCard.title}arrow_forward
)}
{filesCount > 0 && (
この記事の素材(コード・ログ・データ {filesCount}ファイル・まとめてダウンロードあり)arrow_downward
)}
{isEnglish ? 'Read with AI' : 'AIで読む'}
{isEnglish ? 'Ask ChatGPT, Claude and others about this article' : 'ChatGPT・Claude などで、この記事に質問できます'}
expand_more
{kind === 'studio'
?
: body ? :
}
{showConsult && (
)}
{workNote && (
)}
{(newer || older) && (
)}
{nearby.length > 0 && (
)}
{filesIndex && (
)}