@byscott-io/pagelet 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1 (2026-11-13)
4
+
5
+ - **The public renderer no longer imports Puck.** 0.2.0's `PageletPage` used
6
+ `@puckeditor/core/rsc`, which pulls in Tiptap and ProseMirror: about 350 KB
7
+ minified on every public page. pagelet now draws pages with its own renderer
8
+ (`src/render.jsx`), which produces byte-for-byte the same HTML as Puck's (a
9
+ test holds it to that). The renderer bundle is now about 27 KB minified.
10
+ - The build fails if the renderer imports Puck or exceeds a size budget.
11
+ - **Older Puck data with DropZones renders correctly.** A block that draws
12
+ `puck.renderDropZone({ zone })` gets its blocks from `data.zones`, exactly as
13
+ Puck's renderer does (held to Puck's HTML by a test). 0.2.0 ran Puck's
14
+ `migrate()` on public pages, which throws for such a block.
15
+ - The editor converts zones to slots with Puck's `migrate()` when the block
16
+ now has a matching slot field, and opens the data unchanged when it does not
17
+ (`migrate()` throws there), instead of crashing.
18
+ - `upgradeDocument(document)` no longer takes the palette.
19
+
20
+ ## 0.2.0 (2026-11-13)
21
+
22
+ ### npm `@byscott-io/pagelet`
23
+ - **Page settings** (the page's own fields): a browser title, and a background
24
+ that follows the site's, is custom, or is none, with an overlay color and
25
+ **opacity %**. `pageSettings(document)` returns the title.
26
+ - **Site background**, set in the theme: an image, overlay color and opacity,
27
+ `fit` (`cover`, or `photo` to show the whole photo), and `frame` (`none`, or
28
+ `card` for a bordered, rounded, padded frame).
29
+ - **New blocks:** Quote (italic text plus a citation), Icon list (icon, text, an
30
+ optional link), Schedule (rows of day / time / what), and Embedded page.
31
+ - **Embedded page** is offered only when the app names trusted hosts:
32
+ `createPalette({ embedHosts: ['example.org'] })`. https only, sandboxed.
33
+ - **Right alignment** for Text, Heading, Button and the new blocks; Image gains
34
+ alignment and a width %; Section gains a background image with an overlay
35
+ color and opacity.
36
+ - A built-in **icon set** (inline SVG): location, phone, email, video, calendar,
37
+ clock, link, Facebook, YouTube. `<Icon name>` is exported.
38
+ - **`<PageletMenu>` and `<PageletLayout>`:** a site's menu from its settings
39
+ (a brand logo, items with optional icon and highlight, left and right groups,
40
+ external links in a new tab, a menu button on narrow screens) and the frame
41
+ around a page. Pass your router's link component as `linkComponent`.
42
+ - The menu's brand link is a plain anchor for `tel:`/`mailto:` (like menu
43
+ items), never passed to the app's router.
44
+ - **Client:** `getSite`, `updateSite`, and `getPublishedPage(path)` returning
45
+ `{ document, pagePath, rest }`; `createPage`/`updatePage` take `matchesSubpaths`.
46
+
47
+ ### gem `pagelet`
48
+ - **Site settings** per owner (`Pagelet::Site`): the theme and the menu.
49
+ `GET /site` is public; `PATCH /site` needs `:publish`, since changes are live
50
+ at once. Shapes are checked (menu style, labels, links, groups, size).
51
+ - **Pages that answer for sub-paths:** `matches_subpaths` on a page makes
52
+ `/messages` serve `/messages/43`. An exact page wins, then the longest such
53
+ prefix. `Page.published_match(owner, path)` returns the document, the page's
54
+ path and the remainder; `GET /published` returns them too.
55
+ - **New migration:** run `bin/rails pagelet:install:migrations` and migrate
56
+ (see docs/UPGRADING.md).
57
+
3
58
  ## 0.1.0 (2026-11-13)
4
59
 
5
60
  First release.
package/README.md CHANGED
@@ -8,11 +8,15 @@ canvas, and your site draws the saved pages with the same React components.
8
8
  - **A small renderer** for public pages. It has no editor code in it, and it
9
9
  works in server-side rendering, so search engines see real HTML.
10
10
  - **A standard set of blocks**: sections, columns, hero, headings, text,
11
- buttons, calls to action, cards, FAQ, images, image with text, and
12
- YouTube/Vimeo video.
11
+ quotes, icon lists, schedules, buttons, calls to action, cards, FAQ, images,
12
+ image with text, YouTube/Vimeo video, and (for hosts you allow) embedded pages.
13
+ - **Page backgrounds**: a site-wide photo under a panel of any color and
14
+ opacity, which each page can follow, replace or turn off.
13
15
  - **Your own blocks**: register any React component and it appears in the
