efficient-easel 0.0.0 → 0.97.0

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.
Files changed (119) hide show
  1. package/LICENSE +10 -0
  2. package/README.md +818 -4
  3. package/dist/autosave/index.d.ts +101 -0
  4. package/dist/autosave/index.js +1 -0
  5. package/dist/blocks/index.d.ts +306 -0
  6. package/dist/blocks/index.js +0 -0
  7. package/dist/buckets-1vRlQceY.d.ts +13 -0
  8. package/dist/canvas/index.d.ts +194 -0
  9. package/dist/canvas/index.js +1 -0
  10. package/dist/chrome/chrome.css +570 -0
  11. package/dist/chrome/index.d.ts +219 -0
  12. package/dist/chrome/index.js +21 -0
  13. package/dist/chunk-2LUSK32B.js +1 -0
  14. package/dist/chunk-2OAZYSWJ.js +1 -0
  15. package/dist/chunk-67VZRQLG.js +13 -0
  16. package/dist/chunk-6OPSJGAI.js +1 -0
  17. package/dist/chunk-6PS6STGA.js +1 -0
  18. package/dist/chunk-6WY6ZXJG.js +1 -0
  19. package/dist/chunk-7A7O2JWQ.js +1 -0
  20. package/dist/chunk-BZ5ASXOL.js +1 -0
  21. package/dist/chunk-CERTFXCV.js +13 -0
  22. package/dist/chunk-D2BCHTQR.js +1 -0
  23. package/dist/chunk-FI23ALVJ.js +1 -0
  24. package/dist/chunk-GGTPNGFJ.js +1 -0
  25. package/dist/chunk-H4B72XCJ.js +1 -0
  26. package/dist/chunk-H6DDIBQ4.js +1 -0
  27. package/dist/chunk-KDIVA6WD.js +1 -0
  28. package/dist/chunk-KRXQM65X.js +1 -0
  29. package/dist/chunk-MCLBHENY.js +1 -0
  30. package/dist/chunk-MFIRYGHW.js +1 -0
  31. package/dist/chunk-NKB3KVAB.js +1 -0
  32. package/dist/chunk-O2NLQBFX.js +1 -0
  33. package/dist/chunk-OGZFDJ6G.js +1 -0
  34. package/dist/chunk-OWBO4WKA.js +0 -0
  35. package/dist/chunk-QB3BZIRA.js +5 -0
  36. package/dist/chunk-RE4LP46F.js +1 -0
  37. package/dist/chunk-STFMZJKU.js +14 -0
  38. package/dist/chunk-SUO5627P.js +1 -0
  39. package/dist/chunk-TDA4HH54.js +1 -0
  40. package/dist/chunk-WK3AFPXY.js +1 -0
  41. package/dist/chunk-YIMRTIBN.js +3 -0
  42. package/dist/chunk-YNZZYHA4.js +450 -0
  43. package/dist/chunk-YRLPFZW3.js +1 -0
  44. package/dist/chunk-ZAOM6PFH.js +1 -0
  45. package/dist/chunk-ZNCEOSXO.js +1 -0
  46. package/dist/copy/copy.css +282 -0
  47. package/dist/copy/index.d.ts +336 -0
  48. package/dist/copy/index.js +10 -0
  49. package/dist/data/index.d.ts +375 -0
  50. package/dist/data/index.js +1 -0
  51. package/dist/drag/drag.css +360 -0
  52. package/dist/drag/index.d.ts +379 -0
  53. package/dist/drag/index.js +18 -0
  54. package/dist/edit-lock/index.d.ts +354 -0
  55. package/dist/edit-lock/index.js +1 -0
  56. package/dist/editor/editor.css +58 -0
  57. package/dist/editor/index.d.ts +361 -0
  58. package/dist/editor/index.js +9 -0
  59. package/dist/fields/fields.css +207 -0
  60. package/dist/fields/index.d.ts +273 -0
  61. package/dist/fields/index.js +46 -0
  62. package/dist/gold/gold.css +196 -0
  63. package/dist/gold/index.d.ts +280 -0
  64. package/dist/gold/index.js +1 -0
  65. package/dist/icons/index.d.ts +474 -0
  66. package/dist/icons/index.js +1 -0
  67. package/dist/index.d.ts +6 -0
  68. package/dist/index.js +1 -0
  69. package/dist/markup/index.d.ts +452 -0
  70. package/dist/markup/index.js +1 -0
  71. package/dist/menu/index.d.ts +49 -0
  72. package/dist/menu/index.js +15 -0
  73. package/dist/menu/menu.css +254 -0
  74. package/dist/node-ops/index.d.ts +91 -0
  75. package/dist/node-ops/index.js +1 -0
  76. package/dist/panel/index.d.ts +1088 -0
  77. package/dist/panel/index.js +16 -0
  78. package/dist/panel/panel.css +1460 -0
  79. package/dist/pick/index.d.ts +153 -0
  80. package/dist/pick/index.js +14 -0
  81. package/dist/pick/pick.css +235 -0
  82. package/dist/plan-DChbUXdS.d.ts +88 -0
  83. package/dist/rail/index.d.ts +145 -0
  84. package/dist/rail/index.js +1 -0
  85. package/dist/rail/rail.css +175 -0
  86. package/dist/render/index.d.ts +117 -0
  87. package/dist/render/index.js +5 -0
  88. package/dist/resolve-D68pb3Bx.d.ts +57 -0
  89. package/dist/shell/index.d.ts +159 -0
  90. package/dist/shell/index.js +1 -0
  91. package/dist/shell/shell.css +122 -0
  92. package/dist/strings/catalogue.d.ts +141 -0
  93. package/dist/strings/catalogue.js +1 -0
  94. package/dist/strings/index.d.ts +56 -0
  95. package/dist/strings/index.js +2 -0
  96. package/dist/style-clipboard/index.d.ts +184 -0
  97. package/dist/style-clipboard/index.js +1 -0
  98. package/dist/tree/index.d.ts +173 -0
  99. package/dist/tree/index.js +1 -0
  100. package/dist/tree/tree.css +618 -0
  101. package/dist/types-41704JE4.d.ts +112 -0
  102. package/dist/types-CEqUFH2j.d.ts +129 -0
  103. package/dist/types-CuNiSnX6.d.ts +204 -0
  104. package/dist/types-DNrn_w4V.d.ts +88 -0
  105. package/dist/types-Dy6oR6Z2.d.ts +153 -0
  106. package/dist/types-pGBXevBU.d.ts +131 -0
  107. package/dist/useNodeOps-BF0fQsif.d.ts +68 -0
  108. package/dist/watch-DmasQA61.d.ts +60 -0
  109. package/dist/wordFit-9B9nqyf3.d.ts +35 -0
  110. package/dist/words-B2YLaKEr.d.ts +30 -0
  111. package/dist/words-B87GIFWH.d.ts +86 -0
  112. package/dist/words-BK-npta-.d.ts +27 -0
  113. package/dist/words-CD3WADyb.d.ts +51 -0
  114. package/dist/words-CsRWXkY4.d.ts +129 -0
  115. package/dist/words-D9ChvbNC.d.ts +52 -0
  116. package/dist/words-DOhF-4D4.d.ts +35 -0
  117. package/dist/words-DagJdwIA.d.ts +78 -0
  118. package/dist/words-DfxEp_RH.d.ts +142 -0
  119. package/package.json +202 -3
