@einblick/sdk 0.6.4 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +61 -13
  3. package/dist/cli.js +1 -7
  4. package/dist/cli.js.map +1 -1
  5. package/dist/client.js +2 -2
  6. package/dist/client.js.map +1 -1
  7. package/dist/codegen.d.ts.map +1 -1
  8. package/dist/codegen.js +63 -11
  9. package/dist/codegen.js.map +1 -1
  10. package/dist/next/edit-boot.d.ts +10 -19
  11. package/dist/next/edit-boot.d.ts.map +1 -1
  12. package/dist/next/edit-boot.js +14 -28
  13. package/dist/next/edit-boot.js.map +1 -1
  14. package/dist/next/revalidate.d.ts +4 -2
  15. package/dist/next/revalidate.d.ts.map +1 -1
  16. package/dist/next/revalidate.js +46 -12
  17. package/dist/next/revalidate.js.map +1 -1
  18. package/dist/react/markers.d.ts +5 -1
  19. package/dist/react/markers.d.ts.map +1 -1
  20. package/dist/react/markers.js +23 -6
  21. package/dist/react/markers.js.map +1 -1
  22. package/dist/react/provider.d.ts +1 -1
  23. package/dist/react/provider.d.ts.map +1 -1
  24. package/dist/react/provider.js +17 -3
  25. package/dist/react/provider.js.map +1 -1
  26. package/dist/react/runtime.d.ts +5 -0
  27. package/dist/react/runtime.d.ts.map +1 -1
  28. package/dist/react/runtime.js +396 -147
  29. package/dist/react/runtime.js.map +1 -1
  30. package/dist/react/shared.d.ts.map +1 -1
  31. package/dist/react/shared.js +10 -12
  32. package/dist/react/shared.js.map +1 -1
  33. package/dist/react/types.d.ts +3 -1
  34. package/dist/react/types.d.ts.map +1 -1
  35. package/dist/react/types.js.map +1 -1
  36. package/package.json +9 -12
  37. package/dist/client.test.d.ts +0 -2
  38. package/dist/client.test.d.ts.map +0 -1
  39. package/dist/client.test.js +0 -342
  40. package/dist/client.test.js.map +0 -1
  41. package/dist/codegen.test.d.ts +0 -2
  42. package/dist/codegen.test.d.ts.map +0 -1
  43. package/dist/codegen.test.js +0 -219
  44. package/dist/codegen.test.js.map +0 -1
@@ -1,9 +1,11 @@
1
1
  import { EINBLICK_AUTH_SOURCE, EINBLICK_BRIDGE_SOURCE, EINBLICK_EDITOR_SOURCE, EINBLICK_PARENT_SOURCE, formatValueForText, isMediaEditableBinding, isEditSessionExpired, readBindingFromElement, resolveAppOrigin, serializeBindingValue, shouldUseInlineEditor, } from './shared.js';
2
2
  import { getEinblickSdkMessages, resolveEinblickSdkLocale, } from './i18n.js';
3
3
  const RUNTIME_STYLE_ID = 'einblick-edit-runtime-styles';
4
- const EINBLICK_ACCENT_RGB = '250, 105, 62';
5
- const EINBLICK_ACCENT_COLOR = `rgb(${EINBLICK_ACCENT_RGB})`;
6
- const EINBLICK_ACCENT_BORDER = `rgba(${EINBLICK_ACCENT_RGB}, 0.92)`;
4
+ const DEFAULT_EINBLICK_ACCENT_COLOR = 'rgb(250, 105, 62)';
5
+ const EINBLICK_ACCENT_COLOR_VAR = '--einblick-editor-accent-color';
6
+ const EINBLICK_ACCENT_COLOR = `var(${EINBLICK_ACCENT_COLOR_VAR}, ${DEFAULT_EINBLICK_ACCENT_COLOR})`;
7
+ const EINBLICK_ACCENT_TINT = `color-mix(in srgb, ${EINBLICK_ACCENT_COLOR} 12%, transparent)`;
8
+ const EINBLICK_ACCENT_HOVER_COLOR = `color-mix(in srgb, ${EINBLICK_ACCENT_COLOR} 92%, black)`;
7
9
  const CONTROL_BAR_HEIGHT = 44;
8
10
  function getBrandBadgeSvg(size) {
9
11
  return `
@@ -40,12 +42,40 @@ function getMoreActionsSvg(size) {
40
42
  </svg>
41
43
  `;
42
44
  }