14
16
  palette beside the built-in ones.
15
17
  - **Theming** through CSS variables: colors, fonts, corner radius, page width.
18
+ - **A site menu and layout**: logo, items with icons and highlights, left and
19
+ right groups, and a menu button on phones, all from site settings.
16
20
 
17
21
  pagelet is built on [Puck](https://puckeditor.com), an MIT-licensed visual
18
22
  editor for React. Your app talks only to pagelet, which keeps Puck's upgrades in
@@ -124,6 +128,20 @@ Any subset of these tokens; the rest use the defaults:
124
128
  They become CSS variables (`--pagelet-color-accent`, …) on the page wrapper, so
125
129
  your own blocks can use them too: `color: var(--pagelet-color-accent)`.
126
130
 
131
+ A site background goes in the theme too, and each page can follow it, use its
132
+ own, or have none (the page's Background setting):
133
+
134
+ ```js
135
+ background: {
136
+ image: '/images/building.jpg',
137
+ overlayColor: '#ffffff',
138
+ overlayOpacity: 85, // percent
139
+ fit: 'photo', // or 'cover'
140
+ frame: 'card', // or 'none'
141
+ rounded: true,
142
+ }
143
+ ```
144
+
127
145
  ## Writing your own blocks
128
146
 
129
147
  A block is a [Puck component config](https://puckeditor.com/docs/api-reference/configuration/component-config)
package/dist/editor.cjs CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var core = require('@puckeditor/core');
5
- var rsc = require('@puckeditor/core/rsc');
6
5
 
7
6
  const DOCUMENT_VERSION = 1;
8
7
  function emptyData() {
@@ -11,7 +10,7 @@ function emptyData() {
11
10
  function createDocument(data = emptyData()) {
12
11
  return { pagelet: DOCUMENT_VERSION, data };
13
12
  }
14
- function upgradeDocument(document, palette) {
13
+ function upgradeDocument(document) {
15
14
  if (document == null) return createDocument();
16
15
  if (typeof document !== "object") {
17
16
  throw new TypeError("pagelet: a page document must be an object");
@@ -29,7 +28,12 @@ function upgradeDocument(document, palette) {
29
28
  );
30
29
  }
31
30
  const data = { ...emptyData(), ...doc.data };
32
- return createDocument(palette && needsPuckMigration(data) ? rsc.migrate(data, palette.config) : data);
31
+ return createDocument({ ...data, root: upgradeRoot(data.root) });
32
+ }
33
+ function upgradeRoot(root = {}) {
34
+ const { props = {}, readOnly, ...legacy } = root;
35
+ if (Object.keys(legacy).length === 0) return root;
36
+ return { ...readOnly ? { readOnly } : {}, props: { ...legacy, ...props } };
33
37
  }
34
38
  function needsPuckMigration(data) {
35
39
  const zones = data.zones ?? {};
@@ -40,7 +44,7 @@ function needsPuckMigration(data) {
40
44
 
41
45
  function PageletEditor({ document, palette, theme, onPublish, onChange, headerTitle, ...rest }) {
42
46
  if (!palette) throw new Error("pagelet: <PageletEditor> needs a palette (see createPalette)");
43
- const { data } = upgradeDocument(document, palette);
47
+ const data = editorData(document, palette);
44
48
  return /* @__PURE__ */ jsxRuntime.jsx(
45
49
  core.Puck,
46
50
  {
@@ -54,6 +58,15 @@ function PageletEditor({ document, palette, theme, onPublish, onChange, headerTi
54
58
  }
55
59
  );
56
60
  }
61
+ function editorData(document, palette) {
62
+ const data = upgradeDocument(document).data;
63
+ if (!needsPuckMigration(document?.data ?? document ?? {})) return data;
64
+ try {
65
+ return core.migrate(data, palette.config);
66
+ } catch {
67
+ return data;
68
+ }
69
+ }
57
70
 
58
71
  exports.PageletEditor = PageletEditor;
59
72
  //# sourceMappingURL=editor.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor.cjs","sources":["../src/document.js","../src/PageletEditor.jsx"],"sourcesContent":["import { migrate } from '@puckeditor/core/rsc';\n\n// What an app stores for a page: Puck's data inside an envelope that carries\n// pagelet's own format version. The version lets a stored page be upgraded when\n// pagelet or Puck changes shape, so apps never hold raw Puck data whose format\n// they do not control.\nexport const DOCUMENT_VERSION = 1;\n\nexport function emptyData() {\n return { root: { props: {} }, content: [] };\n}\n\nexport function createDocument(data = emptyData()) {\n return { pagelet: DOCUMENT_VERSION, data };\n}\n\n// Bring a stored document up to the current format.\n//\n// Accepts bare Puck data too, since an app adopting pagelet may already hold\n// some. Passing the palette also runs Puck's own migrate(), which converts\n// older Puck structures (DropZone zones) into the current one (slots).\nexport function upgradeDocument(document, palette) {\n if (document == null) return createDocument();\n if (typeof document !== 'object') {\n throw new TypeError('pagelet: a page document must be an object');\n }\n\n let doc = document;\n if (!('pagelet' in doc)) {\n if (!('content' in doc) && !('root' in doc)) {\n throw new TypeError('pagelet: not a page document (no \"pagelet\" version and no Puck data)');\n }\n doc = createDocument(doc);\n }\n\n if (doc.pagelet > DOCUMENT_VERSION) {\n throw new Error(\n `pagelet: this page was saved by a newer pagelet (format ${doc.pagelet}); ` +\n `this version reads up to format ${DOCUMENT_VERSION}. Upgrade @byscott-io/pagelet.`,\n );\n }\n\n // Format migrations go here, one step per version:\n // if (doc.pagelet === 1) doc = migrateV1toV2(doc);\n\n const data = { ...emptyData(), ...doc.data };\n return createDocument(palette && needsPuckMigration(data) ? migrate(data, palette.config) : data);\n}\n\n// Puck's migrate() logs to the console every time it is given a config, even\n// when there is nothing to convert. On a public page that would be a log line\n// per page view, so only call it for data in an older Puck shape: DropZone\n// zones holding content, or a root whose fields sit outside `props`.\nexport function needsPuckMigration(data) {\n const zones = data.zones ?? {};\n if (Object.values(zones).some((content) => Array.isArray(content) && content.length > 0)) return true;\n const root = data.root ?? {};\n return Object.keys(root).some((key) => key !== 'props' && key !== 'readOnly');\n}\n","import { Puck } from '@puckeditor/core';\nimport { createDocument, upgradeDocument } from './document.js';\n\n// The visual editor. Import from \"@byscott-io/pagelet/editor\" (and its\n// stylesheet from \"@byscott-io/pagelet/editor.css\") in admin screens only.\n//\n// onPublish and onChange receive a pagelet document, ready to store as-is.\nexport function PageletEditor({ document, palette, theme, onPublish, onChange, headerTitle, ...rest }) {\n if (!palette) throw new Error('pagelet: <PageletEditor> needs a palette (see createPalette)');\n const { data } = upgradeDocument(document, palette);\n return (\n <Puck\n {...rest}\n config={palette.config}\n data={data}\n metadata={{ pageletTheme: theme }}\n headerTitle={headerTitle}\n onPublish={onPublish ? (next) => onPublish(createDocument(next)) : undefined}\n onChange={onChange ? (next) => onChange(createDocument(next)) : undefined}\n />\n );\n}\n"],"names":["migrate","jsx","Puck"],"mappings":";;;;;;AAMO,MAAM,gBAAA,GAAmB,CAAA;AAEzB,SAAS,SAAA,GAAY;AAC1B,EAAA,OAAO,EAAE,MAAM,EAAE,KAAA,EAAO,EAAC,EAAE,EAAG,OAAA,EAAS,EAAC,EAAE;AAC5C;AAEO,SAAS,cAAA,CAAe,IAAA,GAAO,SAAA,EAAU,EAAG;AACjD,EAAA,OAAO,EAAE,OAAA,EAAS,gBAAA,EAAkB,IAAA,EAAK;AAC3C;AAOO,SAAS,eAAA,CAAgB,UAAU,OAAA,EAAS;AACjD,EAAA,IAAI,QAAA,IAAY,IAAA,EAAM,OAAO,cAAA,EAAe;AAC5C,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,MAAM,IAAI,UAAU,4CAA4C,CAAA;AAAA,EAClE;AAEA,EAAA,IAAI,GAAA,GAAM,QAAA;AACV,EAAA,IAAI,EAAE,aAAa,GAAA,CAAA,EAAM;AACvB,IAAA,IAAI,EAAE,SAAA,IAAa,GAAA,CAAA,IAAQ,EAAE,UAAU,GAAA,CAAA,EAAM;AAC3C,MAAA,MAAM,IAAI,UAAU,sEAAsE,CAAA;AAAA,IAC5F;AACA,IAAA,GAAA,GAAM,eAAe,GAAG,CAAA;AAAA,EAC1B;AAEA,EAAA,IAAI,GAAA,CAAI,UAAU,gBAAA,EAAkB;AAClC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,wDAAA,EAA2D,GAAA,CAAI,OAAO,CAAA,mCAAA,EACjC,gBAAgB,CAAA,8BAAA;AAAA,KACvD;AAAA,EACF;AAKA,EAAA,MAAM,OAAO,EAAE,GAAG,WAAU,EAAG,GAAG,IAAI,IAAA,EAAK;AAC3C,EAAA,OAAO,cAAA,CAAe,OAAA,IAAW,kBAAA,CAAmB,IAAI,CAAA,GAAIA,YAAQ,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA,GAAI,IAAI,CAAA;AAClG;AAMO,SAAS,mBAAmB,IAAA,EAAM;AACvC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,IAAS,EAAC;AAC7B,EAAA,IAAI,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAK,CAAC,OAAA,KAAY,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,OAAA,CAAQ,MAAA,GAAS,CAAC,GAAG,OAAO,IAAA;AACjG,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,IAAQ,EAAC;AAC3B,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,IAAA,CAAK,CAAC,GAAA,KAAQ,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,UAAU,CAAA;AAC9E;;ACnDO,SAAS,aAAA,CAAc,EAAE,QAAA,EAAU,OAAA,EAAS,KAAA,EAAO,WAAW,QAAA,EAAU,WAAA,EAAa,GAAG,IAAA,EAAK,EAAG;AACrG,EAAA,IAAI,CAAC,OAAA,EAAS,MAAM,IAAI,MAAM,8DAA8D,CAAA;AAC5F,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,eAAA,CAAgB,UAAU,OAAO,CAAA;AAClD,EAAA,uBACEC,cAAA;AAAA,IAACC,SAAA;AAAA,IAAA;AAAA,MACE,GAAG,IAAA;AAAA,MACJ,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,IAAA;AAAA,MACA,QAAA,EAAU,EAAE,YAAA,EAAc,KAAA,EAAM;AAAA,MAChC,WAAA;AAAA,MACA,SAAA,EAAW,YAAY,CAAC,IAAA,KAAS,UAAU,cAAA,CAAe,IAAI,CAAC,CAAA,GAAI,MAAA;AAAA,MACnE,QAAA,EAAU,WAAW,CAAC,IAAA,KAAS,SAAS,cAAA,CAAe,IAAI,CAAC,CAAA,GAAI;AAAA;AAAA,GAClE;AAEJ;;"}
1
+ {"version":3,"file":"editor.cjs","sources":["../src/document.js","../src/PageletEditor.jsx"],"sourcesContent":["// What an app stores for a page: Puck's data inside an envelope that carries\n// pagelet's own format version. The version lets a stored page be upgraded when\n// pagelet or Puck changes shape, so apps never hold raw Puck data whose format\n// they do not control.\nexport const DOCUMENT_VERSION = 1;\n\nexport function emptyData() {\n return { root: { props: {} }, content: [] };\n}\n\nexport function createDocument(data = emptyData()) {\n return { pagelet: DOCUMENT_VERSION, data };\n}\n\n// Bring a stored document up to the current format.\n//\n// Accepts bare Puck data too, since an app adopting pagelet may already hold\n// some. Imports nothing from Puck, because the public renderer uses this: an\n// old root shape (fields beside \"props\") is converted here, and DropZone zones,\n// which pagelet never wrote, are converted by the editor (needsPuckMigration).\nexport function upgradeDocument(document) {\n if (document == null) return createDocument();\n if (typeof document !== 'object') {\n throw new TypeError('pagelet: a page document must be an object');\n }\n\n let doc = document;\n if (!('pagelet' in doc)) {\n if (!('content' in doc) && !('root' in doc)) {\n throw new TypeError('pagelet: not a page document (no \"pagelet\" version and no Puck data)');\n }\n doc = createDocument(doc);\n }\n\n if (doc.pagelet > DOCUMENT_VERSION) {\n throw new Error(\n `pagelet: this page was saved by a newer pagelet (format ${doc.pagelet}); ` +\n `this version reads up to format ${DOCUMENT_VERSION}. Upgrade @byscott-io/pagelet.`,\n );\n }\n\n // Format migrations go here, one step per version:\n // if (doc.pagelet === 1) doc = migrateV1toV2(doc);\n\n const data = { ...emptyData(), ...doc.data };\n return createDocument({ ...data, root: upgradeRoot(data.root) });\n}\n\n// Puck once kept root fields beside \"props\" ({ title } rather than\n// { props: { title } }). Move any such fields into props.\nfunction upgradeRoot(root = {}) {\n const { props = {}, readOnly, ...legacy } = root;\n if (Object.keys(legacy).length === 0) return root;\n return { ...(readOnly ? { readOnly } : {}), props: { ...legacy, ...props } };\n}\n\n// Data in an older Puck shape: DropZone zones holding content, or a root whose\n// fields sit outside props. The editor runs Puck's migrate() only then, because\n// migrate() logs to the console every time it is called.\nexport function needsPuckMigration(data) {\n const zones = data.zones ?? {};\n if (Object.values(zones).some((content) => Array.isArray(content) && content.length > 0)) return true;\n const root = data.root ?? {};\n return Object.keys(root).some((key) => key !== 'props' && key !== 'readOnly');\n}\n\n// A page's own settings (title, background), for the app to use outside the\n// page body, e.g. the browser title.\nexport function pageSettings(document) {\n const props = upgradeDocument(document)?.data?.root?.props ?? {};\n return { title: props.title || '', background: props.background ?? 'site' };\n}\n","import { Puck, migrate } from '@puckeditor/core';\nimport { createDocument, needsPuckMigration, upgradeDocument } from './document.js';\n\n// The visual editor. Import from \"@byscott-io/pagelet/editor\" (and its\n// stylesheet from \"@byscott-io/pagelet/editor.css\") in admin screens only.\n//\n// onPublish and onChange receive a pagelet document, ready to store as-is.\nexport function PageletEditor({ document, palette, theme, onPublish, onChange, headerTitle, ...rest }) {\n if (!palette) throw new Error('pagelet: <PageletEditor> needs a palette (see createPalette)');\n const data = editorData(document, palette);\n return (\n <Puck\n {...rest}\n config={palette.config}\n data={data}\n metadata={{ pageletTheme: theme }}\n headerTitle={headerTitle}\n onPublish={onPublish ? (next) => onPublish(createDocument(next)) : undefined}\n onChange={onChange ? (next) => onChange(createDocument(next)) : undefined}\n />\n );\n}\n\n// Older Puck data (DropZone zones) is converted to slots here, in the editor,\n// where Puck is loaded anyway; the public renderer never imports Puck.\n//\n// Puck's migrate() only converts a zone whose block now has a slot field of the\n// same name, and THROWS otherwise. A block that still draws a DropZone is fine\n// as it is (Puck's editor and pagelet's renderer both draw zones), so then the\n// data opens unconverted rather than crashing the editor.\nfunction editorData(document, palette) {\n const data = upgradeDocument(document).data;\n if (!needsPuckMigration(document?.data ?? document ?? {})) return data;\n try {\n return migrate(data, palette.config);\n } catch {\n return data;\n }\n}\n"],"names":["jsx","Puck","migrate"],"mappings":"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"}
package/dist/editor.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { Puck } from '@puckeditor/core';
3
- import { migrate } from '@puckeditor/core/rsc';
2
+ import { Puck, migrate } from '@puckeditor/core';
4
3
 
5
4
  const DOCUMENT_VERSION = 1;
6
5
  function emptyData() {
@@ -9,7 +8,7 @@ function emptyData() {
9
8
  function createDocument(data = emptyData()) {
10
9
  return { pagelet: DOCUMENT_VERSION, data };
11
10
  }
12
- function upgradeDocument(document, palette) {
11
+ function upgradeDocument(document) {
13
12
  if (document == null) return createDocument();
14
13
  if (typeof document !== "object") {
15
14
  throw new TypeError("pagelet: a page document must be an object");
@@ -27,7 +26,12 @@ function upgradeDocument(document, palette) {
27
26
  );
28
27
  }
29
28
  const data = { ...emptyData(), ...doc.data };
30
- return createDocument(palette && needsPuckMigration(data) ? migrate(data, palette.config) : data);
29
+ return createDocument({ ...data, root: upgradeRoot(data.root) });
30
+ }
31
+ function upgradeRoot(root = {}) {
32
+ const { props = {}, readOnly, ...legacy } = root;
33
+ if (Object.keys(legacy).length === 0) return root;
34
+ return { ...readOnly ? { readOnly } : {}, props: { ...legacy, ...props } };
31
35
  }
32
36
  function needsPuckMigration(data) {
33
37
  const zones = data.zones ?? {};
@@ -38,7 +42,7 @@ function needsPuckMigration(data) {
38
42
 
39
43
  function PageletEditor({ document, palette, theme, onPublish, onChange, headerTitle, ...rest }) {
40
44
  if (!palette) throw new Error("pagelet: <PageletEditor> needs a palette (see createPalette)");
41
- const { data } = upgradeDocument(document, palette);
45
+ const data = editorData(document, palette);
42
46
  return /* @__PURE__ */ jsx(
43
47
  Puck,
44
48
  {
@@ -52,6 +56,15 @@ function PageletEditor({ document, palette, theme, onPublish, onChange, headerTi
52
56
  }
53
57
  );
54
58
  }
59
+ function editorData(document, palette) {
60
+ const data = upgradeDocument(document).data;
61
+ if (!needsPuckMigration(document?.data ?? document ?? {})) return data;
62
+ try {
63
+ return migrate(data, palette.config);
64
+ } catch {
65
+ return data;
66
+ }
67
+ }
55
68
 
56
69
  export { PageletEditor };
57
70
  //# sourceMappingURL=editor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"editor.js","sources":["../src/document.js","../src/PageletEditor.jsx"],"sourcesContent":["import { migrate } from '@puckeditor/core/rsc';\n\n// What an app stores for a page: Puck's data inside an envelope that carries\n// pagelet's own format version. The version lets a stored page be upgraded when\n// pagelet or Puck changes shape, so apps never hold raw Puck data whose format\n// they do not control.\nexport const DOCUMENT_VERSION = 1;\n\nexport function emptyData() {\n return { root: { props: {} }, content: [] };\n}\n\nexport function createDocument(data = emptyData()) {\n return { pagelet: DOCUMENT_VERSION, data };\n}\n\n// Bring a stored document up to the current format.\n//\n// Accepts bare Puck data too, since an app adopting pagelet may already hold\n// some. Passing the palette also runs Puck's own migrate(), which converts\n// older Puck structures (DropZone zones) into the current one (slots).\nexport function upgradeDocument(document, palette) {\n if (document == null) return createDocument();\n if (typeof document !== 'object') {\n throw new TypeError('pagelet: a page document must be an object');\n }\n\n let doc = document;\n if (!('pagelet' in doc)) {\n if (!('content' in doc) && !('root' in doc)) {\n throw new TypeError('pagelet: not a page document (no \"pagelet\" version and no Puck data)');\n }\n doc = createDocument(doc);\n }\n\n if (doc.pagelet > DOCUMENT_VERSION) {\n throw new Error(\n `pagelet: this page was saved by a newer pagelet (format ${doc.pagelet}); ` +\n `this version reads up to format ${DOCUMENT_VERSION}. Upgrade @byscott-io/pagelet.`,\n );\n }\n\n // Format migrations go here, one step per version:\n // if (doc.pagelet === 1) doc = migrateV1toV2(doc);\n\n const data = { ...emptyData(), ...doc.data };\n return createDocument(palette && needsPuckMigration(data) ? migrate(data, palette.config) : data);\n}\n\n// Puck's migrate() logs to the console every time it is given a config, even\n// when there is nothing to convert. On a public page that would be a log line\n// per page view, so only call it for data in an older Puck shape: DropZone\n// zones holding content, or a root whose fields sit outside `props`.\nexport function needsPuckMigration(data) {\n const zones = data.zones ?? {};\n if (Object.values(zones).some((content) => Array.isArray(content) && content.length > 0)) return true;\n const root = data.root ?? {};\n return Object.keys(root).some((key) => key !== 'props' && key !== 'readOnly');\n}\n","import { Puck } from '@puckeditor/core';\nimport { createDocument, upgradeDocument } from './document.js';\n\n// The visual editor. Import from \"@byscott-io/pagelet/editor\" (and its\n// stylesheet from \"@byscott-io/pagelet/editor.css\") in admin screens only.\n//\n// onPublish and onChange receive a pagelet document, ready to store as-is.\nexport function PageletEditor({ document, palette, theme, onPublish, onChange, headerTitle, ...rest }) {\n if (!palette) throw new Error('pagelet: <PageletEditor> needs a palette (see createPalette)');\n const { data } = upgradeDocument(document, palette);\n return (\n <Puck\n {...rest}\n config={palette.config}\n data={data}\n metadata={{ pageletTheme: theme }}\n headerTitle={headerTitle}\n onPublish={onPublish ? (next) => onPublish(createDocument(next)) : undefined}\n onChange={onChange ? (next) => onChange(createDocument(next)) : undefined}\n />\n );\n}\n"],"names":[],"mappings":";;;;AAMO,MAAM,gBAAA,GAAmB,CAAA;AAEzB,SAAS,SAAA,GAAY;AAC1B,EAAA,OAAO,EAAE,MAAM,EAAE,KAAA,EAAO,EAAC,EAAE,EAAG,OAAA,EAAS,EAAC,EAAE;AAC5C;AAEO,SAAS,cAAA,CAAe,IAAA,GAAO,SAAA,EAAU,EAAG;AACjD,EAAA,OAAO,EAAE,OAAA,EAAS,gBAAA,EAAkB,IAAA,EAAK;AAC3C;AAOO,SAAS,eAAA,CAAgB,UAAU,OAAA,EAAS;AACjD,EAAA,IAAI,QAAA,IAAY,IAAA,EAAM,OAAO,cAAA,EAAe;AAC5C,EAAA,IAAI,OAAO,aAAa,QAAA,EAAU;AAChC,IAAA,MAAM,IAAI,UAAU,4CAA4C,CAAA;AAAA,EAClE;AAEA,EAAA,IAAI,GAAA,GAAM,QAAA;AACV,EAAA,IAAI,EAAE,aAAa,GAAA,CAAA,EAAM;AACvB,IAAA,IAAI,EAAE,SAAA,IAAa,GAAA,CAAA,IAAQ,EAAE,UAAU,GAAA,CAAA,EAAM;AAC3C,MAAA,MAAM,IAAI,UAAU,sEAAsE,CAAA;AAAA,IAC5F;AACA,IAAA,GAAA,GAAM,eAAe,GAAG,CAAA;AAAA,EAC1B;AAEA,EAAA,IAAI,GAAA,CAAI,UAAU,gBAAA,EAAkB;AAClC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,wDAAA,EAA2D,GAAA,CAAI,OAAO,CAAA,mCAAA,EACjC,gBAAgB,CAAA,8BAAA;AAAA,KACvD;AAAA,EACF;AAKA,EAAA,MAAM,OAAO,EAAE,GAAG,WAAU,EAAG,GAAG,IAAI,IAAA,EAAK;AAC3C,EAAA,OAAO,cAAA,CAAe,OAAA,IAAW,kBAAA,CAAmB,IAAI,CAAA,GAAI,QAAQ,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA,GAAI,IAAI,CAAA;AAClG;AAMO,SAAS,mBAAmB,IAAA,EAAM;AACvC,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,IAAS,EAAC;AAC7B,EAAA,IAAI,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,CAAE,KAAK,CAAC,OAAA,KAAY,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,IAAK,OAAA,CAAQ,MAAA,GAAS,CAAC,GAAG,OAAO,IAAA;AACjG,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,IAAQ,EAAC;AAC3B,EAAA,OAAO,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,CAAE,IAAA,CAAK,CAAC,GAAA,KAAQ,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,UAAU,CAAA;AAC9E;;ACnDO,SAAS,aAAA,CAAc,EAAE,QAAA,EAAU,OAAA,EAAS,KAAA,EAAO,WAAW,QAAA,EAAU,WAAA,EAAa,GAAG,IAAA,EAAK,EAAG;AACrG,EAAA,IAAI,CAAC,OAAA,EAAS,MAAM,IAAI,MAAM,8DAA8D,CAAA;AAC5F,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,eAAA,CAAgB,UAAU,OAAO,CAAA;AAClD,EAAA,uBACE,GAAA;AAAA,IAAC,IAAA;AAAA,IAAA;AAAA,MACE,GAAG,IAAA;AAAA,MACJ,QAAQ,OAAA,CAAQ,MAAA;AAAA,MAChB,IAAA;AAAA,MACA,QAAA,EAAU,EAAE,YAAA,EAAc,KAAA,EAAM;AAAA,MAChC,WAAA;AAAA,MACA,SAAA,EAAW,YAAY,CAAC,IAAA,KAAS,UAAU,cAAA,CAAe,IAAI,CAAC,CAAA,GAAI,MAAA;AAAA,MACnE,QAAA,EAAU,WAAW,CAAC,IAAA,KAAS,SAAS,cAAA,CAAe,IAAI,CAAC,CAAA,GAAI;AAAA;AAAA,GAClE;AAEJ;;"}
1
+ {"version":3,"file":"editor.js","sources":["../src/document.js","../src/PageletEditor.jsx"],"sourcesContent":["// What an app stores for a page: Puck's data inside an envelope that carries\n// pagelet's own format version. The version lets a stored page be upgraded when\n// pagelet or Puck changes shape, so apps never hold raw Puck data whose format\n// they do not control.\nexport const DOCUMENT_VERSION = 1;\n\nexport function emptyData() {\n return { root: { props: {} }, content: [] };\n}\n\nexport function createDocument(data = emptyData()) {\n return { pagelet: DOCUMENT_VERSION, data };\n}\n\n// Bring a stored document up to the current format.\n//\n// Accepts bare Puck data too, since an app adopting pagelet may already hold\n// some. Imports nothing from Puck, because the public renderer uses this: an\n// old root shape (fields beside \"props\") is converted here, and DropZone zones,\n// which pagelet never wrote, are converted by the editor (needsPuckMigration).\nexport function upgradeDocument(document) {\n if (document == null) return createDocument();\n if (typeof document !== 'object') {\n throw new TypeError('pagelet: a page document must be an object');\n }\n\n let doc = document;\n if (!('pagelet' in doc)) {\n if (!('content' in doc) && !('root' in doc)) {\n throw new TypeError('pagelet: not a page document (no \"pagelet\" version and no Puck data)');\n }\n doc = createDocument(doc);\n }\n\n if (doc.pagelet > DOCUMENT_VERSION) {\n throw new Error(\n `pagelet: this page was saved by a newer pagelet (format ${doc.pagelet}); ` +\n `this version reads up to format ${DOCUMENT_VERSION}. Upgrade @byscott-io/pagelet.`,\n );\n }\n\n // Format migrations go here, one step per version:\n // if (doc.pagelet === 1) doc = migrateV1toV2(doc);\n\n const data = { ...emptyData(), ...doc.data };\n return createDocument({ ...data, root: upgradeRoot(data.root) });\n}\n\n// Puck once kept root fields beside \"props\" ({ title } rather than\n// { props: { title } }). Move any such fields into props.\nfunction upgradeRoot(root = {}) {\n const { props = {}, readOnly, ...legacy } = root;\n if (Object.keys(legacy).length === 0) return root;\n return { ...(readOnly ? { readOnly } : {}), props: { ...legacy, ...props } };\n}\n\n// Data in an older Puck shape: DropZone zones holding content, or a root whose\n// fields sit outside props. The editor runs Puck's migrate() only then, because\n// migrate() logs to the console every time it is called.\nexport function needsPuckMigration(data) {\n const zones = data.zones ?? {};\n if (Object.values(zones).some((content) => Array.isArray(content) && content.length > 0)) return true;\n const root = data.root ?? {};\n return Object.keys(root).some((key) => key !== 'props' && key !== 'readOnly');\n}\n\n// A page's own settings (title, background), for the app to use outside the\n// page body, e.g. the browser title.\nexport function pageSettings(document) {\n const props = upgradeDocument(document)?.data?.root?.props ?? {};\n return { title: props.title || '', background: props.background ?? 'site' };\n}\n","import { Puck, migrate } from '@puckeditor/core';\nimport { createDocument, needsPuckMigration, upgradeDocument } from './document.js';\n\n// The visual editor. Import from \"@byscott-io/pagelet/editor\" (and its\n// stylesheet from \"@byscott-io/pagelet/editor.css\") in admin screens only.\n//\n// onPublish and onChange receive a pagelet document, ready to store as-is.\nexport function PageletEditor({ document, palette, theme, onPublish, onChange, headerTitle, ...rest }) {\n if (!palette) throw new Error('pagelet: <PageletEditor> needs a palette (see createPalette)');\n const data = editorData(document, palette);\n return (\n <Puck\n {...rest}\n config={palette.config}\n data={data}\n metadata={{ pageletTheme: theme }}\n headerTitle={headerTitle}\n onPublish={onPublish ? (next) => onPublish(createDocument(next)) : undefined}\n onChange={onChange ? (next) => onChange(createDocument(next)) : undefined}\n />\n );\n}\n\n// Older Puck data (DropZone zones) is converted to slots here, in the editor,\n// where Puck is loaded anyway; the public renderer never imports Puck.\n//\n// Puck's migrate() only converts a zone whose block now has a slot field of the\n// same name, and THROWS otherwise. A block that still draws a DropZone is fine\n// as it is (Puck's editor and pagelet's renderer both draw zones), so then the\n// data opens unconverted rather than crashing the editor.\nfunction editorData(document, palette) {\n const data = upgradeDocument(document).data;\n if (!needsPuckMigration(document?.data ?? document ?? {})) return data;\n try {\n return migrate(data, palette.config);\n } catch {\n return data;\n }\n}\n"],"names":[],"mappings":"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"}