@@ -0,0 +1,101 @@
1
+ import { A as Autosave, a as AutosaveSnapshot, b as SaveIndicator, c as AutosaveOptions } from '../words-CsRWXkY4.js';
2
+ export { d as AutosaveClock, S as SaveWords, e as createAutosave, s as saveIndicator } from '../words-CsRWXkY4.js';
3
+ import '../types-CuNiSnX6.js';
4
+ import '../types-DNrn_w4V.js';
5
+ import '../wordFit-9B9nqyf3.js';
6
+ import 'react';
7
+ import '../edit-lock/index.js';
8
+
9
+ type UseAutosaveOptions<T> = Omit<AutosaveOptions<T>, 'save' | 'onFailure'> & {
10
+ /** Write one document. Read at call time, so it may close over current props. */
11
+ save: (value: T) => Promise<void>;
12
+ /** Read at call time, like `save`. */
13
+ onFailure?: (error: unknown, attempt: number) => void;
14
+ /** Ask the browser to warn before unloading while an edit is off the server. Default true. */
15
+ warnBeforeUnload?: boolean;
16
+ };
17
+ type UseAutosave<T> = Omit<Autosave<T>, 'setSave' | 'getSnapshot' | 'subscribe' | 'dispose'> & {
18
+ state: AutosaveSnapshot;
19
+ /** The pill's tone and words — `<SaveState {...indicator} />`. */
20
+ indicator: SaveIndicator;
21
+ };
22
+ declare function useAutosave<T>(options: UseAutosaveOptions<T>): UseAutosave<T>;
23
+
24
+ /**
25
+ * Save when the TAB goes away — the case an unmount does not cover.
26
+ *
27
+ * React does not unmount an editor when the operator switches tab or window or
28
+ * locks the screen, so the edit waiting out its second stays in memory; and an
29
+ * async save cannot be finished during unload, which is why the close-tab
30
+ * handler only warns. Both editors had these same two listeners of their own
31
+ * (efficient-admin-portal `src/components/email/autosaveTabAway.ts`,
32
+ * efficient-shop `components/StorefrontDocumentContext.tsx`).
33
+ *
34
+ * - `visibilitychange` to `hidden` is the one that matters, and an ordinary
35
+ * save: the page is alive, so it completes. Guarded on `hidden` because the
36
+ * event also fires on the way BACK, when there is nothing to hurry.
37
+ * - `pagehide` is the last gasp: the document is being torn down, so the save
38
+ * is best-effort — but starting it is more than the warning does.
39
+ *
40
+ * Returns the uninstaller, which removes the SAME handlers it added.
41
+ */
42
+ declare function installTabAwayFlush(flush: () => void, target?: {
43
+ document: Document;
44
+ window: Window;
45
+ }): () => void;
46
+
47
+ /**
48
+ * How long the autosave waits, and how it backs off — one set of numbers for
49
+ * every editor (blueprint step 2.5, Lewis's rule of 2026-09-29: "save a second
50
+ * after you stop, and say nothing").
51
+ *
52
+ * Until this module the two editors each carried their own: the campaign
53
+ * designer waited ten seconds (`AUTOSAVE_DEBOUNCE_MS`, efficient-admin-portal
54
+ * `src/components/email/autosaveStatus.ts`), the website page editor ten
55
+ * (`components/StorefrontDocumentContext.tsx`) and its section editor five
56
+ * (`lib/editor/autosave-status.ts`). Ten seconds was chosen to keep
57
+ * whole-document writes rare; what it cost was ten seconds of work that lived
58
+ * only in memory. A one-second save with a single write in flight at a time
59
+ * (./engine) is at most one write per second while someone is typing, and it
60
+ * makes the "not saved yet" window small enough that the editor never has to
61
+ * mention it.
62
+ */
63
+ /** Save once nobody has edited for this long. */
64
+ declare const AUTOSAVE_DEBOUNCE_MS = 1000;
65
+ /**
66
+ * …but never wait longer than this after the first unsaved edit, however
67
+ * steadily someone types.
68
+ *
69
+ * A pure debounce never fires while the typing goes on, so a long sentence
70
+ * would sit unsaved until the last key — and would pass
71
+ * {@link AUTOSAVE_UNSAVED_AFTER_MS} on the way and put "Unsaved changes" up
72
+ * while the connection was fine. This cap keeps it under that threshold on a
73
+ * healthy connection, which is what makes the rule "the top bar does not change
74
+ * while you type" true (blueprint 2.6).
75
+ */
76
+ declare const AUTOSAVE_MAX_WAIT_MS = 3000;
77
+ /**
78
+ * How long an edit may be off the server before the editor says so.
79
+ *
80
+ * Measured from the OLDEST edit not yet saved, not the newest, so a stream of
81
+ * edits over a failing connection is reported five seconds after the first one
82
+ * and not held off for as long as the typing goes on.
83
+ */
84
+ declare const AUTOSAVE_UNSAVED_AFTER_MS = 5000;
85
+ /**
86
+ * The waits before each retry of a failed save. The last one repeats for as
87
+ * long as saves keep failing: giving up is how a blip becomes lost work, and a
88
+ * real outage is a thirty-second retry, not a hammer.
89
+ *
90
+ * Carried over from the campaign designer's `AUTOSAVE_RETRY_DELAYS_MS`
91
+ * (efficient-admin-portal `src/components/email/autosaveRetry.ts`).
92
+ */
93
+ declare const AUTOSAVE_RETRY_DELAYS_MS: readonly number[];
94
+ /**
95
+ * The wait before retry number `attempt` (0-based). Past the end of the table
96
+ * it stays on the last entry, so attempt 99 still waits thirty seconds rather
97
+ * than reading `undefined` and retrying in a hot loop.
98
+ */
99
+ declare function autosaveRetryDelay(attempt: number, delays?: readonly number[]): number;
100
+
101
+ export { AUTOSAVE_DEBOUNCE_MS, AUTOSAVE_MAX_WAIT_MS, AUTOSAVE_RETRY_DELAYS_MS, AUTOSAVE_UNSAVED_AFTER_MS, Autosave, AutosaveOptions, AutosaveSnapshot, SaveIndicator, type UseAutosave, type UseAutosaveOptions, autosaveRetryDelay, installTabAwayFlush, useAutosave };
@@ -0,0 +1 @@
1
+ import{saveIndicator as e}from"../chunk-H6DDIBQ4.js";export{saveIndicator}from"../chunk-H6DDIBQ4.js";import{useRef as n,useEffect as t,useState as u,useSyncExternalStore as r,useMemo as o}from"react";var l=1e3,s=3e3,i=5e3,a=[2e3,5e3,15e3,3e4];function c(e,n=a){const t=n.length-1;return n[Number.isFinite(e)?Math.min(Math.max(Math.trunc(e),0),t):t]}var d={now:()=>Date.now(),setTimeout:(e,n)=>globalThis.setTimeout(e,n),clearTimeout:e=>globalThis.clearTimeout(e)};function f(e={}){const n=e.clock??d,t=e.debounceMs??1e3,u=e.maxWaitMs??3e3,r=e.unsavedAfterMs??5e3,o=e.retryDelays??a;let l=e.save??(()=>Promise.reject(new Error("autosave used before setSave"))),s=null,i=null,f=null,m=0,h=null,v=0,p=!1,w=!1,g=!1,b=!1,y=!1,T=!1,M=0,P=null,S=null;const D=/* @__PURE__ */new Set;let E=L();function L(){return{pending:null!==s,unsaved:T,failed:p,attempt:v,saved:w,paused:g,stopped:b}}function x(){if(y)return;const e=L();if(!Object.keys(e).every(n=>e[n]===E[n])){E=e;for(const e of[...D])e()}}function j(){null!==P&&n.clearTimeout(P),P=null}function k(e){j(),P=n.setTimeout(()=>{P=null,B()},e)}function F(){const e=n.now(),r=Math.min(m+t,(i??e)+u);k(Math.max(0,r-e))}function N(){if(null!==S&&n.clearTimeout(S),S=null,null===i)return void(T=!1);const e=i+r-n.now();T=e<=0,T||(S=n.setTimeout(()=>{S=null,T=!0,x()},e))}function B(){if(h)return h;if(y||b||!s)return Promise.resolve(null===s);j();const t=s,u=M;return f=null,h=Promise.resolve().then(()=>l(t.value)).then(()=>(u!==M||(h=null,w=!0,p=!1,v=0,s===t?(s=null,i=null):i=f??n.now(),f=null,N(),!s||g||b||y||F(),x()),!0),n=>{if(u!==M)return!1;if(h=null,f=null,b||y)return x(),!1;p=!0;const t=v;return v=t+1,e.onFailure?.(n,t),g||k(c(t,o)),x(),!1}),h}async function I(){for(;;){if(y||b)return null===s;if(!h&&!s)return!0;if(!await(h??B()))return!1}}return{setSave(e){l=e},push(e){if(y)return;const t=n.now();s={value:e},m=t,h&&null===f&&(f=t),null===i&&(i=t,N()),g||b||h||p||F(),x()},flush:I,retryNow:()=>(v=0,I()),pause(){y||g||(g=!0,j(),x())},resume:()=>y?Promise.resolve(null===s):(g=!1,x(),I()),stop(){y||(b=!0,j(),x())},reset(){y||(M++,h=null,j(),s=null,i=null,f=null,v=0,p=!1,w=!1,b=!1,N(),x())},hasPending:()=>null!==s,getSnapshot:()=>E,subscribe:e=>(D.add(e),()=>{D.delete(e)}),dispose(){j(),null!==S&&n.clearTimeout(S),S=null,y=!0}}}function m(e,n={document:document,window:window}){const t=n.document,u=n.window,r=()=>{"hidden"===t.visibilityState&&e()},o=()=>e();return t.addEventListener("visibilitychange",r),u.addEventListener("pagehide",o),()=>{t.removeEventListener("visibilitychange",r),u.removeEventListener("pagehide",o)}}function h(e){const n=e.getSnapshot();n.paused||n.stopped||e.flush()}function v(l){const s=n(l);t(()=>{s.current=l});const[i]=u(()=>f({...l,save:e=>s.current.save(e),onFailure:(e,n)=>s.current.onFailure?.(e,n)})),a=r(i.subscribe,i.getSnapshot,i.getSnapshot);t(()=>m(()=>h(i)),[i]);const c=l.warnBeforeUnload??!0;return t(()=>{if(!c)return;const e=e=>{i.hasPending()&&(e.preventDefault(),e.returnValue="")};return window.addEventListener("beforeunload",e),()=>window.removeEventListener("beforeunload",e)},[i,c]),t(()=>()=>h(i),[i]),o(()=>({state:a,indicator:e(a),push:i.push,flush:i.flush,retryNow:i.retryNow,pause:i.pause,resume:i.resume,stop:i.stop,reset:i.reset,hasPending:i.hasPending}),[i,a])}export{l as AUTOSAVE_DEBOUNCE_MS,s as AUTOSAVE_MAX_WAIT_MS,a as AUTOSAVE_RETRY_DELAYS_MS,i as AUTOSAVE_UNSAVED_AFTER_MS,c as autosaveRetryDelay,f as createAutosave,m as installTabAwayFlush,v as useAutosave};
@@ -0,0 +1,306 @@
1
+ import { ReactElement, FC, CSSProperties, ReactNode, JSX } from 'react';
2
+ import { P as Permissions, A as AppState } from '../types-Dy6oR6Z2.js';
3
+ import '../data/index.js';
4
+ import '../types-41704JE4.js';
5
+ import '../resolve-D68pb3Bx.js';
6
+ import '../plan-DChbUXdS.js';
7
+
8
+ /**
9
+ * efficient-easel/blocks — how a block is WRITTEN: its config, its fields, the
10
+ * props its `render` receives and the document it lands in.
11
+ *
12
+ * Blueprint default #3 keeps Puck's block writing, so the ~119 blocks both apps
13
+ * wrote move onto the engine without a line changed. Until 6.1 those blocks were
14
+ * typed by importing Puck's own types, which kept `@measured/puck` in both apps'
15
+ * dependencies after nothing ran it any more. These are the same shapes, written
16
+ * here — Puck 0.20's `Config`, `ComponentConfig`, `Fields`, `Field`,
17
+ * `CustomField`, `ComponentData`, `Data`, `Slot` and `SlotComponent` — so an app
18
+ * changes the module it imports them from, and nothing else.
19
+ *
20
+ * WHAT IS LEFT OUT, ON PURPOSE:
21
+ * - the `ConfigParams` form of `Config<{ components, root, fields }>` and the
22
+ * user field-type extension it carries (neither app uses it);
23
+ * - everything about Puck's own editor — its store, overrides, plugins and
24
+ * history. Those are the engine's now (`efficient-easel/editor`).
25
+ *
26
+ * The `puck` prop keeps its name: it is what every block's `render` destructures
27
+ * (`puck.isEditing`, `puck.renderDropZone`), and renaming it is the block rewrite
28
+ * default #3 rules out. Its type is `BlockContext`.
29
+ *
30
+ * TYPES ONLY — nothing here exists at runtime, so a server component, a test
31
+ * or the backend's tooling can import it freely.
32
+ */
33
+
34
+ type Metadata = {
35
+ [key: string]: any;
36
+ };
37
+ type DefaultComponentProps = {
38
+ [key: string]: any;
39
+ };
40
+ type DefaultComponents = Record<string, any>;
41
+ type FieldOption = {
42
+ label: string;
43
+ value: string | number | boolean | undefined | null | object;
44
+ };
45
+ type FieldOptions = Array<FieldOption> | ReadonlyArray<FieldOption>;
46
+ type BaseField = {
47
+ label?: string;
48
+ labelIcon?: ReactElement;
49
+ metadata?: Metadata;
50
+ visible?: boolean;
51
+ /**
52
+ * NOT PUCK'S — the engine's (0.97.0). Whether this prop is written again in
53
+ * each language of a multilingual page (`efficient-easel/data`, the
54
+ * translation overlay). Left out, a `text` or `textarea` field is and every
55
+ * other type is not, so a `custom` field that edits words, or a picture with
56
+ * words in it, says `translatable: true`, and a `text` field that holds an
57
+ * address or a code says `false`. On an `object` or `array` field it is the
58
+ * default for the fields inside it. A `slot` is never translated: its
59
+ * children are blocks with ids of their own.
60
+ */
61
+ translatable?: boolean;
62
+ };
63
+ type TextField = BaseField & {
64
+ type: 'text';
65
+ placeholder?: string;
66
+ contentEditable?: boolean;
67
+ };
68
+ type NumberField = BaseField & {
69
+ type: 'number';
70
+ placeholder?: string;
71
+ min?: number;
72
+ max?: number;
73
+ step?: number;
74
+ };
75
+ type TextareaField = BaseField & {
76
+ type: 'textarea';
77
+ placeholder?: string;
78
+ contentEditable?: boolean;
79
+ };
80
+ type SelectField = BaseField & {
81
+ type: 'select';
82
+ options: FieldOptions;
83
+ };
84
+ type RadioField = BaseField & {
85
+ type: 'radio';
86
+ options: FieldOptions;
87
+ };
88
+ type AnyRecord = {
89
+ [key: string]: any;
90
+ };
91
+ type ArrayField<Props extends AnyRecord[] = AnyRecord[]> = BaseField & {
92
+ type: 'array';
93
+ arrayFields: {
94
+ [SubPropName in keyof Props[0]]: Field<Props[0][SubPropName]>;
95
+ };
96
+ defaultItemProps?: Props[0];
97
+ getItemSummary?: (item: Props[0], index?: number) => string;
98
+ max?: number;
99
+ min?: number;
100
+ };
101
+ type ObjectField<Props = AnyRecord> = BaseField & {
102
+ type: 'object';
103
+ objectFields: {
104
+ [SubPropName in keyof Props]: Field<Props[SubPropName]>;
105
+ };
106
+ };
107
+ /** A custom field's drawing: called with Puck's six props, as both apps wrote them. */
108
+ type CustomFieldRender<Value> = (props: {
109
+ field: CustomField<Value>;
110
+ name: string;
111
+ id: string;
112
+ value: Value;
113
+ onChange: (value: Value) => void;
114
+ readOnly?: boolean;
115
+ }) => ReactElement;
116
+ type CustomField<Value> = BaseField & {
117
+ type: 'custom';
118
+ render: CustomFieldRender<Value>;
119
+ contentEditable?: boolean;
120
+ };
121
+ type SlotField = BaseField & {
122
+ type: 'slot';
123
+ allow?: string[];
124
+ disallow?: string[];
125
+ };
126
+ /**
127
+ * Puck's `external` field: a value picked from a list the app fetches. Neither
128
+ * app has one and the Inspector does not draw it — it is in the union only so
129
+ * the union is Puck's, and a function that takes `Fields` takes Puck's too.
130
+ */
131
+ type ExternalField<Props = AnyRecord> = BaseField & {
132
+ type: 'external';
133
+ placeholder?: string;
134
+ fetchList: (params: {
135
+ query: string;
136
+ filters: Record<string, unknown>;
137
+ }) => Promise<unknown[] | null>;
138
+ mapProp?: (value: any) => Props;
139
+ mapRow?: (value: any) => Record<string, string | number | ReactElement>;
140
+ getItemSummary?: (item: NonNullable<Props>, index?: number) => string;
141
+ showSearch?: boolean;
142
+ renderFooter?: (props: {
143
+ items: any[];
144
+ }) => ReactElement;
145
+ initialQuery?: string;
146
+ filterFields?: Record<string, Field>;
147
+ initialFilters?: Record<string, any>;
148
+ };
149
+ type Adaptor<AdaptorParams = object, TableShape extends AnyRecord = AnyRecord, PropShape = TableShape> = {
150
+ name: string;
151
+ fetchList: (adaptorParams?: AdaptorParams) => Promise<TableShape[] | null>;
152
+ mapProp?: (value: TableShape) => PropShape;
153
+ };
154
+ type ExternalFieldWithAdaptor<Props = AnyRecord> = BaseField & {
155
+ type: 'external';
156
+ placeholder?: string;
157
+ adaptor: Adaptor<any, any, Props>;
158
+ adaptorParams?: object;
159
+ getItemSummary: (item: NonNullable<Props>, index?: number) => string;
160
+ };
161
+ type Field<ValueType = any> = TextField | NumberField | TextareaField | SelectField | RadioField | ArrayField<ValueType extends AnyRecord[] ? ValueType : never> | ObjectField<ValueType> | ExternalField<ValueType> | ExternalFieldWithAdaptor<ValueType> | CustomField<ValueType> | SlotField;
162
+ /** A block's fields, one per prop (`editMode` is the editor's, never a field). */
163
+ type Fields<ComponentProps extends DefaultComponentProps = DefaultComponentProps> = {
164
+ [PropName in keyof Omit<ComponentProps, 'editMode'>]: Field<ComponentProps[PropName]>;
165
+ };
166
+ type BaseData<Props extends AnyRecord = AnyRecord> = {
167
+ readOnly?: Partial<Record<keyof Props, boolean>>;
168
+ };
169
+ type WithId<Props> = Props & {
170
+ id: string;
171
+ };
172
+ type ComponentData<Props extends DefaultComponentProps = DefaultComponentProps, Name = string, Components extends Record<string, DefaultComponentProps> = Record<string, DefaultComponentProps>> = {
173
+ type: Name;
174
+ props: WithDeepSlots<WithId<Props>, Content<Components>>;
175
+ } & BaseData<Props>;
176
+ type ComponentDataOptionalId<Props extends DefaultComponentProps = DefaultComponentProps, Name = string> = {
177
+ type: Name;
178
+ props: Props & {
179
+ id?: string;
180
+ };
181
+ } & BaseData<Props>;
182
+ type ComponentDataMap<Components extends DefaultComponents = DefaultComponents> = {
183
+ [K in keyof Components]: ComponentData<Components[K], K extends string ? K : never, Components>;
184
+ }[keyof Components];
185
+ /** A list of blocks: the top level, or a slot's. */
186
+ type Content<PropsMap extends {
187
+ [key: string]: DefaultComponentProps;
188
+ } = {
189
+ [key: string]: DefaultComponentProps;
190
+ }> = ComponentDataMap<PropsMap>[];
191
+ /** A slot prop, as STORED: the blocks in it. Its `render` receives a `SlotComponent` instead. */
192
+ type Slot<Props extends {
193
+ [key: string]: DefaultComponentProps;
194
+ } = {
195
+ [key: string]: DefaultComponentProps;
196
+ }> = {
197
+ [K in keyof Props]: ComponentDataOptionalId<Props[K], K extends string ? K : never>;
198
+ }[keyof Props][];
199
+ type BuiltinTypes = Date | RegExp | Error | Function | symbol | null | undefined;
200
+ /** `T` with every `Slot` in it, however deep, replaced by `SlotType`. */
201
+ type WithDeepSlots<T, SlotType = T> = T extends Slot ? SlotType : T extends (infer U)[] ? Array<WithDeepSlots<U, SlotType>> : T extends BuiltinTypes ? T : T extends object ? {
202
+ [K in keyof T]: WithDeepSlots<T[K], SlotType>;
203
+ } : T;
204
+ type WithSlotProps<Target extends AnyRecord, Components extends DefaultComponents = DefaultComponents, SlotType extends Content<Components> = Content<Components>> = WithDeepSlots<Target, SlotType>;
205
+ type DefaultRootFieldProps = {
206
+ title?: string;
207
+ };
208
+ type RootDataWithProps<Props extends DefaultComponentProps = DefaultRootFieldProps> = BaseData<Props> & {
209
+ props: Props;
210
+ };
211
+ type RootDataWithoutProps<Props extends DefaultComponentProps = DefaultRootFieldProps> = Props;
212
+ /** The root: its props under `props`, or (the older format) held directly. */
213
+ type RootData<Props extends DefaultComponentProps = DefaultRootFieldProps> = Partial<RootDataWithProps<AsFieldProps<Props>>> & Partial<RootDataWithoutProps<Props>>;
214
+ /** A saved campaign or page — Puck 0.20's document, which nothing has migrated off. */
215
+ type Data<Components extends DefaultComponents = DefaultComponents, RootProps extends DefaultComponentProps = DefaultRootFieldProps> = {
216
+ root: WithDeepSlots<RootData<RootProps>, Content<Components>>;
217
+ content: Content<Components>;
218
+ zones?: Record<string, Content<Components>>;
219
+ };
220
+ type DragAxis = 'dynamic' | 'y' | 'x';
221
+ type DropZoneProps = {
222
+ zone: string;
223
+ allow?: string[];
224
+ disallow?: string[];
225
+ style?: CSSProperties;
226
+ minEmptyHeight?: number;
227
+ className?: string;
228
+ collisionAxis?: DragAxis;
229
+ };
230
+ /** A slot, as a block's `render` receives it: draw it with `<Content />`. */
231
+ type SlotComponent = (props?: Omit<DropZoneProps, 'zone'>) => ReactNode;
232
+ /** The `puck` prop every block's `render` receives. */
233
+ type BlockContext = {
234
+ renderDropZone: FC<DropZoneProps>;
235
+ metadata: Metadata;
236
+ isEditing: boolean;
237
+ dragRef: ((element: Element | null) => void) | null;
238
+ };
239
+ type WithBlockProps<Props> = Props & {
240
+ puck: BlockContext;
241
+ editMode?: boolean;
242
+ };
243
+ type WithChildren<Props> = Props & {
244
+ children: ReactNode;
245
+ };
246
+ type AsFieldProps<Props> = Omit<Props, 'children' | 'puck' | 'editMode'>;
247
+ /** A block's `render`: its own props with slots as `SlotComponent`s, its id, and `puck`. */
248
+ type BlockComponent<Props> = (props: WithId<WithBlockProps<{
249
+ [K in keyof Props]: WithDeepSlots<Props[K], SlotComponent>;
250
+ }>>) => JSX.Element;
251
+ type ResolveDataTrigger = 'insert' | 'replace' | 'load' | 'force';
252
+ type Changed<FieldProps> = Partial<Record<keyof FieldProps, boolean> & {
253
+ id: string;
254
+ }>;
255
+ type WithPartialProps<T, Props extends DefaultComponentProps> = Omit<T, 'props'> & {
256
+ props?: Partial<Props>;
257
+ };
258
+ type ComponentConfig<RenderProps extends DefaultComponentProps = DefaultComponentProps, FieldProps extends DefaultComponentProps = RenderProps, DataShape = Omit<ComponentData<FieldProps>, 'type'>> = {
259
+ render: BlockComponent<RenderProps>;
260
+ label?: string;
261
+ defaultProps?: FieldProps;
262
+ fields?: Fields<FieldProps>;
263
+ permissions?: Partial<Permissions>;
264
+ inline?: boolean;
265
+ resolveFields?: (data: DataShape, params: {
266
+ changed: Changed<FieldProps>;
267
+ fields: Fields<FieldProps>;
268
+ lastFields: Fields<FieldProps>;
269
+ lastData: DataShape | null;
270
+ appState: AppState;
271
+ parent: ComponentData | null;
272
+ }) => Promise<Fields<FieldProps>> | Fields<FieldProps>;
273
+ resolveData?: (data: DataShape, params: {
274
+ changed: Changed<FieldProps>;
275
+ lastData: DataShape | null;
276
+ metadata: Metadata;
277
+ trigger: ResolveDataTrigger;
278
+ }) => Promise<WithPartialProps<DataShape, FieldProps>> | WithPartialProps<DataShape, FieldProps>;
279
+ resolvePermissions?: (data: DataShape, params: {
280
+ changed: Changed<FieldProps>;
281
+ lastPermissions: Partial<Permissions>;
282
+ permissions: Partial<Permissions>;
283
+ appState: AppState;
284
+ lastData: DataShape | null;
285
+ }) => Promise<Partial<Permissions>> | Partial<Permissions>;
286
+ metadata?: Metadata;
287
+ };
288
+ type RootConfig<RootProps extends DefaultComponentProps = DefaultComponentProps> = Partial<ComponentConfig<WithChildren<RootProps>, AsFieldProps<RootProps>, RootData<AsFieldProps<RootProps>>>>;
289
+ type Category<ComponentName> = {
290
+ components?: ComponentName[];
291
+ title?: string;
292
+ visible?: boolean;
293
+ defaultExpanded?: boolean;
294
+ };
295
+ /** A whole editor's blocks: `components` by type, the `root`, and the Add panel's `categories`. */
296
+ type Config<Props extends DefaultComponents = DefaultComponents, RootProps extends DefaultComponentProps = any, CategoryName extends string = string> = {
297
+ categories?: Record<CategoryName, Category<keyof Props>> & {
298
+ other?: Category<keyof Props>;
299
+ };
300
+ components: {
301
+ [ComponentName in keyof Props]: ComponentConfig<Props[ComponentName], Props[ComponentName]>;
302
+ };
303
+ root?: RootConfig<RootProps>;
304
+ };
305
+
306
+ export { type Adaptor, AppState, type ArrayField, type AsFieldProps, type BaseData, type BaseField, type BlockComponent, type BlockContext, type Category, type ComponentConfig, type ComponentData, type ComponentDataMap, type ComponentDataOptionalId, type Config, type Content, type CustomField, type CustomFieldRender, type Data, type DefaultComponentProps, type DefaultComponents, type DefaultRootFieldProps, type DragAxis, type DropZoneProps, type ExternalField, type ExternalFieldWithAdaptor, type Field, type FieldOption, type FieldOptions, type Fields, type Metadata, type NumberField, type ObjectField, Permissions, type RadioField, type ResolveDataTrigger, type RootConfig, type RootData, type RootDataWithProps, type RootDataWithoutProps, type SelectField, type Slot, type SlotComponent, type SlotField, type TextField, type TextareaField, type WithBlockProps, type WithChildren, type WithDeepSlots, type WithId, type WithPartialProps, type WithSlotProps };
File without changes
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The settings tab words, in the owner's order (2026-09-11): Content, Style,
3
+ * Layout, Animation. A panel shows only the tabs that hold a setting — an empty
4
+ * tab is hidden, never drawn (same day).
5
+ *
6
+ * The website editor wrote this list in `efficient-shop/lib/puck/field-groups.ts`
7
+ * (`BUCKETS`). From 0.43.0 it lives here, so the campaign editor spells its panel
8
+ * the same four words in the same order instead of keeping a second list.
9
+ */
10
+ declare const SETTINGS_BUCKETS: readonly ["Content", "Style", "Layout", "Animation"];
11
+ type SettingsBucket = (typeof SETTINGS_BUCKETS)[number];
12
+
13
+ export { type SettingsBucket as S, SETTINGS_BUCKETS as a };
@@ -0,0 +1,194 @@
1
+ import { RefObject } from 'react';
2
+ import { C as CanvasTarget, W as WatchOptions } from '../watch-DmasQA61.js';
3
+ export { B as BLOCK_SELECTOR, a as CANVAS_SELECTOR, i as isFrameReady, w as watchCanvasFrame } from '../watch-DmasQA61.js';
4
+
5
+ /**
6
+ * The small facts every canvas surface needs, written once — pure DOM, no
7
+ * React, nothing run at import.
8
+ *
9
+ * Each answers a question both editors used to answer for themselves, and
10
+ * sometimes differently (the blueprint's §2 rows 6–10):
11
+ *
12
+ * isTypingTarget is this keystroke someone TYPING, so a shortcut must stay out?
13
+ * nodeIdAt which block did this pointer event land on?
14
+ * frameScale how much is the canvas frame scaled (a phone view drawn small)?
15
+ * frameRectToHost where is a box measured INSIDE the frame, on the host page?
16
+ * fitCanvas how wide is the canvas, and at what scale does it fit?
17
+ * canvas styles a stylesheet that lives in the canvas document ONLY.
18
+ */
19
+ /**
20
+ * Is the event's target somewhere a person TYPES — a text field, a textarea, a
21
+ * native select, an editable region? A shortcut (undo, delete, copy style) must
22
+ * not fire there: the key belongs to the field. Duck-typed rather than
23
+ * `instanceof`, because a canvas element belongs to the frame's own realm and
24
+ * is not an instance of the host's `HTMLElement`.
25
+ */
26
+ declare function isTypingTarget(target: EventTarget | null): boolean;
27
+ /** The attribute on every block the canvas draws (Puck 0.20's name, kept). */
28
+ declare const BLOCK_ATTRIBUTE = "data-puck-component";
29
+ /**
30
+ * The id of the block a pointer event landed on: the NEAREST ancestor (or the
31
+ * target itself) carrying `attribute`. `null` on bare canvas.
32
+ */
33
+ declare function nodeIdAt(target: EventTarget | null, attribute?: string): string | null;
34
+ interface Box {
35
+ left: number;
36
+ top: number;
37
+ width: number;
38
+ height: number;
39
+ }
40
+ /**
41
+ * How much the frame is drawn scaled on the host page: its on-screen width over
42
+ * its layout width. The storefront draws tablet and phone at their real widths
43
+ * and scales the frame down to fit the pane; the campaign designer does not
44
+ * scale. 1 when the frame has no layout width yet.
45
+ */
46
+ declare function frameScale(frame: HTMLElement): number;
47
+ /**
48
+ * A box measured INSIDE the frame (an element's getBoundingClientRect in the
49
+ * frame's own viewport) as a box on the HOST page, relative to `origin` — the
50
+ * overlay layer the box will be drawn in — and through the frame's scale. The
51
+ * scale is the term both apps' own conversions left out on a scaled view.
52
+ */
53
+ declare function frameRectToHost(frame: HTMLElement, box: Box, origin?: Element | null): Box;
54
+ /**
55
+ * How much `el` is drawn scaled on screen: its on-screen width over its layout
56
+ * width. 1 when it is not scaled, or has no layout width yet. An overlay that
57
+ * shares a scaled ancestor with the frame (the storefront scales the whole
58
+ * device box, frame and overlay together) is scaled too, and its own CSS pixels
59
+ * are then that much smaller or larger than the screen's.
60
+ */
61
+ declare function elementScale(el: Element): number;
62
+ /**
63
+ * A box measured inside the frame as a box in `layer`'s OWN CSS coordinates —
64
+ * what an absolutely positioned child of `layer` is placed with. The same as
65
+ * `frameRectToHost` when the layer is not scaled; when it shares a scaled
66
+ * ancestor with the frame, every screen distance is divided back into the
67
+ * layer's pixels, or everything it draws lands the scale's factor away.
68
+ */
69
+ declare function frameRectToLayer(frame: HTMLElement, box: Box, layer: Element): Box;
70
+ /** A pointer position on the HOST page (clientX/clientY there) in `layer`'s own CSS coordinates. */
71
+ declare function clientPointToLayer(layer: Element, x: number, y: number): {
72
+ x: number;
73
+ y: number;
74
+ };
75
+ /** A pointer position inside the frame (clientX/clientY there) on the host page. */
76
+ declare function framePointToHost(frame: HTMLElement, x: number, y: number, origin?: Element | null): {
77
+ x: number;
78
+ y: number;
79
+ };
80
+ interface CanvasFit {
81
+ /** The canvas's layout width — the device's, or the pane's for a fluid view. */
82
+ width: number;
83
+ /** The scale that fits `width` into the pane, capped at `maxScale`. */
84
+ scale: number;
85
+ /** What the canvas occupies on screen: `width * scale`. */
86
+ shownWidth: number;
87
+ }
88
+ /**
89
+ * The canvas width and scale for a pane `available` px wide. A device with no
90
+ * width of its own (desktop, an email's inbox view) takes the pane's width at
91
+ * scale 1; a device width that does not fit is scaled down to fit, and one that
92
+ * does is scaled up no further than `maxScale` (the storefront allows 2; pass 1
93
+ * for "never enlarge").
94
+ */
95
+ declare function fitCanvas({ available, width, maxScale, }: {
96
+ available: number;
97
+ width?: number | null;
98
+ maxScale?: number;
99
+ }): CanvasFit;
100
+ /**
101
+ * Say which CSP nonce the stylesheets this module puts in `doc` must carry —
102
+ * for a host whose policy allows inline styles by nonce (`style-src
103
+ * 'nonce-…'`), where a `<style>` without it is refused. Every sheet
104
+ * `upsertCanvasStyle` / `useCanvasStyle` creates in `doc` afterwards wears it.
105
+ *
106
+ * `CanvasFrame` (`efficient-easel/editor`) calls this with its `nonce` prop as
107
+ * it builds the frame, so a host says it once; a host that builds its own
108
+ * frame calls it itself. `undefined` (or `''`) forgets it.
109
+ */
110
+ declare function setCanvasNonce(doc: Document, nonce: string | undefined): void;
111
+ /**
112
+ * Put `css` in the canvas document's `<head>` under `key`, replacing what that
113
+ * key held before. It lives in the canvas ONLY — not mirrored from the host
114
+ * head, so it cannot leak into the host page, and not dependent on anyone
115
+ * noticing a change to it. Writes nothing when the text is already current.
116
+ * A new sheet carries the document's nonce, when it was given one
117
+ * ({@link setCanvasNonce}).
118
+ */
119
+ declare function upsertCanvasStyle(doc: Document, key: string, css: string): HTMLStyleElement;
120
+ /** Take `key`'s stylesheet out of the canvas document, if it is there. */
121
+ declare function removeCanvasStyle(doc: Document, key: string): void;
122
+
123
+ /**
124
+ * efficient-easel/canvas — the one way into the editor's canvas frame.
125
+ *
126
+ * Step 2.1 of the engine that replaces Puck (blueprint "Puck From The Ground Up
127
+ * For Efficient Editor", 2026-09-29). Both editors draw their canvas in an
128
+ * iframe — Puck's `#preview-frame` today, the engine's own same-origin
129
+ * CanvasFrame once the editors leave Puck — and each reached into it its own
130
+ * way: eight polling loops with eight intervals, stylesheets pushed into the
131
+ * host `<head>` hoping to be mirrored, key listeners re-bound on a one-second
132
+ * timer, coordinate conversions that forgot the frame's scale. This module is
133
+ * what they all use instead:
134
+ *
135
+ * useCanvasFrame(rootRef, options) the frame, its document and window — or null
136
+ * useCanvasEvent(canvas, type, fn) a listener in the canvas that follows a swap
137
+ * useCanvasStyle(canvas, key, css) a stylesheet in the canvas document only
138
+ * useCanvasLayout(canvas) a number that changes whenever anything that
139
+ * moves the canvas does — re-measure on it
140
+ *
141
+ * plus the pure pieces in ./dom (the typing guard, the block under a pointer,
142
+ * frame→host geometry with scale, the canvas fit) and ./watch (the watcher the
143
+ * hook wraps, usable without React).
144
+ *
145
+ * LEAF DISCIPLINE: React is the only import, nothing touches a browser global
146
+ * at import time, and there is no `'use client'` — the host's editor chrome is
147
+ * already the client boundary (tests/canvas-leaf-safety.test.ts).
148
+ */
149
+
150
+ /**
151
+ * The canvas frame under `rootRef` — `{ frame, document, window }` once it is
152
+ * ready, `null` before then and after it goes. Re-renders only when the frame
153
+ * or its document actually changes (a Refresh remount, a document swap).
154
+ */
155
+ declare function useCanvasFrame(rootRef: RefObject<Element | null>, { selector, readySelector }?: WatchOptions): CanvasTarget | null;
156
+ type CanvasEventMap = DocumentEventMap & WindowEventMap;
157
+ /**
158
+ * Listen for `type` in the canvas — on its document (default) or its window.
159
+ * Re-binds when the canvas document is replaced, and always calls the LATEST
160
+ * `handler`, so passing an inline function never re-binds (the storefront's
161
+ * context menu re-bound on every render because its handler's identity did).
162
+ */
163
+ declare function useCanvasEvent<K extends keyof CanvasEventMap>(canvas: CanvasTarget | null, type: K, handler: (event: CanvasEventMap[K]) => void, { on, capture }?: {
164
+ on?: 'document' | 'window';
165
+ capture?: boolean;
166
+ }): void;
167
+ /**
168
+ * Keep `css` in the canvas document's `<head>` under `key` while mounted, put it
169
+ * back in a replacement document, and take it out on unmount. An empty string
170
+ * removes it. Nothing is written to the host page.
171
+ *
172
+ * "Keep" means it comes back if something else takes it out. Puck's AutoFrame
173
+ * empties the frame's `<head>` once, when the host styles it mirrors have
174
+ * loaded (`doc.head.innerHTML = ""` in CopyHostStyles), and nothing orders that
175
+ * after the canvas is published — so a sheet written first would silently go.
176
+ * The observer watches the head's children only, which change about as often
177
+ * as a stylesheet is added: never per keystroke.
178
+ */
179
+ declare function useCanvasStyle(canvas: CanvasTarget | null, key: string, css: string): void;
180
+ /**
181
+ * A number that changes whenever the canvas may have MOVED — its document
182
+ * scrolled or resized, its content resized, a block added, removed or moved in
183
+ * it, the host page scrolled or resized, or the canvas itself replaced — plus
184
+ * two settle passes after each new canvas (fonts and images land after the
185
+ * first paint). An overlay re-measures when it
186
+ * changes, instead of each surface wiring its own listeners and timers.
187
+ *
188
+ * Coalesced to one change per animation frame.
189
+ */
190
+ declare function useCanvasLayout(canvas: CanvasTarget | null, { settle }?: {
191
+ settle?: readonly number[];
192
+ }): number;
193
+
194
+ export { BLOCK_ATTRIBUTE, type Box, type CanvasFit, CanvasTarget, WatchOptions, clientPointToLayer, elementScale, fitCanvas, framePointToHost, frameRectToHost, frameRectToLayer, frameScale, isTypingTarget, nodeIdAt, removeCanvasStyle, setCanvasNonce, upsertCanvasStyle, useCanvasEvent, useCanvasFrame, useCanvasLayout, useCanvasStyle };
@@ -0,0 +1 @@
1
+ export{BLOCK_SELECTOR,CANVAS_SELECTOR,isFrameReady,useCanvasEvent,useCanvasFrame,useCanvasLayout,useCanvasStyle,watchCanvasFrame}from"../chunk-MCLBHENY.js";export{BLOCK_ATTRIBUTE,clientPointToLayer,elementScale,fitCanvas,framePointToHost,frameRectToHost,frameRectToLayer,frameScale,isTypingTarget,nodeIdAt,removeCanvasStyle,setCanvasNonce,upsertCanvasStyle}from"../chunk-ZNCEOSXO.js";