@valbuild/tanstack 0.124.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/LICENSE.md +7 -0
- package/README.md +380 -0
- package/client/dist/valbuild-tanstack-client.cjs.d.ts +2 -0
- package/client/dist/valbuild-tanstack-client.cjs.dev.js +288 -0
- package/client/dist/valbuild-tanstack-client.cjs.js +7 -0
- package/client/dist/valbuild-tanstack-client.cjs.prod.js +288 -0
- package/client/dist/valbuild-tanstack-client.esm.js +280 -0
- package/client/package.json +4 -0
- package/dist/ValOverlayContext-87cb022d.esm.js +284 -0
- package/dist/ValOverlayContext-abd6ab9a.cjs.prod.js +292 -0
- package/dist/ValOverlayContext-d3bfacb1.cjs.dev.js +292 -0
- package/dist/createForOfIteratorHelper-0324e063.cjs.prod.js +157 -0
- package/dist/createForOfIteratorHelper-485c2ce1.esm.js +151 -0
- package/dist/createForOfIteratorHelper-e75681d7.cjs.dev.js +157 -0
- package/dist/declarations/src/ValApp.d.ts +6 -0
- package/dist/declarations/src/ValImage.d.ts +24 -0
- package/dist/declarations/src/ValModulesClient.d.ts +34 -0
- package/dist/declarations/src/ValProvider.d.ts +6 -0
- package/dist/declarations/src/ValTypes.d.ts +4 -0
- package/dist/declarations/src/client/index.d.ts +1 -0
- package/dist/declarations/src/client/initValClient.d.ts +27 -0
- package/dist/declarations/src/decodeValPathsOfString.d.ts +2 -0
- package/dist/declarations/src/external_exempt_from_val_quickjs.d.ts +41 -0
- package/dist/declarations/src/getUnpatchedUnencodedVal.d.ts +3 -0
- package/dist/declarations/src/index.d.ts +2 -0
- package/dist/declarations/src/initVal.d.ts +74 -0
- package/dist/declarations/src/server/index.d.ts +5 -0
- package/dist/declarations/src/server/initValContent.d.ts +122 -0
- package/dist/declarations/src/server/initValMcp.d.ts +27 -0
- package/dist/declarations/src/server/initValServer.d.ts +41 -0
- package/dist/declarations/src/server/valDraftMode.d.ts +49 -0
- package/dist/declarations/src/version.d.ts +1 -0
- package/dist/routeFromVal-6693e037.cjs.prod.js +218 -0
- package/dist/routeFromVal-728295a0.esm.js +212 -0
- package/dist/routeFromVal-a9147ceb.cjs.dev.js +218 -0
- package/dist/valbuild-tanstack.cjs.d.ts +2 -0
- package/dist/valbuild-tanstack.cjs.dev.js +2005 -0
- package/dist/valbuild-tanstack.cjs.js +7 -0
- package/dist/valbuild-tanstack.cjs.prod.js +2005 -0
- package/dist/valbuild-tanstack.esm.js +1950 -0
- package/dist/version-1a88fd21.cjs.dev.js +229 -0
- package/dist/version-5ac70ccf.cjs.prod.js +229 -0
- package/dist/version-ac8df696.esm.js +225 -0
- package/package.json +84 -0
- package/server/dist/valbuild-tanstack-server.cjs.d.ts +2 -0
- package/server/dist/valbuild-tanstack-server.cjs.dev.js +1032 -0
- package/server/dist/valbuild-tanstack-server.cjs.js +7 -0
- package/server/dist/valbuild-tanstack-server.cjs.prod.js +1032 -0
- package/server/dist/valbuild-tanstack-server.esm.js +1005 -0
- package/server/package.json +4 -0
|
@@ -0,0 +1,2005 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var version = require('./version-5ac70ccf.cjs.prod.js');
|
|
6
|
+
var createForOfIteratorHelper = require('./createForOfIteratorHelper-0324e063.cjs.prod.js');
|
|
7
|
+
var core = require('@valbuild/core');
|
|
8
|
+
var stega = require('@valbuild/react/stega');
|
|
9
|
+
var internal = require('@valbuild/react/internal');
|
|
10
|
+
var ui = require('@valbuild/ui');
|
|
11
|
+
var reactRouter = require('@tanstack/react-router');
|
|
12
|
+
var React = require('react');
|
|
13
|
+
var ValOverlayContext = require('./ValOverlayContext-abd6ab9a.cjs.prod.js');
|
|
14
|
+
var client = require('@valbuild/shared/client');
|
|
15
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
16
|
+
require('@valbuild/core/patch');
|
|
17
|
+
|
|
18
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
19
|
+
|
|
20
|
+
function _interopNamespace(e) {
|
|
21
|
+
if (e && e.__esModule) return e;
|
|
22
|
+
var n = Object.create(null);
|
|
23
|
+
if (e) {
|
|
24
|
+
Object.keys(e).forEach(function (k) {
|
|
25
|
+
if (k !== 'default') {
|
|
26
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
27
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () { return e[k]; }
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
n["default"] = e;
|
|
35
|
+
return Object.freeze(n);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var core__namespace = /*#__PURE__*/_interopNamespace(core);
|
|
39
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
40
|
+
|
|
41
|
+
function getUnpatchedUnencodedVal(selector) {
|
|
42
|
+
// It is weird to use stega encode here since we never actually should stega encode,
|
|
43
|
+
// however, this is a workaround, since if it is disabled, it will behave as we want
|
|
44
|
+
return stega.stegaEncode(selector, {
|
|
45
|
+
disabled: true
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function decodeValPathsOfString(encodedString) {
|
|
50
|
+
return stega.stegaDecodeStrings(encodedString);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
var tanstackRouter = core.Internal.tanstackRouter;
|
|
54
|
+
var externalPageRouter = core.Internal.externalPageRouter;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Returns true if the Val Enable cookie is set. Must be called on the server —
|
|
58
|
+
* inside a server function, a server route handler, or during SSR — and
|
|
59
|
+
* returns false anywhere else.
|
|
60
|
+
*
|
|
61
|
+
* Not needed for the `suspend` prop on ValProvider, which detects the cookie
|
|
62
|
+
* client-side; reserve it for advanced server-side conditionals.
|
|
63
|
+
*/
|
|
64
|
+
function isValEnabled() {
|
|
65
|
+
return _isValEnabled.apply(this, arguments);
|
|
66
|
+
}
|
|
67
|
+
function _isValEnabled() {
|
|
68
|
+
_isValEnabled = version._asyncToGenerator(/*#__PURE__*/version._regenerator().m(function _callee() {
|
|
69
|
+
var _yield$import, getCookie;
|
|
70
|
+
return version._regenerator().w(function (_context) {
|
|
71
|
+
while (1) switch (_context.p = _context.n) {
|
|
72
|
+
case 0:
|
|
73
|
+
_context.p = 0;
|
|
74
|
+
_context.n = 1;
|
|
75
|
+
return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('@tanstack/react-start/server')); });
|
|
76
|
+
case 1:
|
|
77
|
+
_yield$import = _context.v;
|
|
78
|
+
getCookie = _yield$import.getCookie;
|
|
79
|
+
return _context.a(2, getCookie(core.Internal.VAL_ENABLE_COOKIE_NAME) === "true");
|
|
80
|
+
case 2:
|
|
81
|
+
_context.p = 2;
|
|
82
|
+
_context.v;
|
|
83
|
+
return _context.a(2, false);
|
|
84
|
+
}
|
|
85
|
+
}, _callee, null, [[0, 2]]);
|
|
86
|
+
}));
|
|
87
|
+
return _isValEnabled.apply(this, arguments);
|
|
88
|
+
}
|
|
89
|
+
var initVal = function initVal(config) {
|
|
90
|
+
var _createValSystem = core.initVal(config),
|
|
91
|
+
s = _createValSystem.s,
|
|
92
|
+
c = _createValSystem.c,
|
|
93
|
+
val = _createValSystem.val,
|
|
94
|
+
systemConfig = _createValSystem.config;
|
|
95
|
+
var currentConfig = createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, systemConfig), config);
|
|
96
|
+
return {
|
|
97
|
+
s: s,
|
|
98
|
+
c: c,
|
|
99
|
+
isValEnabled: isValEnabled,
|
|
100
|
+
tanstackRouter: tanstackRouter,
|
|
101
|
+
externalPageRouter: externalPageRouter,
|
|
102
|
+
val: createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, val), {}, {
|
|
103
|
+
attrs: stega.attrs,
|
|
104
|
+
unstable_decodeValPathsOfString: decodeValPathsOfString,
|
|
105
|
+
raw: stega.raw,
|
|
106
|
+
unstable_getUnpatchedUnencodedVal: getUnpatchedUnencodedVal
|
|
107
|
+
}),
|
|
108
|
+
config: currentConfig
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Loads the Studio's ES module bundle.
|
|
114
|
+
*
|
|
115
|
+
* `next/script` in the Next package; here it is a `<script>` appended to the
|
|
116
|
+
* head, because TanStack Start has no script component and React 19's own
|
|
117
|
+
* script hoisting does not cover `type="module"` — it dedupes `<script async
|
|
118
|
+
* src>` only, and a module script rendered into the tree would be re-inserted
|
|
119
|
+
* on every remount.
|
|
120
|
+
*
|
|
121
|
+
* Deduped by `src` against the document, so the overlay and the studio page
|
|
122
|
+
* can both ask for the same bundle (and a remount can ask again) without
|
|
123
|
+
* loading it twice. Never removed on unmount: the bundle has registered custom
|
|
124
|
+
* elements and event listeners by then, and re-running it is not free.
|
|
125
|
+
*/
|
|
126
|
+
function ValScript(_ref) {
|
|
127
|
+
var src = _ref.src;
|
|
128
|
+
React.useEffect(function () {
|
|
129
|
+
var existing = document.querySelector("script[data-val-script=\"".concat(CSS.escape(src), "\"]"));
|
|
130
|
+
if (existing) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
var script = document.createElement("script");
|
|
134
|
+
script.type = "module";
|
|
135
|
+
script.crossOrigin = "anonymous";
|
|
136
|
+
script.src = src;
|
|
137
|
+
script.dataset["valScript"] = src;
|
|
138
|
+
document.head.appendChild(script);
|
|
139
|
+
}, [src]);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/*
|
|
144
|
+
* See the useConfig.tsx where the config is loaded from session storage
|
|
145
|
+
*/
|
|
146
|
+
function useConfigStorageSave(config) {
|
|
147
|
+
React__default["default"].useEffect(function () {
|
|
148
|
+
sessionStorage.setItem(client.VAL_CONFIG_SESSION_STORAGE_KEY, JSON.stringify(config));
|
|
149
|
+
}, [config]);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function initSessionTheme(config) {
|
|
153
|
+
if (typeof window === "undefined") {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
var existingSessionTheme = sessionStorage.getItem(client.VAL_THEME_SESSION_STORAGE_KEY);
|
|
157
|
+
if (existingSessionTheme && (existingSessionTheme === "dark" || existingSessionTheme === "light")) {
|
|
158
|
+
return existingSessionTheme;
|
|
159
|
+
}
|
|
160
|
+
var storedTheme = localStorage.getItem("val-theme-" + ((config === null || config === void 0 ? void 0 : config.project) || "unknown"));
|
|
161
|
+
var theme = "dark";
|
|
162
|
+
if (storedTheme === "light" || storedTheme === "dark") {
|
|
163
|
+
theme = storedTheme;
|
|
164
|
+
} else if ((config === null || config === void 0 ? void 0 : config.defaultTheme) === "dark" || (config === null || config === void 0 ? void 0 : config.defaultTheme) === "light") {
|
|
165
|
+
theme = config.defaultTheme;
|
|
166
|
+
}
|
|
167
|
+
sessionStorage.setItem(client.VAL_THEME_SESSION_STORAGE_KEY, theme);
|
|
168
|
+
return theme;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// All DOM element that are outside the Shadow DOM should use this class name prefix
|
|
172
|
+
var valPrefixedClass = "val-prefix-" + core.Internal.getSHA256Hash(new TextEncoder().encode(ui.VERSION || "")).slice(0, 8) + "-";
|
|
173
|
+
var prefixStyles = function prefixStyles(styles) {
|
|
174
|
+
return Object.entries(styles).map(function (_ref) {
|
|
175
|
+
var _ref2 = createForOfIteratorHelper._slicedToArray(_ref, 2),
|
|
176
|
+
key = _ref2[0],
|
|
177
|
+
value = _ref2[1];
|
|
178
|
+
return ".".concat(valPrefixedClass).concat(key.replace(/\//g, "\\/"), " { ").concat(value, " }");
|
|
179
|
+
}).join("\n");
|
|
180
|
+
};
|
|
181
|
+
var cn = function cn(className) {
|
|
182
|
+
return className.map(function (c) {
|
|
183
|
+
return "".concat(valPrefixedClass).concat(c);
|
|
184
|
+
}).join(" ");
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Returns true if the Val Enable cookie is set to "true" in the given cookie
|
|
189
|
+
* string (typically `document.cookie`).
|
|
190
|
+
*
|
|
191
|
+
* Exact-token match: a naive `includes("val_enable=true")` would also match
|
|
192
|
+
* unrelated cookies like `xval_enable=true`. The server sets the cookie to
|
|
193
|
+
* "false" (rather than deleting it) on disable, so the value must be checked
|
|
194
|
+
* too.
|
|
195
|
+
*/
|
|
196
|
+
function hasValEnableCookie(cookieString) {
|
|
197
|
+
var _iterator = createForOfIteratorHelper._createForOfIteratorHelper(cookieString.split(";")),
|
|
198
|
+
_step;
|
|
199
|
+
try {
|
|
200
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
201
|
+
var part = _step.value;
|
|
202
|
+
var eq = part.indexOf("=");
|
|
203
|
+
if (eq === -1) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
var name = part.slice(0, eq).trim();
|
|
207
|
+
var value = part.slice(eq + 1).trim();
|
|
208
|
+
if (name === core.Internal.VAL_ENABLE_COOKIE_NAME && value === "true") {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
} catch (err) {
|
|
213
|
+
_iterator.e(err);
|
|
214
|
+
} finally {
|
|
215
|
+
_iterator.f();
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Colours for the moments before Val's own stylesheet exists.
|
|
222
|
+
*
|
|
223
|
+
* Two of them, and they are not interchangeable. The studio's loading screen
|
|
224
|
+
* covers the whole viewport and is replaced by the studio's canvas, so it has
|
|
225
|
+
* to match `--bg-canvas`. The loading pill sits on the customer's page as a
|
|
226
|
+
* piece of Val's floating chrome, so it has to match `--bg-float`. Getting
|
|
227
|
+
* them the wrong way round is not invisible: the studio flashes one colour and
|
|
228
|
+
* repaints in another the moment the stylesheet lands.
|
|
229
|
+
*
|
|
230
|
+
* Hardcoded because that is the whole point — these are used exactly when the
|
|
231
|
+
* variables that define them are not loaded yet. They are copies, so they go
|
|
232
|
+
* stale silently: if `--bg-canvas` or `--bg-float` changes in
|
|
233
|
+
* `packages/ui/spa/index.css`, change them here too.
|
|
234
|
+
*/
|
|
235
|
+
|
|
236
|
+
/** `--bg-canvas`, dark. The studio's own background. */
|
|
237
|
+
var canvasDarkBg = "#08080a";
|
|
238
|
+
/** `--bg-canvas`, light. */
|
|
239
|
+
var canvasLightBg = "#f4f4f5";
|
|
240
|
+
|
|
241
|
+
/** `--bg-float`, dark. Val's chrome, floating over the customer's page. */
|
|
242
|
+
var floatDarkBg = "#131316";
|
|
243
|
+
/** `--bg-float`, light. */
|
|
244
|
+
var floatLightBg = "#fcfcfc";
|
|
245
|
+
|
|
246
|
+
var SELECTION = "#079455";
|
|
247
|
+
var SELECTION_SOFT = "rgba(7, 148, 85, 0.4)";
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* How often the page is re-measured even when nothing said it changed.
|
|
251
|
+
*
|
|
252
|
+
* A backstop, not the mechanism — the observers below carry the responsive
|
|
253
|
+
* case. Slow enough to be free, fast enough that a field which appeared late is
|
|
254
|
+
* in the list before anyone goes looking for it.
|
|
255
|
+
*/
|
|
256
|
+
var RESCAN_INTERVAL_MS = 5000;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* The page's half of the canvas protocol.
|
|
260
|
+
*
|
|
261
|
+
* Mounted only in the studio's canvas frame. Its job is to tell the studio
|
|
262
|
+
* where Val's content is on the page, and to do the two things the studio can
|
|
263
|
+
* ask of a document it cannot touch: outline an element, and treat a click as a
|
|
264
|
+
* pick rather than as a click.
|
|
265
|
+
*
|
|
266
|
+
* Nothing here is visible on the customer's own site — this component is not
|
|
267
|
+
* mounted there at all.
|
|
268
|
+
*/
|
|
269
|
+
function ValCanvasBridge(_ref) {
|
|
270
|
+
var draftMode = _ref.draftMode,
|
|
271
|
+
isRefreshing = _ref.isRefreshing;
|
|
272
|
+
var _React$useState = React__default["default"].useState(false),
|
|
273
|
+
_React$useState2 = createForOfIteratorHelper._slicedToArray(_React$useState, 2),
|
|
274
|
+
picking = _React$useState2[0],
|
|
275
|
+
setPicking = _React$useState2[1];
|
|
276
|
+
var _React$useState3 = React__default["default"].useState(null),
|
|
277
|
+
_React$useState4 = createForOfIteratorHelper._slicedToArray(_React$useState3, 2),
|
|
278
|
+
highlighted = _React$useState4[0],
|
|
279
|
+
setHighlighted = _React$useState4[1];
|
|
280
|
+
// Kept in a ref as well, because the capture-phase click listener below is
|
|
281
|
+
// installed once and would otherwise close over the first value forever.
|
|
282
|
+
var pickingRef = React__default["default"].useRef(false);
|
|
283
|
+
pickingRef.current = picking;
|
|
284
|
+
var post = React__default["default"].useCallback(function (message) {
|
|
285
|
+
// `*` as the target origin: the studio and the page share an origin today,
|
|
286
|
+
// but the frame does not know the parent's origin for certain and naming
|
|
287
|
+
// the wrong one silently drops the message. Nothing sent here is
|
|
288
|
+
// sensitive — it is the positions of elements on a page the parent is
|
|
289
|
+
// already displaying.
|
|
290
|
+
window.parent.postMessage(message, "*");
|
|
291
|
+
}, []);
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* The last payload posted, as a string.
|
|
295
|
+
*
|
|
296
|
+
* The periodic scan below runs whether or not anything moved, and an
|
|
297
|
+
* unchanged payload costs the studio a re-render of the fields column for
|
|
298
|
+
* nothing. Compared as a string because the comparison has to be by value —
|
|
299
|
+
* every scan builds fresh objects.
|
|
300
|
+
*/
|
|
301
|
+
var lastPosted = React__default["default"].useRef(null);
|
|
302
|
+
|
|
303
|
+
/** Every element Val tagged, with where it is on the page. */
|
|
304
|
+
var scan = React__default["default"].useCallback(function () {
|
|
305
|
+
var elements = [];
|
|
306
|
+
var scrollX = window.scrollX;
|
|
307
|
+
var scrollY = window.scrollY;
|
|
308
|
+
document.querySelectorAll("[data-val-path]").forEach(function (el) {
|
|
309
|
+
if (!(el instanceof HTMLElement) && !(el instanceof SVGElement)) return;
|
|
310
|
+
var attribute = el.getAttribute("data-val-path");
|
|
311
|
+
if (!attribute) return;
|
|
312
|
+
/**
|
|
313
|
+
* A `<source>` has no box of its own, so its `<video>`/`<picture>` parent
|
|
314
|
+
* is measured instead — the same substitution the overlay makes, and for
|
|
315
|
+
* the same reason: an element with a zero-size rect cannot be pointed at.
|
|
316
|
+
*/
|
|
317
|
+
var measured = el;
|
|
318
|
+
if (el.tagName === "SOURCE") {
|
|
319
|
+
var parent = el.parentElement;
|
|
320
|
+
if (parent && (parent.tagName === "VIDEO" || parent.tagName === "PICTURE")) {
|
|
321
|
+
measured = parent;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
var rect = measured.getBoundingClientRect();
|
|
325
|
+
if (rect.width === 0 && rect.height === 0) return;
|
|
326
|
+
elements.push({
|
|
327
|
+
// Split by the same helper the rest of Val uses, so the studio gets
|
|
328
|
+
// the paths back in exactly the form it holds them in.
|
|
329
|
+
paths: core.Internal.splitJoinedSourcePaths(attribute),
|
|
330
|
+
// Page coordinates, not viewport: the studio scales and offsets the
|
|
331
|
+
// whole document, so a viewport-relative box would be wrong the moment
|
|
332
|
+
// the frame scrolled.
|
|
333
|
+
rect: {
|
|
334
|
+
top: rect.top + scrollY,
|
|
335
|
+
left: rect.left + scrollX,
|
|
336
|
+
width: rect.width,
|
|
337
|
+
height: rect.height
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
var message = {
|
|
342
|
+
val: client.VAL_CANVAS_MESSAGE,
|
|
343
|
+
type: "elements",
|
|
344
|
+
elements: elements,
|
|
345
|
+
pageSize: {
|
|
346
|
+
width: document.documentElement.scrollWidth,
|
|
347
|
+
height: document.documentElement.scrollHeight
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
var serialized = JSON.stringify(message);
|
|
351
|
+
if (serialized === lastPosted.current) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
lastPosted.current = serialized;
|
|
355
|
+
post(message);
|
|
356
|
+
}, [post]);
|
|
357
|
+
|
|
358
|
+
// Relayed as it changes, not polled: the studio only has to know the two
|
|
359
|
+
// edges, and a message per render would be a message per keystroke.
|
|
360
|
+
React__default["default"].useEffect(function () {
|
|
361
|
+
post({
|
|
362
|
+
val: client.VAL_CANVAS_MESSAGE,
|
|
363
|
+
type: "refreshing",
|
|
364
|
+
pending: isRefreshing === true
|
|
365
|
+
});
|
|
366
|
+
}, [post, isRefreshing]);
|
|
367
|
+
|
|
368
|
+
// Announce the frame, and say whether it is actually showing draft content.
|
|
369
|
+
React__default["default"].useEffect(function () {
|
|
370
|
+
post({
|
|
371
|
+
val: client.VAL_CANVAS_MESSAGE,
|
|
372
|
+
type: "ready",
|
|
373
|
+
draftMode: draftMode,
|
|
374
|
+
url: window.location.href
|
|
375
|
+
});
|
|
376
|
+
}, [post, draftMode]);
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Re-scan whenever the page could have moved.
|
|
380
|
+
*
|
|
381
|
+
* Coalesced into one animation frame: a layout change fires mutation,
|
|
382
|
+
* resize and scroll events together, and scanning three times produces three
|
|
383
|
+
* identical messages.
|
|
384
|
+
*/
|
|
385
|
+
React__default["default"].useEffect(function () {
|
|
386
|
+
var frame = null;
|
|
387
|
+
var schedule = function schedule() {
|
|
388
|
+
if (frame !== null) return;
|
|
389
|
+
frame = requestAnimationFrame(function () {
|
|
390
|
+
frame = null;
|
|
391
|
+
scan();
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
schedule();
|
|
395
|
+
var mutations = new MutationObserver(schedule);
|
|
396
|
+
mutations.observe(document.body, {
|
|
397
|
+
subtree: true,
|
|
398
|
+
childList: true,
|
|
399
|
+
attributes: true,
|
|
400
|
+
attributeFilter: ["data-val-path", "class", "style"]
|
|
401
|
+
});
|
|
402
|
+
var resizes = new ResizeObserver(schedule);
|
|
403
|
+
resizes.observe(document.documentElement);
|
|
404
|
+
window.addEventListener("scroll", schedule, {
|
|
405
|
+
passive: true
|
|
406
|
+
});
|
|
407
|
+
window.addEventListener("resize", schedule);
|
|
408
|
+
// Images and fonts land after the first paint and move everything below
|
|
409
|
+
// them, so the boxes measured before `load` are stale by definition.
|
|
410
|
+
window.addEventListener("load", schedule);
|
|
411
|
+
return function () {
|
|
412
|
+
if (frame !== null) cancelAnimationFrame(frame);
|
|
413
|
+
mutations.disconnect();
|
|
414
|
+
resizes.disconnect();
|
|
415
|
+
window.removeEventListener("scroll", schedule);
|
|
416
|
+
window.removeEventListener("resize", schedule);
|
|
417
|
+
window.removeEventListener("load", schedule);
|
|
418
|
+
};
|
|
419
|
+
}, [scan]);
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* And re-scan every few seconds regardless.
|
|
423
|
+
*
|
|
424
|
+
* The event-driven scan above is not enough in practice, and the way it fails
|
|
425
|
+
* is confusing rather than obviously broken: the fields column shows some of
|
|
426
|
+
* what is on the page and not the rest, until you happen to edit one of the
|
|
427
|
+
* missing ones and they appear. Anything that tags content after the observers
|
|
428
|
+
* are set up but produces no mutation they watch — a streamed-in server
|
|
429
|
+
* component, an element that measured zero because its image had not decoded,
|
|
430
|
+
* content revealed by something outside `data-val-path`, `class` and `style` —
|
|
431
|
+
* is invisible until the next unrelated change. A slow poll costs nothing and
|
|
432
|
+
* closes all of those at once, without having to guess which one it was.
|
|
433
|
+
*
|
|
434
|
+
* Idle-scheduled, and never on the frame's critical path. A scan is a
|
|
435
|
+
* `querySelectorAll` plus a forced layout per tagged element, which is exactly
|
|
436
|
+
* the kind of work that should wait for a gap rather than take one — and this
|
|
437
|
+
* runs inside the customer's own page, where Val is a guest. Skipped entirely
|
|
438
|
+
* while the frame is hidden, where the boxes cannot have moved and nobody is
|
|
439
|
+
* looking at them.
|
|
440
|
+
*/
|
|
441
|
+
React__default["default"].useEffect(function () {
|
|
442
|
+
var idle = null;
|
|
443
|
+
var runWhenIdle = function runWhenIdle() {
|
|
444
|
+
if (document.hidden || idle !== null) return;
|
|
445
|
+
// `requestIdleCallback` where it exists, a timeout where it does not
|
|
446
|
+
// (Safari). The timeout option matters: without it a busy page can defer
|
|
447
|
+
// an idle callback indefinitely, which is the one outcome worse than
|
|
448
|
+
// scanning at a bad moment.
|
|
449
|
+
if (typeof window.requestIdleCallback === "function") {
|
|
450
|
+
idle = window.requestIdleCallback(function () {
|
|
451
|
+
idle = null;
|
|
452
|
+
scan();
|
|
453
|
+
}, {
|
|
454
|
+
timeout: 1000
|
|
455
|
+
});
|
|
456
|
+
} else {
|
|
457
|
+
idle = window.setTimeout(function () {
|
|
458
|
+
idle = null;
|
|
459
|
+
scan();
|
|
460
|
+
}, 0);
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
var interval = window.setInterval(runWhenIdle, RESCAN_INTERVAL_MS);
|
|
464
|
+
return function () {
|
|
465
|
+
window.clearInterval(interval);
|
|
466
|
+
if (idle === null) return;
|
|
467
|
+
if (typeof window.cancelIdleCallback === "function") {
|
|
468
|
+
window.cancelIdleCallback(idle);
|
|
469
|
+
} else {
|
|
470
|
+
window.clearTimeout(idle);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}, [scan]);
|
|
474
|
+
|
|
475
|
+
// What the studio asks for.
|
|
476
|
+
React__default["default"].useEffect(function () {
|
|
477
|
+
var listener = function listener(event) {
|
|
478
|
+
if (!client.isValCanvasStudioMessage(event.data)) return;
|
|
479
|
+
var message = event.data;
|
|
480
|
+
if (message.type === "rescan") {
|
|
481
|
+
scan();
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (message.type === "setPicking") {
|
|
485
|
+
setPicking(message.picking);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (message.type === "sourceUpdate") {
|
|
489
|
+
/**
|
|
490
|
+
* Handed straight to the listener the page already has.
|
|
491
|
+
*
|
|
492
|
+
* `ValTanStackProvider` listens for this event on its own window to make an
|
|
493
|
+
* inline edit visible behind the overlay; the only thing different here
|
|
494
|
+
* is that the edit was made in another window. Re-dispatching rather
|
|
495
|
+
* than reaching into the store keeps one path into the page — including
|
|
496
|
+
* the `router.invalidate()` it throttles behind it, which is what brings a
|
|
497
|
+
* server component's own re-read across.
|
|
498
|
+
*/
|
|
499
|
+
window.dispatchEvent(new CustomEvent("val-event", {
|
|
500
|
+
detail: {
|
|
501
|
+
type: "source-update",
|
|
502
|
+
moduleFilePath: message.moduleFilePath,
|
|
503
|
+
source: message.source
|
|
504
|
+
}
|
|
505
|
+
}));
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (message.type === "sourcesSynced") {
|
|
509
|
+
// Relayed the same way and for the same reason as `sourceUpdate`: the
|
|
510
|
+
// page is listening on its own window, and the studio is in another.
|
|
511
|
+
window.dispatchEvent(new CustomEvent("val-event", {
|
|
512
|
+
detail: {
|
|
513
|
+
type: "sources-synced"
|
|
514
|
+
}
|
|
515
|
+
}));
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
setHighlighted(message.path);
|
|
519
|
+
if (message.path !== null && message.scrollIntoView) {
|
|
520
|
+
var target = findByPath(message.path);
|
|
521
|
+
// NOT `scrollIntoView` — see `scrollWithinPage`.
|
|
522
|
+
if (target !== null) scrollWithinPage(target);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
window.addEventListener("message", listener);
|
|
526
|
+
return function () {
|
|
527
|
+
return window.removeEventListener("message", listener);
|
|
528
|
+
};
|
|
529
|
+
}, [scan]);
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Picking.
|
|
533
|
+
*
|
|
534
|
+
* Capture phase, and the event is stopped: the point of picking mode is that
|
|
535
|
+
* a click means "this one" rather than what the page would do with it, and a
|
|
536
|
+
* link that navigates has taken the canvas somewhere else before the studio
|
|
537
|
+
* hears anything.
|
|
538
|
+
*/
|
|
539
|
+
React__default["default"].useEffect(function () {
|
|
540
|
+
var onClick = function onClick(event) {
|
|
541
|
+
if (!pickingRef.current) return;
|
|
542
|
+
var target = event.target;
|
|
543
|
+
if (!(target instanceof Element)) return;
|
|
544
|
+
var tagged = target.closest("[data-val-path]");
|
|
545
|
+
var attribute = tagged === null || tagged === void 0 ? void 0 : tagged.getAttribute("data-val-path");
|
|
546
|
+
if (!attribute) return;
|
|
547
|
+
event.preventDefault();
|
|
548
|
+
event.stopPropagation();
|
|
549
|
+
post({
|
|
550
|
+
val: client.VAL_CANVAS_MESSAGE,
|
|
551
|
+
type: "clicked",
|
|
552
|
+
paths: core.Internal.splitJoinedSourcePaths(attribute)
|
|
553
|
+
});
|
|
554
|
+
};
|
|
555
|
+
document.addEventListener("click", onClick, {
|
|
556
|
+
capture: true
|
|
557
|
+
});
|
|
558
|
+
return function () {
|
|
559
|
+
return document.removeEventListener("click", onClick, {
|
|
560
|
+
capture: true
|
|
561
|
+
});
|
|
562
|
+
};
|
|
563
|
+
}, [post]);
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Two-finger gestures, handed to the studio.
|
|
567
|
+
*
|
|
568
|
+
* The window around the page is the studio's, and the page is a frame: a
|
|
569
|
+
* finger that lands here is this document's and the studio never hears about
|
|
570
|
+
* it. So a pinch on the page could not zoom the window, which on a phone is
|
|
571
|
+
* the only zoom there is — the toolbar's + and - are a poor substitute for the
|
|
572
|
+
* gesture everyone already makes.
|
|
573
|
+
*
|
|
574
|
+
* Two fingers and only two. One finger is the page's: it scrolls it, taps its
|
|
575
|
+
* links, drags whatever the page put there. Two are the window's, for zooming
|
|
576
|
+
* and for moving the page around inside it. Nothing has to be moded, and the
|
|
577
|
+
* two cannot be confused for one another.
|
|
578
|
+
*
|
|
579
|
+
* `passive: false` because both handlers cancel: the default for two fingers
|
|
580
|
+
* is the browser's own page zoom, which inside a frame the studio is already
|
|
581
|
+
* scaling produces two zooms fighting over one gesture.
|
|
582
|
+
*/
|
|
583
|
+
React__default["default"].useEffect(function () {
|
|
584
|
+
var midpoint = function midpoint(touches) {
|
|
585
|
+
return {
|
|
586
|
+
x: (touches[0].clientX + touches[1].clientX) / 2,
|
|
587
|
+
y: (touches[0].clientY + touches[1].clientY) / 2
|
|
588
|
+
};
|
|
589
|
+
};
|
|
590
|
+
var span = function span(touches) {
|
|
591
|
+
return Math.hypot(touches[0].clientX - touches[1].clientX, touches[0].clientY - touches[1].clientY);
|
|
592
|
+
};
|
|
593
|
+
/**
|
|
594
|
+
* Whether a gesture is in progress.
|
|
595
|
+
*
|
|
596
|
+
* `touchend` fires per finger, so lifting one of two leaves a `touchend`
|
|
597
|
+
* carrying a single touch — which is the end of the gesture — and the
|
|
598
|
+
* second leaves one carrying none. Without this the second would post a
|
|
599
|
+
* second `end` for a gesture that was already over.
|
|
600
|
+
*/
|
|
601
|
+
var active = false;
|
|
602
|
+
var onStart = function onStart(event) {
|
|
603
|
+
if (event.touches.length !== 2) return;
|
|
604
|
+
event.preventDefault();
|
|
605
|
+
active = true;
|
|
606
|
+
post({
|
|
607
|
+
val: client.VAL_CANVAS_MESSAGE,
|
|
608
|
+
type: "pinch",
|
|
609
|
+
phase: "start",
|
|
610
|
+
span: span(event.touches),
|
|
611
|
+
center: midpoint(event.touches)
|
|
612
|
+
});
|
|
613
|
+
};
|
|
614
|
+
var onMove = function onMove(event) {
|
|
615
|
+
if (event.touches.length !== 2 || !active) return;
|
|
616
|
+
event.preventDefault();
|
|
617
|
+
post({
|
|
618
|
+
val: client.VAL_CANVAS_MESSAGE,
|
|
619
|
+
type: "pinch",
|
|
620
|
+
phase: "move",
|
|
621
|
+
span: span(event.touches),
|
|
622
|
+
center: midpoint(event.touches)
|
|
623
|
+
});
|
|
624
|
+
};
|
|
625
|
+
var onEnd = function onEnd(event) {
|
|
626
|
+
if (!active || event.touches.length >= 2) return;
|
|
627
|
+
active = false;
|
|
628
|
+
post({
|
|
629
|
+
val: client.VAL_CANVAS_MESSAGE,
|
|
630
|
+
type: "pinch",
|
|
631
|
+
phase: "end",
|
|
632
|
+
span: 0,
|
|
633
|
+
center: {
|
|
634
|
+
x: 0,
|
|
635
|
+
y: 0
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
};
|
|
639
|
+
document.addEventListener("touchstart", onStart, {
|
|
640
|
+
passive: false
|
|
641
|
+
});
|
|
642
|
+
document.addEventListener("touchmove", onMove, {
|
|
643
|
+
passive: false
|
|
644
|
+
});
|
|
645
|
+
document.addEventListener("touchend", onEnd);
|
|
646
|
+
document.addEventListener("touchcancel", onEnd);
|
|
647
|
+
return function () {
|
|
648
|
+
document.removeEventListener("touchstart", onStart);
|
|
649
|
+
document.removeEventListener("touchmove", onMove);
|
|
650
|
+
document.removeEventListener("touchend", onEnd);
|
|
651
|
+
document.removeEventListener("touchcancel", onEnd);
|
|
652
|
+
};
|
|
653
|
+
}, [post]);
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* And ctrl/cmd + wheel, which is what a trackpad pinch reports as.
|
|
657
|
+
*
|
|
658
|
+
* The same problem as touches and the same answer: the wheel event lands on
|
|
659
|
+
* this document, so without relaying it a trackpad pinch over the page does
|
|
660
|
+
* nothing while the identical gesture over the canvas background zooms.
|
|
661
|
+
*
|
|
662
|
+
* A PLAIN wheel is deliberately left alone — that is the page scrolling
|
|
663
|
+
* itself, which is exactly what it should do.
|
|
664
|
+
*/
|
|
665
|
+
React__default["default"].useEffect(function () {
|
|
666
|
+
var onWheel = function onWheel(event) {
|
|
667
|
+
if (!event.ctrlKey && !event.metaKey) return;
|
|
668
|
+
event.preventDefault();
|
|
669
|
+
post({
|
|
670
|
+
val: client.VAL_CANVAS_MESSAGE,
|
|
671
|
+
type: "zoom",
|
|
672
|
+
// The same response curve the studio uses for a wheel on its own
|
|
673
|
+
// background, so the gesture feels the same on both sides of the frame.
|
|
674
|
+
factor: 1 - event.deltaY / 300,
|
|
675
|
+
center: {
|
|
676
|
+
x: event.clientX,
|
|
677
|
+
y: event.clientY
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
};
|
|
681
|
+
document.addEventListener("wheel", onWheel, {
|
|
682
|
+
passive: false
|
|
683
|
+
});
|
|
684
|
+
return function () {
|
|
685
|
+
return document.removeEventListener("wheel", onWheel);
|
|
686
|
+
};
|
|
687
|
+
}, [post]);
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* The outlines, as a stylesheet rather than as injected elements.
|
|
691
|
+
*
|
|
692
|
+
* A style rule cannot disturb the page's layout, which an absolutely
|
|
693
|
+
* positioned box on top of it eventually would — and the whole value of
|
|
694
|
+
* showing the real page is that it is laid out the way the customer built
|
|
695
|
+
* it. `outline` rather than `border` for the same reason: outlines do not
|
|
696
|
+
* take up space.
|
|
697
|
+
*/
|
|
698
|
+
return /*#__PURE__*/jsxRuntime.jsx("style", {
|
|
699
|
+
// The attribute selector is built from the path, so it is escaped as a
|
|
700
|
+
// CSS string. A path contains quotes (`?p="/"`) and would otherwise
|
|
701
|
+
// close the selector early.
|
|
702
|
+
dangerouslySetInnerHTML: {
|
|
703
|
+
__html: [picking ? "[data-val-path] { outline: 1px solid ".concat(SELECTION_SOFT, "; outline-offset: 1px; cursor: pointer; }\n[data-val-path]:hover { outline: 2px solid ").concat(SELECTION, "; }") : "", highlighted ? highlightRule(highlighted) : ""].filter(Boolean).join("\n")
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* A rule that outlines exactly the elements carrying `path`.
|
|
710
|
+
*
|
|
711
|
+
* The attribute holds a comma-separated list, so this cannot be one `=`
|
|
712
|
+
* comparison; and it cannot be a `*=` substring test either, because a path is
|
|
713
|
+
* a prefix of every path below it. `?p="/blogs/blog1"` is contained in
|
|
714
|
+
* `?p="/blogs/blog1"."title"`, so a substring test on the route would outline
|
|
715
|
+
* every field on the page — which is what it did.
|
|
716
|
+
*
|
|
717
|
+
* Four selectors instead: the whole attribute, the first item, the last item,
|
|
718
|
+
* and one in the middle. Between them they match the path as a complete list
|
|
719
|
+
* item and nothing else.
|
|
720
|
+
*/
|
|
721
|
+
function highlightRule(path) {
|
|
722
|
+
var value = JSON.stringify(path);
|
|
723
|
+
var inner = JSON.stringify(",".concat(path, ","));
|
|
724
|
+
var first = JSON.stringify("".concat(path, ","));
|
|
725
|
+
var last = JSON.stringify(",".concat(path));
|
|
726
|
+
var selectors = ["[data-val-path=".concat(value, "]"), "[data-val-path^=".concat(first, "]"), "[data-val-path$=".concat(last, "]"), "[data-val-path*=".concat(inner, "]")].join(",\n");
|
|
727
|
+
return "".concat(selectors, " { outline: 2px solid ").concat(SELECTION, "; outline-offset: 1px; }");
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Bring an element into view WITHOUT scrolling anything outside this page.
|
|
732
|
+
*
|
|
733
|
+
* `Element.scrollIntoView` would be the obvious thing, and it is the thing that
|
|
734
|
+
* broke the studio. It scrolls every scroll container between the element and
|
|
735
|
+
* the viewport, and for a same-origin frame that chain does not stop at the
|
|
736
|
+
* frame: it continues into the embedder and scrolls the studio's own containers
|
|
737
|
+
* too. Measured in Chromium — with the studio's phone panes placed on the
|
|
738
|
+
* editor, a single `scrollIntoView` in here pulls them back onto the canvas, and
|
|
739
|
+
* because it reveals the element rather than the frame it can leave them
|
|
740
|
+
* anywhere in between, showing half of each.
|
|
741
|
+
*
|
|
742
|
+
* That is not a thing the page has any business doing. It was asked to show one
|
|
743
|
+
* of its own elements; where the studio is looking is the studio's affair. So
|
|
744
|
+
* the scrolling is done by hand, one scrollport at a time, and the walk stops at
|
|
745
|
+
* this document.
|
|
746
|
+
*
|
|
747
|
+
* Only the document centres the target vertically — it is being pointed out,
|
|
748
|
+
* and something flush against the top edge of the fold does not read as pointed
|
|
749
|
+
* out. Every scrollport inside the page is moved as little as it takes to bring
|
|
750
|
+
* the target inside it: a carousel, a sticky sidebar or a sideways-scrolling
|
|
751
|
+
* table is showing what it is showing on purpose, and re-centring one that did
|
|
752
|
+
* not need moving is the page rearranging itself around a highlight.
|
|
753
|
+
*/
|
|
754
|
+
function scrollWithinPage(target) {
|
|
755
|
+
var node = target.parentElement;
|
|
756
|
+
while (node !== null) {
|
|
757
|
+
var isRoot = node === document.body || node === document.documentElement;
|
|
758
|
+
if (isRoot) break;
|
|
759
|
+
if (isScrollable(node)) {
|
|
760
|
+
var port = node.getBoundingClientRect();
|
|
761
|
+
var _box = target.getBoundingClientRect();
|
|
762
|
+
node.scrollTop += delta(_box.top - port.top, _box.height, node.clientHeight, false);
|
|
763
|
+
node.scrollLeft += delta(_box.left - port.left, _box.width, node.clientWidth, false);
|
|
764
|
+
}
|
|
765
|
+
node = node.parentElement;
|
|
766
|
+
}
|
|
767
|
+
// And this document, which is the last scrollport there is as far as the page
|
|
768
|
+
// is concerned. `window`, not `documentElement.scrollIntoView`-by-another-name:
|
|
769
|
+
// this cannot reach past the frame.
|
|
770
|
+
var box = target.getBoundingClientRect();
|
|
771
|
+
window.scrollTo({
|
|
772
|
+
top: Math.max(0, window.scrollY + delta(box.top, box.height, window.innerHeight, true)),
|
|
773
|
+
left: Math.max(0, window.scrollX + delta(box.left, box.width, window.innerWidth, false)),
|
|
774
|
+
behavior: "smooth"
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* How far a scrollport has to move for `size` at `offset` to be shown.
|
|
780
|
+
*
|
|
781
|
+
* `center` asks for the middle of the port; without it the answer is zero
|
|
782
|
+
* whenever the thing is already inside, which is what keeps a scrollport that
|
|
783
|
+
* did not need moving from being disturbed.
|
|
784
|
+
*/
|
|
785
|
+
function delta(offset, size, port, center) {
|
|
786
|
+
if (center) return offset - (port - size) / 2;
|
|
787
|
+
if (offset < 0) return offset;
|
|
788
|
+
if (offset + size > port) return Math.min(offset, offset + size - port);
|
|
789
|
+
return 0;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
/** Whether this element scrolls its own content, rather than growing with it. */
|
|
793
|
+
function isScrollable(el) {
|
|
794
|
+
var style = getComputedStyle(el);
|
|
795
|
+
var scrolls = function scrolls(value) {
|
|
796
|
+
return value === "auto" || value === "scroll" || value === "overlay";
|
|
797
|
+
};
|
|
798
|
+
return scrolls(style.overflowY) && el.scrollHeight > el.clientHeight || scrolls(style.overflowX) && el.scrollWidth > el.clientWidth;
|
|
799
|
+
}
|
|
800
|
+
function findByPath(path) {
|
|
801
|
+
var elements = document.querySelectorAll("[data-val-path]");
|
|
802
|
+
for (var _i = 0, _Array$from = Array.from(elements); _i < _Array$from.length; _i++) {
|
|
803
|
+
var el = _Array$from[_i];
|
|
804
|
+
var attribute = el.getAttribute("data-val-path");
|
|
805
|
+
if (!attribute) continue;
|
|
806
|
+
// Compared as strings: the split returns branded `SourcePath`s and what
|
|
807
|
+
// came back over `postMessage` is a plain string, which is the same value.
|
|
808
|
+
if (core.Internal.splitJoinedSourcePaths(attribute).some(function (candidate) {
|
|
809
|
+
return candidate === path;
|
|
810
|
+
})) {
|
|
811
|
+
return el;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return null;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Whether the page should re-read the server on its own, right now.
|
|
819
|
+
*
|
|
820
|
+
* The page cannot know when its own edit has been persisted. An edit is applied
|
|
821
|
+
* to the client store immediately and written to the server asynchronously, so
|
|
822
|
+
* a `router.invalidate()` fired on the edit can reach a server that has not saved
|
|
823
|
+
* the patch yet: the payload comes back with the old content, and nothing
|
|
824
|
+
* schedules another look. What an editor sees is a canvas that flickered and did
|
|
825
|
+
* not change — which is indistinguishable from an edit that did not work.
|
|
826
|
+
*
|
|
827
|
+
* So the page asks again a little later. This is the "should I bother" half of
|
|
828
|
+
* that, kept out of the effect and pure so the reasons NOT to are checkable:
|
|
829
|
+
* every one of them is a whole-route request avoided, and in development Next
|
|
830
|
+
* re-renders (and sometimes recompiles) the page for each one.
|
|
831
|
+
*/
|
|
832
|
+
function shouldSafetyRefresh(state) {
|
|
833
|
+
// Never edited: a preview someone is only looking at has nothing to catch up
|
|
834
|
+
// on, and polling it would cost a route render per tick for nothing.
|
|
835
|
+
if (state.lastEditAt === 0) return false;
|
|
836
|
+
// The editing stopped long enough ago that anything in flight has landed.
|
|
837
|
+
if (state.now - state.lastEditAt > state.windowMs) return false;
|
|
838
|
+
// Nobody is looking. The caller refreshes once on the way back instead.
|
|
839
|
+
if (state.hidden) return false;
|
|
840
|
+
// Already on its way. Stacking whole-route requests makes the slowness this
|
|
841
|
+
// exists to paper over worse, not better.
|
|
842
|
+
if (state.isRefreshing) return false;
|
|
843
|
+
// One just went out — usually the edit-driven refresh, a moment ago.
|
|
844
|
+
if (state.now - state.lastRefreshAt < state.minIntervalMs) return false;
|
|
845
|
+
return true;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
var REFRESH_INTERVAL_MS = 500;
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* How often the page re-reads the server on its own, without being told to.
|
|
852
|
+
*
|
|
853
|
+
* A safety net for a race no amount of care on this side can close. An edit is
|
|
854
|
+
* applied to the client store immediately and saved to the server
|
|
855
|
+
* asynchronously, so `router.invalidate()` fired on the edit can reach a server
|
|
856
|
+
* that has not written the patch yet: the loaders come back with the old
|
|
857
|
+
* content, the counter has already been cleared, and nothing schedules another
|
|
858
|
+
* look. What you see is a canvas that flickered and did not change — which is
|
|
859
|
+
* indistinguishable from an edit that did not work.
|
|
860
|
+
*
|
|
861
|
+
* The honest fix would be for the page to know when its patch has been
|
|
862
|
+
* persisted, and it has no way to ask. So it asks again a little later instead.
|
|
863
|
+
*/
|
|
864
|
+
var SAFETY_REFRESH_MS = 10000;
|
|
865
|
+
|
|
866
|
+
/**
|
|
867
|
+
* How long after an edit the safety net keeps looking.
|
|
868
|
+
*
|
|
869
|
+
* The reason to stop is that this re-runs every loader on the page: in
|
|
870
|
+
* development that is a server round trip and a recompile, and a preview
|
|
871
|
+
* nobody is editing has nothing to catch up on. So the net is armed by editing
|
|
872
|
+
* and disarms itself when the editing stops — long enough after the last
|
|
873
|
+
* keystroke that any write still in flight has certainly landed.
|
|
874
|
+
*/
|
|
875
|
+
var SAFETY_REFRESH_WINDOW_MS = 30000;
|
|
876
|
+
|
|
877
|
+
/**
|
|
878
|
+
* Fired on this window when an edit has been applied to the client store.
|
|
879
|
+
*
|
|
880
|
+
* The refresh loop listens for it so it can start the round trip immediately
|
|
881
|
+
* rather than on its next tick. A DOM event rather than state, because the
|
|
882
|
+
* counter it accompanies is a ref and the loop lives in an effect that must not
|
|
883
|
+
* be torn down and rebuilt per keystroke.
|
|
884
|
+
*/
|
|
885
|
+
var VAL_EDIT_LANDED = "val-edit-landed";
|
|
886
|
+
|
|
887
|
+
/**
|
|
888
|
+
* How often `/draft/stat` is asked when nothing is in progress.
|
|
889
|
+
*
|
|
890
|
+
* Draft mode changes when someone toggles it, which the handshake below already
|
|
891
|
+
* covers — so this is only there to notice it being changed somewhere else.
|
|
892
|
+
*/
|
|
893
|
+
var DRAFT_IDLE_POLL_MS = 20000;
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* And how often while the enable/disable handshake is in flight.
|
|
897
|
+
*
|
|
898
|
+
* Fast on purpose: the hidden iframe redirects through `/draft/enable`, and the
|
|
899
|
+
* page cannot do anything useful until that has landed.
|
|
900
|
+
*/
|
|
901
|
+
var DRAFT_HANDSHAKE_POLL_MS = 100;
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* How long that fast phase may last before it gives up.
|
|
905
|
+
*
|
|
906
|
+
* Without a deadline it does not end: the only thing that clears `iframeSrc` is
|
|
907
|
+
* a `val-ready` message posted by the redirect target, so a frame that fails to
|
|
908
|
+
* load — a 500, an auth redirect, a dev server still compiling — left the page
|
|
909
|
+
* asking ten times a second, forever. A browser runs about six connections per
|
|
910
|
+
* origin, so that is enough to push `/stat`, `/patches` and the canvas document
|
|
911
|
+
* itself into the browser's own queue, where they are indistinguishable from a
|
|
912
|
+
* slow server.
|
|
913
|
+
*
|
|
914
|
+
* Generous enough for a first compile in the dev server, which is the slowest case
|
|
915
|
+
* that is not a fault.
|
|
916
|
+
*/
|
|
917
|
+
var DRAFT_HANDSHAKE_TIMEOUT_MS = 10000;
|
|
918
|
+
var ValTanStackProvider = function ValTanStackProvider(props) {
|
|
919
|
+
var _draftModeReady$curre2;
|
|
920
|
+
// TODO: use config:
|
|
921
|
+
var route = "/api/val";
|
|
922
|
+
/**
|
|
923
|
+
* Loaded on first use, never at import time.
|
|
924
|
+
*
|
|
925
|
+
* `createValClient` validates every request and response against the zod
|
|
926
|
+
* schemas in `@valbuild/shared/internal`'s `ApiRoutes`, and preconstruct
|
|
927
|
+
* publishes that entrypoint as one module - so importing it statically put
|
|
928
|
+
* ~113 KB of zod in the bundle of every page of every Val site. The only
|
|
929
|
+
* caller is the draft-mode poll below, which returns early unless
|
|
930
|
+
* `mountOverlay` is set, so a visitor without the Val Enable cookie never
|
|
931
|
+
* triggers this import and never downloads that chunk.
|
|
932
|
+
*/
|
|
933
|
+
var clientRef = React__default["default"].useRef(null);
|
|
934
|
+
var configRef = React__default["default"].useRef(props.config);
|
|
935
|
+
configRef.current = props.config;
|
|
936
|
+
var getClient = React__default["default"].useCallback(/*#__PURE__*/version._asyncToGenerator(/*#__PURE__*/version._regenerator().m(function _callee() {
|
|
937
|
+
var _yield$import, createValClient;
|
|
938
|
+
return version._regenerator().w(function (_context) {
|
|
939
|
+
while (1) switch (_context.n) {
|
|
940
|
+
case 0:
|
|
941
|
+
if (clientRef.current) {
|
|
942
|
+
_context.n = 2;
|
|
943
|
+
break;
|
|
944
|
+
}
|
|
945
|
+
_context.n = 1;
|
|
946
|
+
return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('@valbuild/shared/internal')); });
|
|
947
|
+
case 1:
|
|
948
|
+
_yield$import = _context.v;
|
|
949
|
+
createValClient = _yield$import.createValClient;
|
|
950
|
+
clientRef.current = createValClient(route, createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, configRef.current), {}, {
|
|
951
|
+
contentHostUrl: core.DEFAULT_CONTENT_HOST
|
|
952
|
+
}));
|
|
953
|
+
case 2:
|
|
954
|
+
return _context.a(2, clientRef.current);
|
|
955
|
+
}
|
|
956
|
+
}, _callee);
|
|
957
|
+
})), [route]);
|
|
958
|
+
|
|
959
|
+
// TODO: move below into react package
|
|
960
|
+
var valStore = React__default["default"].useMemo(function () {
|
|
961
|
+
return new ValOverlayContext.ValExternalStore();
|
|
962
|
+
}, []);
|
|
963
|
+
// Whether useValStega should actually suspend. False during SSR and the
|
|
964
|
+
// hydration render — the server store is never populated (draft data
|
|
965
|
+
// arrives via browser CustomEvents only), so suspending there would just
|
|
966
|
+
// stall into the waitForLoad timeout, and hydration must render the static
|
|
967
|
+
// source so it matches the server HTML exactly. Activated post-hydration
|
|
968
|
+
// (in an effect, only when the Val Enable cookie is present) inside a
|
|
969
|
+
// transition: React keeps the static content visible while hooks suspend
|
|
970
|
+
// and then swaps to draft data as a normal update — no Suspense fallback
|
|
971
|
+
// flash and no hydration mismatch.
|
|
972
|
+
var _React$useState = React__default["default"].useState(false),
|
|
973
|
+
_React$useState2 = createForOfIteratorHelper._slicedToArray(_React$useState, 2),
|
|
974
|
+
suspendActive = _React$useState2[0],
|
|
975
|
+
setSuspendActive = _React$useState2[1];
|
|
976
|
+
/**
|
|
977
|
+
* Whether the studio has said it sent everything it holds.
|
|
978
|
+
*
|
|
979
|
+
* Until then, a module missing from the store might still be on its way. After
|
|
980
|
+
* it, a missing module simply has no draft — see `sourcesSynced` in the canvas
|
|
981
|
+
* protocol. Without this the page could only wait out `waitForLoad`'s timeout,
|
|
982
|
+
* ten seconds per module nobody had edited.
|
|
983
|
+
*/
|
|
984
|
+
var _React$useState3 = React__default["default"].useState(false),
|
|
985
|
+
_React$useState4 = createForOfIteratorHelper._slicedToArray(_React$useState3, 2),
|
|
986
|
+
draftSourcesSynced = _React$useState4[0],
|
|
987
|
+
setDraftSourcesSynced = _React$useState4[1];
|
|
988
|
+
var _React$useState5 = React__default["default"].useState(),
|
|
989
|
+
_React$useState6 = createForOfIteratorHelper._slicedToArray(_React$useState5, 2),
|
|
990
|
+
mountOverlay = _React$useState6[0],
|
|
991
|
+
setMountOverlay = _React$useState6[1];
|
|
992
|
+
/**
|
|
993
|
+
* Whether this document is the studio's canvas frame.
|
|
994
|
+
*
|
|
995
|
+
* The canvas needs everything `mountOverlay` turns on — draft content,
|
|
996
|
+
* `data-val-path` on every tracked element, the refresh loop that brings an
|
|
997
|
+
* edit across — and none of what it *shows*, because the studio around the
|
|
998
|
+
* frame is already that UI. So it is a second flag rather than a different
|
|
999
|
+
* value of the first one: two overlays on one screen, one of them inside the
|
|
1000
|
+
* other, is the thing to avoid.
|
|
1001
|
+
*
|
|
1002
|
+
* Read once, in the same effect as `mountOverlay`: it is a property of how
|
|
1003
|
+
* the document was loaded, and it cannot change without a navigation.
|
|
1004
|
+
*/
|
|
1005
|
+
var _React$useState7 = React__default["default"].useState(false),
|
|
1006
|
+
_React$useState8 = createForOfIteratorHelper._slicedToArray(_React$useState7, 2),
|
|
1007
|
+
isCanvas = _React$useState8[0],
|
|
1008
|
+
setIsCanvas = _React$useState8[1];
|
|
1009
|
+
var _React$useState9 = React__default["default"].useState(null),
|
|
1010
|
+
_React$useState0 = createForOfIteratorHelper._slicedToArray(_React$useState9, 2),
|
|
1011
|
+
draftMode = _React$useState0[0],
|
|
1012
|
+
setDraftMode = _React$useState0[1];
|
|
1013
|
+
/**
|
|
1014
|
+
* Resolves when `draftMode` stops being unknown.
|
|
1015
|
+
*
|
|
1016
|
+
* `null` is the state before `/draft/stat` has answered, and it is NOT a
|
|
1017
|
+
* synonym for "off": the reader that turns a selector into content treats it
|
|
1018
|
+
* as off, so a render that happens while draft mode is still unknown resolves
|
|
1019
|
+
* against committed source. For a route that only exists in an uncommitted
|
|
1020
|
+
* patch that means `notFound()` — terminal, before the answer even arrives.
|
|
1021
|
+
*
|
|
1022
|
+
* So a page that is going to suspend waits for the answer first. A promise
|
|
1023
|
+
* rather than a re-render, because the thing that has to wait is a render.
|
|
1024
|
+
*/
|
|
1025
|
+
var draftModeReady = React__default["default"].useRef(null);
|
|
1026
|
+
// Only for a page that opted in. A visitor with Val off never reads it, and
|
|
1027
|
+
// an object created per mount for nobody is the kind of cost that is invisible
|
|
1028
|
+
// until it is in every page of every app.
|
|
1029
|
+
if (props.suspend && draftModeReady.current === null) {
|
|
1030
|
+
var resolve = function resolve() {
|
|
1031
|
+
return undefined;
|
|
1032
|
+
};
|
|
1033
|
+
var promise = new Promise(function (r) {
|
|
1034
|
+
resolve = r;
|
|
1035
|
+
});
|
|
1036
|
+
draftModeReady.current = {
|
|
1037
|
+
promise: promise,
|
|
1038
|
+
resolve: resolve
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
React__default["default"].useEffect(function () {
|
|
1042
|
+
if (draftMode !== null) {
|
|
1043
|
+
var _draftModeReady$curre;
|
|
1044
|
+
(_draftModeReady$curre = draftModeReady.current) === null || _draftModeReady$curre === void 0 || _draftModeReady$curre.resolve();
|
|
1045
|
+
}
|
|
1046
|
+
}, [draftMode]);
|
|
1047
|
+
var _React$useState1 = React__default["default"].useState(false),
|
|
1048
|
+
_React$useState10 = createForOfIteratorHelper._slicedToArray(_React$useState1, 2),
|
|
1049
|
+
spaReady = _React$useState10[0],
|
|
1050
|
+
setSpaReady = _React$useState10[1]; // TODO: consider removing spaReady - it is not used? If we remove, clean up the custom events that send the message too...
|
|
1051
|
+
var router = reactRouter.useRouter();
|
|
1052
|
+
var _React$useTransition = React__default["default"].useTransition(),
|
|
1053
|
+
_React$useTransition2 = createForOfIteratorHelper._slicedToArray(_React$useTransition, 2),
|
|
1054
|
+
isRefreshing = _React$useTransition2[0],
|
|
1055
|
+
startTransition = _React$useTransition2[1];
|
|
1056
|
+
/**
|
|
1057
|
+
* The same answer as `isRefreshing`, readable from the refresh effect.
|
|
1058
|
+
*
|
|
1059
|
+
* The effect must not be rebuilt every time a refresh starts and ends — it
|
|
1060
|
+
* owns the timers and the "when did we last edit" bookkeeping, and tearing
|
|
1061
|
+
* that down mid-flurry loses it. A ref updated on render is the value without
|
|
1062
|
+
* the dependency.
|
|
1063
|
+
*/
|
|
1064
|
+
var isRefreshingRef = React__default["default"].useRef(false);
|
|
1065
|
+
var rerenderCounterRef = React__default["default"].useRef(0);
|
|
1066
|
+
var _React$useState11 = React__default["default"].useState(null),
|
|
1067
|
+
_React$useState12 = createForOfIteratorHelper._slicedToArray(_React$useState11, 2),
|
|
1068
|
+
iframeSrc = _React$useState12[0],
|
|
1069
|
+
setIframeSrc = _React$useState12[1];
|
|
1070
|
+
var pathname = reactRouter.useRouterState({
|
|
1071
|
+
select: function select(state) {
|
|
1072
|
+
return state.location.pathname;
|
|
1073
|
+
}
|
|
1074
|
+
});
|
|
1075
|
+
isRefreshingRef.current = isRefreshing;
|
|
1076
|
+
React.useEffect(function () {
|
|
1077
|
+
window.dispatchEvent(new CustomEvent("val-provider:pathname", {
|
|
1078
|
+
detail: pathname
|
|
1079
|
+
}));
|
|
1080
|
+
}, [pathname]);
|
|
1081
|
+
useConsoleLogEnableVal(mountOverlay);
|
|
1082
|
+
React__default["default"].useEffect(function () {
|
|
1083
|
+
if (location.search === "?message_onready=true") {
|
|
1084
|
+
console.warn("Val is verifying draft mode...");
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
if (isValStudioPath(location.pathname)) {
|
|
1088
|
+
setMountOverlay(false);
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
setIsCanvas(client.isValCanvasFrame(location.search));
|
|
1092
|
+
setMountOverlay(hasValEnableCookie(document.cookie));
|
|
1093
|
+
}, []);
|
|
1094
|
+
React__default["default"].useEffect(function () {
|
|
1095
|
+
// Activate the Suspense gate after hydration. Inside a transition so
|
|
1096
|
+
// already-visible (static) content stays on screen while useValStega
|
|
1097
|
+
// suspends — no fallback flash — and the swap to draft data commits as a
|
|
1098
|
+
// normal update instead of a hydration mismatch. Never deactivated:
|
|
1099
|
+
// components must not stop suspending across renders (the draft-mode-off
|
|
1100
|
+
// release valve lives in useValStega instead).
|
|
1101
|
+
/*
|
|
1102
|
+
* Gated on `mountOverlay`, not on the cookie directly — the ORDER is the
|
|
1103
|
+
* point.
|
|
1104
|
+
*
|
|
1105
|
+
* `useValStega` suspends on `draftModeReady`, and the only thing that ever
|
|
1106
|
+
* resolves that is the `/draft/stat` poll below, which does not start until
|
|
1107
|
+
* `mountOverlay` is set. Activating the gate in the same effect flush that
|
|
1108
|
+
* sets `mountOverlay` means the tree can suspend before that state has
|
|
1109
|
+
* committed, and then it never does: React was left re-rendering a
|
|
1110
|
+
* suspended tree in which `mountOverlay` is forever `undefined`, so the
|
|
1111
|
+
* poll never ran, so the promise never resolved. The page hydrated and then
|
|
1112
|
+
* simply stopped — no overlay, no draft content, no error.
|
|
1113
|
+
*
|
|
1114
|
+
* Reading `mountOverlay` instead makes the dependency explicit: the poll is
|
|
1115
|
+
* running by the time anything is allowed to wait on it.
|
|
1116
|
+
*/
|
|
1117
|
+
if (props.suspend && mountOverlay) {
|
|
1118
|
+
startTransition(function () {
|
|
1119
|
+
setSuspendActive(true);
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
}, [props.suspend, mountOverlay]);
|
|
1123
|
+
|
|
1124
|
+
/**
|
|
1125
|
+
* Re-render the server tree when an edit lands, then at most every 500ms.
|
|
1126
|
+
*
|
|
1127
|
+
* Leading edge, which is the whole point. This polled every 500ms and only
|
|
1128
|
+
* refreshed on the tick, so a keystroke waited up to half a second before the
|
|
1129
|
+
* round trip even began — pure dead time on top of a request that is already
|
|
1130
|
+
* the slow part in development. Refreshing on arrival and *then* enforcing the
|
|
1131
|
+
* gap keeps the same ceiling on requests while removing the wait.
|
|
1132
|
+
*
|
|
1133
|
+
* Still a counter rather than a boolean: edits that arrive during the gap have
|
|
1134
|
+
* to be picked up by the next refresh, and the count is what says whether
|
|
1135
|
+
* there are any.
|
|
1136
|
+
*/
|
|
1137
|
+
React__default["default"].useEffect(function () {
|
|
1138
|
+
if (!mountOverlay || props.disableRefresh) {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
var timer = null;
|
|
1142
|
+
var lastRefreshAt = 0;
|
|
1143
|
+
/** When an edit last landed here. 0 means this page has never been edited. */
|
|
1144
|
+
var lastEditAt = 0;
|
|
1145
|
+
/** Whether a safety tick was skipped because nobody was looking. */
|
|
1146
|
+
var missedWhileHidden = false;
|
|
1147
|
+
var refresh = function refresh() {
|
|
1148
|
+
rerenderCounterRef.current = 0;
|
|
1149
|
+
lastRefreshAt = Date.now();
|
|
1150
|
+
startTransition(function () {
|
|
1151
|
+
/*
|
|
1152
|
+
* Re-run the loaders of every match, then re-render.
|
|
1153
|
+
*
|
|
1154
|
+
* TanStack's equivalent of `router.refresh()`. `invalidate()` marks the
|
|
1155
|
+
* current matches stale and re-runs their loaders — which is where a
|
|
1156
|
+
* TanStack Start app reads Val content on the server — so this is the
|
|
1157
|
+
* round trip that brings an edit across. It resolves when the loaders
|
|
1158
|
+
* have settled, which is what keeps `isRefreshing` honest.
|
|
1159
|
+
*/
|
|
1160
|
+
void router.invalidate();
|
|
1161
|
+
});
|
|
1162
|
+
};
|
|
1163
|
+
var _tick = function tick() {
|
|
1164
|
+
timer = null;
|
|
1165
|
+
if (rerenderCounterRef.current === 0) return;
|
|
1166
|
+
var since = Date.now() - lastRefreshAt;
|
|
1167
|
+
if (since >= REFRESH_INTERVAL_MS) {
|
|
1168
|
+
refresh();
|
|
1169
|
+
} else {
|
|
1170
|
+
timer = setTimeout(_tick, REFRESH_INTERVAL_MS - since);
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
var onEdit = function onEdit() {
|
|
1174
|
+
lastEditAt = Date.now();
|
|
1175
|
+
if (timer !== null) return;
|
|
1176
|
+
timer = setTimeout(_tick, 0);
|
|
1177
|
+
};
|
|
1178
|
+
window.addEventListener(VAL_EDIT_LANDED, onEdit);
|
|
1179
|
+
// A backstop for the counter being bumped without the event — the two are
|
|
1180
|
+
// incremented in the same places today, but the poll is what made this
|
|
1181
|
+
// correct regardless of who bumped it.
|
|
1182
|
+
var interval = setInterval(_tick, REFRESH_INTERVAL_MS);
|
|
1183
|
+
|
|
1184
|
+
/**
|
|
1185
|
+
* The safety net: look again, whether or not anything told us to.
|
|
1186
|
+
*
|
|
1187
|
+
* The reasons not to are in `shouldSafetyRefresh`, where they can be tested.
|
|
1188
|
+
* `hidden` is the one this side has to remember something about: a skipped
|
|
1189
|
+
* tick is worth one look when the tab comes back.
|
|
1190
|
+
*/
|
|
1191
|
+
var safetyTick = function safetyTick() {
|
|
1192
|
+
if (document.hidden) {
|
|
1193
|
+
missedWhileHidden = true;
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
var go = shouldSafetyRefresh({
|
|
1197
|
+
now: Date.now(),
|
|
1198
|
+
lastEditAt: lastEditAt,
|
|
1199
|
+
lastRefreshAt: lastRefreshAt,
|
|
1200
|
+
hidden: false,
|
|
1201
|
+
isRefreshing: isRefreshingRef.current,
|
|
1202
|
+
windowMs: SAFETY_REFRESH_WINDOW_MS,
|
|
1203
|
+
minIntervalMs: REFRESH_INTERVAL_MS
|
|
1204
|
+
});
|
|
1205
|
+
if (go) refresh();
|
|
1206
|
+
};
|
|
1207
|
+
var safety = setInterval(safetyTick, SAFETY_REFRESH_MS);
|
|
1208
|
+
/**
|
|
1209
|
+
* Coming back to the tab is worth one look.
|
|
1210
|
+
*
|
|
1211
|
+
* Otherwise a background tab skips its ticks and then has to wait out a
|
|
1212
|
+
* whole interval on return — which is exactly the moment someone is looking
|
|
1213
|
+
* at the page to see whether their edit took.
|
|
1214
|
+
*/
|
|
1215
|
+
var onVisibilityChange = function onVisibilityChange() {
|
|
1216
|
+
if (document.hidden || !missedWhileHidden) return;
|
|
1217
|
+
missedWhileHidden = false;
|
|
1218
|
+
safetyTick();
|
|
1219
|
+
};
|
|
1220
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
1221
|
+
return function () {
|
|
1222
|
+
window.removeEventListener(VAL_EDIT_LANDED, onEdit);
|
|
1223
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
1224
|
+
clearInterval(interval);
|
|
1225
|
+
clearInterval(safety);
|
|
1226
|
+
if (timer !== null) clearTimeout(timer);
|
|
1227
|
+
};
|
|
1228
|
+
}, [mountOverlay, props.disableRefresh]);
|
|
1229
|
+
React__default["default"].useEffect(function () {
|
|
1230
|
+
if (!mountOverlay) {
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
if (draftMode === null) {
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
var valProviderOverlayListener = function valProviderOverlayListener(event) {
|
|
1237
|
+
if (event instanceof CustomEvent) {
|
|
1238
|
+
if (!(event !== null && event !== void 0 && event.detail.type)) {
|
|
1239
|
+
console.error("Val: invalid event detail (val-overlay-provider)", event);
|
|
1240
|
+
}
|
|
1241
|
+
if (event.detail.type === "spa-ready") {
|
|
1242
|
+
setSpaReady(true);
|
|
1243
|
+
} else if (event.detail.type === "draftMode" && (typeof event.detail.value === "boolean" || event.detail.value === null)) {
|
|
1244
|
+
var _draftMode = event.detail.value;
|
|
1245
|
+
if (_draftMode === true) {
|
|
1246
|
+
setIframeSrc(function (prev) {
|
|
1247
|
+
if (prev === null) {
|
|
1248
|
+
return "".concat(route, "/draft/enable?redirect_to=").concat(encodeURIComponent(window.location.origin + "/val?message_onready=true"));
|
|
1249
|
+
}
|
|
1250
|
+
return prev;
|
|
1251
|
+
});
|
|
1252
|
+
} else if (_draftMode === false) {
|
|
1253
|
+
setIframeSrc(function (prev) {
|
|
1254
|
+
if (prev === null) {
|
|
1255
|
+
return "".concat(route, "/draft/disable?redirect_to=").concat(encodeURIComponent(window.location.origin + "/val?message_onready=true"));
|
|
1256
|
+
}
|
|
1257
|
+
return prev;
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
} else {
|
|
1261
|
+
console.error("Val: invalid event detail (val-overlay-provider)", event.detail);
|
|
1262
|
+
}
|
|
1263
|
+
} else {
|
|
1264
|
+
console.error("Val: invalid event (val-overlay-provider)", event);
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
window.addEventListener("val-overlay-provider", valProviderOverlayListener);
|
|
1268
|
+
return function () {
|
|
1269
|
+
window.removeEventListener("val-overlay-provider", valProviderOverlayListener);
|
|
1270
|
+
};
|
|
1271
|
+
}, [mountOverlay, draftMode]);
|
|
1272
|
+
var pollDraftStatIdRef = React__default["default"].useRef(0);
|
|
1273
|
+
React__default["default"].useEffect(function () {
|
|
1274
|
+
// continuous polling to check for updates:
|
|
1275
|
+
|
|
1276
|
+
var timeout;
|
|
1277
|
+
function pollCurrentDraftMode() {
|
|
1278
|
+
if (!mountOverlay) {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
window.dispatchEvent(new CustomEvent("val-overlay-spa", {
|
|
1282
|
+
detail: {
|
|
1283
|
+
type: "draftModeLoading",
|
|
1284
|
+
value: iframeSrc !== null
|
|
1285
|
+
}
|
|
1286
|
+
}));
|
|
1287
|
+
var pollDraftStatId = ++pollDraftStatIdRef.current;
|
|
1288
|
+
getClient().then(function (client) {
|
|
1289
|
+
return client("/draft/stat", "GET", {});
|
|
1290
|
+
}).then(function (res) {
|
|
1291
|
+
if (pollDraftStatIdRef.current !== pollDraftStatId) {
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
if (res.status === null) {
|
|
1295
|
+
// ignore network errors
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
if (res.status === 401) {
|
|
1299
|
+
// Not authorized (e.g. stale Val Enable cookie after the session
|
|
1300
|
+
// expired): treat draft mode as off so useValStega's Suspense gate
|
|
1301
|
+
// is released instead of leaving draftMode stuck at null and
|
|
1302
|
+
// re-suspending into the waitForLoad timeout.
|
|
1303
|
+
setDraftMode(false);
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
if (res.status !== 200) {
|
|
1307
|
+
console.error("Val: could not get draft mode status", res);
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
setDraftMode(function (prev) {
|
|
1311
|
+
if (prev !== res.json.draftMode) {
|
|
1312
|
+
rerenderCounterRef.current++;
|
|
1313
|
+
window.dispatchEvent(new Event(VAL_EDIT_LANDED));
|
|
1314
|
+
return res.json.draftMode;
|
|
1315
|
+
}
|
|
1316
|
+
return prev;
|
|
1317
|
+
});
|
|
1318
|
+
})["catch"](function (err) {
|
|
1319
|
+
console.error("Val: could not get draft mode status", err);
|
|
1320
|
+
})["finally"](function () {
|
|
1321
|
+
if (pollDraftStatIdRef.current !== pollDraftStatId) {
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
pollDraftStatIdRef.current--;
|
|
1325
|
+
var handshaking = iframeSrc !== null;
|
|
1326
|
+
if (handshaking && Date.now() - startedAt > DRAFT_HANDSHAKE_TIMEOUT_MS) {
|
|
1327
|
+
/**
|
|
1328
|
+
* The handshake did not complete, so stop hammering.
|
|
1329
|
+
*
|
|
1330
|
+
* Clearing `iframeSrc` unmounts the hidden frame and takes the poll
|
|
1331
|
+
* back to its idle interval — which is the honest state: nothing is
|
|
1332
|
+
* in progress any more. Leaving it set is what made this
|
|
1333
|
+
* unrecoverable, because the ONLY other thing that clears it is the
|
|
1334
|
+
* `val-ready` message the frame never sent.
|
|
1335
|
+
*/
|
|
1336
|
+
console.warn("Val: draft mode did not confirm in time. Falling back to the " + "idle poll — try toggling preview again.");
|
|
1337
|
+
setIframeSrc(null);
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
timeout = setTimeout(pollCurrentDraftMode, handshaking ? DRAFT_HANDSHAKE_POLL_MS : DRAFT_IDLE_POLL_MS);
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* When this spell of polling began.
|
|
1345
|
+
*
|
|
1346
|
+
* The effect re-runs when `iframeSrc` changes, so for the fast phase this is
|
|
1347
|
+
* the moment the handshake started — which is what the deadline below is
|
|
1348
|
+
* measured from.
|
|
1349
|
+
*/
|
|
1350
|
+
var startedAt = Date.now();
|
|
1351
|
+
pollCurrentDraftMode();
|
|
1352
|
+
return function () {
|
|
1353
|
+
clearTimeout(timeout);
|
|
1354
|
+
};
|
|
1355
|
+
}, [mountOverlay, iframeSrc]);
|
|
1356
|
+
React__default["default"].useEffect(function () {
|
|
1357
|
+
if (!mountOverlay) {
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
window.dispatchEvent(new CustomEvent("val-overlay-spa", {
|
|
1361
|
+
detail: {
|
|
1362
|
+
type: "draftMode",
|
|
1363
|
+
value: draftMode !== null && draftMode !== void 0 ? draftMode : false
|
|
1364
|
+
}
|
|
1365
|
+
}));
|
|
1366
|
+
}, [mountOverlay, draftMode, spaReady]);
|
|
1367
|
+
React__default["default"].useEffect(function () {
|
|
1368
|
+
if (!mountOverlay) {
|
|
1369
|
+
stega.SET_AUTO_TAG_JSX_ENABLED(false);
|
|
1370
|
+
} else {
|
|
1371
|
+
if (draftMode) {
|
|
1372
|
+
stega.SET_AUTO_TAG_JSX_ENABLED(true);
|
|
1373
|
+
var reactServerComponentRefreshListener = function reactServerComponentRefreshListener(event) {
|
|
1374
|
+
if (event instanceof CustomEvent) {
|
|
1375
|
+
var _event$detail, _event$detail2;
|
|
1376
|
+
if (((_event$detail = event.detail) === null || _event$detail === void 0 ? void 0 : _event$detail.type) === "sources-synced") {
|
|
1377
|
+
setDraftSourcesSynced(true);
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
if (((_event$detail2 = event.detail) === null || _event$detail2 === void 0 ? void 0 : _event$detail2.type) === "source-update") {
|
|
1381
|
+
var _event$detail3, _event$detail4;
|
|
1382
|
+
var moduleFilePath = (_event$detail3 = event.detail) === null || _event$detail3 === void 0 ? void 0 : _event$detail3.moduleFilePath;
|
|
1383
|
+
var source = (_event$detail4 = event.detail) === null || _event$detail4 === void 0 ? void 0 : _event$detail4.source;
|
|
1384
|
+
if (typeof moduleFilePath === "string" && source !== undefined) {
|
|
1385
|
+
var changed = valStore.update(moduleFilePath, source);
|
|
1386
|
+
/**
|
|
1387
|
+
* Armed by a CHANGE, not by a message.
|
|
1388
|
+
*
|
|
1389
|
+
* The editor re-sends everything it holds whenever this page
|
|
1390
|
+
* becomes a new document — a reload, or the one the dev server does
|
|
1391
|
+
* when a publish rewrites the `.val.ts` files — and most of that
|
|
1392
|
+
* snapshot is content this page already has. Counting those
|
|
1393
|
+
* bought a whole-route request per publish whether or not the
|
|
1394
|
+
* page was out of date, which with auto-save on is one per pause
|
|
1395
|
+
* in typing.
|
|
1396
|
+
*
|
|
1397
|
+
* The catch-up that MATTERS still refreshes: a page whose server
|
|
1398
|
+
* render predates the write has no value for that module, so the
|
|
1399
|
+
* first arrival counts as a change.
|
|
1400
|
+
*/
|
|
1401
|
+
if (changed && !props.disableRefresh) {
|
|
1402
|
+
rerenderCounterRef.current++;
|
|
1403
|
+
window.dispatchEvent(new Event(VAL_EDIT_LANDED));
|
|
1404
|
+
}
|
|
1405
|
+
} else {
|
|
1406
|
+
console.error("Val: invalid event detail", event.detail);
|
|
1407
|
+
}
|
|
1408
|
+
} else {
|
|
1409
|
+
console.error("Val: invalid custom event details (val-event)", event.detail);
|
|
1410
|
+
}
|
|
1411
|
+
} else {
|
|
1412
|
+
console.error("Val: invalid custom event (val-event)", event);
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
window.addEventListener("val-event", reactServerComponentRefreshListener);
|
|
1416
|
+
return function () {
|
|
1417
|
+
window.removeEventListener("val-event", reactServerComponentRefreshListener);
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}, [mountOverlay, draftMode, props.disableRefresh]);
|
|
1422
|
+
React__default["default"].useEffect(function () {
|
|
1423
|
+
if (!mountOverlay) {
|
|
1424
|
+
return;
|
|
1425
|
+
}
|
|
1426
|
+
var listener = function listener(event) {
|
|
1427
|
+
if (event.origin === location.origin && event.data.type === "val-ready") {
|
|
1428
|
+
setIframeSrc(null);
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
window.addEventListener("message", listener);
|
|
1432
|
+
return function () {
|
|
1433
|
+
window.removeEventListener("message", listener);
|
|
1434
|
+
};
|
|
1435
|
+
}, [mountOverlay]);
|
|
1436
|
+
var container = React__default["default"].useRef(null);
|
|
1437
|
+
React__default["default"].useEffect(function () {
|
|
1438
|
+
var _container$current;
|
|
1439
|
+
if (((_container$current = container.current) === null || _container$current === void 0 ? void 0 : _container$current.childElementCount) === 0) {
|
|
1440
|
+
window.dispatchEvent(new CustomEvent("val-append-overlay"));
|
|
1441
|
+
}
|
|
1442
|
+
});
|
|
1443
|
+
var _React$useState13 = React__default["default"].useState(null),
|
|
1444
|
+
_React$useState14 = createForOfIteratorHelper._slicedToArray(_React$useState13, 2),
|
|
1445
|
+
dropZone = _React$useState14[0],
|
|
1446
|
+
setDropZone = _React$useState14[1];
|
|
1447
|
+
React__default["default"].useEffect(function () {
|
|
1448
|
+
var storedDropZone = localStorage.getItem("val-menu-drop-zone-default");
|
|
1449
|
+
if (storedDropZone) {
|
|
1450
|
+
setDropZone(storedDropZone);
|
|
1451
|
+
} else {
|
|
1452
|
+
setDropZone("val-menu-right-center");
|
|
1453
|
+
}
|
|
1454
|
+
}, []);
|
|
1455
|
+
useConfigStorageSave(props.config);
|
|
1456
|
+
var initTheme = React__default["default"].useMemo(function () {
|
|
1457
|
+
return initSessionTheme(props.config);
|
|
1458
|
+
}, [props.config]);
|
|
1459
|
+
var _React$useState15 = React__default["default"].useState(false),
|
|
1460
|
+
_React$useState16 = createForOfIteratorHelper._slicedToArray(_React$useState15, 2),
|
|
1461
|
+
spaLoaded = _React$useState16[0],
|
|
1462
|
+
setSpaLoaded = _React$useState16[1];
|
|
1463
|
+
React__default["default"].useEffect(function () {
|
|
1464
|
+
var listener = function listener() {
|
|
1465
|
+
setSpaLoaded(true);
|
|
1466
|
+
};
|
|
1467
|
+
window.addEventListener("val-ui-created", listener);
|
|
1468
|
+
return function () {
|
|
1469
|
+
window.removeEventListener("val-ui-created", listener);
|
|
1470
|
+
};
|
|
1471
|
+
}, []);
|
|
1472
|
+
// The pill is Val's chrome floating over the customer's page, so it takes
|
|
1473
|
+
// the float background rather than the studio's canvas.
|
|
1474
|
+
var _React$useMemo = React__default["default"].useMemo(function () {
|
|
1475
|
+
if (initTheme !== "light") {
|
|
1476
|
+
return [floatDarkBg, "white"];
|
|
1477
|
+
}
|
|
1478
|
+
return [floatLightBg, "black"];
|
|
1479
|
+
}, [initTheme]),
|
|
1480
|
+
_React$useMemo2 = createForOfIteratorHelper._slicedToArray(_React$useMemo, 2),
|
|
1481
|
+
backgroundColor = _React$useMemo2[0],
|
|
1482
|
+
textColor = _React$useMemo2[1];
|
|
1483
|
+
var commonStyles = React__default["default"].useMemo(function () {
|
|
1484
|
+
return {
|
|
1485
|
+
"backdrop-blur": "backdrop-filter: blur(10px);",
|
|
1486
|
+
"text-text-primary": "color: ".concat(textColor, ";"),
|
|
1487
|
+
"bg-bg-primary": "background: ".concat(backgroundColor, ";"),
|
|
1488
|
+
rounded: "border-radius: 0.25rem;",
|
|
1489
|
+
fixed: "position: fixed;",
|
|
1490
|
+
"bottom-4": "bottom: 1rem;",
|
|
1491
|
+
"right-12": "right: 3rem;",
|
|
1492
|
+
"right-16": "right: 4rem;",
|
|
1493
|
+
"p-4": "padding: 1rem;",
|
|
1494
|
+
"p-2": "padding: 0.5rem;",
|
|
1495
|
+
"p-1": "padding: 0.25rem;",
|
|
1496
|
+
flex: "display: flex;",
|
|
1497
|
+
"items-center": "align-items: center;",
|
|
1498
|
+
"justify-center": "justify-content: center;"
|
|
1499
|
+
};
|
|
1500
|
+
}, [valPrefixedClass]);
|
|
1501
|
+
return /*#__PURE__*/jsxRuntime.jsxs(ValOverlayContext.ValOverlayProvider, {
|
|
1502
|
+
draftMode: draftMode,
|
|
1503
|
+
draftModeReady: (_draftModeReady$curre2 = draftModeReady.current) === null || _draftModeReady$curre2 === void 0 ? void 0 : _draftModeReady$curre2.promise,
|
|
1504
|
+
draftSourcesSynced: draftSourcesSynced,
|
|
1505
|
+
suspend: suspendActive,
|
|
1506
|
+
store: valStore,
|
|
1507
|
+
children: [props.children, dropZone !== null && !spaLoaded && mountOverlay &&
|
|
1508
|
+
// The canvas frame loads no SPA, so its pill would spin for ever.
|
|
1509
|
+
!isCanvas && initTheme !== null && /*#__PURE__*/jsxRuntime.jsxs(React__default["default"].Fragment, {
|
|
1510
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("style", {
|
|
1511
|
+
children: "\n".concat(positionStyles, "\n").concat(prefixStyles(commonStyles), "\n@keyframes rotate-clock {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n")
|
|
1512
|
+
}), /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
1513
|
+
className: "".concat(getPositionClassName(dropZone), " ").concat(cn(["p-4"])),
|
|
1514
|
+
children: /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
1515
|
+
className: "".concat(cn(["flex", "justify-center", "items-center", "p-2"]), " ") + "".concat(cn(["text-text-primary", "bg-bg-primary", "rounded", "backdrop-blur"])),
|
|
1516
|
+
children: /*#__PURE__*/jsxRuntime.jsxs("svg", {
|
|
1517
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1518
|
+
width: "16",
|
|
1519
|
+
height: "16",
|
|
1520
|
+
viewBox: "0 0 24 24",
|
|
1521
|
+
fill: "none",
|
|
1522
|
+
stroke: "currentColor",
|
|
1523
|
+
strokeWidth: "2",
|
|
1524
|
+
strokeLinecap: "round",
|
|
1525
|
+
strokeLinejoin: "round",
|
|
1526
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("circle", {
|
|
1527
|
+
cx: "12",
|
|
1528
|
+
cy: "12",
|
|
1529
|
+
r: "11"
|
|
1530
|
+
}), /*#__PURE__*/jsxRuntime.jsx("line", {
|
|
1531
|
+
x1: "12",
|
|
1532
|
+
y1: "4",
|
|
1533
|
+
x2: "12",
|
|
1534
|
+
y2: "12",
|
|
1535
|
+
style: {
|
|
1536
|
+
transformOrigin: "center",
|
|
1537
|
+
animation: "rotate-clock 1000ms linear infinite"
|
|
1538
|
+
}
|
|
1539
|
+
}), /*#__PURE__*/jsxRuntime.jsx("line", {
|
|
1540
|
+
x1: "12",
|
|
1541
|
+
y1: "8",
|
|
1542
|
+
x2: "12",
|
|
1543
|
+
y2: "12",
|
|
1544
|
+
style: {
|
|
1545
|
+
transformOrigin: "center",
|
|
1546
|
+
animation: "rotate-clock 12000ms linear infinite"
|
|
1547
|
+
}
|
|
1548
|
+
})]
|
|
1549
|
+
})
|
|
1550
|
+
})
|
|
1551
|
+
})]
|
|
1552
|
+
}), mountOverlay && !isCanvas && /*#__PURE__*/jsxRuntime.jsxs(React__default["default"].Fragment, {
|
|
1553
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(ValScript, {
|
|
1554
|
+
src: "".concat(route, "/static").concat(ui.VERSION ? "/".concat(ui.VERSION) : "").concat(ui.VAL_APP_PATH)
|
|
1555
|
+
}), /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
1556
|
+
id: ui.VAL_OVERLAY_ID,
|
|
1557
|
+
ref: container
|
|
1558
|
+
})]
|
|
1559
|
+
}), mountOverlay && isCanvas && /*#__PURE__*/jsxRuntime.jsx(ValCanvasBridge, {
|
|
1560
|
+
draftMode: draftMode === true,
|
|
1561
|
+
isRefreshing: isRefreshing
|
|
1562
|
+
}), mountOverlay && iframeSrc && /*#__PURE__*/jsxRuntime.jsx("iframe", {
|
|
1563
|
+
loading: "eager",
|
|
1564
|
+
style: {
|
|
1565
|
+
top: 10,
|
|
1566
|
+
left: 10,
|
|
1567
|
+
position: "absolute",
|
|
1568
|
+
width: 1000,
|
|
1569
|
+
height: 1000,
|
|
1570
|
+
visibility: "hidden"
|
|
1571
|
+
},
|
|
1572
|
+
src: iframeSrc
|
|
1573
|
+
}, iframeSrc)]
|
|
1574
|
+
});
|
|
1575
|
+
};
|
|
1576
|
+
function useConsoleLogEnableVal(showOverlay) {
|
|
1577
|
+
React__default["default"].useEffect(function () {
|
|
1578
|
+
if (process.env["NODE_ENV"] === "development" && showOverlay === false && !isValStudioPath(location.pathname)) {
|
|
1579
|
+
console.warn("\n###########\n###########\n########### @@@@\n########### @@\n########### @@ @@ @@@@@@ @ @@\n########### @@ @@ @@ @@ @@\n########### @@ @@ %@ @ @@\n#### ##### @@ @@ .@ .@ @@\n### #### @@@@ @@: @@@. @@\n#### ##### @@@@ @@@@ =@@@@@@@@@\n###########\n\nThis page is built with Val Build - the lightweight CMS where content is code.\n\nVal is currently hidden.\n\nTo show Val, go to the following URL:\n".concat(window.location.origin, "/api/val/enable?redirect_to=").concat(encodeURIComponent(window.location.href), "\n \nYou are seeing this message because you are in development mode."));
|
|
1580
|
+
}
|
|
1581
|
+
}, [showOverlay]);
|
|
1582
|
+
}
|
|
1583
|
+
var positionStyles = prefixStyles({
|
|
1584
|
+
"left-0": "left: 0;",
|
|
1585
|
+
"top-0": "top: 0;",
|
|
1586
|
+
"left-1/2": "left: 50%;",
|
|
1587
|
+
"top-1/2": "top: 50%;",
|
|
1588
|
+
"-translate-y-1/2": "transform: translateY(-50%);",
|
|
1589
|
+
"-translate-x-1/2": "transform: translateX(-50%);",
|
|
1590
|
+
"right-0": "right: 0;",
|
|
1591
|
+
"bottom-0": "bottom: 0;"
|
|
1592
|
+
});
|
|
1593
|
+
|
|
1594
|
+
// This is a copy of the function from the ValMenu component.
|
|
1595
|
+
function getPositionClassName(dropZone) {
|
|
1596
|
+
var className = cn(["fixed", "transform"]);
|
|
1597
|
+
if (dropZone === "val-menu-left-top") {
|
|
1598
|
+
className += " ".concat(cn(["left-0", "top-0"]));
|
|
1599
|
+
} else if (dropZone === "val-menu-left-center") {
|
|
1600
|
+
className += " ".concat(cn(["left-0", "top-1/2", "-translate-y-1/2"]));
|
|
1601
|
+
} else if (dropZone === "val-menu-left-bottom") {
|
|
1602
|
+
className += " ".concat(cn(["left-0", "bottom-0"]));
|
|
1603
|
+
} else if (dropZone === "val-menu-center-top") {
|
|
1604
|
+
className += " ".concat(cn(["left-1/2", "-translate-x-1/2", "top-0"]));
|
|
1605
|
+
} else if (dropZone === "val-menu-center-bottom") {
|
|
1606
|
+
className += " ".concat(cn(["left-1/2", "-translate-x-1/2", "bottom-0"]));
|
|
1607
|
+
} else if (dropZone === "val-menu-right-top") {
|
|
1608
|
+
className += " ".concat(cn(["right-0", "top-0"]));
|
|
1609
|
+
} else if (dropZone === "val-menu-right-center") {
|
|
1610
|
+
className += " ".concat(cn(["right-0", "top-1/2", "-translate-y-1/2"]));
|
|
1611
|
+
} else if (dropZone === "val-menu-right-bottom") {
|
|
1612
|
+
className += " ".concat(cn(["right-0", "bottom-0"]));
|
|
1613
|
+
} else {
|
|
1614
|
+
className += " ".concat(cn(["right-0", "bottom-0"]));
|
|
1615
|
+
}
|
|
1616
|
+
return className;
|
|
1617
|
+
}
|
|
1618
|
+
function isValStudioPath(pathname) {
|
|
1619
|
+
return pathname.startsWith("/val");
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// function ValIcon() {
|
|
1623
|
+
// return (
|
|
1624
|
+
// <svg
|
|
1625
|
+
// width="32"
|
|
1626
|
+
// height="32"
|
|
1627
|
+
// viewBox="0 0 105 149"
|
|
1628
|
+
// fill="none"
|
|
1629
|
+
// xmlns="http://www.w3.org/2000/svg"
|
|
1630
|
+
// >
|
|
1631
|
+
// <g filter="url(#filter0_d_14_634)">
|
|
1632
|
+
// <path
|
|
1633
|
+
// d="M21.4768 23.3474C21.4768 22.4628 22.1939 21.7457 23.0785 21.7457H77.1357C78.0203 21.7457 78.7374 22.4628 78.7374 23.3474V125.055C78.7374 125.94 78.0203 126.657 77.1357 126.657H23.0785C22.1939 126.657 21.4768 125.94 21.4768 125.055V23.3474Z"
|
|
1634
|
+
// fill="#38CD98"
|
|
1635
|
+
// />
|
|
1636
|
+
// </g>
|
|
1637
|
+
// <g filter="url(#filter1_i_14_634)">
|
|
1638
|
+
// <circle cx="49.9068" cy="104.233" r="9.61017" fill="#1E1F2A" />
|
|
1639
|
+
// </g>
|
|
1640
|
+
// <defs>
|
|
1641
|
+
// <filter
|
|
1642
|
+
// id="filter0_d_14_634"
|
|
1643
|
+
// x="0.0397091"
|
|
1644
|
+
// y="0.30863"
|
|
1645
|
+
// width="100.135"
|
|
1646
|
+
// height="147.785"
|
|
1647
|
+
// filterUnits="userSpaceOnUse"
|
|
1648
|
+
// colorInterpolationFilters="sRGB"
|
|
1649
|
+
// >
|
|
1650
|
+
// <feFlood floodOpacity="0" result="BackgroundImageFix" />
|
|
1651
|
+
// <feColorMatrix
|
|
1652
|
+
// in="SourceAlpha"
|
|
1653
|
+
// type="matrix"
|
|
1654
|
+
// values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
1655
|
+
// result="hardAlpha"
|
|
1656
|
+
// />
|
|
1657
|
+
// <feOffset />
|
|
1658
|
+
// <feGaussianBlur stdDeviation="10.7185" />
|
|
1659
|
+
// <feComposite in2="hardAlpha" operator="out" />
|
|
1660
|
+
// <feColorMatrix
|
|
1661
|
+
// type="matrix"
|
|
1662
|
+
// values="0 0 0 0 0.219608 0 0 0 0 0.803922 0 0 0 0 0.501961 0 0 0 0.3 0"
|
|
1663
|
+
// />
|
|
1664
|
+
// <feBlend
|
|
1665
|
+
// mode="normal"
|
|
1666
|
+
// in2="BackgroundImageFix"
|
|
1667
|
+
// result="effect1_dropShadow_14_634"
|
|
1668
|
+
// />
|
|
1669
|
+
// <feBlend
|
|
1670
|
+
// mode="normal"
|
|
1671
|
+
// in="SourceGraphic"
|
|
1672
|
+
// in2="effect1_dropShadow_14_634"
|
|
1673
|
+
// result="shape"
|
|
1674
|
+
// />
|
|
1675
|
+
// </filter>
|
|
1676
|
+
// <filter
|
|
1677
|
+
// id="filter1_i_14_634"
|
|
1678
|
+
// x="40.2966"
|
|
1679
|
+
// y="94.6229"
|
|
1680
|
+
// width="19.2205"
|
|
1681
|
+
// height="19.2204"
|
|
1682
|
+
// filterUnits="userSpaceOnUse"
|
|
1683
|
+
// colorInterpolationFilters="sRGB"
|
|
1684
|
+
// >
|
|
1685
|
+
// <feFlood floodOpacity="0" result="BackgroundImageFix" />
|
|
1686
|
+
// <feBlend
|
|
1687
|
+
// mode="normal"
|
|
1688
|
+
// in="SourceGraphic"
|
|
1689
|
+
// in2="BackgroundImageFix"
|
|
1690
|
+
// result="shape"
|
|
1691
|
+
// />
|
|
1692
|
+
// <feColorMatrix
|
|
1693
|
+
// in="SourceAlpha"
|
|
1694
|
+
// type="matrix"
|
|
1695
|
+
// values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
|
|
1696
|
+
// result="hardAlpha"
|
|
1697
|
+
// />
|
|
1698
|
+
// <feOffset />
|
|
1699
|
+
// <feGaussianBlur stdDeviation="2.40254" />
|
|
1700
|
+
// <feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1" />
|
|
1701
|
+
// <feColorMatrix
|
|
1702
|
+
// type="matrix"
|
|
1703
|
+
// values="0 0 0 0 0.219608 0 0 0 0 0.803922 0 0 0 0 0.501961 0 0 0 0.3 0"
|
|
1704
|
+
// />
|
|
1705
|
+
// <feBlend
|
|
1706
|
+
// mode="normal"
|
|
1707
|
+
// in2="shape"
|
|
1708
|
+
// result="effect1_innerShadow_14_634"
|
|
1709
|
+
// />
|
|
1710
|
+
// </filter>
|
|
1711
|
+
// </defs>
|
|
1712
|
+
// </svg>
|
|
1713
|
+
// );
|
|
1714
|
+
// }
|
|
1715
|
+
|
|
1716
|
+
var ValProvider = ValTanStackProvider;
|
|
1717
|
+
|
|
1718
|
+
function _objectWithoutPropertiesLoose(r, e) {
|
|
1719
|
+
if (null == r) return {};
|
|
1720
|
+
var t = {};
|
|
1721
|
+
for (var n in r) if ({}.hasOwnProperty.call(r, n)) {
|
|
1722
|
+
if (-1 !== e.indexOf(n)) continue;
|
|
1723
|
+
t[n] = r[n];
|
|
1724
|
+
}
|
|
1725
|
+
return t;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
function _objectWithoutProperties(e, t) {
|
|
1729
|
+
if (null == e) return {};
|
|
1730
|
+
var o,
|
|
1731
|
+
r,
|
|
1732
|
+
i = _objectWithoutPropertiesLoose(e, t);
|
|
1733
|
+
if (Object.getOwnPropertySymbols) {
|
|
1734
|
+
var n = Object.getOwnPropertySymbols(e);
|
|
1735
|
+
for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]);
|
|
1736
|
+
}
|
|
1737
|
+
return i;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
var _excluded = ["src", "alt", "style", "width", "disableHotspot", "height"];
|
|
1741
|
+
function ValImage(props) {
|
|
1742
|
+
var src = props.src,
|
|
1743
|
+
alt = props.alt,
|
|
1744
|
+
style = props.style,
|
|
1745
|
+
width = props.width,
|
|
1746
|
+
disableHotspot = props.disableHotspot,
|
|
1747
|
+
height = props.height,
|
|
1748
|
+
rest = _objectWithoutProperties(props, _excluded);
|
|
1749
|
+
var valPathsOfUrl = src !== null && src !== void 0 && src.url ? decodeValPathsOfString(src.url) : undefined;
|
|
1750
|
+
var valPaths = valPathsOfUrl ? valPathsOfUrl : [];
|
|
1751
|
+
var maybeValPathOfAlt = alt ? decodeValPathsOfString(alt) : undefined;
|
|
1752
|
+
if (maybeValPathOfAlt) {
|
|
1753
|
+
var _iterator = createForOfIteratorHelper._createForOfIteratorHelper(maybeValPathOfAlt),
|
|
1754
|
+
_step;
|
|
1755
|
+
try {
|
|
1756
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
1757
|
+
var valPath = _step.value;
|
|
1758
|
+
valPaths.push(valPath);
|
|
1759
|
+
}
|
|
1760
|
+
} catch (err) {
|
|
1761
|
+
_iterator.e(err);
|
|
1762
|
+
} finally {
|
|
1763
|
+
_iterator.f();
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
var hotspot = src === null || src === void 0 ? void 0 : src.hotspot;
|
|
1767
|
+
var imageStyle = hotspot && !disableHotspot ? createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, style), {}, {
|
|
1768
|
+
objectPosition: "".concat(hotspot.x * 100, "% ").concat(hotspot.y * 100, "%")
|
|
1769
|
+
}) : style;
|
|
1770
|
+
// The authored dimensions are only used when the caller has not said
|
|
1771
|
+
// otherwise: an explicit width/height is the caller sizing the element, and
|
|
1772
|
+
// overriding it with the file's own size would fight them.
|
|
1773
|
+
var preferMetadataDims = ((src === null || src === void 0 ? void 0 : src.width) !== undefined || (src === null || src === void 0 ? void 0 : src.height) !== undefined) && !width && !height;
|
|
1774
|
+
return /*#__PURE__*/jsxRuntime.jsx("img", createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, rest), {}, {
|
|
1775
|
+
src: valPathsOfUrl && valPathsOfUrl.length > 0 ? stega.raw(src === null || src === void 0 ? void 0 : src.url) : src === null || src === void 0 ? void 0 : src.url,
|
|
1776
|
+
"data-val-path": valPaths.join(","),
|
|
1777
|
+
"data-val-attr-alt": maybeValPathOfAlt,
|
|
1778
|
+
"data-val-attr-src": valPathsOfUrl && valPathsOfUrl.length > 0 ? valPathsOfUrl.join(",") : undefined,
|
|
1779
|
+
style: imageStyle,
|
|
1780
|
+
alt: alt ? stega.raw(alt) : src !== null && src !== void 0 && src.alt ? stega.raw(src === null || src === void 0 ? void 0 : src.alt) : "",
|
|
1781
|
+
width: preferMetadataDims ? src === null || src === void 0 ? void 0 : src.width : width,
|
|
1782
|
+
height: preferMetadataDims ? src === null || src === void 0 ? void 0 : src.height : height
|
|
1783
|
+
}));
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
var VAL_READY_PING_MS = 100;
|
|
1787
|
+
var ValApp = function ValApp(_ref) {
|
|
1788
|
+
var config = _ref.config,
|
|
1789
|
+
children = _ref.children;
|
|
1790
|
+
var route = "/api/val"; // TODO: make configurable
|
|
1791
|
+
var _useState = React.useState(),
|
|
1792
|
+
_useState2 = createForOfIteratorHelper._slicedToArray(_useState, 2),
|
|
1793
|
+
inMessageMode = _useState2[0],
|
|
1794
|
+
setInMessageMode = _useState2[1];
|
|
1795
|
+
var isClientSIde = inMessageMode === undefined;
|
|
1796
|
+
useConfigStorageSave(config);
|
|
1797
|
+
var container = React.useRef(null);
|
|
1798
|
+
React.useEffect(function () {
|
|
1799
|
+
if (location.search === "?message_onready=true") {
|
|
1800
|
+
setInMessageMode(true);
|
|
1801
|
+
var interval = setInterval(function () {
|
|
1802
|
+
window.parent.postMessage({
|
|
1803
|
+
type: "val-ready"
|
|
1804
|
+
}, "*");
|
|
1805
|
+
}, VAL_READY_PING_MS);
|
|
1806
|
+
return function () {
|
|
1807
|
+
clearInterval(interval);
|
|
1808
|
+
};
|
|
1809
|
+
} else {
|
|
1810
|
+
setInMessageMode(false);
|
|
1811
|
+
}
|
|
1812
|
+
}, []);
|
|
1813
|
+
React.useEffect(function () {
|
|
1814
|
+
var _container$current;
|
|
1815
|
+
if (((_container$current = container.current) === null || _container$current === void 0 ? void 0 : _container$current.childElementCount) === 0) {
|
|
1816
|
+
window.dispatchEvent(new CustomEvent("val-append-studio"));
|
|
1817
|
+
}
|
|
1818
|
+
});
|
|
1819
|
+
|
|
1820
|
+
// this theme is used to avoid flickering
|
|
1821
|
+
var _useState3 = React.useState(config.defaultTheme || null),
|
|
1822
|
+
_useState4 = createForOfIteratorHelper._slicedToArray(_useState3, 2),
|
|
1823
|
+
loadingTheme = _useState4[0],
|
|
1824
|
+
setLoadingTheme = _useState4[1];
|
|
1825
|
+
React.useEffect(function () {
|
|
1826
|
+
var theme = localStorage.getItem("val-theme-" + ((config === null || config === void 0 ? void 0 : config.project) || "unknown"));
|
|
1827
|
+
if (theme === "dark") {
|
|
1828
|
+
setLoadingTheme("dark");
|
|
1829
|
+
} else if (theme === "light") {
|
|
1830
|
+
setLoadingTheme("light");
|
|
1831
|
+
} else if (config.defaultTheme) {
|
|
1832
|
+
setLoadingTheme(config.defaultTheme);
|
|
1833
|
+
}
|
|
1834
|
+
}, [config]);
|
|
1835
|
+
// The studio's canvas, so the loading screen is the same colour as what
|
|
1836
|
+
// replaces it rather than a flash of a different one.
|
|
1837
|
+
var darkBg = canvasDarkBg;
|
|
1838
|
+
var lightBg = canvasLightBg;
|
|
1839
|
+
React.useEffect(function () {
|
|
1840
|
+
if (inMessageMode || loadingTheme === null) {
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
var body = document.body;
|
|
1844
|
+
var prevBodyBg = body.style.backgroundColor;
|
|
1845
|
+
var prevBodyMinHeight = body.style.minHeight;
|
|
1846
|
+
var prevBodyMinWidth = body.style.minWidth;
|
|
1847
|
+
body.style.backgroundColor = loadingTheme === "dark" ? darkBg : lightBg;
|
|
1848
|
+
body.style.minHeight = "100vh";
|
|
1849
|
+
body.style.minWidth = "100%";
|
|
1850
|
+
// A stable reference: `removeEventListener` compares by identity, so a
|
|
1851
|
+
// second arrow function removes nothing and every re-run of this effect
|
|
1852
|
+
// used to leave another listener behind.
|
|
1853
|
+
var onCssLoaded = function onCssLoaded() {
|
|
1854
|
+
// css was loaded, has been loaded, so let app decide what to do
|
|
1855
|
+
setLoadingTheme(null);
|
|
1856
|
+
};
|
|
1857
|
+
window.addEventListener("val-css-loaded", onCssLoaded);
|
|
1858
|
+
return function () {
|
|
1859
|
+
body.style.backgroundColor = prevBodyBg;
|
|
1860
|
+
body.style.minHeight = prevBodyMinHeight;
|
|
1861
|
+
body.style.minWidth = prevBodyMinWidth;
|
|
1862
|
+
window.removeEventListener("val-css-loaded", onCssLoaded);
|
|
1863
|
+
};
|
|
1864
|
+
}, [inMessageMode, loadingTheme]);
|
|
1865
|
+
if (loadingTheme !== null && isClientSIde) {
|
|
1866
|
+
return /*#__PURE__*/jsxRuntime.jsxs("div", {
|
|
1867
|
+
style: {
|
|
1868
|
+
color: loadingTheme === "dark" ? "white" : "black",
|
|
1869
|
+
display: "flex",
|
|
1870
|
+
justifyContent: "center",
|
|
1871
|
+
alignItems: "center",
|
|
1872
|
+
padding: "1rem",
|
|
1873
|
+
backgroundColor: loadingTheme === "dark" ? darkBg : lightBg,
|
|
1874
|
+
minHeight: "100vh",
|
|
1875
|
+
minWidth: "100%"
|
|
1876
|
+
},
|
|
1877
|
+
children: [/*#__PURE__*/jsxRuntime.jsx("style", {
|
|
1878
|
+
children: ".".concat(valPrefixedClass, "animate-spin {\n animation: ").concat(valPrefixedClass, "spin 2s linear infinite;\n}\n@keyframes ").concat(valPrefixedClass, "spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}")
|
|
1879
|
+
}), /*#__PURE__*/jsxRuntime.jsx("svg", {
|
|
1880
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1881
|
+
width: "24",
|
|
1882
|
+
height: "24",
|
|
1883
|
+
viewBox: "0 0 24 24",
|
|
1884
|
+
fill: "none",
|
|
1885
|
+
stroke: "currentColor",
|
|
1886
|
+
strokeWidth: "2",
|
|
1887
|
+
strokeLinecap: "round",
|
|
1888
|
+
strokeLinejoin: "round",
|
|
1889
|
+
className: cn(["animate-spin"]),
|
|
1890
|
+
children: /*#__PURE__*/jsxRuntime.jsx("path", {
|
|
1891
|
+
d: "M21 12a9 9 0 1 1-6.219-8.56"
|
|
1892
|
+
})
|
|
1893
|
+
})]
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
if (inMessageMode) {
|
|
1897
|
+
return /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
1898
|
+
children: "Val Studio is disabled: in message mode"
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1901
|
+
return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
1902
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(ValScript, {
|
|
1903
|
+
src: "".concat(route, "/static").concat(ui.VERSION ? "/".concat(ui.VERSION) : "").concat(ui.VAL_APP_PATH)
|
|
1904
|
+
}), /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
1905
|
+
id: ui.VAL_APP_ID,
|
|
1906
|
+
ref: container
|
|
1907
|
+
}), children]
|
|
1908
|
+
});
|
|
1909
|
+
};
|
|
1910
|
+
|
|
1911
|
+
// Keep in sync with VAL_MODULES_UPDATED_EVENT in @valbuild/ui (useValModules.ts).
|
|
1912
|
+
var VAL_MODULES_UPDATED_EVENT = "val-modules-updated";
|
|
1913
|
+
|
|
1914
|
+
/**
|
|
1915
|
+
* Registers the user's `val.modules` registry on `window.__VAL_MODULES__` so
|
|
1916
|
+
* the Val editor SPA (both the `/val` app and the on-page overlay) can read it.
|
|
1917
|
+
*
|
|
1918
|
+
* Must be called from a component that statically imports `val.modules`: the
|
|
1919
|
+
* module `def` entries are function closures, so the registry cannot be
|
|
1920
|
+
* serialized through a loader and has to be pulled into the client bundle and
|
|
1921
|
+
* registered via the window global instead.
|
|
1922
|
+
*
|
|
1923
|
+
* Re-runs when the `modules` reference changes (e.g. on HMR) and dispatches
|
|
1924
|
+
* `val-modules-updated` so the SPA picks up the new registry.
|
|
1925
|
+
*/
|
|
1926
|
+
function useRegisterValModules(modules) {
|
|
1927
|
+
React.useEffect(function () {
|
|
1928
|
+
window.__VAL_MODULES__ = modules;
|
|
1929
|
+
window.dispatchEvent(new CustomEvent(VAL_MODULES_UPDATED_EVENT));
|
|
1930
|
+
}, [modules]);
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
/**
|
|
1934
|
+
* Registers the user's `val.modules` registry for the Val editor SPA.
|
|
1935
|
+
*
|
|
1936
|
+
* Render this inside both `<ValProvider>` (the root route) and `<ValApp>` (the
|
|
1937
|
+
* `/val` route), passing your `val.modules` default export:
|
|
1938
|
+
*
|
|
1939
|
+
* @example
|
|
1940
|
+
* import { ValModulesClient } from "@valbuild/tanstack";
|
|
1941
|
+
* import valModules from "../val.modules";
|
|
1942
|
+
*
|
|
1943
|
+
* <ValModulesClient modules={valModules} />
|
|
1944
|
+
*/
|
|
1945
|
+
function ValModulesClient(_ref) {
|
|
1946
|
+
var modules = _ref.modules;
|
|
1947
|
+
useRegisterValModules(modules);
|
|
1948
|
+
return null;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
// NOTE! Side effects:
|
|
1952
|
+
stega.autoTagJSX();
|
|
1953
|
+
|
|
1954
|
+
// Convenience types
|
|
1955
|
+
|
|
1956
|
+
/**
|
|
1957
|
+
* Annotated rather than inferred.
|
|
1958
|
+
*
|
|
1959
|
+
* The inferred type of the spread below reaches into @valbuild/core's internal
|
|
1960
|
+
* declaration files for names like `NonEmptyArray` and `createValPathOfItem`,
|
|
1961
|
+
* which the declaration emitter cannot write down portably: `preconstruct
|
|
1962
|
+
* build` fails with a wall of TS2883 "the inferred type of 'Internal' cannot be
|
|
1963
|
+
* named without a reference to ..." while `tsc --noEmit` stays happy, so it only
|
|
1964
|
+
* shows up in the build job. Naming the type in terms of `typeof InternalCore`
|
|
1965
|
+
* keeps everything the emitter needs reachable through the package entry point.
|
|
1966
|
+
*/
|
|
1967
|
+
|
|
1968
|
+
var Internal = createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, core.Internal), {}, {
|
|
1969
|
+
VERSION: createForOfIteratorHelper._objectSpread2(createForOfIteratorHelper._objectSpread2({}, core.Internal.VERSION), {}, {
|
|
1970
|
+
tanstack: version.VERSION
|
|
1971
|
+
})
|
|
1972
|
+
});
|
|
1973
|
+
|
|
1974
|
+
Object.defineProperty(exports, 'GenericSelector', {
|
|
1975
|
+
enumerable: true,
|
|
1976
|
+
get: function () { return core.GenericSelector; }
|
|
1977
|
+
});
|
|
1978
|
+
Object.defineProperty(exports, 'Schema', {
|
|
1979
|
+
enumerable: true,
|
|
1980
|
+
get: function () { return core.Schema; }
|
|
1981
|
+
});
|
|
1982
|
+
Object.defineProperty(exports, 'VAL_EXTENSION', {
|
|
1983
|
+
enumerable: true,
|
|
1984
|
+
get: function () { return core.VAL_EXTENSION; }
|
|
1985
|
+
});
|
|
1986
|
+
Object.defineProperty(exports, 'derefPatch', {
|
|
1987
|
+
enumerable: true,
|
|
1988
|
+
get: function () { return core.derefPatch; }
|
|
1989
|
+
});
|
|
1990
|
+
exports.expr = core__namespace;
|
|
1991
|
+
Object.defineProperty(exports, 'modules', {
|
|
1992
|
+
enumerable: true,
|
|
1993
|
+
get: function () { return core.modules; }
|
|
1994
|
+
});
|
|
1995
|
+
Object.defineProperty(exports, 'ValRichText', {
|
|
1996
|
+
enumerable: true,
|
|
1997
|
+
get: function () { return internal.ValRichText; }
|
|
1998
|
+
});
|
|
1999
|
+
exports.Internal = Internal;
|
|
2000
|
+
exports.ValApp = ValApp;
|
|
2001
|
+
exports.ValImage = ValImage;
|
|
2002
|
+
exports.ValModulesClient = ValModulesClient;
|
|
2003
|
+
exports.ValProvider = ValProvider;
|
|
2004
|
+
exports.initVal = initVal;
|
|
2005
|
+
exports.useRegisterValModules = useRegisterValModules;
|