diff --git a/README.md b/README.md index e215bc4..2efff98 100644 --- a/README.md +++ b/README.md @@ -34,3 +34,61 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. + +## Export Bundle (Landing ZIP) + +The Builder can export a production-ready ZIP that contains: + +- index.html, styles.css, app.js +- assets/ hashed assets (optionally grouped by type) +- site.webmanifest, robots.txt +- Optional: sheets/ (Google Apps Script template) +- Metadata files: assets.json, assets.index.json + +### Export Options UI + +- Manifest + - Name, Short Name, Start URL, Display (dropdown), Theme Color (HEX only) +- Robots + - Disallow lines (textarea). Each line becomes a `Disallow:` rule. +- Google Sheets Template Mode + - Auto: include when `form.actionUrl` is empty + - Include: always include + - Exclude: never include + +### site.webmanifest and robots.txt + +- `site.webmanifest` is derived from page data; Export Options can override fields. +- `robots.txt` defaults to a permissive header and adds `Disallow` lines from UI. + +### Asset paths and grouping + +- AssetManager writes files to `assets/` with stable hash-based filenames. +- Path strategy can be configured: + - `flat` (default): `assets/.` + - `grouped`: `assets/images/...`, `assets/fonts/...`, `assets/other/...` + +### assets.json + +Structured metadata for all assets. Example fields per entry: + +- originalName, mimeType, size +- hash8, ext, zipPath +- integrity: `sha256-` (placeholder, base64 of bytes) +- group: `images|fonts|other` +- referencedAt: array of `local:` when referenced by the page + +### assets.index.json + +Simple index to quickly find assets by group: + +- images: string[] of zip paths +- fonts: string[] of zip paths +- other: string[] of zip paths + +### Google Sheets template (optional) + +- When included, ZIP contains: + - `sheets/Code.gs`: minimal Apps Script handler (Web App) + - `sheets/README.txt`: setup instructions +- Inclusion is controlled by `sheetsMode` (Auto/Include/Exclude) and `form.actionUrl` in Auto mode. diff --git a/components/BuilderClientPage.tsx b/components/BuilderClientPage.tsx index a0b1e5d..d28bdff 100644 --- a/components/BuilderClientPage.tsx +++ b/components/BuilderClientPage.tsx @@ -58,6 +58,8 @@ export default function BuilderClientPage() { const [manifestThemeColor, setManifestThemeColor] = useState('') const [robotsDisallow, setRobotsDisallow] = useState('') const [sheetsMode, setSheetsMode] = useState<'auto' | 'include' | 'exclude'>('auto') + // Font optimization: allow toggling Google Fonts preconnect links in exported HTML + const [fontPreconnect, setFontPreconnect] = useState(true) const [viewport, setViewport] = useState<'desktop' | 'mobile'>(() => { if (typeof window !== 'undefined') { try { @@ -178,7 +180,8 @@ export default function BuilderClientPage() { const w = window as unknown as { __exportPreview?: () => { html: string; css: string; js: string } } w.__exportPreview = () => { try { - return exportPage(pageSchema.parse(pageData), { assetManager: am ?? undefined }) + // Export preview with current optimization options + return exportPage(pageSchema.parse(pageData), { assetManager: am ?? undefined, fontPreconnect }) } catch { const fixed: Page = { ...pageData, @@ -188,12 +191,12 @@ export default function BuilderClientPage() { spamProtection: pageData.form?.spamProtection ?? { honeypotFieldName: '_hp', minSubmitSeconds: 2 }, }, } - return exportPage(pageSchema.parse(fixed), { assetManager: am ?? undefined }) + return exportPage(pageSchema.parse(fixed), { assetManager: am ?? undefined, fontPreconnect }) } } } } catch {} - }, [pageData, am]) + }, [pageData, am, fontPreconnect]) const doExport = useCallback(async () => { let valid = pageData as Page @@ -210,7 +213,8 @@ export default function BuilderClientPage() { } as Page valid = pageSchema.parse(fixed) } - const exported = exportPage(valid, { assetManager: am ?? undefined }) + // Respect font optimization toggle when exporting + const exported = exportPage(valid, { assetManager: am ?? undefined, fontPreconnect }) try { if (typeof window !== 'undefined') { const w = window as unknown as { __captureExport?: boolean; __lastExport?: { html: string; css: string; js: string } } @@ -247,7 +251,7 @@ export default function BuilderClientPage() { } const zipBytes = await buildZip({ html: exported.html, css: exported.css, js: exported.js, assets, extras }) download('landing.zip', zipBytes) - }, [pageData, am, manifestName, manifestShortName, manifestStartUrl, manifestDisplay, manifestThemeColor, robotsDisallow, sheetsMode]) + }, [pageData, am, manifestName, manifestShortName, manifestStartUrl, manifestDisplay, manifestThemeColor, robotsDisallow, sheetsMode, fontPreconnect]) return (
@@ -312,6 +316,17 @@ export default function BuilderClientPage() {

Export Options

+ {/* Font optimization toggle for Google Fonts preconnect */} +