efficient-easel 0.99.1 → 0.100.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/README.md +281 -36
- package/dist/autosave/index.d.ts +12 -6
- package/dist/blocks/index.d.ts +2 -2
- package/dist/canvas/index.d.ts +3 -3
- package/dist/canvas/index.js +1 -1
- package/dist/chrome/chrome.css +96 -29
- package/dist/chrome/index.d.ts +72 -19
- package/dist/chrome/index.js +17 -21
- package/dist/chunk-3EDSDPIP.js +1 -0
- package/dist/chunk-67GNT6PF.js +1 -0
- package/dist/{chunk-PI26YXYD.js → chunk-6EIVTKZZ.js} +21 -14
- package/dist/chunk-A5UK76FC.js +6 -0
- package/dist/chunk-AZT7V4UM.js +10 -0
- package/dist/chunk-DICUMYMG.js +12 -0
- package/dist/chunk-HDVD27VB.js +1 -0
- package/dist/chunk-JQM3V7XQ.js +1 -0
- package/dist/{chunk-TDA4HH54.js → chunk-JUQ6YJCG.js} +1 -1
- package/dist/chunk-LA54QLP5.js +1 -0
- package/dist/chunk-LGKQXSKO.js +16 -0
- package/dist/chunk-M6F6LS3H.js +1 -0
- package/dist/chunk-MH5GUZFQ.js +9 -0
- package/dist/chunk-P6O363EE.js +6 -0
- package/dist/chunk-PAELDYYS.js +1 -0
- package/dist/{chunk-JYPOOHII.js → chunk-QKVR3K73.js} +3 -3
- package/dist/chunk-SAJUCHOX.js +1 -0
- package/dist/chunk-TRVZ7C23.js +1 -0
- package/dist/chunk-V7GIIXFB.js +16 -0
- package/dist/{chunk-IBOFSS56.js → chunk-WZ7TGAAT.js} +1 -1
- package/dist/chunk-ZJEBF4NB.js +1 -0
- package/dist/copy/copy.css +388 -5
- package/dist/copy/index.d.ts +271 -8
- package/dist/copy/index.js +31 -10
- package/dist/data/index.d.ts +4 -4
- package/dist/drag/drag.css +10 -1
- package/dist/drag/index.d.ts +102 -11
- package/dist/drag/index.js +18 -17
- package/dist/{editor/editor.css → easel/easel.css} +16 -1
- package/dist/{editor → easel}/index.d.ts +146 -51
- package/dist/easel/index.js +9 -0
- package/dist/edit-lock/index.d.ts +3 -3
- package/dist/edit-lock/index.js +1 -1
- package/dist/fields/fields.css +103 -26
- package/dist/fields/index.d.ts +51 -13
- package/dist/fields/index.js +30 -46
- package/dist/gold/index.js +1 -1
- package/dist/icons/index.d.ts +10 -1
- package/dist/icons/index.js +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/markup/index.d.ts +94 -10
- package/dist/markup/index.js +1 -1
- package/dist/menu/index.d.ts +61 -6
- package/dist/menu/index.js +15 -15
- package/dist/menu/menu.css +17 -6
- package/dist/node-ops/index.d.ts +9 -9
- package/dist/node-ops/index.js +1 -1
- package/dist/panel/index.d.ts +73 -5
- package/dist/panel/index.js +18 -16
- package/dist/panel/panel.css +56 -1
- package/dist/pick/index.d.ts +42 -4
- package/dist/pick/index.js +7 -14
- package/dist/pick/pick.css +14 -0
- package/dist/rail/index.d.ts +6 -6
- package/dist/rail/index.js +1 -1
- package/dist/rail/rail.css +1 -1
- package/dist/strings/catalogue.d.ts +24 -40
- package/dist/strings/catalogue.js +1 -1
- package/dist/strings/index.d.ts +25 -23
- package/dist/strings/index.js +2 -2
- package/dist/style-clipboard/index.d.ts +5 -5
- package/dist/style-clipboard/index.js +1 -1
- package/dist/text-styles/index.d.ts +328 -0
- package/dist/text-styles/index.js +38 -0
- package/dist/text-styles/text-styles.css +309 -0
- package/dist/tree/index.d.ts +44 -3
- package/dist/tree/index.js +1 -1
- package/dist/tree/tree.css +71 -0
- package/dist/{types-CnRzp9oc.d.ts → types-DGcCsxs8.d.ts} +79 -12
- package/dist/{types-DNrn_w4V.d.ts → types-DWS-s9z-.d.ts} +10 -10
- package/dist/{types-pGBXevBU.d.ts → types-Djdq6Xkd.d.ts} +15 -8
- package/dist/{types-Dy6oR6Z2.d.ts → types-E3HrTPg2.d.ts} +20 -14
- package/dist/{types-C7HFvdtk.d.ts → types-VqL6t5HH.d.ts} +82 -5
- package/dist/{watch-DmasQA61.d.ts → watch-BTYOJpQU.d.ts} +19 -2
- package/dist/{wordFit-9B9nqyf3.d.ts → wordFit-D4S36PvB.d.ts} +1 -1
- package/dist/{words-DagJdwIA.d.ts → words-AHHfiaRY.d.ts} +31 -2
- package/dist/{words-D9ChvbNC.d.ts → words-BDignMl_.d.ts} +1 -1
- package/dist/{words-Cqmyey0s.d.ts → words-BMzV04b1.d.ts} +2 -2
- package/dist/words-CJjUKcV5.d.ts +37 -0
- package/dist/words-CRYvAiHX.d.ts +105 -0
- package/dist/{words-B2YLaKEr.d.ts → words-CnnHb9jo.d.ts} +1 -1
- package/dist/{words-CD3WADyb.d.ts → words-CzoyDNHm.d.ts} +4 -0
- package/dist/{words-bOMsUJk9.d.ts → words-DBq7vfHy.d.ts} +59 -6
- package/dist/{words-DOhF-4D4.d.ts → words-DMG4dYq2.d.ts} +1 -1
- package/dist/{words-BK-npta-.d.ts → words-DgHcY5qB.d.ts} +10 -4
- package/dist/{words-B87GIFWH.d.ts → words-is_Vy6hn.d.ts} +1 -1
- package/package.json +14 -8
- package/dist/chunk-66CKSTXP.js +0 -14
- package/dist/chunk-67VZRQLG.js +0 -13
- package/dist/chunk-6PS6STGA.js +0 -1
- package/dist/chunk-7A7O2JWQ.js +0 -1
- package/dist/chunk-H4B72XCJ.js +0 -1
- package/dist/chunk-JE63JHKO.js +0 -1
- package/dist/chunk-KDIVA6WD.js +0 -1
- package/dist/chunk-MCLBHENY.js +0 -1
- package/dist/chunk-QB3BZIRA.js +0 -5
- package/dist/chunk-SJQG5ENJ.js +0 -1
- package/dist/chunk-SUO5627P.js +0 -1
- package/dist/chunk-ZT7M24XD.js +0 -1
- package/dist/editor/index.js +0 -9
- /package/dist/{chunk-GGTPNGFJ.js → chunk-AELNNAFM.js} +0 -0
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
import { DataInput } from '../data/index.js';
|
|
2
2
|
import { N as NodeSelection } from '../plan-DChbUXdS.js';
|
|
3
3
|
export { I as ItemSelector } from '../plan-DChbUXdS.js';
|
|
4
|
-
import { E as
|
|
5
|
-
export { A as AppState, e as
|
|
4
|
+
import { E as EaselConfig, a as EaselView, b as EaselAction, P as Permissions, c as EaselCore, d as EaselUi } from '../types-E3HrTPg2.js';
|
|
5
|
+
export { A as AppState, e as EaselHistory, H as HistoryEntry, S as SetDataPatch } from '../types-E3HrTPg2.js';
|
|
6
6
|
import { S as SlotAddress, D as Data, P as Place } from '../types-41704JE4.js';
|
|
7
7
|
import { c as ResolveTrigger, a as ResolveCache, R as ResolvableItem } from '../resolve-D68pb3Bx.js';
|
|
8
8
|
import * as react from 'react';
|
|
9
9
|
import { ReactNode, CSSProperties, ComponentType, ReactElement } from 'react';
|
|
10
10
|
import { b as NodeOpsHost, C as ClipboardEntry } from '../useNodeOps-BF0fQsif.js';
|
|
11
|
-
import { I as InspectorFields } from '../types-
|
|
11
|
+
import { I as InspectorFields } from '../types-VqL6t5HH.js';
|
|
12
12
|
import '../buckets-CuLM47h9.js';
|
|
13
13
|
|
|
14
|
-
interface
|
|
14
|
+
interface EaselOptions<C extends EaselConfig = EaselConfig> {
|
|
15
15
|
config: C;
|
|
16
16
|
/** The document — Puck 0.20's shape, unchanged. */
|
|
17
17
|
data: DataInput | Data;
|
|
18
18
|
/** The host's own UI keys to start with (and an `itemSelector`, if any). */
|
|
19
19
|
ui?: Record<string, unknown>;
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
20
|
+
/**
|
|
21
|
+
* Called with the new document after every change that changed it — once
|
|
22
|
+
* per `batch`, once per undo / redo. Not called for `load`.
|
|
23
|
+
*
|
|
24
|
+
* THIS is what feeds autosave (`autosave.push`), not `subscribe`: a
|
|
25
|
+
* subscriber is also told of a `load()`, a selection and a permission, and a
|
|
26
|
+
* host that pushed every notification would save the document it had just
|
|
27
|
+
* loaded as if it were an edit.
|
|
28
|
+
*/
|
|
22
29
|
onChange?: (data: Data) => void;
|
|
23
|
-
/**
|
|
30
|
+
/** Easel-wide permissions, under each block's own (Puck's `permissions`). */
|
|
24
31
|
permissions?: Partial<Permissions>;
|
|
25
32
|
/**
|
|
26
33
|
* Changes recorded closer together than this collapse into ONE undo step —
|
|
@@ -32,12 +39,22 @@ interface EditorOptions<C extends EditorConfig = EditorConfig> {
|
|
|
32
39
|
/** The clock, for tests. */
|
|
33
40
|
now?: () => number;
|
|
34
41
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
/**
|
|
43
|
+
* `EaselProvider` takes an `Easel<EaselConfig>`, and `createEasel` infers `C`
|
|
44
|
+
* from the config it is handed. A config typed more narrowly than
|
|
45
|
+
* `EaselConfig` (a `Config<…>` from `efficient-easel/blocks`) therefore infers
|
|
46
|
+
* an `Easel<ThatConfig>`, which the provider does not accept (`setConfig`
|
|
47
|
+
* takes a `C`). Say the type: `createEasel<EaselConfig>({ config, … })`.
|
|
48
|
+
*/
|
|
49
|
+
interface Easel<C extends EaselConfig = EaselConfig> {
|
|
50
|
+
/** What `useEasel` reads. The same object until the state changes. */
|
|
51
|
+
getState: () => EaselView<C>;
|
|
38
52
|
subscribe: (listener: () => void) => () => void;
|
|
39
|
-
/**
|
|
40
|
-
|
|
53
|
+
/**
|
|
54
|
+
* One Puck action. Outside a `batch`, one undo step when it records.
|
|
55
|
+
* Permissions are not checked here (see `setPermissions`).
|
|
56
|
+
*/
|
|
57
|
+
dispatch: (action: EaselAction) => void;
|
|
41
58
|
/**
|
|
42
59
|
* Run several changes as ONE: one undo step, one `onChange`, and one
|
|
43
60
|
* notification to subscribers. Each dispatch inside sees the ones before it.
|
|
@@ -65,20 +82,57 @@ interface Editor<C extends EditorConfig = EditorConfig> {
|
|
|
65
82
|
}) => void;
|
|
66
83
|
undo: () => void;
|
|
67
84
|
redo: () => void;
|
|
85
|
+
/**
|
|
86
|
+
* Replace the easel-wide permissions (not merged over the last ones: name
|
|
87
|
+
* every key you mean). Subscribers are told, and `getPermissions` becomes a
|
|
88
|
+
* new function, so a reader of either re-renders — a settings panel reading
|
|
89
|
+
* `getPermissions().edit` follows an edit lock as it is claimed and lost.
|
|
90
|
+
* The same permissions again tell nobody, so it is safe in an effect.
|
|
91
|
+
*
|
|
92
|
+
* These are what the PARTS read (the settings panel, the menus, the undo
|
|
93
|
+
* keys). The store itself enforces none of them: `dispatch`, `run`, `undo`
|
|
94
|
+
* and `redo` write whatever they are asked to. A host that must not write —
|
|
95
|
+
* somebody else holds the document — gates its own calls.
|
|
96
|
+
*/
|
|
68
97
|
setPermissions: (permissions: Partial<Permissions>) => void;
|
|
69
98
|
/**
|
|
70
99
|
* Run every block's `resolveData` over the document, as `<Puck>` did when it
|
|
71
100
|
* mounted (trigger `'load'`), and write what changed as ONE change folded
|
|
72
101
|
* into the step it happens in — a resolver bringing a stale document up to
|
|
73
102
|
* date is not an undo step (Puck made it one, which undid back to the stale
|
|
74
|
-
* document). `onChange` hears it, as Puck's did. `
|
|
103
|
+
* document). `onChange` hears it, as Puck's did. `EaselProvider` runs this
|
|
75
104
|
* on mount; a node changed by hand while it ran is left as the hand left it.
|
|
76
105
|
*/
|
|
77
106
|
resolveAll: (options?: {
|
|
78
107
|
trigger?: ResolveTrigger;
|
|
79
108
|
metadata?: Record<string, unknown>;
|
|
80
109
|
}) => Promise<void>;
|
|
81
|
-
/**
|
|
110
|
+
/**
|
|
111
|
+
* Run ONE node's `resolveData`, and that of everything in its slots, and
|
|
112
|
+
* write what changed folded into the step it happens in — `resolveAll`, for
|
|
113
|
+
* one node. Resolves to whether anything was written.
|
|
114
|
+
*
|
|
115
|
+
* It is what Puck's own insert ran: a block dropped from its component list
|
|
116
|
+
* was resolved with trigger `'insert'` (`insertComponent`), which is how a
|
|
117
|
+
* block seeds its children or stamps a default the moment it arrives. The
|
|
118
|
+
* `insert` ACTION never did — Puck's reducer was pure, and so is this one —
|
|
119
|
+
* and the engine has no drop of its own: the Add list's click and the drag's
|
|
120
|
+
* drop are the host's. So a host that puts a new block in, by `dispatch` or
|
|
121
|
+
* by `run`, follows it with this (second host, 2026-10-07):
|
|
122
|
+
*
|
|
123
|
+
* easel.dispatch({ type: 'insert', componentType, destinationZone, destinationIndex, id });
|
|
124
|
+
* void easel.resolveNode(id, { trigger: 'insert' });
|
|
125
|
+
*
|
|
126
|
+
* ONE undo step with the insert (Puck made the resolver's write a second
|
|
127
|
+
* one, and a single undo left a block that had never been resolved). A node
|
|
128
|
+
* changed by hand, or taken out, while its resolver ran is left as it is.
|
|
129
|
+
* `trigger` is `'replace'` when not given, as `resolveNodeData`'s is.
|
|
130
|
+
*/
|
|
131
|
+
resolveNode: (id: string, options?: {
|
|
132
|
+
trigger?: ResolveTrigger;
|
|
133
|
+
metadata?: Record<string, unknown>;
|
|
134
|
+
}) => Promise<boolean>;
|
|
135
|
+
/** This easel's memory of the last change each node was resolved for —
|
|
82
136
|
* Puck kept one per editor, shared by every write that resolves. */
|
|
83
137
|
readonly resolveCache: ResolveCache;
|
|
84
138
|
/**
|
|
@@ -98,11 +152,11 @@ interface Editor<C extends EditorConfig = EditorConfig> {
|
|
|
98
152
|
*/
|
|
99
153
|
declare const DEFAULT_UI: Record<string, unknown>;
|
|
100
154
|
/**
|
|
101
|
-
* THE
|
|
155
|
+
* THE EASEL STORE (blueprint step 5.1): the document, the selection by id and
|
|
102
156
|
* the host's UI keys, with Puck's actions, Puck's history and a `batch` Puck
|
|
103
|
-
* never had. Framework-free — `
|
|
157
|
+
* never had. Framework-free — `useEasel` (./react) is the React binding.
|
|
104
158
|
*/
|
|
105
|
-
declare function
|
|
159
|
+
declare function createEasel<C extends EaselConfig>(options: EaselOptions<C>): Easel<C>;
|
|
106
160
|
|
|
107
161
|
/**
|
|
108
162
|
* Puck 0.20's reducer, on `efficient-easel/data` — blueprint step 5.1.
|
|
@@ -123,31 +177,39 @@ declare function createEditor<C extends EditorConfig>(options: EditorOptions<C>)
|
|
|
123
177
|
*/
|
|
124
178
|
/** Whether Puck records an action in history when the action does not say:
|
|
125
179
|
* everything but the state-setters. */
|
|
126
|
-
declare function recordsByDefault(action:
|
|
180
|
+
declare function recordsByDefault(action: EaselAction): boolean;
|
|
127
181
|
/** The UI as Puck shows it: the host's keys, with the selection translated. */
|
|
128
|
-
declare function uiView(core:
|
|
182
|
+
declare function uiView(core: EaselCore, config: EaselConfig): EaselUi;
|
|
129
183
|
/** A selection that still points at something; otherwise nothing. */
|
|
130
|
-
declare function validSelection(data: Data, config:
|
|
184
|
+
declare function validSelection(data: Data, config: EaselConfig, selection: NodeSelection): NodeSelection;
|
|
131
185
|
/** One action, applied. Pure: the same core and action give the same core. */
|
|
132
|
-
declare function reduce(core:
|
|
186
|
+
declare function reduce(core: EaselCore, action: EaselAction, config: EaselConfig): EaselCore;
|
|
133
187
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
188
|
+
interface EaselProviderProps {
|
|
189
|
+
easel: Easel;
|
|
190
|
+
/**
|
|
191
|
+
* Bind undo / redo: to the page here, and in the canvas frame
|
|
192
|
+
* (`EaselCanvas`, whose own `hotkeys` can say otherwise for the frame
|
|
193
|
+
* alone). `false` binds NEITHER, for a host that binds its own. On by
|
|
194
|
+
* default. Whatever this says, the keys do nothing while the easel-wide
|
|
195
|
+
* `edit` permission is off (`bindUndoKeys`).
|
|
196
|
+
*/
|
|
140
197
|
hotkeys?: boolean;
|
|
141
|
-
/** Run every block's `resolveData` once the
|
|
142
|
-
* (`
|
|
198
|
+
/** Run every block's `resolveData` once the easel mounts, as `<Puck>` did
|
|
199
|
+
* (`easel.resolveAll`). On by default. */
|
|
143
200
|
resolveOnMount?: boolean;
|
|
144
201
|
children?: ReactNode;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
202
|
+
}
|
|
203
|
+
/** Puts one easel in reach of everything under it — `<Puck>`'s provider
|
|
204
|
+
* half — and, as `<Puck>` did, binds undo / redo to the page's keyboard
|
|
205
|
+
* (`hotkeys={false}` for a host that binds its own) and runs every block's
|
|
206
|
+
* `resolveData` once on mount (`resolveOnMount={false}` to skip). */
|
|
207
|
+
declare function EaselProvider({ easel, hotkeys, resolveOnMount, children }: EaselProviderProps): react.JSX.Element;
|
|
208
|
+
/** The easel itself, for handlers that read the state at the moment they run
|
|
147
209
|
* (`useGetPuck`'s job) and for `dispatch` / `batch` / `select`. */
|
|
148
|
-
declare function
|
|
210
|
+
declare function useEaselApi<C extends EaselConfig = EaselConfig>(): Easel<C>;
|
|
149
211
|
/**
|
|
150
|
-
* Read a slice of the
|
|
212
|
+
* Read a slice of the easel and re-render when it changes — `usePuck` /
|
|
151
213
|
* `createUsePuck()`'s selector form, over the same state shape, so the apps'
|
|
152
214
|
* selectors move over by renaming the hook.
|
|
153
215
|
*
|
|
@@ -155,12 +217,12 @@ declare function useEditorApi<C extends EditorConfig = EditorConfig>(): Editor<C
|
|
|
155
217
|
* per selector, so React sees the same value for the same state and does not
|
|
156
218
|
* loop. A primitive or an unchanged reference does not re-render.
|
|
157
219
|
*/
|
|
158
|
-
declare function
|
|
220
|
+
declare function useEasel<T, C extends EaselConfig = EaselConfig>(selector: (view: EaselView<C>) => T): T;
|
|
159
221
|
/**
|
|
160
|
-
* A typed `
|
|
222
|
+
* A typed `useEasel` for one config — `createUsePuck<Config>()`'s shape, so
|
|
161
223
|
* a file that made its own typed hook keeps the one line that made it.
|
|
162
224
|
*/
|
|
163
|
-
declare function
|
|
225
|
+
declare function createUseEasel<C extends EaselConfig = EaselConfig>(): <T>(selector: (view: EaselView<C>) => T) => T;
|
|
164
226
|
|
|
165
227
|
/** What a slot component accepts — Puck's. */
|
|
166
228
|
interface CanvasSlotProps {
|
|
@@ -177,10 +239,12 @@ interface CanvasFrameProps {
|
|
|
177
239
|
id?: string;
|
|
178
240
|
/**
|
|
179
241
|
* The frame's accessible name. Default "Canvas", in the strings provider's
|
|
180
|
-
* language (`
|
|
242
|
+
* language (`EaselStrings.canvas.frame`).
|
|
181
243
|
*/
|
|
182
244
|
title?: string;
|
|
245
|
+
/** Added to the frame's own classes (`es-canvas-frame`, and `is-fill` when it fills). */
|
|
183
246
|
className?: string;
|
|
247
|
+
/** The host's own style for the frame. The engine writes none: see the note on `CanvasFrame`. */
|
|
184
248
|
style?: CSSProperties;
|
|
185
249
|
/** Copy the host page's stylesheets into the frame, and keep them in step.
|
|
186
250
|
* On by default: the blocks are styled by the app's own CSS. */
|
|
@@ -208,7 +272,7 @@ interface CanvasFrameProps {
|
|
|
208
272
|
nonce?: string;
|
|
209
273
|
}
|
|
210
274
|
/**
|
|
211
|
-
* An iframe of the
|
|
275
|
+
* An iframe of the easel's own, with the content portalled in. No `src`: the
|
|
212
276
|
* frame's first document is used — there to write into at once, with no load
|
|
213
277
|
* to wait for and nothing fetched. The host's stylesheets are copied into its
|
|
214
278
|
* head and copied again whenever the host's head changes (a route's CSS
|
|
@@ -221,12 +285,21 @@ interface CanvasFrameProps {
|
|
|
221
285
|
* empty slot grew to the frame's full height), tables stop inheriting the
|
|
222
286
|
* page's font, and an image's line box changes. (0.89.0–0.92.0 shipped the
|
|
223
287
|
* quirks-mode frame.)
|
|
288
|
+
*
|
|
289
|
+
* ── Its look is a CLASS, not a `style` (second host, 2026-10-06) ───────────
|
|
290
|
+
* No border, the pane's width, and the pane's height when it fills are rules
|
|
291
|
+
* in easel.css (`.es-canvas-frame`, `.is-fill`). They were a `style` prop,
|
|
292
|
+
* which a server renders as a `style=""` attribute — and a Content-Security-
|
|
293
|
+
* Policy without `'unsafe-inline'` for styles refuses those: a server-rendered
|
|
294
|
+
* editor showed a bordered 300×150 frame, and hydration does not put a refused
|
|
295
|
+
* attribute right. The measured height of an `auto` frame is still written to
|
|
296
|
+
* `style`, on the client, through the CSSOM, which no policy governs.
|
|
224
297
|
*/
|
|
225
298
|
declare function CanvasFrame({ children, id, title, className, style, mirrorStyles, onDocument, height, nonce, }: CanvasFrameProps): react.JSX.Element;
|
|
226
299
|
/**
|
|
227
300
|
* The whole document, for editing. Works in a `CanvasFrame` or on its own.
|
|
228
301
|
*
|
|
229
|
-
* `metadata` is the
|
|
302
|
+
* `metadata` is the easel-wide metadata: the root's `puck.metadata`, and —
|
|
230
303
|
* under each block's own — every block's (see the header).
|
|
231
304
|
*/
|
|
232
305
|
declare function EditDocument({ metadata }: {
|
|
@@ -240,13 +313,13 @@ type CanvasOverlay = ComponentType<{
|
|
|
240
313
|
kind: 'selected' | 'hover';
|
|
241
314
|
children: ReactNode;
|
|
242
315
|
}>;
|
|
243
|
-
interface
|
|
244
|
-
/** Draw in an iframe of the
|
|
316
|
+
interface EaselCanvasProps {
|
|
317
|
+
/** Draw in an iframe of the easel's own (default), or straight into the page. */
|
|
245
318
|
frame?: boolean;
|
|
246
319
|
className?: string;
|
|
247
320
|
style?: CSSProperties;
|
|
248
321
|
/**
|
|
249
|
-
* The
|
|
322
|
+
* The easel-wide metadata — what the published page passes `Render` as
|
|
250
323
|
* `metadata`. Every block reads it as `puck.metadata` (its own `metadata`
|
|
251
324
|
* laid over it) and the root as it is. Change it whenever the host's data
|
|
252
325
|
* changes: the blocks redraw in place, nothing remounts, and it never enters
|
|
@@ -265,6 +338,16 @@ interface EditorCanvasProps {
|
|
|
265
338
|
title?: string;
|
|
266
339
|
/** The page's CSP nonce for the frame's stylesheets — see `CanvasFrameProps.nonce`. Unused with `frame={false}`. */
|
|
267
340
|
nonce?: string;
|
|
341
|
+
/**
|
|
342
|
+
* Bind undo / redo in the frame's document — a key pressed in the frame
|
|
343
|
+
* never reaches the page. Left out, it follows `EaselProvider`'s `hotkeys`,
|
|
344
|
+
* so one switch turns the easel's keys off everywhere. Say `true` for a
|
|
345
|
+
* host that binds the PAGE's keys itself (to its own root, because the page
|
|
346
|
+
* is another app's) and still wants the frame's; `false` for the frame
|
|
347
|
+
* alone. Either way the keys do nothing while the easel-wide `edit`
|
|
348
|
+
* permission is off (`bindUndoKeys`). Unused with `frame={false}`.
|
|
349
|
+
*/
|
|
350
|
+
hotkeys?: boolean;
|
|
268
351
|
onDocument?: (doc: Document | null) => void;
|
|
269
352
|
/**
|
|
270
353
|
* Wraps everything drawn in the frame, and is handed the frame's document —
|
|
@@ -282,9 +365,9 @@ interface EditorCanvasProps {
|
|
|
282
365
|
}
|
|
283
366
|
/**
|
|
284
367
|
* `<Puck.Preview>`, ours: the document for editing, its outlines, and the
|
|
285
|
-
* clicks that select. Must sit under an `
|
|
368
|
+
* clicks that select. Must sit under an `EaselProvider`.
|
|
286
369
|
*/
|
|
287
|
-
declare function
|
|
370
|
+
declare function EaselCanvas({ frame, className, style, metadata, mirrorStyles, onDocument, height, title, nonce, hotkeys, frameWrapper: FrameWrapper, overlay, children, }: EaselCanvasProps): ReactElement;
|
|
288
371
|
|
|
289
372
|
/**
|
|
290
373
|
* THE PARTS, ON THE STORE (blueprint step 5.2).
|
|
@@ -297,7 +380,7 @@ declare function EditorCanvas({ frame, className, style, metadata, mirrorStyles,
|
|
|
297
380
|
*/
|
|
298
381
|
/** `useNodeOps` with the store as its host: reads the document, and writes one
|
|
299
382
|
* `run` — one undo step, the selection travelling with it. */
|
|
300
|
-
declare function
|
|
383
|
+
declare function useEaselNodeOps(options?: Omit<NodeOpsHost, 'data' | 'config' | 'commit'>): {
|
|
301
384
|
held: ClipboardEntry | null;
|
|
302
385
|
remove: (id: string) => boolean;
|
|
303
386
|
duplicate: (id: string) => boolean;
|
|
@@ -331,7 +414,7 @@ interface SelectedFields {
|
|
|
331
414
|
* - fields resolve through `useResolvedFields` (Puck's
|
|
332
415
|
* `useRegisterFieldsSlice`: `changed`, `lastFields`, `appState`, `parent`);
|
|
333
416
|
* - a change runs the block's `resolveData` (`resolveNodeData`, Puck's
|
|
334
|
-
* `resolveComponentData`, with this
|
|
417
|
+
* `resolveComponentData`, with this easel's own memory of the last
|
|
335
418
|
* change) and then writes ONE `replace` of the node, or ONE `replaceRoot`.
|
|
336
419
|
*
|
|
337
420
|
* The write reads the node AT THE MOMENT IT RUNS, by id: a field changed while
|
|
@@ -349,13 +432,25 @@ declare function useSelectedFields(): SelectedFields;
|
|
|
349
432
|
* a layout that moves Z still undoes and ⌥⌘Z does nothing.
|
|
350
433
|
*
|
|
351
434
|
* ⚠️ PUCK FIRED THESE IN A TEXT FIELD TOO, and so does this: ⌘Z while typing
|
|
352
|
-
* in a setting undoes the
|
|
435
|
+
* in a setting undoes the easel's last step — which, since a field writes as
|
|
353
436
|
* it is typed, is the typing — and the browser's own text undo is prevented,
|
|
354
437
|
* so there are never two undo histories answering one key.
|
|
355
438
|
*
|
|
356
|
-
* Bound per DOCUMENT: the host page (`
|
|
357
|
-
* (`
|
|
439
|
+
* Bound per DOCUMENT: the host page (`EaselProvider`) and the canvas frame
|
|
440
|
+
* (`EaselCanvas`), because a key pressed in the frame never reaches the page.
|
|
441
|
+
* ONE switch turns both off (`EaselProvider hotkeys={false}`); the frame's
|
|
442
|
+
* alone is `EaselCanvas hotkeys`.
|
|
443
|
+
*
|
|
444
|
+
* ── Held with editing (second host, 2026-10-06) ─────────────────────────────
|
|
445
|
+
* While the easel-wide `edit` permission is off — somebody else holds the
|
|
446
|
+
* document, a save was refused, a read-only view — the keys do NOTHING: an
|
|
447
|
+
* undo is a write, and the screen may be the only copy of what it would
|
|
448
|
+
* rewind. The key is not taken either (no `preventDefault`), so a text field
|
|
449
|
+
* outside the document keeps the browser's own undo. Read at each key press,
|
|
450
|
+
* so a binding made once follows `setPermissions`. A block's own
|
|
451
|
+
* `permissions.edit` is that block's fields and is not asked. Handed only
|
|
452
|
+
* `undo` / `redo` (no `getState`), there is nothing to ask and the keys work.
|
|
358
453
|
*/
|
|
359
|
-
declare function bindUndoKeys(doc: Document,
|
|
454
|
+
declare function bindUndoKeys(doc: Document, easel: Pick<Easel, 'undo' | 'redo'> & Partial<Pick<Easel, 'getState'>>): () => void;
|
|
360
455
|
|
|
361
|
-
export { CanvasFrame, type CanvasFrameProps, type CanvasOverlay, type CanvasSlotProps, DEFAULT_UI,
|
|
456
|
+
export { CanvasFrame, type CanvasFrameProps, type CanvasOverlay, type CanvasSlotProps, DEFAULT_UI, type Easel, EaselAction, EaselCanvas, type EaselCanvasProps, EaselConfig, EaselCore, type EaselOptions, EaselProvider, type EaselProviderProps, EaselUi, EaselView, EditDocument, NodeSelection, Permissions, type SelectedFields, bindUndoKeys, createEasel, createUseEasel, recordsByDefault, reduce, uiView, useEasel, useEaselApi, useEaselNodeOps, useSelectedFields, validSelection };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import{DEFAULT_CANVAS_WORDS as e}from"../chunk-AELNNAFM.js";import{useResolvedFields as t}from"../chunk-YRLPFZW3.js";import{toItemSelector as n,fromItemSelector as o,useNodeOps as r}from"../chunk-D2BCHTQR.js";import{useWords as a}from"../chunk-MFIRYGHW.js";import{setCanvasNonce as i}from"../chunk-ZNCEOSXO.js";import{find as s,ROOT_ID as d,getList as l,remove as c,parseZoneKey as u,move as p,duplicate as f,replaceNode as h,createNode as m,insert as v,normalizeData as y,createResolveCache as g,resolveNodeData as k,ROOT_SLOT as b}from"../chunk-KRXQM65X.js";import{topLevelSlotKeys as w,fieldsFrom as S,defaultsFrom as I}from"../chunk-NKB3KVAB.js";import{createContext as C,useEffect as O,useContext as x,useRef as E,useSyncExternalStore as A,useState as D,useMemo as z,useCallback as M,useLayoutEffect as Z}from"react";import{jsx as N,jsxs as $}from"react/jsx-runtime";import{createPortal as j}from"react-dom";function R(e){return!["setData","setUi","set"].includes(e.type)}var B=(e,t,n,o)=>{const r=l(e,t,u(n));return r?.[o]??null},P=(e,t,n,o,r)=>{const a=B(e,t,n,o);if(!a)throw new Error(`${r}: nothing at ${n}[${o}]`);return a};function L(e,t){const{selection:o}=e;return{...e.ui,itemSelector:n(e.data,t,o),selectedSlot:o&&"slot"===o.kind?{parentId:o.parentId,slot:o.slot}:null}}function K(e,t,n){return n?"node"===n.kind?s(e,t,n.id)?n:null:n.parentId===d?l(e,t,n)?n:null:s(e,t,n.parentId)&&l(e,t,n)?n:null:null}function F(e,t,n,r=!1){const{itemSelector:a,selectedSlot:i,...s}=n;let d=e.selection;var l;return"itemSelector"in n&&(d=o(e.data,t,a??null)),"selectedSlot"in n&&(i?d=(l=i)?{kind:"slot",parentId:l.parentId,slot:l.slot}:null:"slot"===d?.kind&&(d=null)),{...e,selection:d,ui:r?s:{...e.ui,...s}}}function H(e,t,n){const o=V(e,t,n);if(o===e)return e;const r=K(o.data,n,o.selection);return r===o.selection?o:{...o,selection:r}}function V(e,t,n){const{data:o}=e;switch(t.type){case"setData":{const n="function"==typeof t.data?t.data(o):t.data;return{...e,data:{...o,...n}}}case"setUi":return F(e,n,"function"==typeof t.ui?t.ui(L(e,n)):t.ui);case"set":{const r={data:o,ui:L(e,n)},a="function"==typeof t.state?t.state(r):t.state;let i=a.data?{...e,data:a.data}:e;return a.ui&&(i=F(i,n,a.ui,!0)),i}case"insert":{const r={...u(t.destinationZone),index:t.destinationIndex},a=m(n,t.componentType,{},t.id?{id:t.id}:{});return{...e,data:v(o,n,r,a)}}case"replace":{const r=P(o,n,t.destinationZone,t.destinationIndex,"replace");if(r.props.id!==t.data.props.id)throw new Error("Can't change the id during a replace action. Use remove and insert for a new node.");return{...e,data:h(o,n,r.props.id,t.data)}}case"replaceRoot":{const n={...o.root,props:{...o.root.props,...t.root.props},readOnly:t.root.readOnly};return{...e,data:{...o,root:n}}}case"duplicate":{const r=P(o,n,t.sourceZone,t.sourceIndex,"duplicate"),a=f(o,n,r.props.id),i=B(a,n,t.sourceZone,t.sourceIndex+1);return{...e,data:a,selection:i?{kind:"node",id:i.props.id}:e.selection}}case"reorder":return V(e,{...t,type:"move",sourceZone:t.destinationZone},n);case"move":{if(t.sourceZone===t.destinationZone&&t.sourceIndex===t.destinationIndex)return e;const r=B(o,n,t.sourceZone,t.sourceIndex);if(!r)return e;const a={...u(t.destinationZone),index:t.destinationIndex};return{...e,data:p(o,n,r.props.id,a)}}case"remove":{const r=P(o,n,t.zone,t.index,"remove");return{...e,data:c(o,n,r.props.id)}}default:return e}}var T={drag:!0,duplicate:!0,delete:!0,edit:!0,insert:!0},q={leftSideBarVisible:!0,rightSideBarVisible:!0,arrayState:{},componentList:{},isDragging:!1,previewMode:"edit",viewports:{current:{width:360,height:"auto"},options:[],controlsVisible:!0},field:{focus:null}},Y=0,U=()=>`history-${Y+=1}`;function W(e){let t=e.config;const r=e.now??(()=>Date.now()),a=e.historyCoalesceMs??250;let i=e.permissions??{};const d=y(e.data,t);let c={data:d,selection:null,ui:{...q}};if(e.ui){const{itemSelector:n,selectedSlot:r,...a}=e.ui;c={data:d,selection:r?{kind:"slot",...r}:o(d,t,n??null),ui:{...q,...a}}}let u=[{core:c,at:-1/0,sealed:!0,id:U()}],p=0;const f=e=>{const t=u[p],n=r();!e&&p===u.length-1&&p>0&&!t.sealed&&n-t.at<a?u=[...u.slice(0,p),{...t,core:c,at:n}]:(u=[...u.slice(0,p+1),{core:c,at:n,sealed:e,id:U()}],p=u.length-1)},m=/* @__PURE__ */new Set;let v=null,b=null,w=0,S=null,I=!1;const C=()=>{for(const e of[...m])e()},O=()=>{u=[...u.slice(0,p),{...u[p],core:c},...u.slice(p+1)]},x=(t,n)=>{w>0?I=I||n:(n?f(!1):c!==t&&O(),c.data!==t.data&&e.onChange?.(c.data),c!==t&&C())},E=e=>{const n=c;if(c=H(c,e,t),c===n)return;const o=e.recordHistory??R(e);x(n,o&&c.data!==n.data)},A=t=>{0===w&&(S=c,I=!1),w+=1;try{t()}catch(e){throw w-=1,0===w&&S&&(c=S,S=null),e}if(w-=1,w>0||!S)return;const n=S;S=null,I&&c.data!==n.data?f(!0):c!==n&&O(),c.data!==n.data&&e.onChange?.(c.data),c!==n&&C()},D=e=>{const n=c,o=K(c.data,t,e);var r,a;(r=o,a=c.selection,r===a||r&&a&&r.kind===a.kind&&("node"===r.kind&&"node"===a.kind?r.id===a.id:"slot"===r.kind&&"slot"===a.kind&&r.parentId===a.parentId&&r.slot===a.slot))||(c={...c,selection:o},x(n,!1))},z=t=>{const n=c;p=t,c=u[p].core,c.data!==n.data&&e.onChange?.(c.data),c!==n&&C()},M=()=>(e={})=>{const n=e.root?t.root?.permissions:t.components[e.item?.type??e.type??""]?.permissions;return{...T,...i,...n}};let Z=M();const N=g(),$={resolveCache:N,async resolveAll({trigger:e="load",metadata:n}={}){const o=c.data,r={type:"root",props:{...o.root.props??{}}},a=[...o.content,...Object.values(o.zones??{}).flat()],[i,...d]=await Promise.all([k(r,t,{cache:N,trigger:e,metadata:n}),...a.map(o=>k(o,t,{cache:N,trigger:e,metadata:n}))]);A(()=>{const e=c;let n=c.data;d.forEach((e,r)=>{if(!e.didChange)return;const i=a[r].props.id,d=s(n,t,i)?.node;d&&d===s(o,t,i)?.node&&(n=h(n,t,i,e.node))}),i.didChange&&n.root===o.root&&(n={...n,root:{...n.root,props:i.node.props,readOnly:i.node.readOnly??n.root.readOnly}}),n!==c.data&&(c={...c,data:n,selection:K(n,t,c.selection)},x(e,!1))})},async resolveNode(e,{trigger:n="replace",metadata:o}={}){const r=s(c.data,t,e)?.node;if(!r)return!1;const a=await k(r,t,{cache:N,trigger:n,metadata:o});if(!a.didChange)return!1;let i=!1;return A(()=>{const n=c;if(s(c.data,t,e)?.node!==r)return;const o=h(c.data,t,e,a.node);o!==c.data&&(c={...c,data:o,selection:K(o,t,c.selection)},x(n,!1),i=!0)}),i},get config(){return t},getState(){if(v&&b===c)return v;const e=c,o={data:e.data,ui:L(e,t)},r="node"===e.selection?.kind?s(e.data,t,e.selection.id)?.node??null:null;return v={appState:o,selection:e.selection,selectedItem:r,config:t,dispatch:E,history:{back:()=>$.undo(),forward:()=>$.redo(),hasPast:p>0,hasFuture:p<u.length-1,histories:u.map(e=>({id:e.id,state:{data:e.core.data,ui:L(e.core,t)}})),index:p,setHistoryIndex:e=>{e>=0&&e<u.length&&e!==p&&z(e)}},getPermissions:Z,getItemById:n=>s(e.data,t,n)?.node,getItemBySelector:n=>function(e,t,n){const[o,r]=(n.zone??"root:default-zone").split(/:(.*)/s);return l(e,t,{parentId:o,slot:r})?.[n.index]}(e.data,t,n),getSelectorForId:o=>{const r=n(e.data,t,{kind:"node",id:o});return r?{index:r.index,zone:r.zone??"root:default-zone"}:void 0}},b=e,v},subscribe:e=>(m.add(e),()=>{m.delete(e)}),dispatch:E,batch:A,select:e=>D(e?{kind:"node",id:e}:null),selectSlot:e=>D(e?{kind:"slot",parentId:e.parentId,slot:e.slot}:null),run(e){A(()=>{const n=c,o=void 0===e.select?c.selection:e.select;c={...c,data:e.data,selection:K(e.data,t,o)},x(n,e.data!==n.data)})},load(e,n={}){const o=c,r=y(e,t);c={...c,data:r,selection:K(r,t,n.selection??c.selection)},n.keepHistory||(u=[{core:c,at:-1/0,sealed:!0,id:U()}],p=0),c!==o&&C()},undo(){p>0&&z(p-1)},redo(){p<u.length-1&&z(p+1)},setPermissions(e){(function(e,t){if(e===t)return!0;const n=Object.keys(e);return n.length===Object.keys(t).length&&n.every(n=>e[n]===t[n])})(e,i)||(i=e,Z=M(),v=null,C())},setConfig(e){e!==t&&(t=e,Z=M(),v=null,C())}};return $}function X(e,t){const n=e=>{if(e.altKey)return;if(!(e.metaKey!==e.ctrlKey))return;const n="KeyZ"===e.code,o="KeyY"===e.code&&!e.shiftKey;(n||o)&&!1!==t.getState?.().getPermissions().edit&&(e.preventDefault(),o||e.shiftKey?t.redo():t.undo())};return e.addEventListener("keydown",n),()=>e.removeEventListener("keydown",n)}var Q=C(null),G=C(!0);function J({easel:e,hotkeys:t=!0,resolveOnMount:n=!0,children:o}){return O(()=>{n&&e.resolveAll()},[e,n]),O(()=>{if(t&&"undefined"!=typeof document)return X(document,e)},[e,t]),/* @__PURE__ */N(Q.Provider,{value:e,children:/* @__PURE__ */N(G.Provider,{value:t,children:o})})}function _(){const e=x(Q);if(!e)throw new Error("useEasel: no <EaselProvider> above this component");return e}function ee(e){const t=_(),n=E({view:null,selector:null,value:void 0,has:!1}),o=()=>{const o=t.getState(),r=n.current;if(r.has&&r.view===o&&r.selector===e)return r.value;const a=e(o);return r.has&&Object.is(r.value,a)?(n.current={view:o,selector:e,value:r.value,has:!0},r.value):(n.current={view:o,selector:e,value:a,has:!0},a)};return A(t.subscribe,o,o)}function te(){return function(e){return ee(e)}}var ne="data-puck-component",oe="data-puck-dropzone",re={},ae="data-es-mirror",ie="undefined"==typeof window?O:Z;function se(e,t,n){for(const e of Array.from(t.head.querySelectorAll(`[${ae}]`)))e.remove();for(const o of Array.from(e.head.querySelectorAll('style, link[rel="stylesheet"]'))){const e=o.cloneNode(!0);e.setAttribute(ae,""),n&&e.setAttribute("nonce",n),t.head.appendChild(e)}}function de({children:t,id:n="preview-frame",title:o,className:r,style:s,mirrorStyles:d=!0,onDocument:l,height:c="auto",nonce:u}){const p=a("canvas",e),f=E(null),[h,m]=D(null);return ie(()=>{const e=f.current,t=h?.ownerDocument.defaultView;if("auto"!==c||!e||!h||!t)return;const n=()=>{e.style.height=`${Math.ceil(h.getBoundingClientRect().height)}px`};n();const o=t.ResizeObserver,r=o?new o(n):null;return r?.observe(h),()=>r?.disconnect()},[h,c]),ie(()=>{let e=f.current?.contentDocument;if(!e)return;"CSS1Compat"!==e.compatMode&&(e.open(),e.write("<!DOCTYPE html><html><head></head><body></body></html>"),e.close(),e=f.current?.contentDocument??e),e.body||e.documentElement.appendChild(e.createElement("body")),e.body.style.margin="0";const t=e.createElement("div");t.setAttribute("data-puck-entry",""),t.id="frame-root","fill"===c&&t.classList.add("es-frame-fill"),e.body.appendChild(t),i(e,u),m(t),l?.(e);let n=null;const o=f.current?.ownerDocument;if(d&&o){se(o,e,u);const t=o.defaultView;t&&"MutationObserver"in t&&(n=new t.MutationObserver(()=>se(o,e,u)),n.observe(o.head,{childList:!0,subtree:!0,characterData:!0}))}return()=>{n?.disconnect(),t.remove(),m(null),l?.(null)}},[]),/* @__PURE__ */N("iframe",{ref:f,id:n,title:o??p.frame,className:["es-canvas-frame","fill"===c?"is-fill":"",r??""].filter(Boolean).join(" "),style:s,children:h?j(t,h):null})}var le=C(null),ce=C(re),ue=[];function pe({parentId:e,slot:t,className:n,style:o,minEmptyHeight:r=128}){const a=_(),i=ee(n=>l(n.appState.data,a.config,{parentId:e,slot:t})??ue),s=ee(t=>"node"===t.selection?.kind&&t.selection.id===e&&e!==d),c=ee(n=>"slot"===n.selection?.kind&&n.selection.parentId===e&&n.selection.slot===t),u=x(le),p=["es-zone"];return i.length&&p.push("has-children"),e===d&&p.push("is-root"),(s||c)&&p.push("is-area-selected"),null!==u&&u===e&&e!==d&&p.push("is-hovering-area"),n&&p.push(n),/* @__PURE__ */N("div",{className:p.join(" "),[oe]:`${e}:${t}`,style:{...o,"--min-empty-height":`${r}px`},children:i.map(e=>/* @__PURE__ */N(me,{node:e},e.props.id))})}function fe(e,t,n,o){const r="root"===t?e.root:e.components[t],a={...o};for(const e of w(S(r)))a[e]=function(t={}){/* @__PURE__ */
|
|
2
|
+
return N(pe,{parentId:n,slot:e,...t})};return a}function he(e){return function({zone:t,...n}){/* @__PURE__ */
|
|
3
|
+
return N(pe,{parentId:e,slot:t,...n})}}function me({node:e}){const t=_().config,n=t.components[e.type],o=e.props.id,r=function(e){const t=E(null);return z(()=>n=>{const o=t.current;o&&o!==n&&(o.removeAttribute(ne),o.removeAttribute("data-puck-dnd")),t.current=n,n&&(n.setAttribute(ne,e),n.setAttribute("data-puck-dnd",e),n.style.position="relative")},[e])}(o),a=x(ce);if(!n?.render)return null;const i=n.render,s=fe(t,e.type,o,{...I(n),...e.props}),d={renderDropZone:he(o),isEditing:!0,dragRef:n.inline?r:null,metadata:{...a,...n.metadata}};return n.inline?/* @__PURE__ */N(i,{...s,puck:d,editMode:!0}):/* @__PURE__ */N("div",{[ne]:o,"data-puck-dnd":o,style:{position:"relative"},children:/* @__PURE__ */N(i,{...s,puck:d,editMode:!0})})}function ve({metadata:e}){const t=ee(e=>e.config),n=ee(e=>e.appState.data.root).props??{},o=t.root?.render,r=/* @__PURE__ */N(pe,{parentId:d,slot:b}),a=o?/* @__PURE__ */N(o,{id:"puck-root",...fe(t,"root",d,n),puck:{renderDropZone:he(d),isEditing:!0,dragRef:null,metadata:e??{}},editMode:!0,children:r}):r;/* @__PURE__ */
|
|
4
|
+
return N(ce.Provider,{value:e??re,children:a})}function ye({id:e,kind:t,overlay:n}){const o=E(null),[r,a]=D(null);ie(()=>{const t=o.current?.ownerDocument,n=t?.defaultView;if(!t||!n||!e)return void a(null);let r=0;const i=()=>{r=0;const i=function(e,t){if(!e||!t)return null;const n=e.getBoundingClientRect(),o=t.getBoundingClientRect();return{top:n.top-o.top,left:n.left-o.left,width:n.width,height:n.height}}(t.querySelector(`[${ne}="${n.CSS?.escape?n.CSS.escape(e):e}"]`),o.current?.parentElement??null);a(e=>{return(t=e)===(n=i)||t&&n&&t.top===n.top&&t.left===n.left&&t.width===n.width&&t.height===n.height?e:i;var t,n})},s=()=>{r||(r=(n.requestAnimationFrame??(e=>n.setTimeout(e,16)))(i))};i();const d="MutationObserver"in n?new n.MutationObserver(s):null;d?.observe(t.body,{childList:!0,subtree:!0,attributes:!0,characterData:!0});const l="ResizeObserver"in n?new n.ResizeObserver(s):null;return l?.observe(t.documentElement),n.addEventListener("resize",s),()=>{d?.disconnect(),l?.disconnect(),n.removeEventListener("resize",s),r&&(n.cancelAnimationFrame??n.clearTimeout)(r)}},[e]);const i=/* @__PURE__ */N("div",{ref:o,"aria-hidden":"true",className:`es-canvas-outline is-${t}`,style:r?{position:"absolute",top:r.top,left:r.left,width:r.width,height:r.height}:{display:"none"}});return n&&e?/* @__PURE__ */N(n,{componentId:e,kind:t,children:i}):i}function ge({frame:e=!0,className:t,style:n,metadata:o,mirrorStyles:r,onDocument:a,height:i,title:s,nonce:d,hotkeys:l,frameWrapper:c,overlay:u,children:p}){const f=_(),h=x(G),m=l??h,[v,y]=D(void 0),g=ee(e=>"node"===e.selection?.kind?e.selection.id:null),[k,b]=D(null),w=/* @__PURE__ */$("div",{className:"es-canvas-content",style:{position:"relative"},onClick:e=>{const t=e.target;t.closest?.("a[href]")&&e.preventDefault();const n=t.closest?.(`[${ne}]`);n?f.select(n.getAttribute(ne)):t.hasAttribute?.(oe)||f.select(null)},onMouseOver:e=>{const t=e.target.closest?.(`[${ne}]`),n=t?.getAttribute(ne)??null;n!==k&&b(n)},onMouseLeave:()=>b(null),children:[
|
|
5
|
+
/* @__PURE__ */N(le.Provider,{value:k,children:/* @__PURE__ */N(ve,{metadata:o})}),
|
|
6
|
+
/* @__PURE__ */$("div",{className:"es-canvas-outlines",style:{position:"absolute",top:0,left:0,pointerEvents:"none"},children:[
|
|
7
|
+
/* @__PURE__ */N(ye,{id:k&&k!==g?k:null,kind:"hover",overlay:u}),
|
|
8
|
+
/* @__PURE__ */N(ye,{id:g,kind:"selected",overlay:u})]}),p]}),S=c?/* @__PURE__ */N(c,{document:v,children:w}):w;O(()=>{if(v&&m)return X(v,f)},[v,m,f]);/* @__PURE__ */
|
|
9
|
+
return N("div",{className:"es-canvas"+(t?` ${t}`:""),style:n,"data-puck-preview":"",children:e?/* @__PURE__ */N(de,{mirrorStyles:r,onDocument:e=>{y(e??void 0),a?.(e)},height:i,title:s,nonce:d,children:S}):/* @__PURE__ */N("div",{id:"preview-frame","data-puck-entry":"",children:S})})}function ke(e={}){const t=_(),n=ee(e=>e.appState.data);return r({...e,data:n,config:t.config,commit:(e,{select:n})=>t.run({data:e,select:n})})}function be(){const e=_(),n=ee(e=>e.config),o=e.resolveCache,r=ee(e=>e.selectedItem),a=ee(e=>e.appState.data.root),i=r?r.props.id:"root",d=a.props??{},l=z(()=>r||{type:"root",props:d},[r,d]),c=r?n.components[r.type]:n.root,u=e.getState(),p=r?s(u.appState.data,n,i):null,f=p&&"root"!==p.place.parentId?p.place.parentId:null,h=f?u.getItemById(f)??null:null,{fields:m}=t({id:i,node:l,block:c,parent:h,appState:u.appState}),v=ee(e=>!1===e.getPermissions(r?{item:r}:{root:!0}).edit),y=M((t,r)=>{(async()=>{const a=e.getState();if("root"!==i){const s=a.getItemById(i),d=a.getSelectorForId(i);if(!s||!d)return;const l={...s,props:{...s.props,[t]:r}},{node:c}=await k(l,n,{cache:o,trigger:"replace"}),u=e.getState().getSelectorForId(i);if(!u)return;return void e.dispatch({type:"replace",destinationZone:u.zone??"root:default-zone",destinationIndex:u.index,data:c})}const s=a.appState.data.root.props??{},{node:d}=await k({type:"root",props:{...s,[t]:r}},n,{cache:o,trigger:"replace"});e.dispatch({type:"replaceRoot",root:{props:d.props,readOnly:d.readOnly},recordHistory:!0})})()},[e,n,o,i]);return{id:i,node:l,fields:m,values:l.props,readOnly:v,onChange:y}}export{de as CanvasFrame,q as DEFAULT_UI,ge as EaselCanvas,J as EaselProvider,ve as EditDocument,X as bindUndoKeys,W as createEasel,te as createUseEasel,R as recordsByDefault,H as reduce,L as uiView,ee as useEasel,_ as useEaselApi,ke as useEaselNodeOps,be as useSelectedFields,K as validSelection};
|
|
@@ -177,7 +177,7 @@ declare const documentPermissions: (canEdit: boolean) => DocumentPermissions;
|
|
|
177
177
|
/**
|
|
178
178
|
* "just now" / "2 minutes ago" — one phrasing, shared by every notice.
|
|
179
179
|
*
|
|
180
|
-
* `words` is the host's language (`
|
|
180
|
+
* `words` is the host's language (`useEaselStrings().lock`, 0.95.0); left out,
|
|
181
181
|
* it is the English it always was. The thresholds are not words and do not
|
|
182
182
|
* move: under 45 seconds is "just now", then whole minutes, then whole hours.
|
|
183
183
|
*/
|
|
@@ -192,7 +192,7 @@ declare const nameOrSomeone: (name: string, words?: LockWords) => string;
|
|
|
192
192
|
* a reload IS a new editor session, and a remembered id would let a closed tab's
|
|
193
193
|
* claim look alive.
|
|
194
194
|
*/
|
|
195
|
-
declare function
|
|
195
|
+
declare function newEaselClientId(): string;
|
|
196
196
|
/**
|
|
197
197
|
* The `code` of a document write refused because somebody else TOOK the
|
|
198
198
|
* document from this tab (backend `efficient/editlock.py`,
|
|
@@ -351,4 +351,4 @@ type EditLock = {
|
|
|
351
351
|
/** Bind the decisions to one editor's HTTP client and document type. */
|
|
352
352
|
declare function createEditLock(adapter: EditLockAdapter): EditLock;
|
|
353
353
|
|
|
354
|
-
export { type ClaimBeatDeps, type DocumentPermissions, EDIT_LOCK_TAKEN, type EditAccess, type EditLock, type EditLockAdapter, type EditLockBody, type EditLockFailure, type HeldWrite, type LockHolder, type LockWords, type SaveConflict, accessForNewDocument, canWriteDocument, createEditLock, describeAge, documentPermissions, documentVersionMoved, holderOf, nameOrSomeone,
|
|
354
|
+
export { type ClaimBeatDeps, type DocumentPermissions, EDIT_LOCK_TAKEN, type EditAccess, type EditLock, type EditLockAdapter, type EditLockBody, type EditLockFailure, type HeldWrite, type LockHolder, type LockWords, type SaveConflict, accessForNewDocument, canWriteDocument, createEditLock, describeAge, documentPermissions, documentVersionMoved, holderOf, nameOrSomeone, newEaselClientId, readLockHolder, reloadIfVersionMoved, shouldReleaseHeldWrite, shouldReloadAfterTakeover };
|
package/dist/edit-lock/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{EDIT_LOCK_TAKEN,accessForNewDocument,canWriteDocument,createEditLock,describeAge,documentPermissions,documentVersionMoved,holderOf,nameOrSomeone,
|
|
1
|
+
export{EDIT_LOCK_TAKEN,accessForNewDocument,canWriteDocument,createEditLock,describeAge,documentPermissions,documentVersionMoved,holderOf,nameOrSomeone,newEaselClientId,readLockHolder,reloadIfVersionMoved,shouldReleaseHeldWrite,shouldReloadAfterTakeover}from"../chunk-JUQ6YJCG.js";import"../chunk-RE4LP46F.js";
|
package/dist/fields/fields.css
CHANGED
|
@@ -35,8 +35,13 @@
|
|
|
35
35
|
border-color: var(--es-accent);
|
|
36
36
|
box-shadow: 0 0 0 3px var(--es-accent-ring);
|
|
37
37
|
}
|
|
38
|
+
/* A value nobody may change is still a value somebody has to READ: the grey
|
|
39
|
+
ground says the state, and the ink stays legible on it. It was `--es-muted`,
|
|
40
|
+
4.39:1 on this ground at 12px — a hair under the 4.5:1 WCAG AA asks (second
|
|
41
|
+
host, axe-core, 2026-10-07). The number cell's read-only state below is the
|
|
42
|
+
same rule. Held by ratio in tests/contrast-small-text.test.ts. */
|
|
38
43
|
.es-fin[readonly],
|
|
39
|
-
.es-fin:disabled { background: var(--es-track-bg); color: var(--es-
|
|
44
|
+
.es-fin:disabled { background: var(--es-track-bg); color: var(--es-text-secondary); }
|
|
40
45
|
|
|
41
46
|
.es-fin--area {
|
|
42
47
|
height: auto;
|
|
@@ -75,7 +80,7 @@
|
|
|
75
80
|
border-color: var(--es-focus-line, #60a5fa);
|
|
76
81
|
box-shadow: 0 0 0 2px var(--es-focus-ring, rgba(96, 165, 250, 0.3));
|
|
77
82
|
}
|
|
78
|
-
.es-fnum.is-readonly { background: var(--es-track-bg); color: var(--es-
|
|
83
|
+
.es-fnum.is-readonly { background: var(--es-track-bg); color: var(--es-text-secondary); }
|
|
79
84
|
|
|
80
85
|
.es-fnum-icon { display: flex; align-items: center; flex: none; color: var(--es-field-placeholder); }
|
|
81
86
|
.es-fnum-icon svg { width: 14px; height: 14px; }
|
|
@@ -239,6 +244,90 @@
|
|
|
239
244
|
.es-fsel-option.is-active { background: var(--es-hover); }
|
|
240
245
|
.es-fsel-option.is-chosen { color: var(--es-accent-text); font-weight: 600; }
|
|
241
246
|
.es-fsel-option[aria-disabled='true'] { color: var(--es-disabled); cursor: default; }
|
|
247
|
+
/* A heading over a run of options (`FieldOption.group`): a quiet label above
|
|
248
|
+
its options, and not an option itself — no hover, no pointer. */
|
|
249
|
+
.es-fsel-group-list { margin: 0; padding: 0; list-style: none; }
|
|
250
|
+
.es-fsel-group + .es-fsel-group,
|
|
251
|
+
.es-fsel-option + .es-fsel-group { margin-top: 4px; }
|
|
252
|
+
.es-fsel-group-head {
|
|
253
|
+
padding: 8px 8px 4px;
|
|
254
|
+
font-size: 11px;
|
|
255
|
+
font-weight: 600;
|
|
256
|
+
color: var(--es-muted);
|
|
257
|
+
cursor: default;
|
|
258
|
+
}
|
|
259
|
+
.es-fsel-group-count { margin-left: 4px; font-weight: 400; }
|
|
260
|
+
|
|
261
|
+
/* The editor's one pop-up list (card [MERGE] One Pop-up List For Editor And
|
|
262
|
+
One For Shoppers): what the website editor's own lists drew, here once.
|
|
263
|
+
A row with more than its label lays its parts out in a line. */
|
|
264
|
+
.es-fsel-option--rich { display: flex; align-items: center; gap: 8px; }
|
|
265
|
+
.es-fsel-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
266
|
+
.es-fsel-meta { flex: none; font-size: 11px; font-weight: 400; color: var(--es-muted); }
|
|
267
|
+
.es-fsel-thumb {
|
|
268
|
+
flex: none;
|
|
269
|
+
width: 26px;
|
|
270
|
+
height: 26px;
|
|
271
|
+
overflow: hidden;
|
|
272
|
+
border-radius: var(--es-field-radius);
|
|
273
|
+
background: var(--es-track-bg);
|
|
274
|
+
}
|
|
275
|
+
.es-fsel-thumb img { display: block; width: 100%; height: 100%; object-fit: cover; }
|
|
276
|
+
/* `multiple`: a tick box per row, drawn (the row is the option, not an <input>). */
|
|
277
|
+
.es-fsel-tick {
|
|
278
|
+
flex: none;
|
|
279
|
+
position: relative;
|
|
280
|
+
box-sizing: border-box;
|
|
281
|
+
width: 14px;
|
|
282
|
+
height: 14px;
|
|
283
|
+
border: 1.5px solid var(--es-input-border);
|
|
284
|
+
border-radius: 3px;
|
|
285
|
+
background: var(--es-field-bg);
|
|
286
|
+
}
|
|
287
|
+
.es-fsel-tick.is-on { border-color: var(--es-accent); background: var(--es-accent); }
|
|
288
|
+
.es-fsel-tick.is-on::after {
|
|
289
|
+
content: '';
|
|
290
|
+
position: absolute;
|
|
291
|
+
left: 3.5px;
|
|
292
|
+
top: 0.5px;
|
|
293
|
+
width: 3.5px;
|
|
294
|
+
height: 7px;
|
|
295
|
+
border-right: 1.6px solid var(--es-on-accent);
|
|
296
|
+
border-bottom: 1.6px solid var(--es-on-accent);
|
|
297
|
+
transform: rotate(45deg);
|
|
298
|
+
}
|
|
299
|
+
/* A tick box says "on" itself, so a ticked row keeps the ordinary ink. */
|
|
300
|
+
.es-fsel-option--rich.is-chosen:has(.es-fsel-tick) { color: var(--es-text); font-weight: 400; }
|
|
301
|
+
/* `searchable`: the box and the list float together; the list inside is plain. */
|
|
302
|
+
.es-fsel-pop {
|
|
303
|
+
position: absolute;
|
|
304
|
+
z-index: 20;
|
|
305
|
+
top: calc(100% + 4px);
|
|
306
|
+
left: 0;
|
|
307
|
+
right: 0;
|
|
308
|
+
display: flex;
|
|
309
|
+
flex-direction: column;
|
|
310
|
+
gap: 4px;
|
|
311
|
+
padding: 4px;
|
|
312
|
+
border: 1px solid var(--es-line);
|
|
313
|
+
border-radius: var(--es-row-radius);
|
|
314
|
+
background: var(--es-raised);
|
|
315
|
+
box-shadow: var(--es-panel-shadow);
|
|
316
|
+
}
|
|
317
|
+
.es-fsel-pop .es-fsel-list {
|
|
318
|
+
position: static;
|
|
319
|
+
padding: 0;
|
|
320
|
+
border: 0;
|
|
321
|
+
border-radius: 0;
|
|
322
|
+
background: none;
|
|
323
|
+
box-shadow: none;
|
|
324
|
+
}
|
|
325
|
+
.es-fsel-empty { padding: 8px; font-size: 12px; color: var(--es-muted); }
|
|
326
|
+
/* `inline`: the list opens in flow and pushes the rows below it down, so a
|
|
327
|
+
panel that scrolls never clips it. */
|
|
328
|
+
.es-fsel--inline .es-fsel-list { position: static; margin-top: 4px; }
|
|
329
|
+
.es-fsel--inline .es-fsel-pop { position: static; margin-top: 4px; }
|
|
330
|
+
.es-fsel--inline .es-fsel-pop .es-fsel-list { margin-top: 0; }
|
|
242
331
|
|
|
243
332
|
/* ── the panel body: the tab strip and the rows (Inspector) ───────────────── */
|
|
244
333
|
|
|
@@ -274,6 +363,16 @@
|
|
|
274
363
|
/* The rows are Layers' rows (tree.css); the list lets them reach the panel's
|
|
275
364
|
edges the way Layers' do, so a row's hover reads as the same row. */
|
|
276
365
|
.es-list-tree { margin: 0 -8px; }
|
|
366
|
+
/* …but the list is NOT a scroll box, as Layers is (`.es-tree { overflow-y:
|
|
367
|
+
auto }`): the panel around it scrolls. A row's drag hint hangs 4px under the
|
|
368
|
+
row, 22px tall, and under the LAST row the tree has less room than that: it
|
|
369
|
+
clipped the hint to 8px (second host, 2026-10-07). What overflowed also
|
|
370
|
+
made the list a scroller of its own inside the panel: a wheel over it moved
|
|
371
|
+
the rows 14px, and it was the nearest box that scrolled, so a drag in the
|
|
372
|
+
list scrolled IT at its edges, not the panel. Measured in Chrome
|
|
373
|
+
(scripts/second-host-clickthrough.mjs). Two classes, so it wins whichever
|
|
374
|
+
sheet loads last. */
|
|
375
|
+
.es-tree.es-list-tree { overflow: visible; }
|
|
277
376
|
|
|
278
377
|
.es-list-add {
|
|
279
378
|
height: 32px;
|
|
@@ -289,31 +388,9 @@
|
|
|
289
388
|
.es-list-add:disabled { color: var(--es-disabled); cursor: default; }
|
|
290
389
|
.es-list-add:focus-visible { outline: 2px solid var(--es-accent); outline-offset: 1px; }
|
|
291
390
|
|
|
292
|
-
/* An item opened: the way back
|
|
391
|
+
/* An item opened: the way back (chrome's InspectorUpLevel, inline), its name,
|
|
392
|
+
its own settings, its two actions. */
|
|
293
393
|
.es-list.is-open { gap: 12px; }
|
|
294
|
-
.es-list-back {
|
|
295
|
-
align-self: flex-start;
|
|
296
|
-
display: inline-flex;
|
|
297
|
-
align-items: center;
|
|
298
|
-
gap: 8px;
|
|
299
|
-
height: 28px;
|
|
300
|
-
padding: 0 8px;
|
|
301
|
-
border: 0;
|
|
302
|
-
border-radius: var(--es-field-radius);
|
|
303
|
-
background: var(--es-hover);
|
|
304
|
-
color: var(--es-text-secondary);
|
|
305
|
-
font: inherit;
|
|
306
|
-
font-size: 12px;
|
|
307
|
-
cursor: pointer;
|
|
308
|
-
}
|
|
309
|
-
.es-list-back:focus-visible { outline: 2px solid var(--es-accent); outline-offset: 1px; }
|
|
310
|
-
.es-list-back-chev {
|
|
311
|
-
width: 6px;
|
|
312
|
-
height: 6px;
|
|
313
|
-
border-left: 1.6px solid currentColor;
|
|
314
|
-
border-bottom: 1.6px solid currentColor;
|
|
315
|
-
transform: rotate(45deg);
|
|
316
|
-
}
|
|
317
394
|
.es-list-title { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
|
|
318
395
|
.es-list-name { font-size: 14px; font-weight: 600; color: var(--es-text); }
|
|
319
396
|
.es-list-item { min-width: 0; }
|