[{"data":1,"prerenderedAt":53},["ShallowReactive",2],{"doc-pages":3},{"html":4,"headings":5},"\u003Cp>Build landing pages in a visual editor and HelpBites hosts them, or keep your\ncopy, images and components here and read them from your own Vue, JavaScript\nor Swift app. Both work off the same content: a page you design in the editor is\nalso JSON in the delivery API, and a component you write in code shows in the\neditor like the built-in ones. An assistant can write the page, its copy and new\ncomponents for you, from a few questions or your GitHub repositories.\u003C\u002Fp>\n\u003Cp>Everything here is in every plan. Files count against the workspace's storage,\nand the assistant spends AI credits like the email designer.\u003C\u002Fp>\n\u003Ch2 id=\"how-content-is-shaped\">How content is shaped\u003Ca class=\"heading-anchor\" href=\"#how-content-is-shaped\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>Components\u003C\u002Fstrong> are the building blocks. Each has a \u003Ccode>key\u003C\u002Fcode> (\u003Ccode>hero\u003C\u002Fcode>, \u003Ccode>pricing\u003C\u002Fcode>),\na list of \u003Cstrong>fields\u003C\u002Fstrong> (its schema) and, optionally, an HTML and CSS \u003Cstrong>template\u003C\u002Fstrong>\nthat hosted pages render. A component without a template is data only: your own\napp renders it. Components are either \u003Cstrong>entry types\u003C\u002Fstrong> (\u003Ccode>root\u003C\u002Fcode>: a page, a blog\npost) or \u003Cstrong>blocks\u003C\u002Fstrong> (\u003Ccode>nestable\u003C\u002Fcode>: they go inside another component), or both.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Entries\u003C\u002Fstrong> are made of one entry type: a page, or a type you define. Each has\na \u003Ccode>slug\u003C\u002Fcode> (\u003Ccode>home\u003C\u002Fcode>, \u003Ccode>pricing\u003C\u002Fcode>, \u003Ccode>blog\u002Fhello-world\u003C\u002Fcode>; \u003Ccode>\u002F\u003C\u002Fcode> separates folders) and a\ncontent tree in the same shape as Storyblok's:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-json\">\u003Ccode>{\n  \u003Cspan class=\"shj-syn-var\">\"_uid\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"a1b2c3d4e5f6\"\u003C\u002Fspan>,\n  \u003Cspan class=\"shj-syn-var\">\"component\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"page\"\u003C\u002Fspan>,\n  \u003Cspan class=\"shj-syn-var\">\"title\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"Acme: invoices in one click\"\u003C\u002Fspan>,\n  \u003Cspan class=\"shj-syn-var\">\"body\"\u003C\u002Fspan>: [\n    { \u003Cspan class=\"shj-syn-var\">\"_uid\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"f6e5d4c3b2a1\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"component\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"hero\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"headline\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"Invoices in one click\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"primaryLabel\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"Start free\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"primaryUrl\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"\u002Fsignup\"\u003C\u002Fspan> },\n    { \u003Cspan class=\"shj-syn-var\">\"_uid\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"0a1b2c3d4e5f\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"component\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"features\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"items\"\u003C\u002Fspan>: [\n      { \u003Cspan class=\"shj-syn-var\">\"_uid\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"5f4e3d2c1b0a\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"component\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"feature\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"icon\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"⚡\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"title\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"Fast\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"body\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"Sent in seconds.\"\u003C\u002Fspan> }\n    ] }\n  ]\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>A \u003Ccode>blocks\u003C\u002Fcode> field holds more blocks, to any depth (eight levels at most). Every\nblock has a \u003Ccode>_uid\u003C\u002Fcode>; \u003Ccode>_anim\u003C\u002Fcode> holds its animation (\u003Ca href=\"#animations\">Animations\u003C\u002Fa>).\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Drafts and versions.\u003C\u002Fstrong> Editing changes the working copy. Publishing freezes it\ninto the next version, and visitors and the delivery API get that one until you\npublish again. Unpublishing takes it offline and keeps the working copy.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Files\u003C\u002Fstrong> (images, SVG, MP4, WebM and PDF, up to 20 MB each) live in the\nproject's file library. An asset field stores \u003Ccode>{ id, url, alt, mime, width, height }\u003C\u002Fcode>, so content never needs a second request to show a picture.\u003C\u002Fp>\n\u003Ch2 id=\"templates-and-the-editor\">Templates and the editor\u003Ca class=\"heading-anchor\" href=\"#templates-and-the-editor\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Project → Pages → \u003Cstrong>New page\u003C\u002Fstrong> starts from a template (SaaS launch, Mobile app,\nWaitlist, Developer tool, Design tool, Blank), with the project's name in the\ncopy, or from the assistant's wizard (\u003Ca href=\"#the-assistant\">The assistant\u003C\u002Fa>).\u003C\u002Fp>\n\u003Cp>The editor has the assistant and \u003Cstrong>Layers\u003C\u002Fstrong> on the left (drag a row onto another\nto move it, into any list that takes it), the live page in the middle (click a\nblock to select it, drag a top-level block by its handle) and the \u003Cstrong>inspector\u003C\u002Fstrong>\non the right: the selected block's fields and animation, or, with the page\nselected, its name, slug, link preview fields and style. Every change autosaves\nand is one undo step; \u003Cstrong>Play\u003C\u002Fstrong> runs the page's animations once.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Style\u003C\u002Fstrong> applies to the whole page: an accent (the project's when empty),\nlight, dark or follow-the-visitor mode, corner radius, heading and body fonts\n(any Google Fonts family, or a font Apple and Windows devices have, such as\nAvenir) and content width.\u003C\u002Fp>\n\u003Ch2 id=\"components-in-code\">Components in code\u003Ca class=\"heading-anchor\" href=\"#components-in-code\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Project → Pages → Components lists the built-ins (read-only; \u003Cstrong>Duplicate\u003C\u002Fstrong> makes\nan editable copy) and your own. The native app has the same tab and editor, and the same\nassistant and wizard, without the live preview. A component's template is HTML and CSS:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-html\">\u003Ccode>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">section\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">class\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"banner\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\n  {{#icon}}\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">span\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">class\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"banner__icon\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>{{@icon}}\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">span\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>{{\u002Ficon}}\n  \u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">p\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>{{@text}}\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">p\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\n  {{#link}}\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">a\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">class\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"hb-btn\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">href\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"{{@link}}\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>{{@label}}\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">a\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>{{\u002Flink}}\n  {{^link}}\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">small\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>No link yet.\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">small\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>{{\u002Flink}}\n\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">section\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Syntax\u003C\u002Fth>\n\u003Cth>Fills in\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>{{@field}}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The field's value, escaped. A \u003Ccode>markdown\u003C\u002Fcode> field becomes HTML (raw HTML in it is dropped), a \u003Ccode>url\u003C\u002Fcode> keeps only web, mail, phone and same-site links, a \u003Ccode>color\u003C\u002Fcode> only CSS colours, an \u003Ccode>icon\u003C\u002Fcode> becomes an inline SVG, a \u003Ccode>blocks\u003C\u002Fcode> field its children's HTML.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>{{@image.alt}}\u003C\u002Fcode>, \u003Ccode>.width\u003C\u002Fcode>, \u003Ccode>.height\u003C\u002Fcode>, \u003Ccode>.mime\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>An asset's details (\u003Ccode>{{@image}}\u003C\u002Fcode> is its URL).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>{{#field}}…{{\u002Ffield}}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Only when the field is set (non-empty text, on, any blocks, a file).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>{{^field}}…{{\u002Ffield}}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Only when it is not.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>{{$pk}}\u003C\u002Fcode>, \u003Ccode>{{$host}}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The project's public key and the HelpBites address, for forms.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>{{$uid}}\u003C\u002Fcode>, \u003Ccode>{{$parent}}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The block's \u003Ccode>_uid\u003C\u002Fcode> and its parent's: radio groups, exclusive \u003Ccode>&lt;details&gt;\u003C\u002Fcode>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>Field types: \u003Ccode>text\u003C\u002Fcode>, \u003Ccode>textarea\u003C\u002Fcode>, \u003Ccode>markdown\u003C\u002Fcode>, \u003Ccode>number\u003C\u002Fcode>, \u003Ccode>boolean\u003C\u002Fcode>, \u003Ccode>color\u003C\u002Fcode>,\n\u003Ccode>url\u003C\u002Fcode>, \u003Ccode>date\u003C\u002Fcode>, \u003Ccode>select\u003C\u002Fcode> (with \u003Ccode>options\u003C\u002Fcode>), \u003Ccode>icon\u003C\u002Fcode> (a Lucide icon from the\nbuilt-in set; \u003Ccode>options\u003C\u002Fcode> narrows it), \u003Ccode>asset\u003C\u002Fcode> and \u003Ccode>blocks\u003C\u002Fcode> (\u003Ccode>allow\u003C\u002Fcode> lists the\ncomponent keys it takes; empty takes any block). Give every field a default so\nthe component looks right when inserted.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Templates never run anything.\u003C\u002Fstrong> Hosted pages share HelpBites' address, so\n\u003Ccode>&lt;script&gt;\u003C\u002Fcode>, \u003Ccode>on…\u003C\u002Fcode> attributes, \u003Ccode>javascript:\u003C\u002Fcode> links, \u003Ccode>&lt;object&gt;\u003C\u002Fcode>, \u003Ccode>&lt;embed&gt;\u003C\u002Fcode>,\n\u003Ccode>&lt;base&gt;\u003C\u002Fcode>, \u003Ccode>&lt;link&gt;\u003C\u002Fcode> and \u003Ccode>srcdoc\u003C\u002Fcode> are refused when you save, and the page's\nContent-Security-Policy blocks what slips past: only HelpBites' own page script\nruns, forms post only to HelpBites, and frames load only YouTube and Vimeo\nplayers. Interactions are CSS: \u003Ccode>:hover\u003C\u002Fcode>, \u003Ccode>:has(input:checked)\u003C\u002Fcode> tabs,\n\u003Ccode>&lt;details&gt;\u003C\u002Fcode> accordions, scroll-driven animations.\u003C\u002Fp>\n\u003Cp>The page's theme is CSS variables your component's CSS can use: \u003Ccode>--hb-accent\u003C\u002Fcode>,\n\u003Ccode>--hb-on-accent\u003C\u002Fcode>, \u003Ccode>--hb-bg\u003C\u002Fcode>, \u003Ccode>--hb-surface\u003C\u002Fcode>, \u003Ccode>--hb-text\u003C\u002Fcode>, \u003Ccode>--hb-muted\u003C\u002Fcode>,\n\u003Ccode>--hb-line\u003C\u002Fcode>, \u003Ccode>--hb-radius\u003C\u002Fcode>, \u003Ccode>--hb-radius-sm\u003C\u002Fcode>, \u003Ccode>--hb-max\u003C\u002Fcode> (content width),\n\u003Ccode>--hb-font\u003C\u002Fcode> and \u003Ccode>--hb-heading-font\u003C\u002Fcode>. Helper classes: \u003Ccode>.hb-container\u003C\u002Fcode>,\n\u003Ccode>.hb-section\u003C\u002Fcode>, \u003Ccode>.hb-btn\u003C\u002Fcode> (\u003Ccode>--ghost\u003C\u002Fcode>, \u003Ccode>--ink\u003C\u002Fcode>, \u003Ccode>--soft\u003C\u002Fcode>, \u003Ccode>--glass\u003C\u002Fcode>), \u003Ccode>.hb-card\u003C\u002Fcode>,\n\u003Ccode>.hb-grid\u003C\u002Fcode> (set \u003Ccode>--hb-cols\u003C\u002Fcode>), \u003Ccode>.hb-pill\u003C\u002Fcode> (\u003Ccode>--blue\u003C\u002Fcode>, \u003Ccode>--amber\u003C\u002Fcode>, \u003Ccode>--violet\u003C\u002Fcode>,\n\u003Ccode>--emerald\u003C\u002Fcode>, \u003Ccode>--dark\u003C\u002Fcode>), \u003Ccode>.hb-title\u003C\u002Fcode>, \u003Ccode>.hb-heading\u003C\u002Fcode>, \u003Ccode>.hb-lead\u003C\u002Fcode>, \u003Ccode>.hb-checks\u003C\u002Fcode>\n(around a Markdown list) and \u003Ccode>.hb-stage\u003C\u002Fcode> (a tinted picture frame). Prefix your\nown classes with the component's key: every component's CSS shares the page.\u003C\u002Fp>\n\u003Ch2 id=\"animations\">Animations\u003Ca class=\"heading-anchor\" href=\"#animations\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Any block can animate: select it and pick an effect (fade, fade up, down, left\nor right, zoom in or out, unblur, parallax), when it plays (as it scrolls into\nview, on page load, or following the scroll position) and its duration, delay,\neasing (including a spring), distance and \u003Cstrong>stagger\u003C\u002Fstrong> (its children one after\nanother). In content it is \u003Ccode>_anim\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-json\">\u003Ccode>{ \u003Cspan class=\"shj-syn-var\">\"effect\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"fade-up\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"trigger\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"in-view\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"duration\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-num\">600\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"delay\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-num\">0\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"easing\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"ease-out\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"stagger\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-num\">80\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"once\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-bool\">true\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"distance\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-num\">32\u003C\u002Fspan> }\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>Hosted pages play them with \u003Ca href=\"https:\u002F\u002Fmotion.dev\" target=\"_blank\" rel=\"noopener\">Motion\u003C\u002Fa> (MIT), served by\nHelpBites itself, so visitors' browsers make no third-party requests. Inside a\ntemplate, \u003Ccode>data-hb-anim=&quot;fade-up&quot;\u003C\u002Fcode> animates an inner element and\n\u003Ccode>data-hb-item\u003C\u002Fcode> marks the children a stagger walks through. Visitors who ask\ntheir system to reduce motion see everything in place, and if the script cannot\nload, the page shows as it is after three seconds.\u003C\u002Fp>\n\u003Cp>In your own app the same \u003Ccode>_anim\u003C\u002Fcode> arrives in the JSON; drive it with whatever you\nuse (\u003Ca href=\"#vue\">Vue\u003C\u002Fa>).\u003C\u002Fp>\n\u003Ch2 id=\"the-assistant\">The assistant\u003Ca class=\"heading-anchor\" href=\"#the-assistant\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>The \u003Cstrong>Assistant\u003C\u002Fstrong> tab writes and edits the page for you. It works through the\nsame tools the editor uses, so every turn shows as it happens and undoes in one\nclick. It can:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>build a layout from a template or from scratch, and write all the copy;\u003C\u002Fli>\n\u003Cli>read your GitHub repositories (the ones linked in the project's settings,\nread-only) to get the facts right;\u003C\u002Fli>\n\u003Cli>write new components, save them to the project and use them;\u003C\u002Fli>\n\u003Cli>add animations, restyle the page, rename it;\u003C\u002Fli>\n\u003Cli>work out your \u003Cstrong>messaging brief\u003C\u002Fstrong> with you: who it is for, the problem, a\none-sentence value proposition, three to five key messages and the proof for\neach. Pages → Settings shows the brief, and every turn reads it.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>The \u003Cstrong>wizard\u003C\u002Fstrong> (New page → Build it with the assistant) asks about the product,\nthe audience, the goal and the tone, fills what you leave empty from your\nrepositories on request, saves the answers as the brief and opens the editor\nwith the assistant already building the page. It never invents numbers,\ncustomers or quotes: missing facts become marked placeholders.\u003C\u002Fp>\n\u003Ch2 id=\"hosted-pages\">Hosted pages\u003Ca class=\"heading-anchor\" href=\"#hosted-pages\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>A published page is at \u003Ccode>https:\u002F\u002F&lt;your HelpBites&gt;\u002Fp\u002F&lt;public key&gt;\u002F&lt;slug&gt;\u003C\u002Fcode>; the\n\u003Ccode>home\u003C\u002Fcode> entry is at \u003Ccode>\u002Fp\u002F&lt;public key&gt;\u003C\u002Fcode>. Links between your pages use those paths.\nThe page carries the entry's \u003Ccode>title\u003C\u002Fcode>, \u003Ccode>description\u003C\u002Fcode> and link preview image as\nmeta tags.\u003C\u002Fp>\n\u003Cp>The built-in \u003Cstrong>Email sign-up\u003C\u002Fstrong> block posts to the project's newsletter sign-up\n(\u003Ca href=\"https:\u002F\u002Fgithub.com\u002Felrumo\u002Fhelp-bites-oss\u002Fblob\u002Fmain\u002Fdocs\u002FMARKETING.md\" target=\"_blank\" rel=\"noopener\">double opt-in and topics\u003C\u002Fa>), so a waitlist needs nothing else.\u003C\u002Fp>\n\u003Ch3 id=\"put-it-on-your-site\">Put it on your site\u003Ca class=\"heading-anchor\" href=\"#put-it-on-your-site\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-html\">\u003Ccode>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">div\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">id\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"launch\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">div\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">script\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">src\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"https:\u002F\u002FYOUR-SUPPORT-HOST\u002Fpages.js\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-project\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"pk_…\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-page\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"launch\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">data-target\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"#launch\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-class\">defer\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&lt;\u002F\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">script\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">&gt;\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Option\u003C\u002Fth>\n\u003Cth>Meaning\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>data-project\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The project's public key (required).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>data-page\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The entry's slug; \u003Ccode>home\u003C\u002Fcode> when left out.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>data-target\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Where the page goes, a CSS selector (required).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>data-height\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The starting height in px (720) until the page reports its own.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>data-title\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The frame's accessible name.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>The same keys work as \u003Ccode>HelpBitesPages.push(['init', { page: 'launch', … }])\u003C\u002Fcode>\nbefore the script. The frame has the class \u003Ccode>help-bites-page\u003C\u002Fcode> and grows with its\ncontent. The page wears its own style from the editor; for anything else, read\nthe content and render it yourself.\u003C\u002Fp>\n\u003Ch2 id=\"reading-content-from-code\">Reading content from code\u003Ca class=\"heading-anchor\" href=\"#reading-content-from-code\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Ch3 id=\"delivery-api\">Delivery API\u003Ca class=\"heading-anchor\" href=\"#delivery-api\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Public, no key beyond the project's public one, open to any origin and cached\nfor a minute behind an ETag. Published entries only.\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-bash\">\u003Ccode>\u003Cspan class=\"shj-syn-var\">HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>https:\u002F\u002FYOUR-SUPPORT-HOST\n\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u002Fv1\u002Fcms\u002Fpk_…\u002Fentries\u002Fhome\"\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u002Fv1\u002Fcms\u002Fpk_…\u002Fentries\u002Fhome?format=html\"\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u002Fv1\u002Fcms\u002Fpk_…\u002Fentries?type=post&#38;starts_with=blog\u002F&#38;per_page=10\"\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-json\">\u003Ccode>{\n  \u003Cspan class=\"shj-syn-var\">\"entry\"\u003C\u002Fspan>: {\n    \u003Cspan class=\"shj-syn-var\">\"id\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"Xq2b…\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"slug\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"home\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"name\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"Home\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"type\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"page\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"version\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-num\">3\u003C\u002Fspan>,\n    \u003Cspan class=\"shj-syn-var\">\"publishedAt\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"2026-10-21T14:00:00.000Z\"\u003C\u002Fspan>,\n    \u003Cspan class=\"shj-syn-var\">\"style\"\u003C\u002Fspan>: { \u003Cspan class=\"shj-syn-var\">\"accent\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"#3b82f6\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"mode\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"light\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"radius\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"round\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"headingFont\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"bodyFont\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"width\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"normal\"\u003C\u002Fspan> },\n    \u003Cspan class=\"shj-syn-var\">\"content\"\u003C\u002Fspan>: { \u003Cspan class=\"shj-syn-var\">\"_uid\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"…\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"component\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"page\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"title\"\u003C\u002Fspan>: \u003Cspan class=\"shj-syn-str\">\"…\"\u003C\u002Fspan>, \u003Cspan class=\"shj-syn-var\">\"body\"\u003C\u002Fspan>: [] }\n  }\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>With \u003Ccode>format=html\u003C\u002Fcode> the answer adds \u003Ccode>html\u003C\u002Fcode> (the body rendered with the project's\ncomponents, blocks still carrying \u003Ccode>data-hb-uid\u003C\u002Fcode>) and \u003Ccode>css\u003C\u002Fcode> (the theme and each\ncomponent's styles), for a site that shows a page as it is. Its sign-up forms\npost to HelpBites, so add your site to the project's allowed origins.\u003C\u002Fp>\n\u003Cp>Drafts are not public. To preview unpublished content on your server, read the\nentry with an admin key (\u003Ccode>GET \u002Fv1\u002Fadmin\u002Fcms\u002Fentries\u002F{id}\u003C\u002Fcode>), or use the live\nbridge below, which needs no key at all.\u003C\u002Fp>\n\u003Ch3 id=\"javascript\">JavaScript\u003Ca class=\"heading-anchor\" href=\"#javascript\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>\u003Ccode>cms.mjs\u003C\u002Fcode> is a small client with no dependencies. Import it by URL or copy it\ninto your project:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-js\">\u003Ccode>\u003Cspan class=\"shj-syn-kwd\">import\u003C\u002Fspan> { createClient\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> editable\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> bridge } \u003Cspan class=\"shj-syn-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'https:\u002F\u002FYOUR-SUPPORT-HOST\u002Fcms.mjs'\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> cms \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-syn-func\">createClient\u003C\u002Fspan>({ host\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'https:\u002F\u002FYOUR-SUPPORT-HOST'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> pk\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'pk_…'\u003C\u002Fspan> })\n\u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> { entry } \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-syn-kwd\">await\u003C\u002Fspan> cms\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">getEntry\u003C\u002Fspan>(\u003Cspan class=\"shj-syn-str\">'home'\u003C\u002Fspan>)\n\u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> { entries } \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-syn-kwd\">await\u003C\u002Fspan> cms\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">getEntries\u003C\u002Fspan>({ type\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'post'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> startsWith\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'blog\u002F'\u003C\u002Fspan> })\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Ch3 id=\"vue\">Vue\u003Ca class=\"heading-anchor\" href=\"#vue\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Map component keys to your own components and render the tree:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-plain\">\u003Ccode>&lt;!-- HbBlocks.vue --&gt;\n&lt;script setup&gt;\nimport { editable } from '@\u002Flib\u002Fcms.mjs'\nimport Hero from '.\u002Fblocks\u002FHero.vue'\nimport Features from '.\u002Fblocks\u002FFeatures.vue'\ndefineProps({ blocks: { type: Array, default: () =&gt; [] } })\nconst map = { hero: Hero, features: Features }\n&lt;\u002Fscript&gt;\n\n&lt;template&gt;\n  &lt;template v-for=\u003Cspan class=\"shj-syn-str\">\"blok in blocks\"\u003C\u002Fspan> :key=\u003Cspan class=\"shj-syn-str\">\"blok._uid\"\u003C\u002Fspan>&gt;\n    &lt;component :is=\u003Cspan class=\"shj-syn-str\">\"map[blok.component]\"\u003C\u002Fspan> v-if=\u003Cspan class=\"shj-syn-str\">\"map[blok.component]\"\u003C\u002Fspan> :blok=\u003Cspan class=\"shj-syn-str\">\"blok\"\u003C\u002Fspan> v-bind=\u003Cspan class=\"shj-syn-str\">\"editable(blok)\"\u003C\u002Fspan> \u002F&gt;\n  &lt;\u002Ftemplate&gt;\n&lt;\u002Ftemplate&gt;\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>A block component spreads \u003Ccode>editable(blok)\u003C\u002Fcode> on its outer element (above, through\n\u003Ccode>v-bind\u003C\u002Fcode> on the root) and renders nested lists with \u003Ccode>&lt;HbBlocks :blocks=&quot;blok.items&quot; \u002F&gt;\u003C\u002Fcode>.\nTo animate with \u003Ca href=\"https:\u002F\u002Fmotion.dev\u002Fdocs\u002Fvue\" target=\"_blank\" rel=\"noopener\">motion-v\u003C\u002Fa>, turn \u003Ccode>_anim\u003C\u002Fcode> into its props:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-js\">\u003Ccode>\u003Cspan class=\"shj-syn-kwd\">export\u003C\u002Fspan> \u003Cspan class=\"shj-syn-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-syn-func\">hbMotion\u003C\u002Fspan>(anim) {\n  \u003Cspan class=\"shj-syn-kwd\">if\u003C\u002Fspan> (\u003Cspan class=\"shj-syn-oper\">!\u003C\u002Fspan>anim) \u003Cspan class=\"shj-syn-kwd\">return\u003C\u002Fspan> {}\n  \u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> d \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> anim\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>distance \u003Cspan class=\"shj-syn-oper\">??\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">32\u003C\u002Fspan>\n  \u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> \u003Cspan class=\"shj-syn-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> { opacity\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">...\u003C\u002Fspan>{ 'fade-up'\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { y\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> d }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> 'fade-down'\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { y\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">-\u003C\u002Fspan>d }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> 'fade-left'\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { x\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> d }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> 'fade-right'\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { x\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">-\u003C\u002Fspan>d }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> 'zoom-in'\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { scale\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">0.92\u003C\u002Fspan> }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> 'zoom-out'\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { scale\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">1.08\u003C\u002Fspan> }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> blur\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { filter\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'blur(12px)'\u003C\u002Fspan> } }[anim\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>effect] }\n  \u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> to \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> { opacity\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> x\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> y\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">0\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> scale\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> filter\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'blur(0px)'\u003C\u002Fspan> }\n  \u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> transition \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> { duration\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> anim\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>duration \u003Cspan class=\"shj-syn-oper\">\u002F 1000, delay: anim.delay \u002F\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">1000\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">...\u003C\u002Fspan>(anim\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>easing \u003Cspan class=\"shj-syn-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'spring'\u003C\u002Fspan> \u003Cspan class=\"shj-syn-oper\">?\u003C\u002Fspan> { type\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'spring'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> bounce\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-num\">0.25\u003C\u002Fspan> } \u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> {}) }\n  \u003Cspan class=\"shj-syn-kwd\">return\u003C\u002Fspan> anim\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>trigger \u003Cspan class=\"shj-syn-oper\">===\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'load'\u003C\u002Fspan>\n    \u003Cspan class=\"shj-syn-oper\">?\u003C\u002Fspan> { initial\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-kwd\">from\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> animate\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> to\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> transition }\n    \u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { initial\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-kwd\">from\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> whileInView\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> to\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> inViewOptions\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> { once\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> anim\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>once }\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> transition }\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>\u003Ccode>&lt;motion.section v-bind=&quot;hbMotion(blok._anim)&quot;&gt;\u003C\u002Fcode> then plays what the editor set.\u003C\u002Fp>\n\u003Ch3 id=\"live-editing-on-your-site\">Live editing on your site\u003Ca class=\"heading-anchor\" href=\"#live-editing-on-your-site\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cp>Set Pages → Settings → \u003Cstrong>Your site's preview URL\u003C\u002Fstrong> (\u003Ccode>http:\u002F\u002Flocalhost:5173\u002F{slug}\u003C\u002Fcode>),\nand the editor's \u003Cstrong>Your site\u003C\u002Fstrong> switch shows that page instead of the HelpBites\nrender. Add the bridge to it:\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-js\">\u003Ccode>\u003Cspan class=\"shj-syn-kwd\">import\u003C\u002Fspan> { bridge } \u003Cspan class=\"shj-syn-kwd\">from\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'@\u002Flib\u002Fcms.mjs'\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-syn-kwd\">const\u003C\u002Fspan> page \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-syn-func\">ref\u003C\u002Fspan>(\u003Cspan class=\"shj-syn-kwd\">await\u003C\u002Fspan> cms\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">getEntry\u003C\u002Fspan>(slug)\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-syn-func\">then\u003C\u002Fspan>(r \u003Cspan class=\"shj-syn-kwd\">=&gt;\u003C\u002Fspan> r\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>entry))\n\u003Cspan class=\"shj-syn-func\">bridge\u003C\u002Fspan>({ host\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'https:\u002F\u002FYOUR-SUPPORT-HOST'\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">,\u003C\u002Fspan> onChange\u003Cspan class=\"shj-syn-oper\">:\u003C\u002Fspan> entry \u003Cspan class=\"shj-syn-kwd\">=&gt;\u003C\u002Fspan> { page\u003Cspan class=\"shj-syn-oper\">.\u003C\u002Fspan>value \u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan> entry } })\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>Inside the editor, \u003Ccode>onChange\u003C\u002Fcode> gets the unsaved draft on every edit, clicks on\n\u003Ccode>editable()\u003C\u002Fcode> elements select the block in the editor (and the selection shows\non your page), and messages are only taken from your HelpBites origin. Outside\nthe editor \u003Ccode>bridge()\u003C\u002Fcode> does nothing. Your site must allow being framed by\nHelpBites (a dev server usually does; check \u003Ccode>frame-ancestors\u003C\u002Fcode> and\n\u003Ccode>X-Frame-Options\u003C\u002Fcode> in production).\u003C\u002Fp>\n\u003Ch3 id=\"in-a-swift-app\">In a Swift app\u003Ca class=\"heading-anchor\" href=\"#in-a-swift-app\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh3>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-plain\">\u003Ccode>\u002F\u002F HelpBitesPage.swift. MIT. Shows a published HelpBites entry's text blocks natively.\nimport SwiftUI\n\nstruct HBBlock: Decodable, Identifiable {\n  let _uid: String\n  let component: String\n  let fields: [String: JSONValue]\n  var id: String { _uid }\n  init(from decoder: Decoder) throws {\n    let all = try decoder.singleValueContainer().decode([String: JSONValue].self)\n    _uid = all[\u003Cspan class=\"shj-syn-str\">\"_uid\"\u003C\u002Fspan>]?.string ?? UUID().uuidString\n    component = all[\u003Cspan class=\"shj-syn-str\">\"component\"\u003C\u002Fspan>]?.string ?? \u003Cspan class=\"shj-syn-str\">\"\"\u003C\u002Fspan>\n    fields = all\n  }\n  func text(_ key: String) -&gt; String? { fields[key]?.string }\n  func blocks(_ key: String) -&gt; [HBBlock] { fields[key]?.blocks ?? [] }\n}\n\nenum JSONValue: Decodable {\n  case string(String), number(Double), bool(Bool), array([JSONValue]), object([String: JSONValue]), null\n  init(from decoder: Decoder) throws {\n    let c = try decoder.singleValueContainer()\n    if let v = try? c.decode(String.self) { self = .string(v) }\n    else if let v = try? c.decode(Double.self) { self = .number(v) }\n    else if let v = try? c.decode(Bool.self) { self = .bool(v) }\n    else if let v = try? c.decode([JSONValue].self) { self = .array(v) }\n    else if let v = try? c.decode([String: JSONValue].self) { self = .object(v) }\n    else { self = .null }\n  }\n  var string: String? { if case .string(let s) = self { s } else { nil } }\n  var blocks: [HBBlock]? {\n    guard case .array = self, let data = try? JSONEncoder().encode(AnyEncodable(self)) else { return nil }\n    return try? JSONDecoder().decode([HBBlock].self, from: data)\n  }\n}\n\nprivate struct AnyEncodable: Encodable {\n  let value: JSONValue\n  init(_ value: JSONValue) { self.value = value }\n  func encode(to encoder: Encoder) throws {\n    var c = encoder.singleValueContainer()\n    switch value {\n    case .string(let s): try c.encode(s)\n    case .number(let n): try c.encode(n)\n    case .bool(let b): try c.encode(b)\n    case .array(let a): try c.encode(a.map(AnyEncodable.init))\n    case .object(let o): try c.encode(o.mapValues(AnyEncodable.init))\n    case .null: try c.encodeNil()\n    }\n  }\n}\n\nstruct HelpBitesPage: View {\n  let host: URL\n  let projectKey: String\n  var slug = \u003Cspan class=\"shj-syn-str\">\"home\"\u003C\u002Fspan>\n  var accent: Color = .accentColor\n  var titleFont: Font = .largeTitle.bold()\n  var cornerRadius: CGFloat = 16\n  @State private var blocks: [HBBlock] = []\n\n  var body: some View {\n    ScrollView {\n      VStack(alignment: .leading, spacing: 24) {\n        ForEach(blocks) { block in\n          VStack(alignment: .leading, spacing: 8) {\n            if let title = block.text(\u003Cspan class=\"shj-syn-str\">\"headline\"\u003C\u002Fspan>) ?? block.text(\u003Cspan class=\"shj-syn-str\">\"title\"\u003C\u002Fspan>) { Text(title).font(titleFont) }\n            if let body = block.text(\u003Cspan class=\"shj-syn-str\">\"subhead\"\u003C\u002Fspan>) ?? block.text(\u003Cspan class=\"shj-syn-str\">\"body\"\u003C\u002Fspan>) { Text(.init(body)).foregroundStyle(.secondary) }\n            if let label = block.text(\u003Cspan class=\"shj-syn-str\">\"primaryLabel\"\u003C\u002Fspan>), let link = block.text(\u003Cspan class=\"shj-syn-str\">\"primaryUrl\"\u003C\u002Fspan>), let url = URL(string: link) {\n              Link(label, destination: url).buttonStyle(.borderedProminent).tint(accent).clipShape(.rect(cornerRadius: cornerRadius))\n            }\n          }\n        }\n      }\n      .padding()\n    }\n    .task {\n      struct Envelope: Decodable { struct Entry: Decodable { let content: HBBlock }; let entry: Entry }\n      let url = host.appending(path: \u003Cspan class=\"shj-syn-str\">\"v1\u002Fcms\u002F\\(projectKey)\u002Fentries\u002F\\(slug)\"\u003C\u002Fspan>)\n      if let (data, _) = try? await URLSession.shared.data(from: url), let page = try? JSONDecoder().decode(Envelope.self, from: data) {\n        blocks = page.entry.content.blocks(\u003Cspan class=\"shj-syn-str\">\"body\"\u003C\u002Fspan>)\n      }\n    }\n  }\n}\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cp>\u003Ccode>accent\u003C\u002Fcode>, \u003Ccode>titleFont\u003C\u002Fcode> and \u003Ccode>cornerRadius\u003C\u002Fcode> style it; everything else is your\napp's own look. It shows headlines, text and the main button of each top-level\nblock; switch on \u003Ccode>block.component\u003C\u002Fcode> to draw your own views for the rest.\u003C\u002Fp>\n\u003Ch2 id=\"managing-content-over-the-api\">Managing content over the API\u003Ca class=\"heading-anchor\" href=\"#managing-content-over-the-api\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cp>Every admin route takes an operator session or an \u003Ccode>ak_\u003C\u002Fcode> admin key and is scoped\nto the workspace. \u003Ccode>project\u003C\u002Fcode> goes in the query or the body.\u003C\u002Fp>\n\u003Cdiv class=\"code-block\">\u003Cbutton type=\"button\" class=\"copy-btn\" data-copy aria-label=\"Copy to clipboard\">Copy\u003C\u002Fbutton>\u003Cpre class=\"shj-lang-bash\">\u003Ccode>\u003Cspan class=\"shj-syn-var\">HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>https:\u002F\u002FYOUR-SUPPORT-HOST\u003Cspan class=\"shj-syn-kwd\">;\u003C\u002Fspan> \u003Cspan class=\"shj-syn-var\">KEY\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>ak_...\n\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -H\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"authorization: Bearer \u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$KEY\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -H\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'content-type: application\u002Fjson'\u003C\u002Fspan> \\\n \u003Cspan class=\"shj-syn-kwd\"> -d\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">'{\"project\":\"PROJECT_ID\",\"template\":\"saas\",\"slug\":\"launch\"}'\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u002Fv1\u002Fadmin\u002Fcms\u002Fentries\"\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -X\u003C\u002Fspan> POST\u003Cspan class=\"shj-syn-kwd\"> -H\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"authorization: Bearer \u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$KEY\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u002Fv1\u002Fadmin\u002Fcms\u002Fentries\u002FENTRY_ID\u002Fpublish\"\u003C\u002Fspan>\n\u003Cspan class=\"shj-syn-func\">curl\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -H\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"authorization: Bearer \u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$KEY\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-kwd\"> -F\u003C\u002Fspan> \u003Cspan class=\"shj-syn-var\">project\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>PROJECT_ID\u003Cspan class=\"shj-syn-kwd\"> -F\u003C\u002Fspan> \u003Cspan class=\"shj-syn-var\">file\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>@hero.png\u003Cspan class=\"shj-syn-kwd\"> -F\u003C\u002Fspan> \u003Cspan class=\"shj-syn-var\">alt\u003C\u002Fspan>\u003Cspan class=\"shj-syn-oper\">=\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\"The dashboard\"\u003C\u002Fspan> \u003Cspan class=\"shj-syn-str\">\"\u003C\u002Fspan>\u003Cspan class=\"shj-syn-var\">$HOST\u003C\u002Fspan>\u003Cspan class=\"shj-syn-str\">\u002Fv1\u002Fadmin\u002Fcms\u002Fassets\"\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fdiv>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Route\u003C\u002Fth>\n\u003Cth>Does\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode> \u002F \u003Ccode>POST \u002Fv1\u002Fadmin\u002Fcms\u002Fcomponents\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>List (built-ins first) or create a component.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode> \u002F \u003Ccode>PATCH\u003C\u002Fcode> \u002F \u003Ccode>DELETE \u002Fv1\u002Fadmin\u002Fcms\u002Fcomponents\u002F{id}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Read, change or delete one of the project's own.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode> \u002F \u003Ccode>POST \u002Fv1\u002Fadmin\u002Fcms\u002Fentries\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>List (\u003Ccode>type\u003C\u002Fcode>) or create an entry (\u003Ccode>template\u003C\u002Fcode>, \u003Ccode>content\u003C\u002Fcode>, \u003Ccode>slug\u003C\u002Fcode>, \u003Ccode>type\u003C\u002Fcode>).\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode> \u002F \u003Ccode>PATCH\u003C\u002Fcode> \u002F \u003Ccode>DELETE \u002Fv1\u002Fadmin\u002Fcms\u002Fentries\u002F{id}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The working copy; changes report what was fixed in \u003Ccode>issues\u003C\u002Fcode>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fcms\u002Fentries\u002F{id}\u002Fpublish\u003C\u002Fcode>, \u003Ccode>…\u002Funpublish\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Publish the next version, or take it offline.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode> \u002F \u003Ccode>POST \u002Fv1\u002Fadmin\u002Fcms\u002Fassets\u003C\u002Fcode>, \u003Ccode>PATCH\u003C\u002Fcode> \u002F \u003Ccode>DELETE …\u002F{id}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The file library (multipart upload). Add \u003Ccode>compress=true\u003C\u002Fcode> to store a PNG, JPEG or WebP smaller as WebP, or \u003Ccode>formats=avif,webp\u003C\u002Fcode> to store those modern formats next to the file, whose \u003Ccode>url\u003C\u002Fcode> is then a JPEG or PNG fallback every client reads; they come back as the asset's \u003Ccode>variants\u003C\u002Fcode>. See \u003Ca href=\"\u002Fdocs\u002Fblog#smaller-images-and-modern-formats\">BLOG.md\u003C\u002Fa>.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fadmin\u002Fcms\u002Ftemplates\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The page templates.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET\u003C\u002Fcode> \u002F \u003Ccode>PATCH \u002Fv1\u002Fadmin\u002Fcms\u002Fsettings\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The preview URL and the messaging brief.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fcms\u002Fchat\u003C\u002Fcode> (and \u003Ccode>GET\u003C\u002Fcode>, \u003Ccode>DELETE\u003C\u002Fcode>, \u003Ccode>…\u002Fchat\u002Fstream\u003C\u002Fcode>)\u003C\u002Ftd>\n\u003Ctd>The assistant, as an AI SDK UI message stream.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>POST \u002Fv1\u002Fadmin\u002Fcms\u002Fwizard\u002Fsuggest\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>Wizard answers read from the linked repos.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>GET \u002Fv1\u002Fcms\u002F{pk}\u002Fentries\u003C\u002Fcode>, \u003Ccode>…\u002Fentries\u002F{slug}\u003C\u002Fcode>\u003C\u002Ftd>\n\u003Ctd>The public delivery API.\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>The full schema is in the API reference at \u003Ccode>\u002F_docs\u002Fscalar\u003C\u002Fcode>.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>Webhooks.\u003C\u002Fstrong> With an event webhook set (Settings → Event webhook), publishing\nsends \u003Ccode>cms.entry.published\u003C\u002Fcode> (\u003Ccode>{ entry: { id, slug, type, version, publishedAt } }\u003C\u002Fcode>)\nand unpublishing \u003Ccode>cms.entry.unpublished\u003C\u002Fcode>: rebuild a static site on them.\u003C\u002Fp>\n\u003Ch2 id=\"limits\">Limits\u003Ca class=\"heading-anchor\" href=\"#limits\" aria-label=\"Link to this section\">#\u003C\u002Fa>\u003C\u002Fh2>\n\u003Cdiv class=\"table-wrap\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>What\u003C\u002Fth>\n\u003Cth>Limit\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>Components per project\u003C\u002Ftd>\n\u003Ctd>200, with 40 fields each and 100 KB of HTML and of CSS\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Entries per project\u003C\u002Ftd>\n\u003Ctd>1,000; 512 KB of content each, 100 blocks per list, 8 levels deep\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Files\u003C\u002Ftd>\n\u003Ctd>2,000 per project, 20 MB each\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Delivery API\u003C\u002Ftd>\n\u003Ctd>1,200 requests a minute per address\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>Assistant\u003C\u002Ftd>\n\u003Ctd>20 turns a minute per person\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\u003C\u002Fdiv>\n\u003Cp>Not built yet: custom domains for hosted pages, scripts in components, content\ntranslations per field (folders such as \u003Ccode>es\u002F…\u003C\u002Fcode> work today), scheduled\npublishing and version history, and generated TypeScript types.\u003C\u002Fp>\n",[6,10,13,16,19,22,25,29,32,35,38,41,44,47,50],{"id":7,"text":8,"level":9},"how-content-is-shaped","How content is shaped",2,{"id":11,"text":12,"level":9},"templates-and-the-editor","Templates and the editor",{"id":14,"text":15,"level":9},"components-in-code","Components in code",{"id":17,"text":18,"level":9},"animations","Animations",{"id":20,"text":21,"level":9},"the-assistant","The assistant",{"id":23,"text":24,"level":9},"hosted-pages","Hosted pages",{"id":26,"text":27,"level":28},"put-it-on-your-site","Put it on your site",3,{"id":30,"text":31,"level":9},"reading-content-from-code","Reading content from code",{"id":33,"text":34,"level":28},"delivery-api","Delivery API",{"id":36,"text":37,"level":28},"javascript","JavaScript",{"id":39,"text":40,"level":28},"vue","Vue",{"id":42,"text":43,"level":28},"live-editing-on-your-site","Live editing on your site",{"id":45,"text":46,"level":28},"in-a-swift-app","In a Swift app",{"id":48,"text":49,"level":9},"managing-content-over-the-api","Managing content over the API",{"id":51,"text":52,"level":9},"limits","Limits",1792677543076]