mirror of
https://github.com/paperclipai/paperclip-docs.git
synced 2026-11-07 14:21:18 -10:00
Use shared docs layout for unlisted beta guide
This commit is contained in:
1 parent
a89f246c67
commit
4b94d17fbb
5 files changed
+43
-44
No files matched your search
@@ -59,8 +59,8 @@ The **documentation in this repository** is licensed under [Creative Commons Att
|
||||
|
||||
### Unlisted hosted beta guide
|
||||
|
||||
`docs/hosted-beta.md` is built at `/hosted-beta/` by `site/build-unlisted.mjs`.
|
||||
It reuses the article stylesheet without booting the navigation/search SPA.
|
||||
`docs/hosted-beta.md` is built at `/hosted-beta/` by `site/build-release.mjs`.
|
||||
It reuses the full docs shell and initializes page tools without adding the page to the navigation/search manifest.
|
||||
Keep it out of `site/content.json`: it is deliberately absent from navigation,
|
||||
search, homepage listings, previous/next links, and the sitemap. It has a
|
||||
`noindex` meta tag; this is a public page, not an access-controlled document.
|
||||
|
||||
@@ -10,8 +10,10 @@ try {
|
||||
const html = await fs.readFile(path.join(out, 'hosted-beta/index.html'), 'utf8');
|
||||
assert.match(html, /name="robots" content="noindex, follow"/);
|
||||
assert.equal((html.match(/<h1(?:\s[^>]*)?>/g) || []).length, 1);
|
||||
assert.ok(html.includes(`href="${base}styles.`));
|
||||
assert.ok(!html.includes('<script'), 'Unlisted page must not boot the manifest-based SPA');
|
||||
assert.ok(html.includes(`<base data-seo-base href="${base}"`));
|
||||
assert.ok(html.includes('data-unlisted-page="hosted-beta"'));
|
||||
assert.ok(html.includes('id="sb-sections" data-server-rendered="true"'));
|
||||
assert.ok(html.includes('id="breadcrumb"'));
|
||||
for (const file of ['content.json', 'index.html', 'sitemap.xml']) {
|
||||
assert.ok(!(await fs.readFile(path.join(out, file), 'utf8')).includes('hosted-beta'), file);
|
||||
}
|
||||
|
||||
+25
-4
@@ -385,8 +385,8 @@ function findHeadingTarget(article, targetHeading) {
|
||||
);
|
||||
}
|
||||
|
||||
function decorateHeadings(article, file) {
|
||||
const page = allPages.find(candidate => candidate.file === file);
|
||||
function decorateHeadings(article, file, unlistedPage = null) {
|
||||
const page = unlistedPage || allPages.find(candidate => candidate.file === file);
|
||||
if (!page) return;
|
||||
|
||||
const usedIds = new Set();
|
||||
@@ -888,6 +888,23 @@ async function init() {
|
||||
buildMobileDrawer();
|
||||
initSearch();
|
||||
|
||||
const unlistedArticle = document.querySelector('article[data-unlisted-page]');
|
||||
if (unlistedArticle) {
|
||||
const page = { file: 'hosted-beta.md', slug: 'hosted-beta', title: 'Hosted beta', sectionTitle: 'Closed beta', navTrail: ['Hosted beta'] };
|
||||
currentFile = page.file;
|
||||
showArticleView();
|
||||
insertMetaRow(unlistedArticle, page);
|
||||
decorateHeadings(unlistedArticle, page.file, page);
|
||||
buildToc(unlistedArticle, page.file, page);
|
||||
hideLoading();
|
||||
const heading = getCurrentHeadingRoute();
|
||||
if (heading) {
|
||||
const target = findHeadingTarget(unlistedArticle, heading);
|
||||
if (target) focusHeading(target);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const pathRoute = getPathRoute();
|
||||
const rawRoute = applyRedirect(pathRoute || getLegacyRoute());
|
||||
const initialRoute = parseRoute(rawRoute);
|
||||
@@ -1090,6 +1107,10 @@ function buildFlatList() {
|
||||
/* ─── Load page ─────────────────────────────────────────────────────────── */
|
||||
async function loadPage(file, targetHeading = null, historyMode = 'push', options = {}) {
|
||||
const page = allPages.find(candidate => candidate.file === file);
|
||||
if (document.querySelector('article[data-unlisted-page]') && page) {
|
||||
location.assign(targetHeading ? getPageHeadingUrl(page, targetHeading) : getPageUrl(page));
|
||||
return;
|
||||
}
|
||||
currentFile = file;
|
||||
showArticleView();
|
||||
setActiveState(file);
|
||||
@@ -1721,7 +1742,7 @@ function headingLabel(heading) {
|
||||
return clone.textContent.trim();
|
||||
}
|
||||
|
||||
function buildToc(article, file) {
|
||||
function buildToc(article, file, unlistedPage = null) {
|
||||
resetToc();
|
||||
|
||||
const metaRow = article.querySelector('.meta-row');
|
||||
@@ -1732,7 +1753,7 @@ function buildToc(article, file) {
|
||||
const headings = [...article.querySelectorAll('h2, h3')];
|
||||
if (headings.length < 2) return;
|
||||
|
||||
const page = allPages.find(candidate => candidate.file === file);
|
||||
const page = unlistedPage || allPages.find(candidate => candidate.file === file);
|
||||
|
||||
const CHEVRON_SVG = '<svg viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="m3 4.5 3 3 3-3"/></svg>';
|
||||
const TOC_SVG = '<svg class="toc-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M3 4h10M3 8h7M3 12h9"/></svg>';
|
||||
|
||||
+12
-2
@@ -8,7 +8,6 @@ import { fileURLToPath } from "node:url";
|
||||
import { createHash } from "node:crypto";
|
||||
import { transform } from "esbuild";
|
||||
import { marked } from "marked";
|
||||
import { buildUnlistedPages } from "./build-unlisted.mjs";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -1743,7 +1742,18 @@ async function main() {
|
||||
await copyDirRecursive(screenshotsSourceDir, screenshotTargetDir);
|
||||
}
|
||||
|
||||
await buildUnlistedPages({ repoRoot, outDir: options.outDir, basePath: options.basePath, stylesName });
|
||||
// Unlisted routes reuse the full docs shell, but never enter the discovery manifest.
|
||||
const betaMarkdown = await fs.readFile(path.join(docsRoot, "hosted-beta.md"), "utf8");
|
||||
const betaPage = { file: "hosted-beta.md", slug: "hosted-beta", title: "Hosted beta", navTrail: ["Hosted beta"] };
|
||||
const betaMetadata = { ...rootMetadata, title: "Welcome to the Paperclip hosted beta", description: markdownDescription(betaMarkdown), url: siteUrlForPath(options.siteUrl, options.basePath, "hosted-beta/"), page: betaPage };
|
||||
const betaHtml = buildStaticPageHtml(
|
||||
withStaticChrome(sourceIndex, releaseNav, options.basePath, sectionIconPaths, betaMetadata),
|
||||
betaMetadata, betaMarkdown, options.basePath, releaseStyles, null, null,
|
||||
).replace("</head>", '<meta name="robots" content="noindex, follow" /></head>')
|
||||
.replace('<article id="article">', '<article id="article" data-unlisted-page="hosted-beta">');
|
||||
await ensureDir(path.join(options.outDir, "hosted-beta"));
|
||||
await fs.writeFile(path.join(options.outDir, "hosted-beta/index.html"), betaHtml);
|
||||
await fs.writeFile(path.join(options.outDir, "hosted-beta.md"), betaMarkdown);
|
||||
|
||||
const missingNavTargets = [];
|
||||
for (const { page } of flattenNavPages(releaseNav)) {
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { marked } from 'marked';
|
||||
|
||||
// Deliberately separate from content.json: no sidebar, search, sitemap,
|
||||
// homepage listing, or previous/next entry. No SPA boot on these pages.
|
||||
export async function buildUnlistedPages({ repoRoot, outDir, basePath, stylesName }) {
|
||||
const base = basePath === 'auto' ? '../' : basePath;
|
||||
const markdown = await fs.readFile(path.join(repoRoot, 'docs/hosted-beta.md'), 'utf8');
|
||||
const html = `<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="robots" content="noindex, follow">
|
||||
<title>Welcome to the Paperclip hosted beta</title>
|
||||
<link rel="stylesheet" href="${base}${stylesName}">
|
||||
<style>
|
||||
body { display: block; overflow: auto; }
|
||||
.beta-shell { max-width: 824px; margin: 0 auto; padding: 32px; }
|
||||
.beta-header { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding-bottom: 24px; margin-bottom: 40px; border-bottom: 1px solid var(--stone); font-family: var(--font-body); }
|
||||
.beta-header a { color: var(--ink); text-decoration: none; }
|
||||
.beta-header a:first-child { font-weight: 650; font-size: 20px; }
|
||||
.beta-header a:last-child { font-size: 14px; text-decoration: underline; }
|
||||
@media (max-width: 600px) { .beta-shell { padding: 24px 20px; } .beta-header { margin-bottom: 28px; } }
|
||||
</style>
|
||||
</head>
|
||||
<body><div class="beta-shell">
|
||||
<header class="beta-header"><a href="${base}">Paperclip Docs</a><a href="https://my.paperclip.app/">Sign in to Paperclip</a></header>
|
||||
<main><article id="article">${marked.parse(markdown)}</article></main>
|
||||
</div></body></html>`;
|
||||
await fs.mkdir(path.join(outDir, 'hosted-beta'), { recursive: true });
|
||||
await fs.writeFile(path.join(outDir, 'hosted-beta/index.html'), html);
|
||||
}
|
||||
Reference in new issue
Block a user