efficient-easel 18.2.0 → 19.1.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 +26 -1
- package/dist/{CanvasDragLayer-dZCPbQCP.d.ts → CanvasDragLayer-ARfaivCu.d.ts} +21 -3
- package/dist/{host-epHo7r8P.d.ts → ScreenDocumentContext-D55vIrG3.d.ts} +208 -208
- package/dist/autosave/index.d.ts +4 -4
- package/dist/{canvas-typing-BsNigaCa.d.ts → canvas-typing-Be4FO4q7.d.ts} +5 -5
- package/dist/chrome/chrome.css +2 -18
- package/dist/chrome/index.d.ts +5 -39
- package/dist/chrome/index.js +1 -1
- package/dist/{chunk-C2RHV42O.js → chunk-23DYNW6S.js} +1 -1
- package/dist/chunk-32JJK7PQ.js +8 -0
- package/dist/chunk-3WLT3GQS.js +1 -0
- package/dist/{chunk-4ES5JGGX.js → chunk-3Z2MVLCD.js} +1 -1
- package/dist/{chunk-HGV3FWOB.js → chunk-5DPIGWUL.js} +1 -1
- package/dist/chunk-5Z6KYASQ.js +1 -0
- package/dist/{chunk-N5AN2RF2.js → chunk-6B542CLG.js} +1 -1
- package/dist/chunk-AJSDWWJW.js +1 -0
- package/dist/chunk-C56MPJBH.js +1 -0
- package/dist/{chunk-S4AWJX4Q.js → chunk-EZG3TGQ7.js} +1 -1
- package/dist/chunk-FCRNT5PM.js +5 -0
- package/dist/{chunk-XHHVMAGM.js → chunk-FGYJ5DJY.js} +1 -1
- package/dist/chunk-GNMBJUG7.js +62 -0
- package/dist/{chunk-VEZPDGDQ.js → chunk-JBOSEHIX.js} +1 -1
- package/dist/chunk-KRL3IUV3.js +1 -0
- package/dist/{chunk-YLQYEDUQ.js → chunk-LBOJCTTC.js} +1 -1
- package/dist/chunk-LNHT2KPX.js +2 -0
- package/dist/chunk-M5Q4VYOH.js +14 -0
- package/dist/chunk-M62P4IQK.js +2 -0
- package/dist/chunk-MS2U6MV5.js +1 -0
- package/dist/chunk-MZ5PWTDV.js +7 -0
- package/dist/{chunk-7TOQCTHV.js → chunk-PY7OOQEZ.js} +3 -3
- package/dist/{chunk-KG7RJWOK.js → chunk-RYNBSKE7.js} +1 -1
- package/dist/chunk-SGFDUXUH.js +18 -0
- package/dist/{chunk-XDZHKYQX.js → chunk-SINAM2IT.js} +1 -1
- package/dist/chunk-Y6RJAOFH.js +1 -0
- package/dist/chunk-YZALWZT7.js +2 -0
- package/dist/chunk-ZG6PVMC3.js +19 -0
- package/dist/copy/index.d.ts +7 -7
- package/dist/copy/index.js +29 -29
- package/dist/drag/drag.css +23 -0
- package/dist/drag/index.d.ts +1 -1
- package/dist/drag/index.js +1 -1
- package/dist/easel/index.d.ts +15 -2
- package/dist/easel/index.js +1 -1
- package/dist/fields/fields.css +3 -3
- package/dist/fields/index.d.ts +1 -1
- package/dist/fields/index.js +1 -1
- package/dist/panel/index.d.ts +65 -84
- package/dist/panel/index.js +14 -19
- package/dist/panel/panel.css +41 -137
- package/dist/{panelWidths-D9hTLR25.d.ts → panelWidths-DgQXiPXU.d.ts} +34 -6
- package/dist/screen/components/AddBlockRow.js +1 -1
- package/dist/screen/components/AddSidePanel.js +1 -1
- package/dist/screen/components/MediaPopup.d.ts +21 -0
- package/dist/screen/components/MediaPopup.js +1 -0
- package/dist/screen/components/NodeContextMenu.d.ts +4 -4
- package/dist/screen/components/NodeContextMenu.js +1 -1
- package/dist/screen/components/PageEditGuardNotices.js +1 -1
- package/dist/screen/components/SaveControls.d.ts +6 -6
- package/dist/screen/components/SaveControls.js +1 -1
- package/dist/screen/components/ScreenChrome.js +1 -1
- package/dist/screen/components/ScreenDocumentContext.d.ts +1 -1
- package/dist/screen/components/ScreenDocumentContext.js +1 -1
- package/dist/screen/components/ScreenMount.d.ts +8 -8
- package/dist/screen/components/ScreenMount.js +1 -1
- package/dist/screen/components/ScreenStoreProvider.d.ts +10 -8
- package/dist/screen/components/ScreenStoreProvider.js +1 -1
- package/dist/screen/components/SectionScreenShell.js +17 -16
- package/dist/screen/components/SidePanelHead.js +1 -1
- package/dist/screen/components/panel-widths.d.ts +7 -6
- package/dist/screen/components/panel-widths.js +1 -1
- package/dist/screen/components/usePartBridge.d.ts +7 -7
- package/dist/screen/components/usePartBridge.js +1 -1
- package/dist/screen/index.d.ts +5 -5
- package/dist/screen/index.js +1 -1
- package/dist/screen/lib/document-addressing.d.ts +1 -1
- package/dist/screen/lib/host-inbound.js +1 -1
- package/dist/screen/lib/host.d.ts +5 -5
- package/dist/screen/lib/key-scope.js +1 -1
- package/dist/screen/lib/media-panel.d.ts +74 -37
- package/dist/screen/lib/media-panel.js +1 -1
- package/dist/screen/lib/media-picker.d.ts +5 -5
- package/dist/screen/lib/media-picker.js +1 -1
- package/dist/screen/lib/puck/device-fields.js +1 -1
- package/dist/screen/lib/puck/preview-dom.js +1 -1
- package/dist/screen/lib/puck/style-clipboard.d.ts +7 -7
- package/dist/screen/lib/puck/style-clipboard.js +1 -1
- package/dist/screen/lib/puck/use-section-ops.js +1 -1
- package/dist/screen/lib/save-status.d.ts +2 -2
- package/dist/screen/lib/screen-parts.d.ts +52 -11
- package/dist/screen/lib/screen-parts.js +1 -1
- package/dist/screen/screen.css +5 -4
- package/dist/screen/words.d.ts +3 -0
- package/dist/screen/words.js +1 -1
- package/dist/strings/index.d.ts +6 -6
- package/dist/strings/index.js +2 -2
- package/dist/text-styles/index.js +21 -21
- package/dist/{types-DT8ywrFQ.d.ts → types-B3iSHX-w.d.ts} +2 -22
- package/dist/{words-zH1H0WFw.d.ts → words-BeX5eGq7.d.ts} +2 -2
- package/dist/words-BpTjCfqO.d.ts +89 -0
- package/dist/{words-DkWcK1rh.d.ts → words-Cm8PMYn0.d.ts} +1 -1
- package/dist/{words-BpiVrsW5.d.ts → words-DYu4iBrR.d.ts} +1 -21
- package/package.json +8 -8
- package/dist/chunk-2ZWA6ARJ.js +0 -8
- package/dist/chunk-5EZ5Z3K7.js +0 -7
- package/dist/chunk-B4JN227A.js +0 -1
- package/dist/chunk-DXROGLB5.js +0 -2
- package/dist/chunk-GWIVPLOX.js +0 -23
- package/dist/chunk-LIRNQXAY.js +0 -1
- package/dist/chunk-LLDHIBYG.js +0 -18
- package/dist/chunk-OBIL7BZO.js +0 -4
- package/dist/chunk-QEAUF632.js +0 -14
- package/dist/chunk-R6WTQWAC.js +0 -2
- package/dist/chunk-SEVGZP36.js +0 -62
- package/dist/chunk-SNB4LQMK.js +0 -1
- package/dist/chunk-STWSCR25.js +0 -2
- package/dist/chunk-U277UX5I.js +0 -1
- package/dist/chunk-W6SDWCOL.js +0 -5
- package/dist/chunk-X52NFKUS.js +0 -1
- package/dist/chunk-Z6PVRXVY.js +0 -1
- package/dist/screen/components/MediaSidePanel.d.ts +0 -49
- package/dist/screen/components/MediaSidePanel.js +0 -1
- package/dist/words-D-yhjdOg.d.ts +0 -183
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ efficient-shell's components (`efficient-shell/ui`): they draw in the shell's
|
|
|
48
48
|
Tailwind classes, which **the host's Tailwind compiles from the shell's dist**
|
|
49
49
|
(the `@source` lines in its `ui.css`). Easel itself emits no Tailwind class.
|
|
50
50
|
The exported names hosts import (`EaselIconButton`, `SettingsSwitch`,
|
|
51
|
-
`SettingsSegmented`, `
|
|
51
|
+
`SettingsSegmented`, `FieldText`, `FieldNumber`, …) stay, as
|
|
52
52
|
thin wrappers with the props they had.
|
|
53
53
|
|
|
54
54
|
`tests/own-ui-ratchet.test.ts` holds the named primitives: since 1.0.0 none
|
|
@@ -307,6 +307,12 @@ it, so a **server-rendered** editor writes the widths through the CSSOM instead:
|
|
|
307
307
|
Until that runs, chrome.css draws the panels at `DEFAULT_PANEL_W`. See
|
|
308
308
|
[Server rendering and a strict Content-Security-Policy](#server-rendering-and-a-strict-content-security-policy).
|
|
309
309
|
|
|
310
|
+
**Each side panel has its own floor (ruling A4g).** `MIN_PANEL_W_BY_SIDE`
|
|
311
|
+
is `{ left: 240, right: 220 }`; the ceiling is `MAX_PANEL_W` (560) on both.
|
|
312
|
+
Clamp with the side, `clampPanelWidth(width, 'left')`, and hand each
|
|
313
|
+
`PanelResizeHandle` its side's `min`. `MIN_PANEL_W` (220) is `@deprecated`: it
|
|
314
|
+
is only the right floor now.
|
|
315
|
+
|
|
310
316
|
**The inspector head's words, and the body's inset (9.0.0).** `headWords()`
|
|
311
317
|
gives the head's two lines the same way in both editors: line 1 is the thing's
|
|
312
318
|
own name (its Layers row's word), line 2 its level.
|
|
@@ -1125,6 +1131,25 @@ backend (`defaultBackend`) and its own reading of its client's errors
|
|
|
1125
1131
|
by the host a field asks through; the Add panel lists its own session's saved
|
|
1126
1132
|
sections.
|
|
1127
1133
|
|
|
1134
|
+
The media library is the host's, shown as ONE popup (harness UI-20, owner
|
|
1135
|
+
decision 2026-10-23; `screen/lib/media-panel`, `components/MediaPopup`). The
|
|
1136
|
+
host hands its component to `MediaPanelProvider` (`panel`); the screen draws it
|
|
1137
|
+
once at its root with `{ pick, browsing, onPick, onClose }` and the host draws
|
|
1138
|
+
it as its popup. A field's `requestMediaPick` opens it for that field; the
|
|
1139
|
+
rail's Media opens it to browse (`useOpenMediaBrowse`); `onClose` leaves the
|
|
1140
|
+
field as it was. There is no media view in the left panel. A host with no
|
|
1141
|
+
popup still gets `storefront-editor:open-media-picker`.
|
|
1142
|
+
|
|
1143
|
+
A file dragged out of that popup (the shell's `MEDIA_FILE_DRAG_TYPE`) is a
|
|
1144
|
+
drag in the screen's own session, like an Add tile, when the layer says what a
|
|
1145
|
+
picture is (`ScreenCanvasDrag.media`, a `ScreenMediaDrop`): between things it
|
|
1146
|
+
becomes `create(file, maker)` at the places Add offers `adds`; held over a
|
|
1147
|
+
node the layer `takes`, that node is outlined ("Replace image") and the drop
|
|
1148
|
+
writes `replace(node, file)`. Before either, the file goes through the host's
|
|
1149
|
+
pick path when the host gives one (`MediaPanelProvider` `place`): a website
|
|
1150
|
+
takes a file another website holds by copying it in, as a pick of it does.
|
|
1151
|
+
`CanvasDragLayer` takes the drop on a node through `dropOn`.
|
|
1152
|
+
|
|
1128
1153
|
Its Layers tree is the layer's to frame (0.103.0). `pinned.isFirst` and
|
|
1129
1154
|
`isLast` say which node is pinned where the type alone does not (the campaign
|
|
1130
1155
|
designer's Header and Footer are sections, told apart by `_region`);
|
|
@@ -122,9 +122,13 @@ declare function canvasTarget(zones: readonly CanvasZone[], x: number, y: number
|
|
|
122
122
|
* two, and after its last (owner 2026-10-14) — for a list that HAS children
|
|
123
123
|
* (an empty one draws its cell instead). Across a 'y' list, down an 'x' one.
|
|
124
124
|
* Between two children the band is the room they leave. At an end it is the
|
|
125
|
-
* room between the list's edge and its child, centred there
|
|
126
|
-
*
|
|
127
|
-
*
|
|
125
|
+
* room between the list's edge and its child, centred there and pushed toward
|
|
126
|
+
* `inset` from the edge (a ➕ half outside its list can be cut by the canvas) —
|
|
127
|
+
* but never past the child's own edge. A list only as tall as its blocks (an
|
|
128
|
+
* email column, a centred row) has no room at its ends: the ➕ sits ON the
|
|
129
|
+
* block's edge, never across the block (Lewis 2026-10-23: the line drawn
|
|
130
|
+
* through a button's words). Across the list, the band spans the block or
|
|
131
|
+
* blocks it sits next to, never the empty room beside them.
|
|
128
132
|
*/
|
|
129
133
|
declare function insertSeams(zone: CanvasZone, label?: string, inset?: number): CanvasSeam[];
|
|
130
134
|
declare function seamsBetween(zone: CanvasZone, keyPrefix?: string): CanvasSeam[];
|
|
@@ -178,6 +182,20 @@ type CanvasDragLayerProps<I> = {
|
|
|
178
182
|
* its answer may have changed: the handle is picked again with it.
|
|
179
183
|
*/
|
|
180
184
|
choose?: (under: readonly CanvasSource<I>[]) => CanvasSource<I> | null;
|
|
185
|
+
/**
|
|
186
|
+
* A drop ON a thing rather than between things (the editors' media popup,
|
|
187
|
+
* harness UI-20, 2026-10-23: a picture dropped on an image replaces it).
|
|
188
|
+
* While a drag is over a thing under the pointer that `accepts` the item —
|
|
189
|
+
* the innermost one that does — its box is outlined with `label` and the
|
|
190
|
+
* drop goes to `onDrop` with it; anywhere else the drop is the ordinary one
|
|
191
|
+
* between things. Absent: every drop is between things, as before.
|
|
192
|
+
*/
|
|
193
|
+
dropOn?: {
|
|
194
|
+
accepts: (item: I, source: CanvasSource<I>) => boolean;
|
|
195
|
+
onDrop: (item: I, source: CanvasSource<I>) => void;
|
|
196
|
+
/** What the outline says the drop does ("Replace image"). */
|
|
197
|
+
label?: (item: I, source: CanvasSource<I>) => string;
|
|
198
|
+
};
|
|
181
199
|
/** The handle CLICKED, not dragged: select what it names. Absent: a click does nothing. */
|
|
182
200
|
onPick?: (item: I) => void;
|
|
183
201
|
/** "+ Add section" in a seam: open the Add panel at that place. */
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { ReactNode, RefObject } from 'react';
|
|
3
3
|
import { Data } from './blocks/index.js';
|
|
4
4
|
import { EaselDocumentAdapter, EaselCapabilities } from './screen/lib/document-contract.js';
|
|
5
|
-
import { SessionBackend, SessionBlocks } from './screen/lib/session.js';
|
|
6
5
|
import { SaveStatus } from './screen/lib/document-switch.js';
|
|
7
6
|
import { BridgedCapabilities } from './screen/lib/admin-bridge.js';
|
|
8
7
|
import { EditAccess, SaveConflict } from './edit-lock/index.js';
|
|
9
8
|
import { A as AutosaveSnapshot } from './engine-CvXpFE86.js';
|
|
9
|
+
import { SessionBackend, SessionBlocks } from './screen/lib/session.js';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* lib/document-addressing.ts — HOW A DOCUMENT IS ADDRESSED: what the
|
|
@@ -138,212 +138,6 @@ type AnyDocumentAddressing = DocumentAddressing<ScreenTarget> | DocumentAddressi
|
|
|
138
138
|
*/
|
|
139
139
|
declare const NO_DOCUMENT_ADDRESSING: DocumentAddressing<AnyScreenTarget>;
|
|
140
140
|
|
|
141
|
-
type ScreenTarget = {
|
|
142
|
-
kind: "page";
|
|
143
|
-
slug: string;
|
|
144
|
-
siteId?: string;
|
|
145
|
-
} | {
|
|
146
|
-
kind: "template";
|
|
147
|
-
id: string;
|
|
148
|
-
pageSlug?: string;
|
|
149
|
-
};
|
|
150
|
-
/**
|
|
151
|
-
* A DOCUMENT OF A HOST'S OWN — not a website's page or template: what the
|
|
152
|
-
* campaign designer opens (0.103.0). `record` is the record that stores it (a
|
|
153
|
-
* campaign's id), and `slug` the document within that record (one of its
|
|
154
|
-
* variants), the empty slug when it names none.
|
|
155
|
-
*
|
|
156
|
-
* The provider asks no more of it than that. One record is one adapter (it
|
|
157
|
-
* re-keys on `record`, as a page target re-keys on its site); two of these are
|
|
158
|
-
* the same document when record and slug match; and a document that has gone
|
|
159
|
-
* is reported, never replaced by a Home, which is a website's.
|
|
160
|
-
*
|
|
161
|
-
* Beside `ScreenTarget` and not in it: that union is the website's two kinds,
|
|
162
|
-
* as its hosts name and build them (and as the storefront publishes them), and
|
|
163
|
-
* its host messages carry one. A host's own document reaches the host through
|
|
164
|
-
* its address (`EaselHostAddress.show`), and never as `navigated`.
|
|
165
|
-
*/
|
|
166
|
-
type DocumentTarget = {
|
|
167
|
-
kind: "document";
|
|
168
|
-
record: string;
|
|
169
|
-
slug?: string;
|
|
170
|
-
};
|
|
171
|
-
/** Every target the document's provider opens: a website's page or template
|
|
172
|
-
* (`ScreenTarget`), or a document of a host's own (`DocumentTarget`). */
|
|
173
|
-
type AnyScreenTarget = ScreenTarget | DocumentTarget;
|
|
174
|
-
|
|
175
|
-
type ScreenDocumentValue = {
|
|
176
|
-
/** The document the editor is currently pointed at (runtime, retargetable). */
|
|
177
|
-
target: AnyScreenTarget;
|
|
178
|
-
/** Record-type plumbing (load/save/list/create/delete) for the pointed-at
|
|
179
|
-
* document. Chrome and panels consult `capabilities`, never the record type. */
|
|
180
|
-
adapter: EaselDocumentAdapter;
|
|
181
|
-
/** What this document type supports — the ONLY sanctioned mode switch. */
|
|
182
|
-
capabilities: EaselCapabilities;
|
|
183
|
-
/** Id of the website template being edited (template mode only). */
|
|
184
|
-
templateId: string | null;
|
|
185
|
-
/** Slug of the page being edited — the template page's slug in template mode,
|
|
186
|
-
* and a host's own document's `slug` (or the empty slug) for one of those. */
|
|
187
|
-
currentSlug: string;
|
|
188
|
-
/** Kind of the loaded page (`dynamic` drives the product-binding controls), or
|
|
189
|
-
* null while loading / in template mode where the record carries no kind. */
|
|
190
|
-
currentKind: string | null;
|
|
191
|
-
/** Id of the website this page belongs to (page mode only; null in template
|
|
192
|
-
* mode or when the editor was opened without a site). The list/save paths and
|
|
193
|
-
* the page switcher scope to this so they act on one site's pages, not the
|
|
194
|
-
* whole tenant's. */
|
|
195
|
-
siteId: string | null;
|
|
196
|
-
/** Whether the editor's backend may be called yet. Under the EFFICIENT backend
|
|
197
|
-
* that is "the console's token has arrived"; an embedder's own backend
|
|
198
|
-
* authorises its calls itself and is ready from the first render. THE gate
|
|
199
|
-
* for anything that goes through the backend — never `token`, which an
|
|
200
|
-
* embedder's backend does not have. */
|
|
201
|
-
ready: boolean;
|
|
202
|
-
/** The bridged admin JWT once it arrives (null while waiting, and always null
|
|
203
|
-
* under an embedder's own backend). Only for the callers that still pass it
|
|
204
|
-
* to the EFFICIENT client themselves. */
|
|
205
|
-
token: string | null;
|
|
206
|
-
/** Latest token for imperative reads (custom fields, pickers, panels). */
|
|
207
|
-
tokenRef: RefObject<string | null>;
|
|
208
|
-
/** Bridge editor events to the embedding admin portal. */
|
|
209
|
-
notifyParent: SendToHost;
|
|
210
|
-
/** Operator capabilities the ADMIN CONSOLE granted over the origin-gated
|
|
211
|
-
* bridge. Distinct from `capabilities` above: those describe the DOCUMENT
|
|
212
|
-
* (can it publish, has it site settings), these describe the PERSON, and the
|
|
213
|
-
* editor is never allowed to infer them for itself. */
|
|
214
|
-
bridgeCapabilities: BridgedCapabilities;
|
|
215
|
-
/** Run an authed op; on 401 ask the parent for a fresh token and retry once. */
|
|
216
|
-
runAuthed: (op: (token: string) => Promise<unknown>) => Promise<boolean>;
|
|
217
|
-
/** Backend id of the loaded document — the identity of what the editor is
|
|
218
|
-
* editing right now. Null while loading and between retargets, so it also
|
|
219
|
-
* keys any once-per-document work (e.g. the pinned-chrome seed). */
|
|
220
|
-
docId: string | null;
|
|
221
|
-
/** The document exactly as it was LOADED from the backend: Puck's initial
|
|
222
|
-
* state, and the persisted snapshot a load-time seed must measure itself
|
|
223
|
-
* against. Null while loading. Edits live in Puck's own store, so this does
|
|
224
|
-
* NOT track them — it stays the last-loaded layout for the whole session. */
|
|
225
|
-
data: Data | null;
|
|
226
|
-
/** The freshest layout the merchant is actually viewing — the latest edit made
|
|
227
|
-
* since load (tracked on every onChange, even after the autosave settles), else
|
|
228
|
-
* the last-loaded `data`. Lets a feature snapshot the live view (e.g. the
|
|
229
|
-
* read-only origin backdrop in footer focus) without reaching into Puck's store.
|
|
230
|
-
* Lean accessor (ISP) — never exposes the ref. */
|
|
231
|
-
getLiveData: () => Data | null;
|
|
232
|
-
error: string | null;
|
|
233
|
-
/** True while the template-entry bounce is redirecting to the Home page. */
|
|
234
|
-
redirecting: boolean;
|
|
235
|
-
/** The pinned-seed gate's four words for where the autosave stands. */
|
|
236
|
-
saveStatus: SaveStatus;
|
|
237
|
-
/** The autosave's state (efficient-easel/autosave) — what the toolbar pill
|
|
238
|
-
* draws, through `saveIndicator`. */
|
|
239
|
-
saveState: AutosaveSnapshot;
|
|
240
|
-
/** Whether autosave is running. The toolbar pill is a switch: clicking it
|
|
241
|
-
* flips this. OFF = edits are HELD (kept in memory, still flushable by Publish
|
|
242
|
-
* / navigation / re-enabling) but never written on a timer. Session-scoped —
|
|
243
|
-
* survives in-place page switches. */
|
|
244
|
-
autoSaveEnabled: boolean;
|
|
245
|
-
/** Auto-save stopped because a save was REFUSED, not because the operator
|
|
246
|
-
* paused it — so a stopped auto-save can never read as a switch somebody
|
|
247
|
-
* flicked themselves. */
|
|
248
|
-
autoSaveStoppedByConflict: boolean;
|
|
249
|
-
/** True when an edit hasn't been persisted yet — including one held while the
|
|
250
|
-
* claim is in flight. Drives the guard that prompts before an in-place
|
|
251
|
-
* navigation would abandon held-but-unsaved edits. */
|
|
252
|
-
hasUnsavedEdits: boolean;
|
|
253
|
-
/** Flip autosave on/off. Turning it back ON writes any held edits at once, so
|
|
254
|
-
* "on" always means caught up. */
|
|
255
|
-
toggleAutoSave: () => void;
|
|
256
|
-
/** "Retry now": a failed save goes again at once instead of waiting out its
|
|
257
|
-
* backoff. */
|
|
258
|
-
retrySave: () => void;
|
|
259
|
-
/** Autosave — hand every Puck onChange to this. */
|
|
260
|
-
onChange: (next: Data) => void;
|
|
261
|
-
/** Persist the pending edit immediately; resolves to whether nothing is left
|
|
262
|
-
* unsaved. */
|
|
263
|
-
flushSave: () => Promise<boolean>;
|
|
264
|
-
/** Persist + publish the current document (Save for templates). */
|
|
265
|
-
onPublish: (next: Data) => Promise<void>;
|
|
266
|
-
/** Whether this editor may write the open page, and why not when it may not.
|
|
267
|
-
* `editing` is the only writable state; the gate is on the save path itself,
|
|
268
|
-
* so hiding or showing chrome from this can't be the thing that protects the
|
|
269
|
-
* document. Always `editing` in template mode (no lock there). */
|
|
270
|
-
access: EditAccess;
|
|
271
|
-
/** SOMEBODY ELSE holds this page, so this editor is read-only: it may look and
|
|
272
|
-
* select, but not move, add, duplicate or delete anything — until they take
|
|
273
|
-
* control themselves. The ONE answer the chrome asks; no panel re-derives it,
|
|
274
|
-
* so none of them can drift.
|
|
275
|
-
*
|
|
276
|
-
* NOT what protects the document — `persistLayout` refuses the write, and it
|
|
277
|
-
* refuses it in more cases than this (while the claim is still `checking`, and
|
|
278
|
-
* after a refused save). This is the narrower question the CHROME asks, and it
|
|
279
|
-
* is narrower on purpose: during `checking` nobody is named yet, and the
|
|
280
|
-
* pinned Header/Footer seed runs in the chrome's mount commit and must still
|
|
281
|
-
* land. Its write is held by `onChange` and released by the claim if it is
|
|
282
|
-
* granted, or never released if it is not — card 1096.
|
|
283
|
-
*
|
|
284
|
-
* …or the document itself is FROZEN (`readOnlyReason`): nobody holds it, and
|
|
285
|
-
* nobody may write it, ever.
|
|
286
|
-
*
|
|
287
|
-
* …or a save was REFUSED (`saveConflict`, 0.106.0): the stored document moved
|
|
288
|
-
* on under somebody else's save, the autosave has stopped, and nothing this
|
|
289
|
-
* editor does is written again until the operator reloads. Until 0.106.0 the
|
|
290
|
-
* canvas, the tree and the inspector went on taking edits nothing would save,
|
|
291
|
-
* and Undo / Redo went on rewinding them; the old campaign designer locked
|
|
292
|
-
* the canvas there, and owner ruling R5 (Victor Mau, 2026-10-09) says the
|
|
293
|
-
* screen's Undo / Redo are DISABLED, not hidden, while this is true. The
|
|
294
|
-
* operator still reads, selects and copies from the screen the notice warns
|
|
295
|
-
* them about. */
|
|
296
|
-
readOnly: boolean;
|
|
297
|
-
/** Why the open document may never be written, as the backend said it
|
|
298
|
-
* (`EaselDocument.readOnlyReason`: a campaign that has been sent), or null
|
|
299
|
-
* for every document that may. Such a document is read-only for good: its
|
|
300
|
-
* saves are refused, its lock is never claimed, and the save pill's place
|
|
301
|
-
* says this instead. Optional for a context built by hand, where it is
|
|
302
|
-
* absent and means null. */
|
|
303
|
-
readOnlyReason?: string | null;
|
|
304
|
-
/** Take the page: from the "someone else is editing" choice, or back after
|
|
305
|
-
* somebody took it from you. */
|
|
306
|
-
takeOverEditing: () => void;
|
|
307
|
-
/** Choose to look without touching (keeps the holder for the ribbon). */
|
|
308
|
-
viewReadOnly: () => void;
|
|
309
|
-
/** A save the backend REFUSED because the stored page moved on. Non-null means
|
|
310
|
-
* autosave has stopped and the operator's work is still only on their screen.
|
|
311
|
-
* Told in a bar under the top bar (efficient-easel/chrome `EditNotice`),
|
|
312
|
-
* never a dialog, so there is nothing to dismiss: the operator reads and
|
|
313
|
-
* copies from the screen it is warning them about. */
|
|
314
|
-
saveConflict: SaveConflict | null;
|
|
315
|
-
/** The last save failed because the server could not be REACHED (no answer
|
|
316
|
-
* at all), not because it answered no (8.0.0). With a failed save, the bar
|
|
317
|
-
* says "You're offline" where it would say "Not saved". Optional for a
|
|
318
|
-
* context built by hand, where it is absent and means false. */
|
|
319
|
-
saveUnreachable?: boolean;
|
|
320
|
-
/** Abandon this editor's version and reload what is stored. The ONLY path that
|
|
321
|
-
* throws the operator's work away, and only they can ask for it. */
|
|
322
|
-
reloadFromServer: () => void;
|
|
323
|
-
/** Retarget the editor in place (guards unsaved changes before it swaps). */
|
|
324
|
-
navigate: (target: AnyScreenTarget) => void;
|
|
325
|
-
/** A retarget held behind the unsaved-changes prompt (null = no prompt). */
|
|
326
|
-
pendingNav: AnyScreenTarget | null;
|
|
327
|
-
cancelPendingNav: () => void;
|
|
328
|
-
discardAndNavigate: () => void;
|
|
329
|
-
saveAndNavigate: () => Promise<void>;
|
|
330
|
-
};
|
|
331
|
-
declare const ScreenDocumentContext: react.Context<ScreenDocumentValue | null>;
|
|
332
|
-
declare function useScreenDocument(): ScreenDocumentValue;
|
|
333
|
-
declare function ScreenDocumentProvider({ target, addressing: handed, onHostMessage, children, }: {
|
|
334
|
-
target: AnyScreenTarget;
|
|
335
|
-
/** How this editor's documents are ADDRESSED — the layer's rules, handed in
|
|
336
|
-
* by whoever mounts the provider (the website's entry hands the website's:
|
|
337
|
-
* lib/website-document-addressing). Without them there is one document that
|
|
338
|
-
* nothing stores. ONE object for the life of the mount, like the session. */
|
|
339
|
-
addressing?: AnyDocumentAddressing;
|
|
340
|
-
/** The layer's own host messages, asked before the ones every editor answers
|
|
341
|
-
* (lib/host-inbound): the website's are lib/website-host-inbound.
|
|
342
|
-
* Returns true when it handled one. */
|
|
343
|
-
onHostMessage?: (raw: unknown) => boolean;
|
|
344
|
-
children: ReactNode;
|
|
345
|
-
}): react.JSX.Element;
|
|
346
|
-
|
|
347
141
|
/**
|
|
348
142
|
* The editor's HOST — whatever embeds the storefront editor, and the one way the
|
|
349
143
|
* editor talks to it.
|
|
@@ -615,4 +409,210 @@ declare function createEaselHost(options: {
|
|
|
615
409
|
deliver: (message: HostToEaselMessage) => void;
|
|
616
410
|
};
|
|
617
411
|
|
|
412
|
+
type ScreenTarget = {
|
|
413
|
+
kind: "page";
|
|
414
|
+
slug: string;
|
|
415
|
+
siteId?: string;
|
|
416
|
+
} | {
|
|
417
|
+
kind: "template";
|
|
418
|
+
id: string;
|
|
419
|
+
pageSlug?: string;
|
|
420
|
+
};
|
|
421
|
+
/**
|
|
422
|
+
* A DOCUMENT OF A HOST'S OWN — not a website's page or template: what the
|
|
423
|
+
* campaign designer opens (0.103.0). `record` is the record that stores it (a
|
|
424
|
+
* campaign's id), and `slug` the document within that record (one of its
|
|
425
|
+
* variants), the empty slug when it names none.
|
|
426
|
+
*
|
|
427
|
+
* The provider asks no more of it than that. One record is one adapter (it
|
|
428
|
+
* re-keys on `record`, as a page target re-keys on its site); two of these are
|
|
429
|
+
* the same document when record and slug match; and a document that has gone
|
|
430
|
+
* is reported, never replaced by a Home, which is a website's.
|
|
431
|
+
*
|
|
432
|
+
* Beside `ScreenTarget` and not in it: that union is the website's two kinds,
|
|
433
|
+
* as its hosts name and build them (and as the storefront publishes them), and
|
|
434
|
+
* its host messages carry one. A host's own document reaches the host through
|
|
435
|
+
* its address (`EaselHostAddress.show`), and never as `navigated`.
|
|
436
|
+
*/
|
|
437
|
+
type DocumentTarget = {
|
|
438
|
+
kind: "document";
|
|
439
|
+
record: string;
|
|
440
|
+
slug?: string;
|
|
441
|
+
};
|
|
442
|
+
/** Every target the document's provider opens: a website's page or template
|
|
443
|
+
* (`ScreenTarget`), or a document of a host's own (`DocumentTarget`). */
|
|
444
|
+
type AnyScreenTarget = ScreenTarget | DocumentTarget;
|
|
445
|
+
|
|
446
|
+
type ScreenDocumentValue = {
|
|
447
|
+
/** The document the editor is currently pointed at (runtime, retargetable). */
|
|
448
|
+
target: AnyScreenTarget;
|
|
449
|
+
/** Record-type plumbing (load/save/list/create/delete) for the pointed-at
|
|
450
|
+
* document. Chrome and panels consult `capabilities`, never the record type. */
|
|
451
|
+
adapter: EaselDocumentAdapter;
|
|
452
|
+
/** What this document type supports — the ONLY sanctioned mode switch. */
|
|
453
|
+
capabilities: EaselCapabilities;
|
|
454
|
+
/** Id of the website template being edited (template mode only). */
|
|
455
|
+
templateId: string | null;
|
|
456
|
+
/** Slug of the page being edited — the template page's slug in template mode,
|
|
457
|
+
* and a host's own document's `slug` (or the empty slug) for one of those. */
|
|
458
|
+
currentSlug: string;
|
|
459
|
+
/** Kind of the loaded page (`dynamic` drives the product-binding controls), or
|
|
460
|
+
* null while loading / in template mode where the record carries no kind. */
|
|
461
|
+
currentKind: string | null;
|
|
462
|
+
/** Id of the website this page belongs to (page mode only; null in template
|
|
463
|
+
* mode or when the editor was opened without a site). The list/save paths and
|
|
464
|
+
* the page switcher scope to this so they act on one site's pages, not the
|
|
465
|
+
* whole tenant's. */
|
|
466
|
+
siteId: string | null;
|
|
467
|
+
/** Whether the editor's backend may be called yet. Under the EFFICIENT backend
|
|
468
|
+
* that is "the console's token has arrived"; an embedder's own backend
|
|
469
|
+
* authorises its calls itself and is ready from the first render. THE gate
|
|
470
|
+
* for anything that goes through the backend — never `token`, which an
|
|
471
|
+
* embedder's backend does not have. */
|
|
472
|
+
ready: boolean;
|
|
473
|
+
/** The bridged admin JWT once it arrives (null while waiting, and always null
|
|
474
|
+
* under an embedder's own backend). Only for the callers that still pass it
|
|
475
|
+
* to the EFFICIENT client themselves. */
|
|
476
|
+
token: string | null;
|
|
477
|
+
/** Latest token for imperative reads (custom fields, pickers, panels). */
|
|
478
|
+
tokenRef: RefObject<string | null>;
|
|
479
|
+
/** Bridge editor events to the embedding admin portal. */
|
|
480
|
+
notifyParent: SendToHost;
|
|
481
|
+
/** Operator capabilities the ADMIN CONSOLE granted over the origin-gated
|
|
482
|
+
* bridge. Distinct from `capabilities` above: those describe the DOCUMENT
|
|
483
|
+
* (can it publish, has it site settings), these describe the PERSON, and the
|
|
484
|
+
* editor is never allowed to infer them for itself. */
|
|
485
|
+
bridgeCapabilities: BridgedCapabilities;
|
|
486
|
+
/** Run an authed op; on 401 ask the parent for a fresh token and retry once. */
|
|
487
|
+
runAuthed: (op: (token: string) => Promise<unknown>) => Promise<boolean>;
|
|
488
|
+
/** Backend id of the loaded document — the identity of what the editor is
|
|
489
|
+
* editing right now. Null while loading and between retargets, so it also
|
|
490
|
+
* keys any once-per-document work (e.g. the pinned-chrome seed). */
|
|
491
|
+
docId: string | null;
|
|
492
|
+
/** The document exactly as it was LOADED from the backend: Puck's initial
|
|
493
|
+
* state, and the persisted snapshot a load-time seed must measure itself
|
|
494
|
+
* against. Null while loading. Edits live in Puck's own store, so this does
|
|
495
|
+
* NOT track them — it stays the last-loaded layout for the whole session. */
|
|
496
|
+
data: Data | null;
|
|
497
|
+
/** The freshest layout the merchant is actually viewing — the latest edit made
|
|
498
|
+
* since load (tracked on every onChange, even after the autosave settles), else
|
|
499
|
+
* the last-loaded `data`. Lets a feature snapshot the live view (e.g. the
|
|
500
|
+
* read-only origin backdrop in footer focus) without reaching into Puck's store.
|
|
501
|
+
* Lean accessor (ISP) — never exposes the ref. */
|
|
502
|
+
getLiveData: () => Data | null;
|
|
503
|
+
error: string | null;
|
|
504
|
+
/** True while the template-entry bounce is redirecting to the Home page. */
|
|
505
|
+
redirecting: boolean;
|
|
506
|
+
/** The pinned-seed gate's four words for where the autosave stands. */
|
|
507
|
+
saveStatus: SaveStatus;
|
|
508
|
+
/** The autosave's state (efficient-easel/autosave) — what the toolbar pill
|
|
509
|
+
* draws, through `saveIndicator`. */
|
|
510
|
+
saveState: AutosaveSnapshot;
|
|
511
|
+
/** Whether autosave is running. The toolbar pill is a switch: clicking it
|
|
512
|
+
* flips this. OFF = edits are HELD (kept in memory, still flushable by Publish
|
|
513
|
+
* / navigation / re-enabling) but never written on a timer. Session-scoped —
|
|
514
|
+
* survives in-place page switches. */
|
|
515
|
+
autoSaveEnabled: boolean;
|
|
516
|
+
/** Auto-save stopped because a save was REFUSED, not because the operator
|
|
517
|
+
* paused it — so a stopped auto-save can never read as a switch somebody
|
|
518
|
+
* flicked themselves. */
|
|
519
|
+
autoSaveStoppedByConflict: boolean;
|
|
520
|
+
/** True when an edit hasn't been persisted yet — including one held while the
|
|
521
|
+
* claim is in flight. Drives the guard that prompts before an in-place
|
|
522
|
+
* navigation would abandon held-but-unsaved edits. */
|
|
523
|
+
hasUnsavedEdits: boolean;
|
|
524
|
+
/** Flip autosave on/off. Turning it back ON writes any held edits at once, so
|
|
525
|
+
* "on" always means caught up. */
|
|
526
|
+
toggleAutoSave: () => void;
|
|
527
|
+
/** "Retry now": a failed save goes again at once instead of waiting out its
|
|
528
|
+
* backoff. */
|
|
529
|
+
retrySave: () => void;
|
|
530
|
+
/** Autosave — hand every Puck onChange to this. */
|
|
531
|
+
onChange: (next: Data) => void;
|
|
532
|
+
/** Persist the pending edit immediately; resolves to whether nothing is left
|
|
533
|
+
* unsaved. */
|
|
534
|
+
flushSave: () => Promise<boolean>;
|
|
535
|
+
/** Persist + publish the current document (Save for templates). */
|
|
536
|
+
onPublish: (next: Data) => Promise<void>;
|
|
537
|
+
/** Whether this editor may write the open page, and why not when it may not.
|
|
538
|
+
* `editing` is the only writable state; the gate is on the save path itself,
|
|
539
|
+
* so hiding or showing chrome from this can't be the thing that protects the
|
|
540
|
+
* document. Always `editing` in template mode (no lock there). */
|
|
541
|
+
access: EditAccess;
|
|
542
|
+
/** SOMEBODY ELSE holds this page, so this editor is read-only: it may look and
|
|
543
|
+
* select, but not move, add, duplicate or delete anything — until they take
|
|
544
|
+
* control themselves. The ONE answer the chrome asks; no panel re-derives it,
|
|
545
|
+
* so none of them can drift.
|
|
546
|
+
*
|
|
547
|
+
* NOT what protects the document — `persistLayout` refuses the write, and it
|
|
548
|
+
* refuses it in more cases than this (while the claim is still `checking`, and
|
|
549
|
+
* after a refused save). This is the narrower question the CHROME asks, and it
|
|
550
|
+
* is narrower on purpose: during `checking` nobody is named yet, and the
|
|
551
|
+
* pinned Header/Footer seed runs in the chrome's mount commit and must still
|
|
552
|
+
* land. Its write is held by `onChange` and released by the claim if it is
|
|
553
|
+
* granted, or never released if it is not — card 1096.
|
|
554
|
+
*
|
|
555
|
+
* …or the document itself is FROZEN (`readOnlyReason`): nobody holds it, and
|
|
556
|
+
* nobody may write it, ever.
|
|
557
|
+
*
|
|
558
|
+
* …or a save was REFUSED (`saveConflict`, 0.106.0): the stored document moved
|
|
559
|
+
* on under somebody else's save, the autosave has stopped, and nothing this
|
|
560
|
+
* editor does is written again until the operator reloads. Until 0.106.0 the
|
|
561
|
+
* canvas, the tree and the inspector went on taking edits nothing would save,
|
|
562
|
+
* and Undo / Redo went on rewinding them; the old campaign designer locked
|
|
563
|
+
* the canvas there, and owner ruling R5 (Victor Mau, 2026-10-09) says the
|
|
564
|
+
* screen's Undo / Redo are DISABLED, not hidden, while this is true. The
|
|
565
|
+
* operator still reads, selects and copies from the screen the notice warns
|
|
566
|
+
* them about. */
|
|
567
|
+
readOnly: boolean;
|
|
568
|
+
/** Why the open document may never be written, as the backend said it
|
|
569
|
+
* (`EaselDocument.readOnlyReason`: a campaign that has been sent), or null
|
|
570
|
+
* for every document that may. Such a document is read-only for good: its
|
|
571
|
+
* saves are refused, its lock is never claimed, and the save pill's place
|
|
572
|
+
* says this instead. Optional for a context built by hand, where it is
|
|
573
|
+
* absent and means null. */
|
|
574
|
+
readOnlyReason?: string | null;
|
|
575
|
+
/** Take the page: from the "someone else is editing" choice, or back after
|
|
576
|
+
* somebody took it from you. */
|
|
577
|
+
takeOverEditing: () => void;
|
|
578
|
+
/** Choose to look without touching (keeps the holder for the ribbon). */
|
|
579
|
+
viewReadOnly: () => void;
|
|
580
|
+
/** A save the backend REFUSED because the stored page moved on. Non-null means
|
|
581
|
+
* autosave has stopped and the operator's work is still only on their screen.
|
|
582
|
+
* Told in a bar under the top bar (efficient-easel/chrome `EditNotice`),
|
|
583
|
+
* never a dialog, so there is nothing to dismiss: the operator reads and
|
|
584
|
+
* copies from the screen it is warning them about. */
|
|
585
|
+
saveConflict: SaveConflict | null;
|
|
586
|
+
/** The last save failed because the server could not be REACHED (no answer
|
|
587
|
+
* at all), not because it answered no (8.0.0). With a failed save, the bar
|
|
588
|
+
* says "You're offline" where it would say "Not saved". Optional for a
|
|
589
|
+
* context built by hand, where it is absent and means false. */
|
|
590
|
+
saveUnreachable?: boolean;
|
|
591
|
+
/** Abandon this editor's version and reload what is stored. The ONLY path that
|
|
592
|
+
* throws the operator's work away, and only they can ask for it. */
|
|
593
|
+
reloadFromServer: () => void;
|
|
594
|
+
/** Retarget the editor in place (guards unsaved changes before it swaps). */
|
|
595
|
+
navigate: (target: AnyScreenTarget) => void;
|
|
596
|
+
/** A retarget held behind the unsaved-changes prompt (null = no prompt). */
|
|
597
|
+
pendingNav: AnyScreenTarget | null;
|
|
598
|
+
cancelPendingNav: () => void;
|
|
599
|
+
discardAndNavigate: () => void;
|
|
600
|
+
saveAndNavigate: () => Promise<void>;
|
|
601
|
+
};
|
|
602
|
+
declare const ScreenDocumentContext: react.Context<ScreenDocumentValue | null>;
|
|
603
|
+
declare function useScreenDocument(): ScreenDocumentValue;
|
|
604
|
+
declare function ScreenDocumentProvider({ target, addressing: handed, onHostMessage, children, }: {
|
|
605
|
+
target: AnyScreenTarget;
|
|
606
|
+
/** How this editor's documents are ADDRESSED — the layer's rules, handed in
|
|
607
|
+
* by whoever mounts the provider (the website's entry hands the website's:
|
|
608
|
+
* lib/website-document-addressing). Without them there is one document that
|
|
609
|
+
* nothing stores. ONE object for the life of the mount, like the session. */
|
|
610
|
+
addressing?: AnyDocumentAddressing;
|
|
611
|
+
/** The layer's own host messages, asked before the ones every editor answers
|
|
612
|
+
* (lib/host-inbound): the website's are lib/website-host-inbound.
|
|
613
|
+
* Returns true when it handled one. */
|
|
614
|
+
onHostMessage?: (raw: unknown) => boolean;
|
|
615
|
+
children: ReactNode;
|
|
616
|
+
}): react.JSX.Element;
|
|
617
|
+
|
|
618
618
|
export { type AnyDocumentAddressing as A, DETACHED_EASEL_HOST as D, type EaselHost as E, type HostPickedMedia as H, type InboundHostMessage as I, NO_DOCUMENT_ADDRESSING as N, type PickedMediaSource as P, type SectionScreenTarget as S, type DocumentAddressing as a, type EaselHostAddress as b, EaselHostContext as c, EaselHostProvider as d, type EaselMessageType as e, type EaselToHostMessage as f, type HostToEaselMessage as g, type SendToHost as h, createEaselHost as i, useHostEscape as j, type AnyScreenTarget as k, type DocumentTarget as l, ScreenDocumentContext as m, ScreenDocumentProvider as n, type ScreenDocumentValue as o, type ScreenTarget as p, useScreenDocument as q, useEaselHost as u };
|
package/dist/autosave/index.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { a as Autosave, A as AutosaveSnapshot, b as AutosaveOptions } from '../engine-CvXpFE86.js';
|
|
2
2
|
export { c as AutosaveClock, d as createAutosave } from '../engine-CvXpFE86.js';
|
|
3
|
-
import { S as SaveIndicator } from '../words-
|
|
4
|
-
export { a as SaveWords, s as saveIndicator } from '../words-
|
|
5
|
-
import '../types-
|
|
3
|
+
import { S as SaveIndicator } from '../words-Cm8PMYn0.js';
|
|
4
|
+
export { a as SaveWords, s as saveIndicator } from '../words-Cm8PMYn0.js';
|
|
5
|
+
import '../types-B3iSHX-w.js';
|
|
6
6
|
import '../types-Cy7rdmXt.js';
|
|
7
7
|
import '../wordFit-7XpmhcaW.js';
|
|
8
8
|
import 'react';
|
|
9
9
|
import '../edit-lock/index.js';
|
|
10
|
-
import '../panelWidths-
|
|
10
|
+
import '../panelWidths-DgQXiPXU.js';
|
|
11
11
|
|
|
12
12
|
type UseAutosaveOptions<T> = Omit<AutosaveOptions<T>, 'save' | 'onFailure'> & {
|
|
13
13
|
/** Write one document. Read at call time, so it may close over current props. */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { MarkStyle, WrapResult, InlineRule, MarkupTool } from 'efficient-shell/markup';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { TextAlign, TextToolbarCase, TextToolbarMark } from 'efficient-shell/ui';
|
|
5
|
+
import { C as CopyWords } from './words-BpTjCfqO.js';
|
|
6
6
|
|
|
7
7
|
type CellWords = NonNullable<CopyWords['cells']>;
|
|
8
8
|
interface CopyCell {
|
|
@@ -177,13 +177,13 @@ interface CopyBoxLastTool {
|
|
|
177
177
|
* `A` is inferred from `allow`, so nothing has to be spelled at the call site.
|
|
178
178
|
*
|
|
179
179
|
* ⚠️ IT DOES NOT FORBID A WIDER HANDLER, and cannot: a function taking the
|
|
180
|
-
* whole `
|
|
180
|
+
* whole `TextAlign` is assignable where one taking three is wanted, because
|
|
181
181
|
* parameters are contravariant — and that direction is SAFE, since only the
|
|
182
182
|
* three can ever arrive. What this buys is that the narrow handler now
|
|
183
183
|
* type-checks without a cast, which is what makes writing the safe one the
|
|
184
184
|
* path of least resistance.
|
|
185
185
|
*/
|
|
186
|
-
interface CopyBoxAlign<A extends
|
|
186
|
+
interface CopyBoxAlign<A extends TextAlign = TextAlign> {
|
|
187
187
|
value: A | '';
|
|
188
188
|
onChange: (next: A) => void;
|
|
189
189
|
/**
|
|
@@ -241,7 +241,7 @@ interface CopyBoxProps {
|
|
|
241
241
|
*/
|
|
242
242
|
actions?: ReactNode;
|
|
243
243
|
/** The alignment strip. Omitted = no strip. */
|
|
244
|
-
align?: CopyBoxAlign<
|
|
244
|
+
align?: CopyBoxAlign<TextAlign>;
|
|
245
245
|
/** @deprecated Not drawn: the toolbar's drawings are the shell set's.
|
|
246
246
|
* Removed in 12.0.0. */
|
|
247
247
|
glyphs?: Partial<Record<MarkStyle, ReactNode>>;
|
package/dist/chrome/chrome.css
CHANGED
|
@@ -248,8 +248,8 @@
|
|
|
248
248
|
background: var(--es-panel);
|
|
249
249
|
overflow: hidden;
|
|
250
250
|
position: relative;
|
|
251
|
-
/* A size container: a panel is DRAGGED between
|
|
252
|
-
ask the panel's width, never the screen's. */
|
|
251
|
+
/* A size container: a panel is DRAGGED between its floor (left 240, right
|
|
252
|
+
220) and 560, so its contents ask the panel's width, never the screen's. */
|
|
253
253
|
container-type: inline-size;
|
|
254
254
|
}
|
|
255
255
|
/* THE PANEL'S SIDE INSET, ONE HOME: 16, AND 12 UNDER A 260px PANEL (one
|
|
@@ -403,9 +403,6 @@ span:has(> .es-insp-up-btn) {
|
|
|
403
403
|
grid-column: 2;
|
|
404
404
|
justify-self: start;
|
|
405
405
|
}
|
|
406
|
-
.es-insp-head:has(.es-insp-textbtn) .es-insp-divider {
|
|
407
|
-
display: none;
|
|
408
|
-
}
|
|
409
406
|
}
|
|
410
407
|
.es-insp-chip {
|
|
411
408
|
width: 26px;
|
|
@@ -474,19 +471,6 @@ span:has(> .es-insp-up-btn) {
|
|
|
474
471
|
flex: none;
|
|
475
472
|
margin-left: auto;
|
|
476
473
|
}
|
|
477
|
-
/* A side panel's head has two pieces besides its glyph buttons (MediaPanelHead,
|
|
478
|
-
card [MERGE] One Media Panel Head, 2026-10-06; until then each editor's own
|
|
479
|
-
sheet drew them, as `.sf-head-*` and `.cd-head-*`). A MODE's way out is a
|
|
480
|
-
word ("Cancel", "Done") on the shell's small Button (`.es-insp-textbtn`
|
|
481
|
-
names it for the narrow layout above); the hairline parts the
|
|
482
|
-
panel's own tools from the Hide panel button that ends every head. */
|
|
483
|
-
.es-insp-divider {
|
|
484
|
-
width: 1px;
|
|
485
|
-
height: 16px;
|
|
486
|
-
margin: 0 4px;
|
|
487
|
-
background: var(--es-line);
|
|
488
|
-
flex: none;
|
|
489
|
-
}
|
|
490
474
|
|
|
491
475
|
/* ── Edit notice — somebody else is in here, or a save was refused ───────── */
|
|
492
476
|
/* A bar under the top bar, never a dialog (blueprint 2.5): the operator has to
|