@ohhwells/bridge 0.1.132 → 0.1.134
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 +181 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +181 -20
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -4159,18 +4159,25 @@ function FadeSlideshow({ node, ctx, path }) {
|
|
|
4159
4159
|
(dir) => onInteract(stepViableSlide(index, failed, count, dir, loop)),
|
|
4160
4160
|
[count, failed, index, loop, onInteract]
|
|
4161
4161
|
);
|
|
4162
|
+
const prefetchRef = import_react.default.useRef(prefetch);
|
|
4163
|
+
import_react.default.useEffect(() => {
|
|
4164
|
+
prefetchRef.current = prefetch;
|
|
4165
|
+
});
|
|
4166
|
+
import_react.default.useEffect(() => {
|
|
4167
|
+
if (autoplay) hasInteractedRef.current = false;
|
|
4168
|
+
}, [autoplay]);
|
|
4162
4169
|
import_react.default.useEffect(() => {
|
|
4163
4170
|
if (!autoplay) return;
|
|
4164
4171
|
const id = window.setInterval(() => {
|
|
4165
4172
|
if (hasInteractedRef.current || hoveredRef.current) return;
|
|
4166
4173
|
setIndex((i) => loop ? (i + 1) % count : Math.min(count - 1, i + 1));
|
|
4167
4174
|
}, intervalMs);
|
|
4168
|
-
const warmId = window.setTimeout(() =>
|
|
4175
|
+
const warmId = window.setTimeout(() => prefetchRef.current(1 % count), Math.min(1500, intervalMs / 2));
|
|
4169
4176
|
return () => {
|
|
4170
4177
|
window.clearInterval(id);
|
|
4171
4178
|
window.clearTimeout(warmId);
|
|
4172
4179
|
};
|
|
4173
|
-
}, [autoplay, count, intervalMs, loop
|
|
4180
|
+
}, [autoplay, count, intervalMs, loop]);
|
|
4174
4181
|
const hasMovedRef = import_react.default.useRef(false);
|
|
4175
4182
|
import_react.default.useEffect(() => {
|
|
4176
4183
|
if (!hasMovedRef.current) {
|
|
@@ -13375,17 +13382,9 @@ var Button = React7.forwardRef(
|
|
|
13375
13382
|
);
|
|
13376
13383
|
Button.displayName = "Button";
|
|
13377
13384
|
|
|
13378
|
-
// src/ui/
|
|
13379
|
-
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
13385
|
+
// src/ui/media-skeleton.ts
|
|
13380
13386
|
var MEDIA_UPLOAD_FADE_MS = 300;
|
|
13381
|
-
var
|
|
13382
|
-
var OVERLAY_BUTTON_STYLE = {
|
|
13383
|
-
pointerEvents: "auto",
|
|
13384
|
-
fontFamily: "Inter, sans-serif",
|
|
13385
|
-
fontSize: 12,
|
|
13386
|
-
color: "#000"
|
|
13387
|
-
};
|
|
13388
|
-
var SKELETON_CSS = `
|
|
13387
|
+
var MEDIA_SKELETON_CSS = `
|
|
13389
13388
|
@keyframes ohw-media-shimmer {
|
|
13390
13389
|
0% { transform: translateX(-100%); }
|
|
13391
13390
|
100% { transform: translateX(100%); }
|
|
@@ -13402,6 +13401,16 @@ var SKELETON_CSS = `
|
|
|
13402
13401
|
animation: ohw-media-shimmer 1.4s infinite;
|
|
13403
13402
|
}
|
|
13404
13403
|
`;
|
|
13404
|
+
|
|
13405
|
+
// src/ui/MediaOverlay.tsx
|
|
13406
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
13407
|
+
var VIDEO_SETTINGS_BAR_INSET = 8;
|
|
13408
|
+
var OVERLAY_BUTTON_STYLE = {
|
|
13409
|
+
pointerEvents: "auto",
|
|
13410
|
+
fontFamily: "Inter, sans-serif",
|
|
13411
|
+
fontSize: 12,
|
|
13412
|
+
color: "#000"
|
|
13413
|
+
};
|
|
13405
13414
|
var REPLACE_BUTTON_INSET = 12;
|
|
13406
13415
|
function replaceButtonMode(image, button) {
|
|
13407
13416
|
if (!button.fullWidth || !button.height) return "none";
|
|
@@ -13469,7 +13478,7 @@ function MediaOverlay({
|
|
|
13469
13478
|
"data-ohw-media-skeleton": "",
|
|
13470
13479
|
"aria-hidden": true,
|
|
13471
13480
|
style: { ...box, pointerEvents: "none" },
|
|
13472
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("style", { children:
|
|
13481
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("style", { children: MEDIA_SKELETON_CSS })
|
|
13473
13482
|
}
|
|
13474
13483
|
);
|
|
13475
13484
|
}
|
|
@@ -15899,6 +15908,17 @@ function setCarouselSlideAsset(tree, index, assetId) {
|
|
|
15899
15908
|
return true;
|
|
15900
15909
|
});
|
|
15901
15910
|
}
|
|
15911
|
+
function carouselAutoplay(tree) {
|
|
15912
|
+
return findCarouselBlock(tree)?.slots?.autoplay === true;
|
|
15913
|
+
}
|
|
15914
|
+
function setCarouselAutoplay(tree, on) {
|
|
15915
|
+
return withCarousel(tree, (block) => {
|
|
15916
|
+
const slots = { ...block.slots, autoplay: on };
|
|
15917
|
+
if (!on) delete slots.interval;
|
|
15918
|
+
block.slots = slots;
|
|
15919
|
+
return true;
|
|
15920
|
+
});
|
|
15921
|
+
}
|
|
15902
15922
|
|
|
15903
15923
|
// src/ui/ai-section/AiFadeCarouselOverlay.tsx
|
|
15904
15924
|
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
@@ -16012,10 +16032,85 @@ function Pill4({ label, onClick, disabled }) {
|
|
|
16012
16032
|
}
|
|
16013
16033
|
);
|
|
16014
16034
|
}
|
|
16035
|
+
function TogglePill({
|
|
16036
|
+
label,
|
|
16037
|
+
on,
|
|
16038
|
+
onToggle,
|
|
16039
|
+
disabled
|
|
16040
|
+
}) {
|
|
16041
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
16042
|
+
"button",
|
|
16043
|
+
{
|
|
16044
|
+
type: "button",
|
|
16045
|
+
role: "switch",
|
|
16046
|
+
"aria-checked": on,
|
|
16047
|
+
onClick: onToggle,
|
|
16048
|
+
disabled,
|
|
16049
|
+
title: on ? `${label} on \u2014 click to turn off` : `${label} off \u2014 click to turn on`,
|
|
16050
|
+
style: {
|
|
16051
|
+
height: 28,
|
|
16052
|
+
padding: "0 8px 0 10px",
|
|
16053
|
+
display: "inline-flex",
|
|
16054
|
+
alignItems: "center",
|
|
16055
|
+
gap: 8,
|
|
16056
|
+
borderRadius: 999,
|
|
16057
|
+
border: "none",
|
|
16058
|
+
cursor: disabled ? "default" : "pointer",
|
|
16059
|
+
background: "#ffffff",
|
|
16060
|
+
color: OHW_BLUE_DARK4,
|
|
16061
|
+
opacity: disabled ? 0.4 : 1,
|
|
16062
|
+
fontSize: 12,
|
|
16063
|
+
lineHeight: "16px",
|
|
16064
|
+
fontFamily: "Figtree, ui-sans-serif, system-ui, sans-serif",
|
|
16065
|
+
whiteSpace: "nowrap",
|
|
16066
|
+
flex: "0 0 auto"
|
|
16067
|
+
},
|
|
16068
|
+
children: [
|
|
16069
|
+
label,
|
|
16070
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
16071
|
+
"span",
|
|
16072
|
+
{
|
|
16073
|
+
"aria-hidden": true,
|
|
16074
|
+
style: {
|
|
16075
|
+
position: "relative",
|
|
16076
|
+
width: 26,
|
|
16077
|
+
height: 15,
|
|
16078
|
+
borderRadius: 999,
|
|
16079
|
+
flex: "0 0 auto",
|
|
16080
|
+
// Off is a grey track, not a washed-out blue: the pill's own white background already
|
|
16081
|
+
// carries the brand colour in the text, and a pale blue track next to it read as
|
|
16082
|
+
// "disabled control" rather than "switch that is off".
|
|
16083
|
+
background: on ? OHW_BLUE_DARK4 : "#CBD5E1",
|
|
16084
|
+
transition: "background 120ms ease"
|
|
16085
|
+
},
|
|
16086
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
16087
|
+
"span",
|
|
16088
|
+
{
|
|
16089
|
+
style: {
|
|
16090
|
+
position: "absolute",
|
|
16091
|
+
top: 2,
|
|
16092
|
+
left: on ? 13 : 2,
|
|
16093
|
+
width: 11,
|
|
16094
|
+
height: 11,
|
|
16095
|
+
borderRadius: "50%",
|
|
16096
|
+
background: "#ffffff",
|
|
16097
|
+
boxShadow: "0 1px 2px rgba(0,0,0,0.3)",
|
|
16098
|
+
transition: "left 120ms ease"
|
|
16099
|
+
}
|
|
16100
|
+
}
|
|
16101
|
+
)
|
|
16102
|
+
}
|
|
16103
|
+
)
|
|
16104
|
+
]
|
|
16105
|
+
}
|
|
16106
|
+
);
|
|
16107
|
+
}
|
|
16015
16108
|
function CarouselSlideToolbar({
|
|
16016
16109
|
pending,
|
|
16017
16110
|
error,
|
|
16018
|
-
|
|
16111
|
+
autoplay,
|
|
16112
|
+
onChangeImage,
|
|
16113
|
+
onToggleAutoplay
|
|
16019
16114
|
}) {
|
|
16020
16115
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
16021
16116
|
"div",
|
|
@@ -16032,11 +16127,18 @@ function CarouselSlideToolbar({
|
|
|
16032
16127
|
padding: 8,
|
|
16033
16128
|
borderRadius: 12,
|
|
16034
16129
|
boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
|
|
16035
|
-
|
|
16036
|
-
|
|
16130
|
+
// Not dimmed while pending, only inert. The toolbar used to drop to 0.6 opacity for the
|
|
16131
|
+
// length of the validate round-trip, which on a local backend is short enough that the
|
|
16132
|
+
// autoplay toggle just flashed — a dim and an un-dim with nothing in between. Progress is
|
|
16133
|
+
// reported where the change is: the switch moves at once (optimistically), and an image
|
|
16134
|
+
// swap raises the skeleton over the slide.
|
|
16135
|
+
pointerEvents: pending ? "none" : "auto"
|
|
16037
16136
|
},
|
|
16038
16137
|
children: [
|
|
16039
|
-
/* @__PURE__ */ (0, import_jsx_runtime22.
|
|
16138
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { style: { display: "flex", gap: 6, alignItems: "center" }, children: [
|
|
16139
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Pill4, { label: "Change Image", onClick: onChangeImage }),
|
|
16140
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TogglePill, { label: "Autoplay", on: autoplay, onToggle: onToggleAutoplay })
|
|
16141
|
+
] }),
|
|
16040
16142
|
error && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { style: { color: "#FCA5A5", fontSize: 11, maxWidth: 220, textAlign: "center" }, children: error })
|
|
16041
16143
|
]
|
|
16042
16144
|
}
|
|
@@ -16052,8 +16154,24 @@ function AiFadeCarouselOverlay({
|
|
|
16052
16154
|
const [active, setActive] = (0, import_react12.useState)(null);
|
|
16053
16155
|
const [pending, setPending] = (0, import_react12.useState)(false);
|
|
16054
16156
|
const [error, setError] = (0, import_react12.useState)(null);
|
|
16157
|
+
const [autoplayOverride, setAutoplayOverride] = (0, import_react12.useState)(null);
|
|
16158
|
+
const [swapping, setSwapping] = (0, import_react12.useState)(null);
|
|
16159
|
+
const skeletonRef = (0, import_react12.useRef)(null);
|
|
16055
16160
|
const activeRef = (0, import_react12.useRef)(null);
|
|
16056
16161
|
activeRef.current = active;
|
|
16162
|
+
(0, import_react12.useEffect)(() => {
|
|
16163
|
+
setAutoplayOverride(null);
|
|
16164
|
+
}, [active?.sectionId, active?.index]);
|
|
16165
|
+
(0, import_react12.useEffect)(() => {
|
|
16166
|
+
if (!swapping?.fadingOut || !skeletonRef.current) return;
|
|
16167
|
+
const anim = skeletonRef.current.animate([{ opacity: 1 }, { opacity: 0 }], {
|
|
16168
|
+
duration: MEDIA_UPLOAD_FADE_MS,
|
|
16169
|
+
easing: "ease-out",
|
|
16170
|
+
fill: "forwards"
|
|
16171
|
+
});
|
|
16172
|
+
anim.finished.then(() => setSwapping(null)).catch(() => setSwapping(null));
|
|
16173
|
+
return () => anim.cancel();
|
|
16174
|
+
}, [swapping?.fadingOut]);
|
|
16057
16175
|
(0, import_react12.useEffect)(() => {
|
|
16058
16176
|
const onMove = (e) => {
|
|
16059
16177
|
const t = e.target;
|
|
@@ -16194,6 +16312,27 @@ function AiFadeCarouselOverlay({
|
|
|
16194
16312
|
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react11.Star, { size: 14, style: { pointerEvents: "none" } })
|
|
16195
16313
|
}
|
|
16196
16314
|
),
|
|
16315
|
+
swapping && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
16316
|
+
"div",
|
|
16317
|
+
{
|
|
16318
|
+
ref: skeletonRef,
|
|
16319
|
+
"data-ohw-bridge": "",
|
|
16320
|
+
"data-ohw-media-skeleton": "",
|
|
16321
|
+
"aria-hidden": true,
|
|
16322
|
+
style: {
|
|
16323
|
+
position: "fixed",
|
|
16324
|
+
top: swapping.rect.top,
|
|
16325
|
+
left: swapping.rect.left,
|
|
16326
|
+
width: swapping.rect.width,
|
|
16327
|
+
height: swapping.rect.height,
|
|
16328
|
+
// Under the toolbar (2147483647) so its controls stay visible and clickable over the
|
|
16329
|
+
// slide being swapped, and inert so it cannot eat the click that dismisses them.
|
|
16330
|
+
zIndex: 2147483646,
|
|
16331
|
+
pointerEvents: "none"
|
|
16332
|
+
},
|
|
16333
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("style", { children: MEDIA_SKELETON_CSS })
|
|
16334
|
+
}
|
|
16335
|
+
),
|
|
16197
16336
|
activeItemRect && active && activeEntry && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
16198
16337
|
ItemInteractionLayer,
|
|
16199
16338
|
{
|
|
@@ -16204,14 +16343,36 @@ function AiFadeCarouselOverlay({
|
|
|
16204
16343
|
{
|
|
16205
16344
|
pending,
|
|
16206
16345
|
error,
|
|
16346
|
+
autoplay: autoplayOverride ?? carouselAutoplay(activeEntry.tree),
|
|
16347
|
+
onToggleAutoplay: () => {
|
|
16348
|
+
const next = !(autoplayOverride ?? carouselAutoplay(activeEntry.tree));
|
|
16349
|
+
setAutoplayOverride(next);
|
|
16350
|
+
void runMutation(active.sectionId, (tree) => setCarouselAutoplay(tree, next)).then(
|
|
16351
|
+
(ok) => {
|
|
16352
|
+
if (!ok) setAutoplayOverride(null);
|
|
16353
|
+
}
|
|
16354
|
+
);
|
|
16355
|
+
},
|
|
16207
16356
|
onChangeImage: () => {
|
|
16208
|
-
void requestAssetPick2(postToParent2).then((picked) => {
|
|
16357
|
+
void requestAssetPick2(postToParent2).then(async (picked) => {
|
|
16209
16358
|
if (!picked) return;
|
|
16210
|
-
|
|
16359
|
+
const slideRect = readRect5(active);
|
|
16360
|
+
if (slideRect) setSwapping({ rect: slideRect, fadingOut: false });
|
|
16361
|
+
const ok = await runMutation(
|
|
16211
16362
|
active.sectionId,
|
|
16212
16363
|
(tree) => setCarouselSlideAsset(tree, active.index, picked.assetId),
|
|
16213
16364
|
{ [picked.assetId]: picked.url }
|
|
16214
16365
|
);
|
|
16366
|
+
if (!ok) {
|
|
16367
|
+
setSwapping(null);
|
|
16368
|
+
return;
|
|
16369
|
+
}
|
|
16370
|
+
const preload = new Image();
|
|
16371
|
+
const done = () => setSwapping((s) => s ? { ...s, fadingOut: true } : s);
|
|
16372
|
+
preload.onload = done;
|
|
16373
|
+
preload.onerror = done;
|
|
16374
|
+
preload.src = picked.url;
|
|
16375
|
+
if (preload.complete) done();
|
|
16215
16376
|
});
|
|
16216
16377
|
}
|
|
16217
16378
|
}
|
|
@@ -30153,7 +30314,7 @@ function OhhwellsBridge() {
|
|
|
30153
30314
|
postToParent2({
|
|
30154
30315
|
type: "ow:ready",
|
|
30155
30316
|
version: "1",
|
|
30156
|
-
bridgeVersion: "0.1.
|
|
30317
|
+
bridgeVersion: "0.1.132",
|
|
30157
30318
|
path: pathname,
|
|
30158
30319
|
nodes: collectEditableNodes(editContentRef.current),
|
|
30159
30320
|
sections
|