// lib/layout.js // layout(cfg) -- Phase-1 returns undefined; Phase-2 returns a real PluginLayout // (ARCHITECTURE.md 5.1, 7.10). When Phase 2 is enabled the plugin registers into // state.layouts["theme-builder"] (state.ts:1113-1117) and is selectable via // layout_by_role[role] === "theme-builder" (or user._attributes.layout). // // wrap() OWNS the document: it links the vendored stock Bootstrap CSS + the // theme overlay (var(--bs-*)) CSS, renders the page chrome from the active // layoutTree (renderTree.js), and threads Saltcorn's headersInHead/Body. Because // wrap() emits the token itself, headers(cfg)'s only_if suppresses the // Phase-1 overlay header for theme-builder-served requests (7.6). Sass is the // documented opt-in; the default engine is the overlay on stock Bootstrap. const { PLUGIN_NAME, CSS_ROUTE } = require("./constants"); const { isLayoutMode, activeHashHint, activeLayoutTree } = require("./cfgReaders"); const { renderTreeToHtml, renderToasts, esc } = require("./renderTree"); const { headersInHead, headersInBody } = require("@saltcorn/markup/layout_utils"); const { renderForm } = require("@saltcorn/markup"); const ASSET_BASE = `/plugins/public/${PLUGIN_NAME}`; const BOOTSTRAP_CSS = `${ASSET_BASE}/themeBootstrap.min.css`; const BOOTSTRAP_JS = `${ASSET_BASE}/themeBootstrap.bundle.min.js`; function headLinks(cfg, role, headers) { const v = activeHashHint(cfg, role); return `` + `` + `` + `` + headersInHead(headers || []); } function layout(cfg) { if (!isLayoutMode(cfg)) { return undefined; // falsy => not registered as a layout (layout mode off) } return { pluginName: PLUGIN_NAME, wrap: ({ title, body, brand, menu, alerts, headers, bodyClass, role, req, currentUrl } = {}) => { const chrome = renderTreeToHtml(activeLayoutTree(cfg, role), { title, body, brand, menu, alerts: alerts || [], role, req, currentUrl, }); return `
${headLinks(cfg, role, headers)}` + `