@ohhwells/bridge 0.1.124 → 0.1.126
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/dist/index.cjs +163 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +162 -46
- package/dist/index.js.map +1 -1
- package/dist/pages.cjs +141 -0
- package/dist/pages.cjs.map +1 -0
- package/dist/pages.d.cts +45 -0
- package/dist/pages.d.ts +45 -0
- package/dist/pages.js +107 -0
- package/dist/pages.js.map +1 -0
- package/dist/styles.css +3 -0
- package/package.json +6 -1
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,7 @@ __export(index_exports, {
|
|
|
46
46
|
DropdownMenuItem: () => DropdownMenuItem,
|
|
47
47
|
DropdownMenuSeparator: () => DropdownMenuSeparator,
|
|
48
48
|
DropdownMenuTrigger: () => DropdownMenuTrigger,
|
|
49
|
+
EmptySection: () => EmptySection,
|
|
49
50
|
ItemActionToolbar: () => ItemActionToolbar,
|
|
50
51
|
ItemInteractionLayer: () => ItemInteractionLayer,
|
|
51
52
|
LinkEditorPanel: () => LinkEditorPanel,
|
|
@@ -295,10 +296,14 @@ function reapRemovedAiSections(state, store, removedIds, excludeIds = /* @__PURE
|
|
|
295
296
|
var BRAND_NAME_KEY = "__ohw_brand_name";
|
|
296
297
|
var BRAND_TITLE_KEY = "__ohw_site_title";
|
|
297
298
|
var BRAND_FAVICON_LETTER_KEY = "__ohw_favicon_letter";
|
|
299
|
+
var SITE_DESCRIPTION_KEY = "__ohw_site_description";
|
|
300
|
+
var SITE_OG_IMAGE_KEY = "__ohw_og_image";
|
|
298
301
|
var BRAND_CHROME_KEYS = /* @__PURE__ */ new Set([
|
|
299
302
|
BRAND_NAME_KEY,
|
|
300
303
|
BRAND_TITLE_KEY,
|
|
301
|
-
BRAND_FAVICON_LETTER_KEY
|
|
304
|
+
BRAND_FAVICON_LETTER_KEY,
|
|
305
|
+
SITE_DESCRIPTION_KEY,
|
|
306
|
+
SITE_OG_IMAGE_KEY
|
|
302
307
|
]);
|
|
303
308
|
function upsertMeta(selector, attr, token, value) {
|
|
304
309
|
let el = document.head.querySelector(selector);
|
|
@@ -309,6 +314,9 @@ function upsertMeta(selector, attr, token, value) {
|
|
|
309
314
|
}
|
|
310
315
|
if (el.getAttribute("content") !== value) el.setAttribute("content", value);
|
|
311
316
|
}
|
|
317
|
+
function removeMeta(selector) {
|
|
318
|
+
document.head.querySelector(selector)?.remove();
|
|
319
|
+
}
|
|
312
320
|
function escapeXml(value) {
|
|
313
321
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
314
322
|
}
|
|
@@ -324,6 +332,22 @@ function applyLetterFavicon(letter) {
|
|
|
324
332
|
link.type = "image/svg+xml";
|
|
325
333
|
if (link.href !== href) link.href = href;
|
|
326
334
|
}
|
|
335
|
+
function applyDescription(description) {
|
|
336
|
+
if (!description) return;
|
|
337
|
+
upsertMeta('meta[name="description"]', "name", "description", description);
|
|
338
|
+
upsertMeta('meta[property="og:description"]', "property", "og:description", description);
|
|
339
|
+
upsertMeta('meta[name="twitter:description"]', "name", "twitter:description", description);
|
|
340
|
+
}
|
|
341
|
+
function applyOgImage(url) {
|
|
342
|
+
if (url) {
|
|
343
|
+
upsertMeta('meta[property="og:image"]', "property", "og:image", url);
|
|
344
|
+
upsertMeta('meta[name="twitter:image"]', "name", "twitter:image", url);
|
|
345
|
+
upsertMeta('meta[name="twitter:card"]', "name", "twitter:card", "summary_large_image");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
removeMeta('meta[property="og:image"]');
|
|
349
|
+
removeMeta('meta[name="twitter:image"]');
|
|
350
|
+
}
|
|
327
351
|
function applyBrandChrome(content) {
|
|
328
352
|
const name = content[BRAND_NAME_KEY];
|
|
329
353
|
if (typeof name === "string" && name.length > 0) {
|
|
@@ -342,6 +366,13 @@ function applyBrandChrome(content) {
|
|
|
342
366
|
if (typeof letter === "string" && letter.length > 0) {
|
|
343
367
|
applyLetterFavicon(letter);
|
|
344
368
|
}
|
|
369
|
+
const description = content[SITE_DESCRIPTION_KEY];
|
|
370
|
+
if (typeof description === "string" && description.length > 0) {
|
|
371
|
+
applyDescription(description);
|
|
372
|
+
}
|
|
373
|
+
if (Object.prototype.hasOwnProperty.call(content, SITE_OG_IMAGE_KEY)) {
|
|
374
|
+
applyOgImage(String(content[SITE_OG_IMAGE_KEY] ?? "").trim());
|
|
375
|
+
}
|
|
345
376
|
}
|
|
346
377
|
|
|
347
378
|
// src/lib/brand-kit.ts
|
|
@@ -19618,21 +19649,10 @@ function parseSchedulingInsertAfter(insertAfter) {
|
|
|
19618
19649
|
insertBefore: insertAfter.slice(idx + INSERT_BEFORE_MARKER.length)
|
|
19619
19650
|
};
|
|
19620
19651
|
}
|
|
19621
|
-
function
|
|
19622
|
-
|
|
19623
|
-
const
|
|
19624
|
-
|
|
19625
|
-
return { effectiveInsertAfter, insertBefore };
|
|
19626
|
-
}
|
|
19627
|
-
function getSchedulingMountPoint(insertAfter) {
|
|
19628
|
-
const { anchor } = parseSchedulingInsertAfter(insertAfter);
|
|
19629
|
-
let anchorEl = document.querySelector(`[data-ohw-section="${anchor}"]`);
|
|
19630
|
-
if (!anchorEl && anchor === "scheduling") {
|
|
19631
|
-
const widgets = Array.from(document.querySelectorAll('[data-ohw-section^="scheduling-"]'));
|
|
19632
|
-
anchorEl = widgets.at(-1) ?? null;
|
|
19633
|
-
}
|
|
19634
|
-
if (!anchorEl) return null;
|
|
19635
|
-
return anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
19652
|
+
function resolveEntryAnchor(entry) {
|
|
19653
|
+
if (entry.anchorId !== void 0) return { anchorId: entry.anchorId, beforeId: entry.beforeId ?? null };
|
|
19654
|
+
const parsed = parseSchedulingInsertAfter(entry.insertAfter);
|
|
19655
|
+
return { anchorId: parsed.anchor, beforeId: parsed.insertBefore };
|
|
19636
19656
|
}
|
|
19637
19657
|
function schedulingMountDepth(insertAfter) {
|
|
19638
19658
|
if (!insertAfter.includes(INSERT_BEFORE_MARKER)) return 0;
|
|
@@ -19649,8 +19669,7 @@ function getPageSchedulingEntries(raw) {
|
|
|
19649
19669
|
}
|
|
19650
19670
|
}
|
|
19651
19671
|
function isSchedulingWidgetMissing(entry) {
|
|
19652
|
-
|
|
19653
|
-
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(effectiveInsertAfter)}"]`);
|
|
19672
|
+
return !document.querySelector(`[data-ohw-section="${schedulingSectionId(entry.insertAfter)}"]`);
|
|
19654
19673
|
}
|
|
19655
19674
|
function hasMissingSchedulingWidgets(entries) {
|
|
19656
19675
|
return entries.some(isSchedulingWidgetMissing);
|
|
@@ -19688,17 +19707,18 @@ function initSectionsFromContent(content, removeExisting = false, currentPath =
|
|
|
19688
19707
|
} catch {
|
|
19689
19708
|
}
|
|
19690
19709
|
}
|
|
19691
|
-
function mountSchedulingWidget(
|
|
19692
|
-
const
|
|
19693
|
-
const sectionId = schedulingSectionId(
|
|
19710
|
+
function mountSchedulingWidget(anchorId, notifyOnConnect = false, scheduleId, beforeId, existingWidgetId) {
|
|
19711
|
+
const widgetId = existingWidgetId ?? (beforeId ? `${anchorId}${INSERT_BEFORE_MARKER}${beforeId}` : anchorId);
|
|
19712
|
+
const sectionId = schedulingSectionId(widgetId);
|
|
19694
19713
|
if (document.querySelector(`[data-ohw-section="${sectionId}"]`)) return false;
|
|
19695
|
-
const
|
|
19696
|
-
if (!
|
|
19714
|
+
const anchorEl = document.querySelector(`[data-ohw-section="${anchorId}"]`);
|
|
19715
|
+
if (!anchorEl) return false;
|
|
19716
|
+
const mountPoint = anchorEl.closest('[data-ohw-section-container="scheduling"]') ?? anchorEl;
|
|
19697
19717
|
const container = document.createElement("div");
|
|
19698
19718
|
container.dataset.ohwSectionContainer = "scheduling";
|
|
19699
19719
|
container.dataset.ohwInstance = sectionId;
|
|
19700
|
-
if (
|
|
19701
|
-
const beforeAnchor = document.querySelector(`[data-ohw-section="${
|
|
19720
|
+
if (beforeId) {
|
|
19721
|
+
const beforeAnchor = document.querySelector(`[data-ohw-section="${beforeId}"]`);
|
|
19702
19722
|
const beforePoint = beforeAnchor?.closest('[data-ohw-section-container="scheduling"]') ?? beforeAnchor;
|
|
19703
19723
|
if (!beforePoint) return false;
|
|
19704
19724
|
beforePoint.insertAdjacentElement("beforebegin", container);
|
|
@@ -19709,20 +19729,26 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
19709
19729
|
}
|
|
19710
19730
|
tail.insertAdjacentElement("afterend", container);
|
|
19711
19731
|
}
|
|
19712
|
-
|
|
19713
|
-
|
|
19714
|
-
|
|
19715
|
-
|
|
19716
|
-
|
|
19717
|
-
|
|
19718
|
-
|
|
19719
|
-
|
|
19720
|
-
|
|
19721
|
-
|
|
19722
|
-
|
|
19723
|
-
|
|
19724
|
-
|
|
19725
|
-
|
|
19732
|
+
try {
|
|
19733
|
+
const root = (0, import_client2.createRoot)(container);
|
|
19734
|
+
schedulingRoots.set(container, root);
|
|
19735
|
+
(0, import_react_dom4.flushSync)(() => {
|
|
19736
|
+
root.render(
|
|
19737
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
19738
|
+
SchedulingWidget,
|
|
19739
|
+
{
|
|
19740
|
+
notifyOnConnect,
|
|
19741
|
+
initialScheduleId: scheduleId,
|
|
19742
|
+
insertAfter: widgetId
|
|
19743
|
+
}
|
|
19744
|
+
)
|
|
19745
|
+
);
|
|
19746
|
+
});
|
|
19747
|
+
} catch (err) {
|
|
19748
|
+
console.error("[ow:scheduling] render threw", err);
|
|
19749
|
+
container.remove();
|
|
19750
|
+
return false;
|
|
19751
|
+
}
|
|
19726
19752
|
const tracker = getSectionsTracker();
|
|
19727
19753
|
let sections = [];
|
|
19728
19754
|
try {
|
|
@@ -19730,10 +19756,12 @@ function mountSchedulingWidget(insertAfter, notifyOnConnect = false, scheduleId,
|
|
|
19730
19756
|
} catch {
|
|
19731
19757
|
}
|
|
19732
19758
|
const inEditor = typeof window !== "undefined" && window.self !== window.top;
|
|
19733
|
-
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter ===
|
|
19759
|
+
if (inEditor && !sections.find((s) => s.type === "scheduling" && s.insertAfter === widgetId)) {
|
|
19734
19760
|
sections.push({
|
|
19735
19761
|
type: "scheduling",
|
|
19736
|
-
insertAfter:
|
|
19762
|
+
insertAfter: widgetId,
|
|
19763
|
+
anchorId,
|
|
19764
|
+
beforeId: beforeId ?? null,
|
|
19737
19765
|
pagePath: window.location.pathname,
|
|
19738
19766
|
...scheduleId ? { scheduleId } : {}
|
|
19739
19767
|
});
|
|
@@ -19747,7 +19775,8 @@ function mountSchedulingEntries(entries, notifyOnConnect = false) {
|
|
|
19747
19775
|
for (let i = pending.length - 1; i >= 0; i--) {
|
|
19748
19776
|
const entry = pending[i];
|
|
19749
19777
|
const shouldNotify = typeof notifyOnConnect === "function" ? notifyOnConnect(entry) : notifyOnConnect;
|
|
19750
|
-
|
|
19778
|
+
const { anchorId, beforeId } = resolveEntryAnchor(entry);
|
|
19779
|
+
if (mountSchedulingWidget(anchorId, shouldNotify, entry.scheduleId ?? null, beforeId, entry.insertAfter)) {
|
|
19751
19780
|
pending.splice(i, 1);
|
|
19752
19781
|
}
|
|
19753
19782
|
}
|
|
@@ -20810,6 +20839,7 @@ function StateToggle({
|
|
|
20810
20839
|
);
|
|
20811
20840
|
}
|
|
20812
20841
|
var contentCache = /* @__PURE__ */ new Map();
|
|
20842
|
+
var fetchedContentPaths = /* @__PURE__ */ new Set();
|
|
20813
20843
|
var brandingCache = /* @__PURE__ */ new Map();
|
|
20814
20844
|
var OHW_LOADER_STYLE = {
|
|
20815
20845
|
position: "fixed",
|
|
@@ -21016,6 +21046,7 @@ function OhhwellsBridge() {
|
|
|
21016
21046
|
if (!subdomainFromQuery || typeof window === "undefined") return;
|
|
21017
21047
|
const url = new URL(window.location.href);
|
|
21018
21048
|
if (!url.searchParams.has("subdomain")) return;
|
|
21049
|
+
if (!resolveSubdomain(null)) return;
|
|
21019
21050
|
url.searchParams.delete("subdomain");
|
|
21020
21051
|
window.history.replaceState(null, "", url.pathname + url.search + url.hash);
|
|
21021
21052
|
}, [subdomainFromQuery]);
|
|
@@ -23062,7 +23093,9 @@ function OhhwellsBridge() {
|
|
|
23062
23093
|
let cancelled = false;
|
|
23063
23094
|
setFetchState("loading");
|
|
23064
23095
|
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
23065
|
-
|
|
23096
|
+
const initialPath = pathname;
|
|
23097
|
+
fetchedContentPaths.add(`${subdomain}::${initialPath}`);
|
|
23098
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(initialPath)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
23066
23099
|
if (cancelled) return;
|
|
23067
23100
|
const content = data?.content ?? {};
|
|
23068
23101
|
const branding = Boolean(data?.showBranding);
|
|
@@ -23259,6 +23292,17 @@ function OhhwellsBridge() {
|
|
|
23259
23292
|
debounceTimer = setTimeout(applyFromCache, 150);
|
|
23260
23293
|
};
|
|
23261
23294
|
applyFromCache();
|
|
23295
|
+
const pathCacheKey = `${subdomain}::${pathname}`;
|
|
23296
|
+
if (!fetchedContentPaths.has(pathCacheKey)) {
|
|
23297
|
+
fetchedContentPaths.add(pathCacheKey);
|
|
23298
|
+
const apiUrl = process.env.NEXT_PUBLIC_FLOWOPS_API_URL ?? "https://flowops-backend-staging-2yfdh7pwpq-as.a.run.app";
|
|
23299
|
+
fetch(`${apiUrl}/api/public/sites/${subdomain}/content?path=${encodeURIComponent(pathname)}`, { cache: "no-store" }).then((r2) => r2.ok ? r2.json() : null).then((data) => {
|
|
23300
|
+
if (!data?.content) return;
|
|
23301
|
+
contentCache.set(subdomain, data.content);
|
|
23302
|
+
applyFromCache();
|
|
23303
|
+
}).catch(() => {
|
|
23304
|
+
});
|
|
23305
|
+
}
|
|
23262
23306
|
observer = new MutationObserver(scheduleApply);
|
|
23263
23307
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
23264
23308
|
return () => {
|
|
@@ -26123,6 +26167,21 @@ function OhhwellsBridge() {
|
|
|
26123
26167
|
type: "text",
|
|
26124
26168
|
text: editContentRef.current[CUSTOM_CODE_AFTER_HEAD_KEY] ?? ""
|
|
26125
26169
|
});
|
|
26170
|
+
nodes.push({
|
|
26171
|
+
key: BRAND_TITLE_KEY,
|
|
26172
|
+
type: "text",
|
|
26173
|
+
text: editContentRef.current[BRAND_TITLE_KEY] ?? ""
|
|
26174
|
+
});
|
|
26175
|
+
nodes.push({
|
|
26176
|
+
key: SITE_DESCRIPTION_KEY,
|
|
26177
|
+
type: "text",
|
|
26178
|
+
text: editContentRef.current[SITE_DESCRIPTION_KEY] ?? ""
|
|
26179
|
+
});
|
|
26180
|
+
nodes.push({
|
|
26181
|
+
key: SITE_OG_IMAGE_KEY,
|
|
26182
|
+
type: "text",
|
|
26183
|
+
text: editContentRef.current[SITE_OG_IMAGE_KEY] ?? ""
|
|
26184
|
+
});
|
|
26126
26185
|
document.querySelectorAll('[data-ohw-editable="form"]').forEach((form) => {
|
|
26127
26186
|
const formKey = formKeyOf(form);
|
|
26128
26187
|
if (!formKey) return;
|
|
@@ -26145,8 +26204,12 @@ function OhhwellsBridge() {
|
|
|
26145
26204
|
if (inserted) {
|
|
26146
26205
|
const tracker = getSectionsTracker();
|
|
26147
26206
|
postToParentRef.current({ type: "ow:change", nodes: [{ key: "__ohw_sections", text: tracker.textContent ?? "[]" }] });
|
|
26148
|
-
const
|
|
26149
|
-
|
|
26207
|
+
const reportHeight = () => {
|
|
26208
|
+
const h = document.body.scrollHeight;
|
|
26209
|
+
if (h > 50) postToParentRef.current({ type: "ow:height", height: h });
|
|
26210
|
+
};
|
|
26211
|
+
reportHeight();
|
|
26212
|
+
setTimeout(reportHeight, 500);
|
|
26150
26213
|
}
|
|
26151
26214
|
};
|
|
26152
26215
|
const handleSwitchSchedule = (e) => {
|
|
@@ -26872,7 +26935,7 @@ function OhhwellsBridge() {
|
|
|
26872
26935
|
postToParent2({
|
|
26873
26936
|
type: "ow:ready",
|
|
26874
26937
|
version: "1",
|
|
26875
|
-
bridgeVersion: "0.1.
|
|
26938
|
+
bridgeVersion: "0.1.125",
|
|
26876
26939
|
path: pathname,
|
|
26877
26940
|
nodes: collectEditableNodes(editContentRef.current),
|
|
26878
26941
|
sections
|
|
@@ -27959,6 +28022,59 @@ function OhhwellsBridge() {
|
|
|
27959
28022
|
) : null
|
|
27960
28023
|
] });
|
|
27961
28024
|
}
|
|
28025
|
+
|
|
28026
|
+
// src/ui/EmptySection.tsx
|
|
28027
|
+
var import_link = __toESM(require("next/link"), 1);
|
|
28028
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
28029
|
+
function EmptySection({ title, homeHref = "/", eyebrowKey, titleKey, subtitleKey }) {
|
|
28030
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
28031
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
28032
|
+
"p",
|
|
28033
|
+
{
|
|
28034
|
+
style: {
|
|
28035
|
+
fontFamily: "var(--brand-font-body)",
|
|
28036
|
+
fontSize: "0.75rem",
|
|
28037
|
+
fontWeight: 500,
|
|
28038
|
+
letterSpacing: "0.15em",
|
|
28039
|
+
textTransform: "uppercase",
|
|
28040
|
+
color: "var(--brand-accent)",
|
|
28041
|
+
marginBottom: "1.5rem"
|
|
28042
|
+
},
|
|
28043
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_link.default, { href: homeHref, style: { color: "inherit" }, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { ...eyebrowKey ? { "data-ohw-editable": "text", "data-ohw-key": eyebrowKey } : {}, children: "Home" }) })
|
|
28044
|
+
}
|
|
28045
|
+
),
|
|
28046
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
28047
|
+
"h1",
|
|
28048
|
+
{
|
|
28049
|
+
style: {
|
|
28050
|
+
fontFamily: "var(--brand-font-heading)",
|
|
28051
|
+
fontSize: "clamp(2rem, 3.5vw, 2.75rem)",
|
|
28052
|
+
lineHeight: 1.1,
|
|
28053
|
+
letterSpacing: "-0.025em",
|
|
28054
|
+
color: "var(--brand-text)",
|
|
28055
|
+
marginBottom: "1rem"
|
|
28056
|
+
},
|
|
28057
|
+
...titleKey ? { "data-ohw-editable": "text", "data-ohw-key": titleKey, "data-ohw-max-length": 80 } : {},
|
|
28058
|
+
children: title
|
|
28059
|
+
}
|
|
28060
|
+
),
|
|
28061
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
28062
|
+
"p",
|
|
28063
|
+
{
|
|
28064
|
+
style: {
|
|
28065
|
+
fontFamily: "var(--brand-font-body)",
|
|
28066
|
+
fontSize: "1rem",
|
|
28067
|
+
lineHeight: 1.7,
|
|
28068
|
+
fontWeight: 300,
|
|
28069
|
+
color: "var(--brand-text-muted)",
|
|
28070
|
+
maxWidth: "340px"
|
|
28071
|
+
},
|
|
28072
|
+
...subtitleKey ? { "data-ohw-editable": "text", "data-ohw-key": subtitleKey, "data-ohw-max-length": 160 } : {},
|
|
28073
|
+
children: "This page doesn't have any content yet."
|
|
28074
|
+
}
|
|
28075
|
+
)
|
|
28076
|
+
] });
|
|
28077
|
+
}
|
|
27962
28078
|
// Annotate the CommonJS export names for ESM import in node:
|
|
27963
28079
|
0 && (module.exports = {
|
|
27964
28080
|
AI_DEFAULT_BRAND,
|
|
@@ -27976,6 +28092,7 @@ function OhhwellsBridge() {
|
|
|
27976
28092
|
DropdownMenuItem,
|
|
27977
28093
|
DropdownMenuSeparator,
|
|
27978
28094
|
DropdownMenuTrigger,
|
|
28095
|
+
EmptySection,
|
|
27979
28096
|
ItemActionToolbar,
|
|
27980
28097
|
ItemInteractionLayer,
|
|
27981
28098
|
LinkEditorPanel,
|