efficient-easel 0.0.0 → 0.96.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.
- package/LICENSE +10 -0
- package/README.md +723 -4
- package/dist/autosave/index.d.ts +101 -0
- package/dist/autosave/index.js +1 -0
- package/dist/blocks/index.d.ts +295 -0
- package/dist/blocks/index.js +0 -0
- package/dist/buckets-1vRlQceY.d.ts +13 -0
- package/dist/canvas/index.d.ts +194 -0
- package/dist/canvas/index.js +1 -0
- package/dist/chrome/chrome.css +570 -0
- package/dist/chrome/index.d.ts +219 -0
- package/dist/chrome/index.js +21 -0
- package/dist/chunk-2LUSK32B.js +1 -0
- package/dist/chunk-2OAZYSWJ.js +1 -0
- package/dist/chunk-67VZRQLG.js +13 -0
- package/dist/chunk-6JDII7N3.js +1 -0
- package/dist/chunk-6OPSJGAI.js +1 -0
- package/dist/chunk-6WY6ZXJG.js +1 -0
- package/dist/chunk-7A7O2JWQ.js +1 -0
- package/dist/chunk-BZ5ASXOL.js +1 -0
- package/dist/chunk-CERTFXCV.js +13 -0
- package/dist/chunk-EFTTSIMK.js +1 -0
- package/dist/chunk-FI23ALVJ.js +1 -0
- package/dist/chunk-FJE4IDBQ.js +1 -0
- package/dist/chunk-GGTPNGFJ.js +1 -0
- package/dist/chunk-H4B72XCJ.js +1 -0
- package/dist/chunk-H6DDIBQ4.js +1 -0
- package/dist/chunk-KDIVA6WD.js +1 -0
- package/dist/chunk-LPRQDDQO.js +1 -0
- package/dist/chunk-MCLBHENY.js +1 -0
- package/dist/chunk-MFIRYGHW.js +1 -0
- package/dist/chunk-NKB3KVAB.js +1 -0
- package/dist/chunk-O2NLQBFX.js +1 -0
- package/dist/chunk-OGZFDJ6G.js +1 -0
- package/dist/chunk-OWBO4WKA.js +0 -0
- package/dist/chunk-QB3BZIRA.js +5 -0
- package/dist/chunk-RE4LP46F.js +1 -0
- package/dist/chunk-STFMZJKU.js +14 -0
- package/dist/chunk-SUO5627P.js +1 -0
- package/dist/chunk-TDA4HH54.js +1 -0
- package/dist/chunk-WK3AFPXY.js +1 -0
- package/dist/chunk-YIMRTIBN.js +3 -0
- package/dist/chunk-YNZZYHA4.js +450 -0
- package/dist/chunk-ZAOM6PFH.js +1 -0
- package/dist/chunk-ZNCEOSXO.js +1 -0
- package/dist/copy/copy.css +282 -0
- package/dist/copy/index.d.ts +336 -0
- package/dist/copy/index.js +10 -0
- package/dist/data/index.d.ts +208 -0
- package/dist/data/index.js +1 -0
- package/dist/drag/drag.css +360 -0
- package/dist/drag/index.d.ts +379 -0
- package/dist/drag/index.js +18 -0
- package/dist/edit-lock/index.d.ts +354 -0
- package/dist/edit-lock/index.js +1 -0
- package/dist/editor/editor.css +58 -0
- package/dist/editor/index.d.ts +361 -0
- package/dist/editor/index.js +9 -0
- package/dist/fields/fields.css +207 -0
- package/dist/fields/index.d.ts +273 -0
- package/dist/fields/index.js +46 -0
- package/dist/gold/gold.css +196 -0
- package/dist/gold/index.d.ts +280 -0
- package/dist/gold/index.js +1 -0
- package/dist/icons/index.d.ts +474 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +1 -0
- package/dist/markup/index.d.ts +452 -0
- package/dist/markup/index.js +1 -0
- package/dist/menu/index.d.ts +49 -0
- package/dist/menu/index.js +15 -0
- package/dist/menu/menu.css +254 -0
- package/dist/node-ops/index.d.ts +91 -0
- package/dist/node-ops/index.js +1 -0
- package/dist/panel/index.d.ts +1088 -0
- package/dist/panel/index.js +16 -0
- package/dist/panel/panel.css +1460 -0
- package/dist/pick/index.d.ts +153 -0
- package/dist/pick/index.js +14 -0
- package/dist/pick/pick.css +235 -0
- package/dist/plan-DtNw2VuI.d.ts +88 -0
- package/dist/rail/index.d.ts +145 -0
- package/dist/rail/index.js +1 -0
- package/dist/rail/rail.css +175 -0
- package/dist/render/index.d.ts +117 -0
- package/dist/render/index.js +5 -0
- package/dist/resolve-DpH8YQOA.d.ts +57 -0
- package/dist/shell/index.d.ts +159 -0
- package/dist/shell/index.js +1 -0
- package/dist/shell/shell.css +122 -0
- package/dist/strings/catalogue.d.ts +141 -0
- package/dist/strings/catalogue.js +1 -0
- package/dist/strings/index.d.ts +56 -0
- package/dist/strings/index.js +2 -0
- package/dist/style-clipboard/index.d.ts +184 -0
- package/dist/style-clipboard/index.js +1 -0
- package/dist/tree/index.d.ts +173 -0
- package/dist/tree/index.js +1 -0
- package/dist/tree/tree.css +618 -0
- package/dist/types-CEqUFH2j.d.ts +129 -0
- package/dist/types-CuNiSnX6.d.ts +204 -0
- package/dist/types-DNrn_w4V.d.ts +88 -0
- package/dist/types-DziKCwsN.d.ts +105 -0
- package/dist/types-_Vayx8ov.d.ts +153 -0
- package/dist/types-pGBXevBU.d.ts +131 -0
- package/dist/useNodeOps-6IAej9jy.d.ts +68 -0
- package/dist/watch-DmasQA61.d.ts +60 -0
- package/dist/wordFit-9B9nqyf3.d.ts +35 -0
- package/dist/words-B2YLaKEr.d.ts +30 -0
- package/dist/words-B87GIFWH.d.ts +86 -0
- package/dist/words-BK-npta-.d.ts +27 -0
- package/dist/words-CD3WADyb.d.ts +51 -0
- package/dist/words-CsRWXkY4.d.ts +129 -0
- package/dist/words-D9ChvbNC.d.ts +52 -0
- package/dist/words-DOhF-4D4.d.ts +35 -0
- package/dist/words-DagJdwIA.d.ts +78 -0
- package/dist/words-DfxEp_RH.d.ts +142 -0
- 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,295 @@
|
|
|
1
|
+
import { ReactElement, FC, CSSProperties, ReactNode, JSX } from 'react';
|
|
2
|
+
import { P as Permissions, A as AppState } from '../types-_Vayx8ov.js';
|
|
3
|
+
import '../data/index.js';
|
|
4
|
+
import '../types-DziKCwsN.js';
|
|
5
|
+
import '../resolve-DpH8YQOA.js';
|
|
6
|
+
import '../plan-DtNw2VuI.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
|
+
type TextField = BaseField & {
|
|
53
|
+
type: 'text';
|
|
54
|
+
placeholder?: string;
|
|
55
|
+
contentEditable?: boolean;
|
|
56
|
+
};
|
|
57
|
+
type NumberField = BaseField & {
|
|
58
|
+
type: 'number';
|
|
59
|
+
placeholder?: string;
|
|
60
|
+
min?: number;
|
|
61
|
+
max?: number;
|
|
62
|
+
step?: number;
|
|
63
|
+
};
|
|
64
|
+
type TextareaField = BaseField & {
|
|
65
|
+
type: 'textarea';
|
|
66
|
+
placeholder?: string;
|
|
67
|
+
contentEditable?: boolean;
|
|
68
|
+
};
|
|
69
|
+
type SelectField = BaseField & {
|
|
70
|
+
type: 'select';
|
|
71
|
+
options: FieldOptions;
|
|
72
|
+
};
|
|
73
|
+
type RadioField = BaseField & {
|
|
74
|
+
type: 'radio';
|
|
75
|
+
options: FieldOptions;
|
|
76
|
+
};
|
|
77
|
+
type AnyRecord = {
|
|
78
|
+
[key: string]: any;
|
|
79
|
+
};
|
|
80
|
+
type ArrayField<Props extends AnyRecord[] = AnyRecord[]> = BaseField & {
|
|
81
|
+
type: 'array';
|
|
82
|
+
arrayFields: {
|
|
83
|
+
[SubPropName in keyof Props[0]]: Field<Props[0][SubPropName]>;
|
|
84
|
+
};
|
|
85
|
+
defaultItemProps?: Props[0];
|
|
86
|
+
getItemSummary?: (item: Props[0], index?: number) => string;
|
|
87
|
+
max?: number;
|
|
88
|
+
min?: number;
|
|
89
|
+
};
|
|
90
|
+
type ObjectField<Props = AnyRecord> = BaseField & {
|
|
91
|
+
type: 'object';
|
|
92
|
+
objectFields: {
|
|
93
|
+
[SubPropName in keyof Props]: Field<Props[SubPropName]>;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
/** A custom field's drawing: called with Puck's six props, as both apps wrote them. */
|
|
97
|
+
type CustomFieldRender<Value> = (props: {
|
|
98
|
+
field: CustomField<Value>;
|
|
99
|
+
name: string;
|
|
100
|
+
id: string;
|
|
101
|
+
value: Value;
|
|
102
|
+
onChange: (value: Value) => void;
|
|
103
|
+
readOnly?: boolean;
|
|
104
|
+
}) => ReactElement;
|
|
105
|
+
type CustomField<Value> = BaseField & {
|
|
106
|
+
type: 'custom';
|
|
107
|
+
render: CustomFieldRender<Value>;
|
|
108
|
+
contentEditable?: boolean;
|
|
109
|
+
};
|
|
110
|
+
type SlotField = BaseField & {
|
|
111
|
+
type: 'slot';
|
|
112
|
+
allow?: string[];
|
|
113
|
+
disallow?: string[];
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Puck's `external` field: a value picked from a list the app fetches. Neither
|
|
117
|
+
* app has one and the Inspector does not draw it — it is in the union only so
|
|
118
|
+
* the union is Puck's, and a function that takes `Fields` takes Puck's too.
|
|
119
|
+
*/
|
|
120
|
+
type ExternalField<Props = AnyRecord> = BaseField & {
|
|
121
|
+
type: 'external';
|
|
122
|
+
placeholder?: string;
|
|
123
|
+
fetchList: (params: {
|
|
124
|
+
query: string;
|
|
125
|
+
filters: Record<string, unknown>;
|
|
126
|
+
}) => Promise<unknown[] | null>;
|
|
127
|
+
mapProp?: (value: any) => Props;
|
|
128
|
+
mapRow?: (value: any) => Record<string, string | number | ReactElement>;
|
|
129
|
+
getItemSummary?: (item: NonNullable<Props>, index?: number) => string;
|
|
130
|
+
showSearch?: boolean;
|
|
131
|
+
renderFooter?: (props: {
|
|
132
|
+
items: any[];
|
|
133
|
+
}) => ReactElement;
|
|
134
|
+
initialQuery?: string;
|
|
135
|
+
filterFields?: Record<string, Field>;
|
|
136
|
+
initialFilters?: Record<string, any>;
|
|
137
|
+
};
|
|
138
|
+
type Adaptor<AdaptorParams = object, TableShape extends AnyRecord = AnyRecord, PropShape = TableShape> = {
|
|
139
|
+
name: string;
|
|
140
|
+
fetchList: (adaptorParams?: AdaptorParams) => Promise<TableShape[] | null>;
|
|
141
|
+
mapProp?: (value: TableShape) => PropShape;
|
|
142
|
+
};
|
|
143
|
+
type ExternalFieldWithAdaptor<Props = AnyRecord> = BaseField & {
|
|
144
|
+
type: 'external';
|
|
145
|
+
placeholder?: string;
|
|
146
|
+
adaptor: Adaptor<any, any, Props>;
|
|
147
|
+
adaptorParams?: object;
|
|
148
|
+
getItemSummary: (item: NonNullable<Props>, index?: number) => string;
|
|
149
|
+
};
|
|
150
|
+
type Field<ValueType = any> = TextField | NumberField | TextareaField | SelectField | RadioField | ArrayField<ValueType extends AnyRecord[] ? ValueType : never> | ObjectField<ValueType> | ExternalField<ValueType> | ExternalFieldWithAdaptor<ValueType> | CustomField<ValueType> | SlotField;
|
|
151
|
+
/** A block's fields, one per prop (`editMode` is the editor's, never a field). */
|
|
152
|
+
type Fields<ComponentProps extends DefaultComponentProps = DefaultComponentProps> = {
|
|
153
|
+
[PropName in keyof Omit<ComponentProps, 'editMode'>]: Field<ComponentProps[PropName]>;
|
|
154
|
+
};
|
|
155
|
+
type BaseData<Props extends AnyRecord = AnyRecord> = {
|
|
156
|
+
readOnly?: Partial<Record<keyof Props, boolean>>;
|
|
157
|
+
};
|
|
158
|
+
type WithId<Props> = Props & {
|
|
159
|
+
id: string;
|
|
160
|
+
};
|
|
161
|
+
type ComponentData<Props extends DefaultComponentProps = DefaultComponentProps, Name = string, Components extends Record<string, DefaultComponentProps> = Record<string, DefaultComponentProps>> = {
|
|
162
|
+
type: Name;
|
|
163
|
+
props: WithDeepSlots<WithId<Props>, Content<Components>>;
|
|
164
|
+
} & BaseData<Props>;
|
|
165
|
+
type ComponentDataOptionalId<Props extends DefaultComponentProps = DefaultComponentProps, Name = string> = {
|
|
166
|
+
type: Name;
|
|
167
|
+
props: Props & {
|
|
168
|
+
id?: string;
|
|
169
|
+
};
|
|
170
|
+
} & BaseData<Props>;
|
|
171
|
+
type ComponentDataMap<Components extends DefaultComponents = DefaultComponents> = {
|
|
172
|
+
[K in keyof Components]: ComponentData<Components[K], K extends string ? K : never, Components>;
|
|
173
|
+
}[keyof Components];
|
|
174
|
+
/** A list of blocks: the top level, or a slot's. */
|
|
175
|
+
type Content<PropsMap extends {
|
|
176
|
+
[key: string]: DefaultComponentProps;
|
|
177
|
+
} = {
|
|
178
|
+
[key: string]: DefaultComponentProps;
|
|
179
|
+
}> = ComponentDataMap<PropsMap>[];
|
|
180
|
+
/** A slot prop, as STORED: the blocks in it. Its `render` receives a `SlotComponent` instead. */
|
|
181
|
+
type Slot<Props extends {
|
|
182
|
+
[key: string]: DefaultComponentProps;
|
|
183
|
+
} = {
|
|
184
|
+
[key: string]: DefaultComponentProps;
|
|
185
|
+
}> = {
|
|
186
|
+
[K in keyof Props]: ComponentDataOptionalId<Props[K], K extends string ? K : never>;
|
|
187
|
+
}[keyof Props][];
|
|
188
|
+
type BuiltinTypes = Date | RegExp | Error | Function | symbol | null | undefined;
|
|
189
|
+
/** `T` with every `Slot` in it, however deep, replaced by `SlotType`. */
|
|
190
|
+
type WithDeepSlots<T, SlotType = T> = T extends Slot ? SlotType : T extends (infer U)[] ? Array<WithDeepSlots<U, SlotType>> : T extends BuiltinTypes ? T : T extends object ? {
|
|
191
|
+
[K in keyof T]: WithDeepSlots<T[K], SlotType>;
|
|
192
|
+
} : T;
|
|
193
|
+
type WithSlotProps<Target extends AnyRecord, Components extends DefaultComponents = DefaultComponents, SlotType extends Content<Components> = Content<Components>> = WithDeepSlots<Target, SlotType>;
|
|
194
|
+
type DefaultRootFieldProps = {
|
|
195
|
+
title?: string;
|
|
196
|
+
};
|
|
197
|
+
type RootDataWithProps<Props extends DefaultComponentProps = DefaultRootFieldProps> = BaseData<Props> & {
|
|
198
|
+
props: Props;
|
|
199
|
+
};
|
|
200
|
+
type RootDataWithoutProps<Props extends DefaultComponentProps = DefaultRootFieldProps> = Props;
|
|
201
|
+
/** The root: its props under `props`, or (the older format) held directly. */
|
|
202
|
+
type RootData<Props extends DefaultComponentProps = DefaultRootFieldProps> = Partial<RootDataWithProps<AsFieldProps<Props>>> & Partial<RootDataWithoutProps<Props>>;
|
|
203
|
+
/** A saved campaign or page — Puck 0.20's document, which nothing has migrated off. */
|
|
204
|
+
type Data<Components extends DefaultComponents = DefaultComponents, RootProps extends DefaultComponentProps = DefaultRootFieldProps> = {
|
|
205
|
+
root: WithDeepSlots<RootData<RootProps>, Content<Components>>;
|
|
206
|
+
content: Content<Components>;
|
|
207
|
+
zones?: Record<string, Content<Components>>;
|
|
208
|
+
};
|
|
209
|
+
type DragAxis = 'dynamic' | 'y' | 'x';
|
|
210
|
+
type DropZoneProps = {
|
|
211
|
+
zone: string;
|
|
212
|
+
allow?: string[];
|
|
213
|
+
disallow?: string[];
|
|
214
|
+
style?: CSSProperties;
|
|
215
|
+
minEmptyHeight?: number;
|
|
216
|
+
className?: string;
|
|
217
|
+
collisionAxis?: DragAxis;
|
|
218
|
+
};
|
|
219
|
+
/** A slot, as a block's `render` receives it: draw it with `<Content />`. */
|
|
220
|
+
type SlotComponent = (props?: Omit<DropZoneProps, 'zone'>) => ReactNode;
|
|
221
|
+
/** The `puck` prop every block's `render` receives. */
|
|
222
|
+
type BlockContext = {
|
|
223
|
+
renderDropZone: FC<DropZoneProps>;
|
|
224
|
+
metadata: Metadata;
|
|
225
|
+
isEditing: boolean;
|
|
226
|
+
dragRef: ((element: Element | null) => void) | null;
|
|
227
|
+
};
|
|
228
|
+
type WithBlockProps<Props> = Props & {
|
|
229
|
+
puck: BlockContext;
|
|
230
|
+
editMode?: boolean;
|
|
231
|
+
};
|
|
232
|
+
type WithChildren<Props> = Props & {
|
|
233
|
+
children: ReactNode;
|
|
234
|
+
};
|
|
235
|
+
type AsFieldProps<Props> = Omit<Props, 'children' | 'puck' | 'editMode'>;
|
|
236
|
+
/** A block's `render`: its own props with slots as `SlotComponent`s, its id, and `puck`. */
|
|
237
|
+
type BlockComponent<Props> = (props: WithId<WithBlockProps<{
|
|
238
|
+
[K in keyof Props]: WithDeepSlots<Props[K], SlotComponent>;
|
|
239
|
+
}>>) => JSX.Element;
|
|
240
|
+
type ResolveDataTrigger = 'insert' | 'replace' | 'load' | 'force';
|
|
241
|
+
type Changed<FieldProps> = Partial<Record<keyof FieldProps, boolean> & {
|
|
242
|
+
id: string;
|
|
243
|
+
}>;
|
|
244
|
+
type WithPartialProps<T, Props extends DefaultComponentProps> = Omit<T, 'props'> & {
|
|
245
|
+
props?: Partial<Props>;
|
|
246
|
+
};
|
|
247
|
+
type ComponentConfig<RenderProps extends DefaultComponentProps = DefaultComponentProps, FieldProps extends DefaultComponentProps = RenderProps, DataShape = Omit<ComponentData<FieldProps>, 'type'>> = {
|
|
248
|
+
render: BlockComponent<RenderProps>;
|
|
249
|
+
label?: string;
|
|
250
|
+
defaultProps?: FieldProps;
|
|
251
|
+
fields?: Fields<FieldProps>;
|
|
252
|
+
permissions?: Partial<Permissions>;
|
|
253
|
+
inline?: boolean;
|
|
254
|
+
resolveFields?: (data: DataShape, params: {
|
|
255
|
+
changed: Changed<FieldProps>;
|
|
256
|
+
fields: Fields<FieldProps>;
|
|
257
|
+
lastFields: Fields<FieldProps>;
|
|
258
|
+
lastData: DataShape | null;
|
|
259
|
+
appState: AppState;
|
|
260
|
+
parent: ComponentData | null;
|
|
261
|
+
}) => Promise<Fields<FieldProps>> | Fields<FieldProps>;
|
|
262
|
+
resolveData?: (data: DataShape, params: {
|
|
263
|
+
changed: Changed<FieldProps>;
|
|
264
|
+
lastData: DataShape | null;
|
|
265
|
+
metadata: Metadata;
|
|
266
|
+
trigger: ResolveDataTrigger;
|
|
267
|
+
}) => Promise<WithPartialProps<DataShape, FieldProps>> | WithPartialProps<DataShape, FieldProps>;
|
|
268
|
+
resolvePermissions?: (data: DataShape, params: {
|
|
269
|
+
changed: Changed<FieldProps>;
|
|
270
|
+
lastPermissions: Partial<Permissions>;
|
|
271
|
+
permissions: Partial<Permissions>;
|
|
272
|
+
appState: AppState;
|
|
273
|
+
lastData: DataShape | null;
|
|
274
|
+
}) => Promise<Partial<Permissions>> | Partial<Permissions>;
|
|
275
|
+
metadata?: Metadata;
|
|
276
|
+
};
|
|
277
|
+
type RootConfig<RootProps extends DefaultComponentProps = DefaultComponentProps> = Partial<ComponentConfig<WithChildren<RootProps>, AsFieldProps<RootProps>, RootData<AsFieldProps<RootProps>>>>;
|
|
278
|
+
type Category<ComponentName> = {
|
|
279
|
+
components?: ComponentName[];
|
|
280
|
+
title?: string;
|
|
281
|
+
visible?: boolean;
|
|
282
|
+
defaultExpanded?: boolean;
|
|
283
|
+
};
|
|
284
|
+
/** A whole editor's blocks: `components` by type, the `root`, and the Add panel's `categories`. */
|
|
285
|
+
type Config<Props extends DefaultComponents = DefaultComponents, RootProps extends DefaultComponentProps = any, CategoryName extends string = string> = {
|
|
286
|
+
categories?: Record<CategoryName, Category<keyof Props>> & {
|
|
287
|
+
other?: Category<keyof Props>;
|
|
288
|
+
};
|
|
289
|
+
components: {
|
|
290
|
+
[ComponentName in keyof Props]: ComponentConfig<Props[ComponentName], Props[ComponentName]>;
|
|
291
|
+
};
|
|
292
|
+
root?: RootConfig<RootProps>;
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
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";
|