45
+ function createDatabaseIconElement(size) {
46
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
47
+ svg.setAttribute('viewBox', '0 0 24 24');
48
+ svg.setAttribute('width', String(size));
49
+ svg.setAttribute('height', String(size));
50
+ svg.setAttribute('fill', 'none');
51
+ svg.setAttribute('stroke', 'currentColor');
52
+ svg.setAttribute('stroke-width', '2');
53
+ svg.setAttribute('stroke-linecap', 'round');
54
+ svg.setAttribute('stroke-linejoin', 'round');
55
+ svg.setAttribute('aria-hidden', 'true');
56
+ svg.setAttribute('focusable', 'false');
57
+ const ellipse = document.createElementNS('http://www.w3.org/2000/svg', 'ellipse');
58
+ ellipse.setAttribute('cx', '12');
59
+ ellipse.setAttribute('cy', '5');
60
+ ellipse.setAttribute('rx', '9');
61
+ ellipse.setAttribute('ry', '3');
62
+ const sidePath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
63
+ sidePath.setAttribute('d', 'M3 5V19A9 3 0 0 0 21 19V5');
64
+ const middlePath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
65
+ middlePath.setAttribute('d', 'M3 12A9 3 0 0 0 21 12');
66
+ svg.append(ellipse, sidePath, middlePath);
67
+ return svg;
68
+ }
43
69
  function createRandomId() {
44
- if (typeof crypto !== 'undefined' &&
45
- typeof crypto.randomUUID === 'function') {
70
+ if (typeof crypto === 'undefined') {
71
+ throw new Error('Secure browser randomness is unavailable');
72
+ }
73
+ if (typeof crypto.randomUUID === 'function') {
46
74
  return crypto.randomUUID();
47
75
  }
48
- return Math.random().toString(36).slice(2);
76
+ const bytes = new Uint8Array(16);
77
+ crypto.getRandomValues(bytes);
78
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
49
79
  }
50
80
  function stopEvent(event) {
51
81
  event.preventDefault();
@@ -54,6 +84,24 @@ function stopEvent(event) {
54
84
  function clamp(value, min, max) {
55
85
  return Math.min(Math.max(value, min), max);
56
86
  }
87
+ function rectsOverlap(first, second, padding = 0) {
88
+ return (first.left < second.right + padding &&
89
+ first.right > second.left - padding &&
90
+ first.top < second.bottom + padding &&
91
+ first.bottom > second.top - padding);
92
+ }
93
+ function normalizeAccentColor(accentColor) {
94
+ const normalized = accentColor?.trim();
95
+ if (!normalized) {
96
+ return null;
97
+ }
98
+ if (typeof CSS !== 'undefined' &&
99
+ typeof CSS.supports === 'function' &&
100
+ !CSS.supports('color', normalized)) {
101
+ return null;
102
+ }
103
+ return normalized;
104
+ }
57
105
  function isTextInputEvent(event) {
58
106
  return (event.key.length === 1 ||
59
107
  event.key === 'Enter' ||
@@ -67,15 +115,37 @@ function fieldTypeAllowsMultiline(binding) {
67
115
  return binding.fieldType === 'text' || binding.fieldType === 'markdown';
68
116
  }
69
117
  function getMediaAcceptValue(binding) {
70
- return binding.fieldType === 'image' || binding.fieldType === 'images'
71
- ? 'image/*'
72
- : '';
118
+ return binding.fieldType === 'image' ? 'image/*' : '';
73
119
  }
74
120
  function getMediaActionLabel(binding, messages) {
75
- return binding.fieldType === 'image' || binding.fieldType === 'images'
121
+ return binding.fieldType === 'image'
76
122
  ? messages.actions.replaceImage
77
123
  : messages.actions.replaceFile;
78
124
  }
125
+ function isMultiMediaBinding(binding) {
126
+ return binding?.fieldType === 'images' || binding?.fieldType === 'files';
127
+ }
128
+ function setOverlayButtonContent(button, label, options) {
129
+ const hasDatabaseIcon = options?.databaseIcon === true;
130
+ if (button.dataset.einblickOverlayButtonLabel === label &&
131
+ button.dataset.einblickOverlayButtonDatabaseIcon ===
132
+ (hasDatabaseIcon ? 'true' : 'false')) {
133
+ return;
134
+ }
135
+ const text = document.createElement('span');
136
+ text.setAttribute('data-einblick-overlay-button-label', 'true');
137
+ text.textContent = label;
138
+ button.replaceChildren();
139
+ if (hasDatabaseIcon) {
140
+ button.append(createDatabaseIconElement(14));
141
+ }
142
+ button.append(text);
143
+ button.title = label;
144
+ button.dataset.einblickOverlayButtonLabel = label;
145
+ button.dataset.einblickOverlayButtonDatabaseIcon = hasDatabaseIcon
146
+ ? 'true'
147
+ : 'false';
148
+ }
79
149
  function injectRuntimeStyles() {
80
150
  if (typeof document === 'undefined') {
81
151
  return;
@@ -91,13 +161,12 @@ function injectRuntimeStyles() {
91
161
  }
92
162
 
93
163
  [data-einblick-editable="true"][data-einblick-hover-active="true"] {
94
- outline: 1px solid ${EINBLICK_ACCENT_BORDER};
164
+ outline: 1px solid ${EINBLICK_ACCENT_COLOR};
95
165
  outline-offset: -1px;
96
- box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.72);
97
166
  }
98
167
 
99
168
  [data-einblick-editable="true"][data-einblick-field-hover-active="true"] {
100
- outline: 1px dashed rgba(${EINBLICK_ACCENT_RGB}, 0.78);
169
+ outline: 1px dashed ${EINBLICK_ACCENT_COLOR};
101
170
  outline-offset: -1px;
102
171
  }
103
172
 
@@ -111,43 +180,46 @@ function injectRuntimeStyles() {
111
180
  z-index: 2147483644;
112
181
  display: inline-flex;
113
182
  align-items: center;
114
- gap: 0;
183
+ gap: 0.25rem;
115
184
  }
116
185
 
117
186
  [data-einblick-edit-button],
118
- [data-einblick-create-button],
187
+ [data-einblick-collection-create-button],
119
188
  [data-einblick-field-button] {
189
+ display: inline-flex;
190
+ align-items: center;
191
+ justify-content: center;
192
+ gap: 0.35rem;
120
193
  border: 1px solid ${EINBLICK_ACCENT_COLOR};
121
194
  border-radius: 0;
122
- min-height: 2rem;
123
- padding: 0 0.72rem;
124
- font: 600 12px/1 system-ui, sans-serif;
125
- letter-spacing: 0;
126
- cursor: pointer;
127
- box-shadow: 0 8px 18px rgba(15, 23, 42, 0.16);
128
- }
129
-
130
- [data-einblick-edit-button],
131
- [data-einblick-field-button="media"] {
195
+ min-width: max-content;
196
+ min-height: 2.05rem;
197
+ max-width: calc(100vw - 0.5rem);
198
+ padding: 0 0.74rem;
199
+ overflow: hidden;
132
200
  background: ${EINBLICK_ACCENT_COLOR};
133
201
  color: rgb(255, 255, 255);
202
+ font: 700 13px/1 system-ui, sans-serif;
203
+ letter-spacing: 0;
204
+ white-space: nowrap;
205
+ text-overflow: ellipsis;
206
+ cursor: pointer;
134
207
  }
135
208
 
136
- [data-einblick-edit-button] {
137
- font-size: 13px;
138
- }
139
-
140
- [data-einblick-create-button],
141
- [data-einblick-field-button="text"] {
142
- background: rgb(255, 255, 255);
143
- color: ${EINBLICK_ACCENT_COLOR};
209
+ [data-einblick-edit-button] svg,
210
+ [data-einblick-collection-create-button] svg,
211
+ [data-einblick-field-button] svg {
212
+ flex: none;
213
+ width: 0.95rem;
214
+ height: 0.95rem;
144
215
  }
145
216
 
146
217
  [data-einblick-edit-button]:hover,
147
- [data-einblick-create-button]:hover,
218
+ [data-einblick-collection-create-button]:hover,
148
219
  [data-einblick-field-button]:hover {
149
- background: rgb(255, 255, 255);
150
- color: ${EINBLICK_ACCENT_COLOR};
220
+ background: ${EINBLICK_ACCENT_COLOR};
221
+ background: ${EINBLICK_ACCENT_HOVER_COLOR};
222
+ color: rgb(255, 255, 255);
151
223
  }
152
224
 
153
225
  [data-einblick-field-overlays] {
@@ -162,8 +234,6 @@ function injectRuntimeStyles() {
162
234
  position: absolute;
163
235
  z-index: 2147483644;
164
236
  pointer-events: auto;
165
- font-size: 11px;
166
- min-height: 1.65rem;
167
237
  }
168
238
 
169
239
  [data-einblick-brand-badge] {
@@ -182,17 +252,15 @@ function injectRuntimeStyles() {
182
252
  background: transparent;
183
253
  color: ${EINBLICK_ACCENT_COLOR};
184
254
  cursor: pointer;
185
- filter: drop-shadow(0 10px 24px rgba(15, 23, 42, 0.22));
186
- transition: transform 120ms ease, filter 120ms ease;
255
+ transition: transform 120ms ease;
187
256
  }
188
257
 
189
258
  [data-einblick-brand-badge]:hover {
190
259
  transform: translateY(-1px) scale(1.04);
191
- filter: drop-shadow(0 12px 28px rgba(15, 23, 42, 0.28));
192
260
  }
193
261
 
194
262
  [data-einblick-brand-badge]:focus-visible {
195
- outline: 2px solid ${EINBLICK_ACCENT_BORDER};
263
+ outline: 2px solid ${EINBLICK_ACCENT_COLOR};
196
264
  outline-offset: 4px;
197
265
  border-radius: 999px;
198
266
  }
@@ -212,7 +280,6 @@ function injectRuntimeStyles() {
212
280
  color: rgb(15, 23, 42);
213
281
  border-radius: 1.25rem;
214
282
  padding: 1rem;
215
- box-shadow: 0 22px 56px rgba(15, 23, 42, 0.2);
216
283
  backdrop-filter: blur(18px);
217
284
  box-sizing: border-box;
218
285
  }
@@ -262,7 +329,8 @@ function injectRuntimeStyles() {
262
329
  display: inline-flex;
263
330
  align-items: center;
264
331
  border-radius: 999px;
265
- background: rgba(${EINBLICK_ACCENT_RGB}, 0.12);
332
+ background: transparent;
333
+ background: ${EINBLICK_ACCENT_TINT};
266
334
  color: ${EINBLICK_ACCENT_COLOR};
267
335
  padding: 0.32rem 0.58rem;
268
336
  font: 600 0.74rem/1 system-ui, sans-serif;
@@ -311,7 +379,8 @@ function injectRuntimeStyles() {
311
379
 
312
380
  [data-einblick-brand-popover-action="primary"]:hover {
313
381
  border-color: ${EINBLICK_ACCENT_COLOR};
314
- background: rgb(237, 92, 49);
382
+ background: ${EINBLICK_ACCENT_COLOR};
383
+ background: ${EINBLICK_ACCENT_HOVER_COLOR};
315
384
  }
316
385
 
317
386
  [data-einblick-inline-editing="true"] {
@@ -337,7 +406,6 @@ function injectRuntimeStyles() {
337
406
  width: min(46rem, 100vw);
338
407
  background: rgb(255, 255, 255);
339
408
  border-left: 1px solid rgba(148, 163, 184, 0.35);
340
- box-shadow: 0 24px 64px rgba(15, 23, 42, 0.26);
341
409
  overflow: hidden;
342
410
  }
343
411
 
@@ -359,7 +427,6 @@ function injectRuntimeStyles() {
359
427
  border-top: 1px solid rgba(148, 163, 184, 0.36);
360
428
  overflow: hidden;
361
429
  background: rgb(255, 255, 255);
362
- box-shadow: 0 -12px 28px rgba(15, 23, 42, 0.12);
363
430
  pointer-events: none;
364
431
  transform: translateY(100%);
365
432
  transition: transform 260ms cubic-bezier(0.22, 1, 0.36, 1);
@@ -394,10 +461,14 @@ class EinblickEditRuntime {
394
461
  callbacks;
395
462
  chrome;
396
463
  reserveBottomSpace;
464
+ persist;
397
465
  locale;
398
466
  messages;
467
+ accentColor;
468
+ originalRootAccentColor;
399
469
  bridgeNonce = createRandomId();
400
470
  session;
471
+ brokerId = null;
401
472
  state = {
402
473
  enabled: true,
403
474
  active: true,
@@ -409,7 +480,6 @@ class EinblickEditRuntime {
409
480
  activeElement = null;
410
481
  hoverButtonContainer = null;
411
482
  hoverButton = null;
412
- hoverCreateButton = null;
413
483
  quickFieldOverlayContainer = null;
414
484
  quickFieldOverlays = [];
415
485
  brandBadge = null;
@@ -436,6 +506,7 @@ class EinblickEditRuntime {
436
506
  currentInlineRestoreState = null;
437
507
  inlineSaveInFlight = null;
438
508
  editorBackdrop = null;
509
+ editorFrame = null;
439
510
  editorNonce = null;
440
511
  editorTarget = null;
441
512
  editorBinding = null;
@@ -453,8 +524,10 @@ class EinblickEditRuntime {
453
524
  this.chrome = options.chrome ?? 'bar';
454
525
  this.reserveBottomSpace =
455
526
  options.reserveBottomSpace ?? this.chrome === 'bar';
527
+ this.persist = options.persist ?? true;
456
528
  this.locale = resolveEinblickSdkLocale(options.locale);
457
529
  this.messages = getEinblickSdkMessages(this.locale);
530
+ this.accentColor = normalizeAccentColor(options.accentColor);
458
531
  this.session =
459
532
  options.initialSession && !isEditSessionExpired(options.initialSession)
460
533
  ? options.initialSession
@@ -475,6 +548,43 @@ class EinblickEditRuntime {
475
548
  this.messages = getEinblickSdkMessages(nextLocale);
476
549
  this.refreshLocalizedUi();
477
550
  }
551
+ setAccentColor(accentColor) {
552
+ const nextAccentColor = normalizeAccentColor(accentColor);
553
+ if (nextAccentColor === this.accentColor) {
554
+ return;
555
+ }
556
+ this.accentColor = nextAccentColor;
557
+ this.applyAccentColor();
558
+ }
559
+ applyAccentColor() {
560
+ if (typeof document === 'undefined') {
561
+ return;
562
+ }
563
+ if (!this.accentColor) {
564
+ this.restoreAccentColor();
565
+ return;
566
+ }
567
+ const root = document.documentElement;
568
+ if (this.originalRootAccentColor === undefined) {
569
+ this.originalRootAccentColor =
570
+ root.style.getPropertyValue(EINBLICK_ACCENT_COLOR_VAR) || null;
571
+ }
572
+ root.style.setProperty(EINBLICK_ACCENT_COLOR_VAR, this.accentColor);
573
+ }
574
+ restoreAccentColor() {
575
+ if (typeof document === 'undefined' ||
576
+ this.originalRootAccentColor === undefined) {
577
+ return;
578
+ }
579
+ const root = document.documentElement;
580
+ if (this.originalRootAccentColor) {
581
+ root.style.setProperty(EINBLICK_ACCENT_COLOR_VAR, this.originalRootAccentColor);
582
+ }
583
+ else {
584
+ root.style.removeProperty(EINBLICK_ACCENT_COLOR_VAR);
585
+ }
586
+ this.originalRootAccentColor = undefined;
587
+ }
478
588
  start() {
479
589
  if (this.started ||
480
590
  typeof window === 'undefined' ||
@@ -482,6 +592,7 @@ class EinblickEditRuntime {
482
592
  return;
483
593
  }
484
594
  this.started = true;
595
+ this.applyAccentColor();
485
596
  injectRuntimeStyles();
486
597
  this.ensureHoverButton();
487
598
  if (this.chrome === 'badge') {
@@ -493,6 +604,7 @@ class EinblickEditRuntime {
493
604
  window.addEventListener('message', this.handleMessage);
494
605
  window.addEventListener('pointerdown', this.handleGlobalPointerDown, true);
495
606
  window.addEventListener('pointermove', this.handlePointerMove, true);
607
+ window.addEventListener('click', this.handleCollectionCreateButtonClick, true);
496
608
  window.addEventListener('scroll', this.handleViewportChange, true);
497
609
  window.addEventListener('resize', this.handleViewportChange);
498
610
  window.addEventListener('blur', this.handleWindowBlur);
@@ -508,12 +620,14 @@ class EinblickEditRuntime {
508
620
  this.closeInlineEditor();
509
621
  }
510
622
  this.updateBottomBarResources();
623
+ this.updateCollectionCreateButtons();
511
624
  });
512
625
  this.mutationObserver.observe(document.body, {
513
626
  childList: true,
514
627
  subtree: true,
515
628
  });
516
629
  this.reloadBridge();
630
+ this.updateCollectionCreateButtons();
517
631
  }
518
632
  destroy() {
519
633
  if (this.destroyed) {
@@ -524,6 +638,7 @@ class EinblickEditRuntime {
524
638
  window.removeEventListener('message', this.handleMessage);
525
639
  window.removeEventListener('pointerdown', this.handleGlobalPointerDown, true);
526
640
  window.removeEventListener('pointermove', this.handlePointerMove, true);
641
+ window.removeEventListener('click', this.handleCollectionCreateButtonClick, true);
527
642
  window.removeEventListener('scroll', this.handleViewportChange, true);
528
643
  window.removeEventListener('resize', this.handleViewportChange);
529
644
  window.removeEventListener('blur', this.handleWindowBlur);
@@ -543,8 +658,6 @@ class EinblickEditRuntime {
543
658
  this.hoverButtonContainer = null;
544
659
  this.hoverButton?.remove();
545
660
  this.hoverButton = null;
546
- this.hoverCreateButton?.remove();
547
- this.hoverCreateButton = null;
548
661
  this.clearQuickFieldOverlays();
549
662
  this.brandBadge?.remove();
550
663
  this.brandBadge = null;
@@ -567,6 +680,7 @@ class EinblickEditRuntime {
567
680
  this.closeInlineEditor();
568
681
  this.removeNavigatorPanel(false);
569
682
  this.closeDrawer();
683
+ this.restoreAccentColor();
570
684
  if (this.pendingLogin) {
571
685
  if (this.pendingLogin.closePollId !== null) {
572
686
  window.clearInterval(this.pendingLogin.closePollId);
@@ -597,9 +711,6 @@ class EinblickEditRuntime {
597
711
  if (this.hoverButton) {
598
712
  this.updateHoverButtonLabel();
599
713
  }
600
- if (this.hoverCreateButton) {
601
- this.hoverCreateButton.textContent = this.messages.actions.addRecord;
602
- }
603
714
  if (this.brandBadge) {
604
715
  this.brandBadge.setAttribute('aria-label', this.messages.brand.quickActionsLabel);
605
716
  this.brandBadge.title = this.messages.brand.quickActionsLabel;
@@ -616,12 +727,19 @@ class EinblickEditRuntime {
616
727
  }
617
728
  this.updateBrandPopover();
618
729
  this.updateBottomBar();
730
+ this.updateCollectionCreateButtons();
619
731
  this.updateHoverChrome();
620
732
  }
621
733
  async login(options) {
622
734
  if (typeof window === 'undefined') {
623
735
  return false;
624
736
  }
737
+ if (!this.brokerId) {
738
+ const message = this.messages.errors.bridgeNotConnected;
739
+ this.callbacks.onError?.(message);
740
+ this.pingBridge();
741
+ return false;
742
+ }
625
743
  const returnTo = options?.returnTo ?? window.location.href;
626
744
  const loginNonce = createRandomId();
627
745
  const loginUrl = this.buildBridgeUrl({
@@ -743,22 +861,31 @@ class EinblickEditRuntime {
743
861
  !this.shouldShowEditButton(this.activeElement, binding)) {
744
862
  return;
745
863
  }
864
+ if (binding.fieldKey && isMultiMediaBinding(binding)) {
865
+ this.openDrawer(this.activeElement, binding);
866
+ return;
867
+ }
746
868
  if (binding.fieldKey && isMediaEditableBinding(binding)) {
747
869
  this.openMediaPicker(this.activeElement, binding, this.hoverButton);
748
870
  return;
749
871
  }
750
872
  this.openEditor(binding, this.activeElement);
751
873
  };
752
- handleCreateButtonClick = (event) => {
874
+ handleCollectionCreateButtonClick = (event) => {
875
+ const button = this.findCollectionCreateButton(event.target);
876
+ if (!button) {
877
+ return;
878
+ }
753
879
  stopEvent(event);
754
- if (!this.activeElement) {
880
+ const collection = this.findCollectionForCreateButton(button);
881
+ if (!collection) {
755
882
  return;
756
883
  }
757
- const binding = readBindingFromElement(this.activeElement);
758
- if (!binding || !this.shouldShowCreateButton(this.activeElement, binding)) {
884
+ const binding = readBindingFromElement(collection);
885
+ if (!binding || !this.shouldShowCreateButton(collection, binding)) {
759
886
  return;
760
887
  }
761
- this.openDrawer(this.activeElement, binding, 'create');
888
+ this.openDrawer(collection, binding, 'create');
762
889
  };
763
890
  handleGlobalPointerDown = (event) => {
764
891
  const target = event.target;
@@ -806,14 +933,24 @@ class EinblickEditRuntime {
806
933
  return;
807
934
  }
808
935
  if (data.source === EINBLICK_BRIDGE_SOURCE) {
936
+ if (event.source !== this.bridgeFrame?.contentWindow) {
937
+ return;
938
+ }
809
939
  this.handleBridgeMessage(data);
810
940
  return;
811
941
  }
812
942
  if (data.source === EINBLICK_AUTH_SOURCE) {
943
+ if (event.source !== this.pendingLogin?.popup) {
944
+ return;
945
+ }
813
946
  this.handleAuthMessage(data);
814
947
  return;
815
948
  }
816
949
  if (data.source === EINBLICK_EDITOR_SOURCE) {
950
+ if (event.source !== this.editorFrame?.contentWindow &&
951
+ event.source !== this.navigatorFrame?.contentWindow) {
952
+ return;
953
+ }
817
954
  this.handleEditorMessage(data);
818
955
  }
819
956
  };
@@ -823,7 +960,9 @@ class EinblickEditRuntime {
823
960
  }
824
961
  if (message.type === 'bridge-ready') {
825
962
  this.clearBridgePing();
963
+ this.brokerId = message.brokerId ?? this.brokerId;
826
964
  if (message.authenticated) {
965
+ this.setSession({ active: true, expiresAt: message.expiresAt });
827
966
  this.setState({
828
967
  enabled: true,
829
968
  active: true,
@@ -851,6 +990,26 @@ class EinblickEditRuntime {
851
990
  }
852
991
  return;
853
992
  }
993
+ if (message.type === 'broker-proof-result') {
994
+ const pending = this.pendingLogin;
995
+ if (!pending ||
996
+ message.requestId !== pending.nonce ||
997
+ message.brokerId !== this.brokerId) {
998
+ return;
999
+ }
1000
+ try {
1001
+ pending.popup?.postMessage({
1002
+ source: EINBLICK_AUTH_SOURCE,
1003
+ type: 'broker-proof-result',
1004
+ nonce: pending.nonce,
1005
+ requestId: message.requestId,
1006
+ brokerId: message.brokerId,
1007
+ ciphertext: message.ciphertext,
1008
+ }, this.appOrigin);
1009
+ }
1010
+ catch { }
1011
+ return;
1012
+ }
854
1013
  if (message.type === 'logout-result') {
855
1014
  const pending = this.pendingLogout;
856
1015
  if (!pending || pending.requestId !== message.requestId) {
@@ -898,28 +1057,43 @@ class EinblickEditRuntime {
898
1057
  if (!this.pendingLogin || message.nonce !== this.pendingLogin.nonce) {
899
1058
  return;
900
1059
  }
1060
+ if (message.type === 'broker-proof-request') {
1061
+ if (message.requestId !== this.pendingLogin.nonce ||
1062
+ message.brokerId !== this.brokerId ||
1063
+ !this.bridgeFrame?.contentWindow) {
1064
+ return;
1065
+ }
1066
+ try {
1067
+ this.bridgeFrame.contentWindow.postMessage({
1068
+ source: EINBLICK_PARENT_SOURCE,
1069
+ type: 'broker-proof-request',
1070
+ nonce: this.bridgeNonce,
1071
+ requestId: message.requestId,
1072
+ brokerId: message.brokerId,
1073
+ publicKey: message.publicKey,
1074
+ }, this.appOrigin);
1075
+ }
1076
+ catch { }
1077
+ return;
1078
+ }
901
1079
  if (this.pendingLogin.closePollId !== null) {
902
1080
  window.clearInterval(this.pendingLogin.closePollId);
903
1081
  }
904
1082
  const pending = this.pendingLogin;
905
1083
  this.pendingLogin = null;
906
1084
  if (message.ok) {
907
- if (!message.sessionToken) {
1085
+ if (!message.brokerId || message.brokerId !== this.brokerId) {
908
1086
  const errorMessage = this.messages.errors.signInDidNotReturnSession;
909
1087
  this.setState({ ...this.state, errorMessage, status: 'error' });
910
1088
  this.callbacks.onError?.(errorMessage);
911
1089
  pending.resolve(false);
912
1090
  return;
913
1091
  }
914
- this.setSession({
915
- sessionToken: message.sessionToken,
916
- expiresAt: message.expiresAt,
917
- });
918
1092
  try {
919
1093
  pending.popup?.close();
920
1094
  }
921
1095
  catch { }
922
- this.reloadBridge();
1096
+ this.pingBridge();
923
1097
  pending.resolve(true);
924
1098
  return;
925
1099
  }
@@ -1020,30 +1194,20 @@ class EinblickEditRuntime {
1020
1194
  }
1021
1195
  }
1022
1196
  setSession(session) {
1023
- const previousSessionToken = this.session?.sessionToken ?? null;
1197
+ const wasActive = this.session !== null;
1024
1198
  this.session = session && !isEditSessionExpired(session) ? session : null;
1025
- const nextSessionToken = this.session?.sessionToken ?? null;
1026
- if (previousSessionToken !== nextSessionToken) {
1199
+ if (wasActive !== (this.session !== null)) {
1027
1200
  this.removeNavigatorPanel(false);
1028
1201
  }
1029
1202
  this.callbacks.onSessionChange(this.session);
1030
1203
  }
1031
- getActiveSessionToken() {
1032
- if (!this.session) {
1033
- return null;
1034
- }
1035
- if (isEditSessionExpired(this.session)) {
1036
- this.setSession(null);
1037
- return null;
1038
- }
1039
- return this.session.sessionToken;
1040
- }
1041
1204
  setState(nextState) {
1042
1205
  this.state = nextState;
1043
1206
  this.callbacks.onStateChange(nextState);
1044
1207
  this.updateBrandBadge();
1045
1208
  this.updateBrandPopover();
1046
1209
  this.updateBottomBar();
1210
+ this.updateCollectionCreateButtons();
1047
1211
  if (!nextState.isAuthenticated) {
1048
1212
  this.setActiveElement(null);
1049
1213
  this.removeNavigatorPanel();
@@ -1058,26 +1222,18 @@ class EinblickEditRuntime {
1058
1222
  container.hidden = true;
1059
1223
  const editButton = document.createElement('button');
1060
1224
  editButton.type = 'button';
1061
- editButton.textContent = this.messages.actions.editElement;
1225
+ setOverlayButtonContent(editButton, this.messages.actions.editElement, {
1226
+ databaseIcon: true,
1227
+ });
1062
1228
  editButton.setAttribute('data-einblick-edit-button', 'true');
1063
1229
  editButton.addEventListener('pointerdown', (event) => {
1064
1230
  event.stopPropagation();
1065
1231
  });
1066
1232
  editButton.addEventListener('click', this.handleHoverButtonClick);
1067
- const createButton = document.createElement('button');
1068
- createButton.type = 'button';
1069
- createButton.textContent = this.messages.actions.addRecord;
1070
- createButton.hidden = true;
1071
- createButton.setAttribute('data-einblick-create-button', 'true');
1072
- createButton.addEventListener('pointerdown', (event) => {
1073
- event.stopPropagation();
1074
- });
1075
- createButton.addEventListener('click', this.handleCreateButtonClick);
1076
- container.append(editButton, createButton);
1233
+ container.append(editButton);
1077
1234
  document.body.append(container);
1078
1235
  this.hoverButtonContainer = container;
1079
1236
  this.hoverButton = editButton;
1080
- this.hoverCreateButton = createButton;
1081
1237
  }
1082
1238
  ensureBrandBadge() {
1083
1239
  if ((this.brandBadge && this.brandPopover) ||
@@ -1254,7 +1410,7 @@ class EinblickEditRuntime {
1254
1410
  border: 0;
1255
1411
  border-radius: 0.35rem;
1256
1412
  background: #ffffff;
1257
- color: #fa693e;
1413
+ color: ${EINBLICK_ACCENT_COLOR};
1258
1414
  padding: 0 0.7rem;
1259
1415
  font: 700 0.78rem/1 system-ui, sans-serif;
1260
1416
  letter-spacing: 0;
@@ -1294,7 +1450,6 @@ class EinblickEditRuntime {
1294
1450
  border-radius: 0.75rem;
1295
1451
  background: rgb(255, 255, 255);
1296
1452
  color: rgb(15, 23, 42);
1297
- box-shadow: 0 14px 32px rgba(15, 23, 42, 0.16);
1298
1453
  overflow: hidden;
1299
1454
  }
1300
1455
 
@@ -1634,10 +1789,15 @@ class EinblickEditRuntime {
1634
1789
  }
1635
1790
  }
1636
1791
  async logoutFromEinblick() {
1792
+ await this.endEditSession(true);
1793
+ }
1794
+ async deactivateEditSession() {
1795
+ await this.endEditSession(false);
1796
+ }
1797
+ async endEditSession(signOut) {
1637
1798
  if (typeof window === 'undefined') {
1638
1799
  return;
1639
1800
  }
1640
- const sessionToken = this.getActiveSessionToken();
1641
1801
  this.setSession(null);
1642
1802
  this.setState({
1643
1803
  enabled: true,
@@ -1647,13 +1807,13 @@ class EinblickEditRuntime {
1647
1807
  siteName: this.state.siteName,
1648
1808
  errorMessage: undefined,
1649
1809
  });
1650
- const loggedOutViaBridge = await this.requestBridgeLogout();
1810
+ const loggedOutViaBridge = await this.requestBridgeLogout(signOut);
1651
1811
  if (!loggedOutViaBridge) {
1652
- await this.logoutViaPopup(sessionToken);
1812
+ await this.logoutViaPopup(signOut);
1653
1813
  }
1654
1814
  this.reloadBridge();
1655
1815
  }
1656
- requestBridgeLogout() {
1816
+ requestBridgeLogout(signOut) {
1657
1817
  if (typeof window === 'undefined' || this.pendingLogout) {
1658
1818
  return Promise.resolve(false);
1659
1819
  }
@@ -1677,6 +1837,7 @@ class EinblickEditRuntime {
1677
1837
  type: 'logout',
1678
1838
  nonce: this.bridgeNonce,
1679
1839
  requestId,
1840
+ signOut,
1680
1841
  }, this.appOrigin);
1681
1842
  }
1682
1843
  catch {
@@ -1688,12 +1849,12 @@ class EinblickEditRuntime {
1688
1849
  }
1689
1850
  });
1690
1851
  }
1691
- async logoutViaPopup(sessionToken) {
1852
+ async logoutViaPopup(signOut) {
1692
1853
  const logoutUrl = this.buildBridgeUrl({
1693
1854
  mode: 'logout',
1694
1855
  nonce: createRandomId(),
1695
1856
  returnTo: window.location.href,
1696
- sessionToken,
1857
+ signOut,
1697
1858
  });
1698
1859
  let popup = null;
1699
1860
  try {
@@ -1736,9 +1897,6 @@ class EinblickEditRuntime {
1736
1897
  if (this.hoverButton) {
1737
1898
  this.hoverButton.hidden = true;
1738
1899
  }
1739
- if (this.hoverCreateButton) {
1740
- this.hoverCreateButton.hidden = true;
1741
- }
1742
1900
  return;
1743
1901
  }
1744
1902
  element.dataset.einblickHoverActive = 'true';
@@ -1774,6 +1932,10 @@ class EinblickEditRuntime {
1774
1932
  }
1775
1933
  this.updateHoverButtonLabel();
1776
1934
  this.updateHoverButtonVisibility(this.activeElement);
1935
+ if (this.hoverButtonContainer?.hidden) {
1936
+ this.updateQuickFieldOverlayPositions();
1937
+ return;
1938
+ }
1777
1939
  this.positionHoverControls(rect);
1778
1940
  this.updateQuickFieldOverlayPositions();
1779
1941
  }
@@ -1822,10 +1984,9 @@ class EinblickEditRuntime {
1822
1984
  const button = document.createElement('button');
1823
1985
  button.type = 'button';
1824
1986
  button.setAttribute('data-einblick-field-button', isMediaBinding ? 'media' : 'text');
1825
- button.textContent =
1826
- isMediaBinding
1827
- ? getMediaActionLabel(binding, this.messages)
1828
- : this.messages.actions.editText;
1987
+ setOverlayButtonContent(button, isMediaBinding
1988
+ ? getMediaActionLabel(binding, this.messages)
1989
+ : this.messages.actions.editText);
1829
1990
  button.addEventListener('pointerdown', (event) => {
1830
1991
  event.stopPropagation();
1831
1992
  });
@@ -1867,8 +2028,37 @@ class EinblickEditRuntime {
1867
2028
  const height = overlay.button.offsetHeight || 26;
1868
2029
  const scrollX = window.scrollX || document.documentElement.scrollLeft;
1869
2030
  const scrollY = window.scrollY || document.documentElement.scrollTop;
1870
- const left = clamp(rect.right + scrollX - width, scrollX, Math.max(scrollX, scrollX + window.innerWidth - width));
1871
- const top = clamp(rect.top + scrollY, scrollY, Math.max(scrollY, scrollY + window.innerHeight - height));
2031
+ let left = clamp(rect.right + scrollX - width, scrollX, Math.max(scrollX, scrollX + window.innerWidth - width));
2032
+ let top = clamp(rect.top + scrollY, scrollY, Math.max(scrollY, scrollY + window.innerHeight - height));
2033
+ const hoverControlsRect = this.hoverButtonContainer && !this.hoverButtonContainer.hidden
2034
+ ? this.hoverButtonContainer.getBoundingClientRect()
2035
+ : null;
2036
+ if (hoverControlsRect) {
2037
+ const candidateRect = {
2038
+ left: left - scrollX,
2039
+ top: top - scrollY,
2040
+ right: left - scrollX + width,
2041
+ bottom: top - scrollY + height,
2042
+ };
2043
+ if (rectsOverlap(candidateRect, hoverControlsRect, 6)) {
2044
+ const minTop = scrollY;
2045
+ const maxTop = Math.max(scrollY, scrollY + window.innerHeight - height);
2046
+ const belowTop = hoverControlsRect.bottom + scrollY + 6;
2047
+ const aboveTop = hoverControlsRect.top + scrollY - height - 6;
2048
+ if (belowTop <= maxTop) {
2049
+ top = clamp(belowTop, minTop, maxTop);
2050
+ }
2051
+ else if (aboveTop >= minTop) {
2052
+ top = clamp(aboveTop, minTop, maxTop);
2053
+ }
2054
+ else {
2055
+ const maxLeft = Math.max(scrollX, scrollX + window.innerWidth - width);
2056
+ const leftOfControls = hoverControlsRect.left + scrollX - width - 6;
2057
+ const rightOfControls = hoverControlsRect.right + scrollX + 6;
2058
+ left = clamp(leftOfControls >= scrollX ? leftOfControls : rightOfControls, scrollX, maxLeft);
2059
+ }
2060
+ }
2061
+ }
1872
2062
  overlay.button.style.left = `${Math.round(left)}px`;
1873
2063
  overlay.button.style.top = `${Math.round(top)}px`;
1874
2064
  }
@@ -1879,14 +2069,19 @@ class EinblickEditRuntime {
1879
2069
  }
1880
2070
  const binding = readBindingFromElement(this.activeElement);
1881
2071
  if (!binding?.fieldKey) {
1882
- this.hoverButton.textContent = this.messages.actions.editElement;
2072
+ setOverlayButtonContent(this.hoverButton, this.messages.actions.editElement, { databaseIcon: true });
1883
2073
  return;
1884
2074
  }
1885
- this.hoverButton.textContent = isMediaEditableBinding(binding)
2075
+ const opensDrawer = isMultiMediaBinding(binding) ||
2076
+ (!isMediaEditableBinding(binding) && !shouldUseInlineEditor(binding));
2077
+ const label = isMediaEditableBinding(binding)
1886
2078
  ? getMediaActionLabel(binding, this.messages)
1887
2079
  : shouldUseInlineEditor(binding)
1888
2080
  ? this.messages.actions.editText
1889
- : this.messages.actions.edit;
2081
+ : this.messages.actions.editElement;
2082
+ setOverlayButtonContent(this.hoverButton, label, {
2083
+ databaseIcon: opensDrawer,
2084
+ });
1890
2085
  }
1891
2086
  reloadBridge() {
1892
2087
  this.clearBridgePing();
@@ -1907,23 +2102,22 @@ class EinblickEditRuntime {
1907
2102
  frame.src = this.buildBridgeUrl({ mode: 'bridge', nonce: this.bridgeNonce });
1908
2103
  this.bridgeFrame = frame;
1909
2104
  document.body.append(frame);
1910
- const sendPing = () => {
1911
- if (!this.bridgeFrame?.contentWindow) {
1912
- return;
1913
- }
1914
- try {
1915
- this.bridgeFrame.contentWindow.postMessage({
1916
- source: EINBLICK_PARENT_SOURCE,
1917
- type: 'bridge-ping',
1918
- nonce: this.bridgeNonce,
1919
- }, this.appOrigin);
1920
- }
1921
- catch { }
1922
- };
1923
2105
  frame.addEventListener('load', () => {
1924
- sendPing();
2106
+ this.pingBridge();
1925
2107
  });
1926
- this.bridgePingInterval = window.setInterval(sendPing, 1500);
2108
+ this.bridgePingInterval = window.setInterval(() => this.pingBridge(), 1500);
2109
+ }
2110
+ pingBridge() {
2111
+ if (!this.bridgeFrame?.contentWindow)
2112
+ return;
2113
+ try {
2114
+ this.bridgeFrame.contentWindow.postMessage({
2115
+ source: EINBLICK_PARENT_SOURCE,
2116
+ type: 'bridge-ping',
2117
+ nonce: this.bridgeNonce,
2118
+ }, this.appOrigin);
2119
+ }
2120
+ catch { }
1927
2121
  }
1928
2122
  clearBridgePing() {
1929
2123
  if (this.bridgePingInterval !== null) {
@@ -1936,28 +2130,33 @@ class EinblickEditRuntime {
1936
2130
  url.searchParams.set('siteKey', this.siteKey);
1937
2131
  url.searchParams.set('origin', window.location.origin);
1938
2132
  url.searchParams.set('nonce', args.nonce);
2133
+ url.searchParams.set('protocol', '2');
2134
+ url.searchParams.set('persist', this.persist ? '1' : '0');
1939
2135
  if (args.mode !== 'bridge') {
1940
2136
  url.searchParams.set('mode', args.mode);
1941
2137
  }
1942
- const sessionToken = args.sessionToken ??
1943
- (args.mode === 'bridge' ? this.getActiveSessionToken() : null);
1944
- if (sessionToken) {
1945
- url.searchParams.set('sessionToken', sessionToken);
2138
+ const brokerId = args.brokerId ?? (args.mode === 'login' ? this.brokerId : null);
2139
+ if (brokerId) {
2140
+ url.searchParams.set('brokerId', brokerId);
1946
2141
  }
1947
2142
  if (args.returnTo) {
1948
2143
  url.searchParams.set('returnTo', args.returnTo);
1949
2144
  }
2145
+ if (args.signOut === false) {
2146
+ url.searchParams.set('signOut', '0');
2147
+ }
1950
2148
  return url.toString();
1951
2149
  }
1952
2150
  buildEditorUrl(binding, nonce, intent = 'edit') {
1953
- const sessionToken = this.getActiveSessionToken();
1954
- if (!sessionToken) {
2151
+ if (!this.session || isEditSessionExpired(this.session)) {
1955
2152
  throw new Error(this.messages.errors.editSessionExpired);
1956
2153
  }
1957
2154
  const url = new URL('/sdk/editor', this.appOrigin);
1958
2155
  url.searchParams.set('origin', window.location.origin);
1959
2156
  url.searchParams.set('nonce', nonce);
1960
- url.searchParams.set('sessionToken', sessionToken);
2157
+ url.searchParams.set('siteKey', this.siteKey);
2158
+ url.searchParams.set('protocol', '2');
2159
+ url.searchParams.set('persist', this.persist ? '1' : '0');
1961
2160
  url.searchParams.set('returnTo', window.location.href);
1962
2161
  url.searchParams.set('sourceType', binding.sourceType ?? 'cms');
1963
2162
  url.searchParams.set('resourceSlug', binding.resourceSlug);
@@ -1976,25 +2175,27 @@ class EinblickEditRuntime {
1976
2175
  return url.toString();
1977
2176
  }
1978
2177
  buildNavigatorUrl(nonce) {
1979
- const sessionToken = this.getActiveSessionToken();
1980
- if (!sessionToken) {
2178
+ if (!this.session || isEditSessionExpired(this.session)) {
1981
2179
  throw new Error(this.messages.errors.editSessionExpired);
1982
2180
  }
1983
2181
  const url = new URL('/sdk/navigator', this.appOrigin);
1984
2182
  url.searchParams.set('origin', window.location.origin);
1985
2183
  url.searchParams.set('nonce', nonce);
1986
- url.searchParams.set('sessionToken', sessionToken);
2184
+ url.searchParams.set('siteKey', this.siteKey);
2185
+ url.searchParams.set('protocol', '2');
2186
+ url.searchParams.set('persist', this.persist ? '1' : '0');
1987
2187
  return url.toString();
1988
2188
  }
1989
2189
  buildCollectionEditorUrl(args) {
1990
- const sessionToken = this.getActiveSessionToken();
1991
- if (!sessionToken) {
2190
+ if (!this.session || isEditSessionExpired(this.session)) {
1992
2191
  throw new Error(this.messages.errors.editSessionExpired);
1993
2192
  }
1994
2193
  const url = new URL('/sdk/editor', this.appOrigin);
1995
2194
  url.searchParams.set('origin', window.location.origin);
1996
2195
  url.searchParams.set('nonce', args.nonce);
1997
- url.searchParams.set('sessionToken', sessionToken);
2196
+ url.searchParams.set('siteKey', this.siteKey);
2197
+ url.searchParams.set('protocol', '2');
2198
+ url.searchParams.set('persist', this.persist ? '1' : '0');
1998
2199
  url.searchParams.set('returnTo', window.location.href);
1999
2200
  url.searchParams.set('sourceType', args.sourceType ?? 'cms');
2000
2201
  url.searchParams.set('collectionId', args.collectionId);
@@ -2008,16 +2209,51 @@ class EinblickEditRuntime {
2008
2209
  return url.toString();
2009
2210
  }
2010
2211
  updateHoverButtonVisibility(element) {
2011
- if (!this.hoverButton || !this.hoverCreateButton) {
2212
+ if (!this.hoverButton) {
2012
2213
  return;
2013
2214
  }
2014
2215
  const binding = element ? readBindingFromElement(element) : null;
2015
- const canCreate = this.shouldShowCreateButton(element, binding);
2016
2216
  const canEdit = this.shouldShowEditButton(element, binding);
2017
2217
  this.hoverButton.hidden = !canEdit;
2018
- this.hoverCreateButton.hidden = !canCreate;
2019
2218
  if (this.hoverButtonContainer) {
2020
- this.hoverButtonContainer.hidden = !canEdit && !canCreate;
2219
+ this.hoverButtonContainer.hidden = !canEdit;
2220
+ }
2221
+ }
2222
+ findCollectionCreateButton(target) {
2223
+ const element = target instanceof Element
2224
+ ? target
2225
+ : target instanceof Node
2226
+ ? target.parentElement
2227
+ : null;
2228
+ if (!element) {
2229
+ return null;
2230
+ }
2231
+ return element.closest('[data-einblick-collection-create-button="true"]');
2232
+ }
2233
+ findCollectionForCreateButton(button) {
2234
+ return button.closest('[data-einblick-editable="true"][data-einblick-kind="collection"]');
2235
+ }
2236
+ updateCollectionCreateButtons() {
2237
+ if (typeof document === 'undefined') {
2238
+ return;
2239
+ }
2240
+ const buttons = Array.from(document.querySelectorAll('[data-einblick-collection-create-button="true"]'));
2241
+ for (const button of buttons) {
2242
+ const label = button.dataset.einblickCollectionCreateLabel?.trim() ||
2243
+ this.messages.actions.addRecord;
2244
+ setOverlayButtonContent(button, label, { databaseIcon: true });
2245
+ button.type = 'button';
2246
+ const collection = this.findCollectionForCreateButton(button);
2247
+ const binding = collection ? readBindingFromElement(collection) : null;
2248
+ const isVisible = !!collection &&
2249
+ !!binding &&
2250
+ this.shouldShowCreateButton(collection, binding);
2251
+ button.hidden = !isVisible;
2252
+ button.disabled = !isVisible;
2253
+ const wrapper = button.closest('[data-einblick-collection-create-wrapper="true"]');
2254
+ if (wrapper) {
2255
+ wrapper.hidden = !isVisible;
2256
+ }
2021
2257
  }
2022
2258
  }
2023
2259
  shouldShowEditButton(element, binding) {
@@ -2033,7 +2269,10 @@ class EinblickEditRuntime {
2033
2269
  if (binding.fieldKey) {
2034
2270
  return false;
2035
2271
  }
2036
- return element.dataset.einblickKind === 'collection';
2272
+ if (element.dataset.einblickKind !== 'collection') {
2273
+ return false;
2274
+ }
2275
+ return true;
2037
2276
  }
2038
2277
  findEditableTarget(target) {
2039
2278
  if (!(target instanceof Element)) {
@@ -2102,7 +2341,8 @@ class EinblickEditRuntime {
2102
2341
  !!this.brandBadge?.contains(target) ||
2103
2342
  !!this.brandPopover?.contains(target) ||
2104
2343
  !!this.bottomBarHost?.contains(target) ||
2105
- !!this.bottomBarMoreMenu?.contains(target));
2344
+ !!this.bottomBarMoreMenu?.contains(target) ||
2345
+ !!this.findCollectionCreateButton(target));
2106
2346
  }
2107
2347
  findElementForBinding(binding) {
2108
2348
  const selector = [
@@ -2354,18 +2594,21 @@ class EinblickEditRuntime {
2354
2594
  this.callbacks.onError?.(this.messages.errors.fieldBindingRequired);
2355
2595
  return;
2356
2596
  }
2597
+ if (isMultiMediaBinding(binding)) {
2598
+ this.openDrawer(target, binding);
2599
+ return;
2600
+ }
2357
2601
  const input = document.createElement('input');
2358
2602
  input.type = 'file';
2359
2603
  input.accept = getMediaAcceptValue(binding);
2360
2604
  input.style.position = 'fixed';
2361
2605
  input.style.left = '-9999px';
2362
2606
  input.style.top = '0';
2363
- const previousText = button?.textContent ?? '';
2607
+ const buttonLabel = getMediaActionLabel(binding, this.messages);
2364
2608
  const resetButton = () => {
2365
2609
  if (button) {
2366
2610
  button.disabled = false;
2367
- button.textContent =
2368
- previousText || getMediaActionLabel(binding, this.messages);
2611
+ setOverlayButtonContent(button, buttonLabel);
2369
2612
  }
2370
2613
  };
2371
2614
  input.addEventListener('change', () => {
@@ -2376,7 +2619,7 @@ class EinblickEditRuntime {
2376
2619
  }
2377
2620
  if (button) {
2378
2621
  button.disabled = true;
2379
- button.textContent = this.messages.actions.uploading;
2622
+ setOverlayButtonContent(button, this.messages.actions.uploading);
2380
2623
  }
2381
2624
  void (async () => {
2382
2625
  try {
@@ -2537,6 +2780,7 @@ class EinblickEditRuntime {
2537
2780
  });
2538
2781
  document.body.append(backdrop);
2539
2782
  this.editorBackdrop = backdrop;
2783
+ this.editorFrame = iframe;
2540
2784
  }
2541
2785
  openCollectionDrawer(args) {
2542
2786
  if (!this.state.isAuthenticated) {
@@ -2593,6 +2837,7 @@ class EinblickEditRuntime {
2593
2837
  });
2594
2838
  document.body.append(backdrop);
2595
2839
  this.editorBackdrop = backdrop;
2840
+ this.editorFrame = iframe;
2596
2841
  }
2597
2842
  isBottomBarMenuOpen() {
2598
2843
  return !!this.bottomBarMoreMenu && !this.bottomBarMoreMenu.hidden;
@@ -2608,6 +2853,7 @@ class EinblickEditRuntime {
2608
2853
  closeDrawer() {
2609
2854
  this.editorBackdrop?.remove();
2610
2855
  this.editorBackdrop = null;
2856
+ this.editorFrame = null;
2611
2857
  this.editorNonce = null;
2612
2858
  this.editorTarget = null;
2613
2859
  this.editorBinding = null;
@@ -2639,8 +2885,11 @@ export function createEinblickEditRuntime(options) {
2639
2885
  start: () => runtime.start(),
2640
2886
  destroy: () => runtime.destroy(),
2641
2887
  login: (loginOptions) => runtime.login(loginOptions),
2888
+ deactivate: () => runtime.deactivateEditSession(),
2889
+ logout: () => runtime.logoutFromEinblick(),
2642
2890
  openEditor: (binding, element) => runtime.openEditor(binding, element),
2643
2891
  setLocale: (locale) => runtime.setLocale(locale),
2892
+ setAccentColor: (accentColor) => runtime.setAccentColor(accentColor),
2644
2893
  };
2645
2894
  }
2646
2895
  //# sourceMappingURL=runtime.js.map